NDK系列02——指針的學(xué)習(xí)


????這是NDK系列的第二章,將會(huì)學(xué)習(xí) 內(nèi)存地址與指針 相關(guān)的知識(shí)。主要分為以下幾個(gè)內(nèi)容:

  • C語(yǔ)言中的基本數(shù)據(jù)類(lèi)型
  • C語(yǔ)言中的基本函數(shù)
  • 內(nèi)存地址與指針
  • 數(shù)組與數(shù)組指針
  • 數(shù)組指針操作的幾種方式

一、C 中的基本數(shù)據(jù)類(lèi)型

????學(xué)習(xí)一門(mén)新的語(yǔ)言都繞不過(guò)基本的數(shù)據(jù)類(lèi)型,跟 Java 類(lèi)似,Java 中有的基本數(shù)據(jù)類(lèi)型,C 中也基本都有。下面用一個(gè)例子來(lái)說(shuō)明:

#include <stdio.h>

int main() {
    printf("Hello, World!\n");

    int intNum = 100;
    float floatNum = 150;
    double doubleNum = 200;
    long longNum  = 3;
    char charNum = 'c';

    printf("intNum 的值為: %d\n", intNum);
    printf("floatNum 的值為: %f\n", floatNum);
    printf("doubleNum 的值為: %lf\n", doubleNum);
    printf("longNum 的值為: %ld\n", longNum);
    printf("charNum 的值為: %c\n", charNum);

    return 0;
}

以下是運(yùn)行結(jié)果:

Hello, World!
intNum 的值為: 100
floatNum 的值為: 150.000000
doubleNum 的值為: 200.000000
longNum 的值為: 3
charNum 的值為: c
Process finished with exit code 0

以上列舉出來(lái)的就是日常經(jīng)常用的基本數(shù)據(jù)類(lèi)型,跟在 Java 中的基本數(shù)據(jù)類(lèi)型基本一致,較為特殊的是字符串,這點(diǎn)在后面的文章再慢慢介紹。現(xiàn)在我們只需知道:

  • 單個(gè)字符用 char 聲明,同時(shí)用單引號(hào) ' ' 包裹
  • 字符串時(shí)用 char* 聲明,同時(shí)用雙引號(hào) " " 包裹
    同時(shí)從打印的日志中可以看到引用時(shí)變量時(shí)需要使用占位符,這一點(diǎn)也是跟 Java 不一樣的點(diǎn),不過(guò)我相信各位多敲幾遍肯定就會(huì)熟悉了的。

二、C語(yǔ)言中的基本函數(shù)

2.1 函數(shù)使用方式:

????Java 中有一個(gè)個(gè)的方法相對(duì)應(yīng)的 C 語(yǔ)言就是一個(gè)一個(gè)的 函數(shù)。在 Java 中 方法可以寫(xiě)在類(lèi)中的任意地方,只要是在類(lèi)里面即可。而 C 語(yǔ)言不同的是,C 語(yǔ)言的函數(shù)必須寫(xiě)在主函數(shù)之前,或者跟聲明頭文件類(lèi)似,需提前聲明才可以在主函數(shù)中調(diào)用。接下來(lái)用一個(gè)例子來(lái)說(shuō)明一下:

//第一種寫(xiě)法:

#include <stdio.h>

void sampleMethod() {
    printf("我是一個(gè) C 函數(shù),快來(lái)調(diào)用我吧");
}

int main() {

    sampleMethod();

    return 0;
}
=================華麗的分割線=======================
// 第二種寫(xiě)法
#include <stdio.h>
void sampleMethod();

int main() {

    sampleMethod();

    return 0;
}

void sampleMethod() {
    printf("我是一個(gè) C 函數(shù),快來(lái)調(diào)用我吧");
}

運(yùn)行結(jié)果:

我是一個(gè) C 函數(shù),快來(lái)調(diào)用我吧
Process finished with exit code 0

可以看到這兩種方式都可以正確運(yùn)行,都是 C 語(yǔ)言中使用函數(shù)的方式。

2.2 函數(shù)的傳參

上面兩個(gè)demo 中都是沒(méi)有傳參的函數(shù),參考 Java 中的傳參方法,C 語(yǔ)言中的傳參函數(shù)也是在函數(shù)名的括號(hào)內(nèi)進(jìn)行傳參。

void sampleMethod(int a, int b) {
    printf("我是一個(gè) C 函數(shù),快來(lái)調(diào)用我吧");
}

三、內(nèi)存地址與指針

3.1 簡(jiǎn)介

????所謂內(nèi)存地址指的就是一個(gè)對(duì)象在內(nèi)存中開(kāi)辟的地址,在 Java 的學(xué)習(xí)中,會(huì)經(jīng)常遇到一個(gè)簡(jiǎn)單的需求:向一個(gè)方法中傳入兩個(gè)數(shù),在方法中對(duì)這兩個(gè)數(shù)進(jìn)行進(jìn)行交換。
那么遇到這個(gè)需求我們的思路:

  • 1 定義一個(gè)臨時(shí)變量
  • 2 將其中一個(gè)值賦值給臨時(shí)變量
  • 3 將另一個(gè)值賦值給已賦值的變量
  • 4 最后將臨時(shí)變量賦值給第二個(gè)值。
    看完可能有點(diǎn)懵,直接上代碼就清楚了:
//交換兩個(gè)數(shù)
public void swap(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
}

那么在 C 語(yǔ)言中當(dāng)我們也需要對(duì)兩個(gè)數(shù)進(jìn)行交換時(shí),采用相同的方法,能否達(dá)到我們的要求呢?用一個(gè)demo來(lái)試驗(yàn)一下:

#include <stdio.h>

void testSwap(int a, int b) {
    printf("a 的值為:%d\n", a);
    printf("b 的值為:%d\n", b);
    int temp = a;
    a = b;
    b = temp;
}

int main() {

  testSwap(10, 20);
  printf("a 的值為:%d\n", a);
  printf("b 的值為:%d\n", b);

  return 0;
}

運(yùn)行結(jié)果為:

a 的值為:10
b 的值為:20
交換結(jié)束后 a 的值為:10
交換結(jié)束后 b 的值為:20

Process finished with exit code 0

從運(yùn)行結(jié)果可以看出,上面這種方法沒(méi)有把 a 和 b 兩個(gè)值進(jìn)行交換,這是為什么呢?這就要請(qǐng)出今天的重頭戲了——內(nèi)存地址。
上面已經(jīng)說(shuō)過(guò)內(nèi)存地址是一個(gè)對(duì)象在內(nèi)存中開(kāi)辟的地址,在上面的 demo 中,傳進(jìn)去的只是 a 和 b 的值,而其內(nèi)存地址對(duì)應(yīng)的值并未發(fā)生改變。所以我們應(yīng)該要傳的是 a 和 b 對(duì)應(yīng)的內(nèi)存地址,通過(guò)內(nèi)存地址交換對(duì)應(yīng)的值,這才是正確做法:

#include <stdio.h>

void swap(int* a, int* b) {
    printf("a 的值為:%d\n", *a);
    printf("b 的值為:%d\n", *b);
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main() {

  int a = 10;
  int b = 20;
  swap(&a, &b);
  printf("a 的值為:%d\n", a);
  printf("b 的值為:%d\n", b);

  return 0;
}

運(yùn)行結(jié)果為:

a 的值為:10
b 的值為:20
交換結(jié)束后 a 的值為:20
交換結(jié)束后 b 的值為:10

Process finished with exit code 0

從上面的 demo 中可以看到 a 與 b 已經(jīng)成功進(jìn)行交換。因此,有如下結(jié)論:

  • 在函數(shù)傳參中的類(lèi)型后面加 * 代表該參數(shù)傳入的是一個(gè)地址
  • 通過(guò) & 符號(hào)獲取對(duì)象其內(nèi)存地址
  • 通過(guò) * 獲取該內(nèi)存地址對(duì)應(yīng)的值
3.2多級(jí)指針

????了解完指針與內(nèi)存地址,我們來(lái)學(xué)習(xí)多一個(gè)概念 多級(jí)指針,上面我們看到的一個(gè)對(duì)象對(duì)應(yīng)一個(gè)內(nèi)存地址,那么內(nèi)存地址又對(duì)應(yīng)有一個(gè)內(nèi)存地址(聽(tīng)起來(lái)好像套娃)用一張圖來(lái)表示就是:

多級(jí)指針

在 C 語(yǔ)言中,所謂的多級(jí)指針最多只有3級(jí),用下面的demo來(lái)說(shuō)明一下:

int main() {
    /**
     * 多級(jí)指針
     */
    int num = 999;
    int *num_p = &num;
    printf("num 的值為:%d\n", num);
    printf("num 的地址為:%p\n", &num);
    printf("num_p 的值為:%p\n", num_p);
    printf("num 地址對(duì)應(yīng)的值為:%d\n", *num_p);
    //二級(jí)指針
    int **num_pp = &num_p;
    printf("num_pp (二級(jí)指針)的值為:%p\n", num_pp);
    //三級(jí)指針
    int *** num_ppp = &num_pp;
    printf("num_ppp (三級(jí)指針)的值為:%p\n", num_ppp);

    return 0;
}

打印結(jié)果為:

num 的值為:999
num 的地址為:000000000061FE00
num_p 的值為:000000000061FE00
num 地址對(duì)應(yīng)的值為:999
num_pp (二級(jí)指針)的值為:000000000061FDF8
num_ppp (三級(jí)指針)的值為:000000000061FDF0

可以看到如果是二級(jí)指針時(shí),則在 變量前 加多一個(gè) * 。
前面說(shuō)過(guò)可以根據(jù)地址拿到對(duì)應(yīng)地址的值。那么對(duì)于多級(jí)指針類(lèi)似地也可以得到對(duì)應(yīng)的值:

int **back = *num_ppp;
printf("二級(jí)指針num_pp 的值為:%p\n", back);

int *back3 = **num_ppp;
printf("num_p 的值為:%p\n", back3);

int back4 = ***num_ppp;
printf("num 的值為:%d\n", back4);

打印的結(jié)果為:

二級(jí)指針num_pp 的值為:000000000061FDF8
num_p 的值為:000000000061FE00
num 的值為:999

可以看出,與上面的多級(jí)取址后的值是一致的,因此當(dāng):

  1. 需要拿二級(jí)指針時(shí)需要對(duì)三級(jí)指針取一次地址,用 ** 接收:int **back = *num_ppp;
  2. 需要拿一級(jí)指針時(shí)需要對(duì)三級(jí)指針取二次地址,用 * 接收:int *back3 = **num_ppp;
  3. 需要拿一級(jí)指針對(duì)應(yīng)的值時(shí)需要對(duì)三級(jí)指針取三次地址:int back4 = ***num_ppp;

四、數(shù)組與數(shù)組指針

4.1數(shù)組 & 數(shù)組指針

????在 C 語(yǔ)言中,可以認(rèn)為 數(shù)組就是指針,指針就是數(shù)組,去閱讀一些外國(guó)大佬寫(xiě)的源碼可以發(fā)現(xiàn),通常用指針去聲明個(gè)數(shù)組。下面用個(gè)簡(jiǎn)單的demo來(lái)說(shuō)明:

int main() {

    /**
     * 數(shù)組與數(shù)組指針
     */

    int arr[] = {1, 2, 3, 4};
    printf("arr 的值為:%p\n", arr);
    printf("arr地址 的值為:%p\n", &arr);
    printf("arr首元素的地址 的值為:%p\n", &arr[0]);

    /**
     * 所以可以看出 數(shù)組 == 內(nèi)存地址
     */
    int *arr_p = arr;
    printf("arr_p 的值為:%p\n", arr_p);
    //數(shù)組第一個(gè)元素
    printf("arr_p 對(duì)應(yīng)的值為:%d\n", *arr_p);

return 0;
}

打印的日志為:

arr 的值為:000000000061FDE0
arr地址 的值為:000000000061FDE0
arr首元素的地址 的值為:000000000061FDE0
arr_p 的值為:000000000061FDE0
arr_p 對(duì)應(yīng)的值為:1

因此作為初學(xué)者,我們可以簡(jiǎn)單的認(rèn)為:

  • 數(shù)組的值 = 數(shù)組的地址 = 數(shù)組首元素的地址
  • 由于可以用指針去接收這個(gè)數(shù)組,因此 指針 = 內(nèi)存地址
4.2 指針移動(dòng)

那么既然可以用指針去接收這個(gè)數(shù)組,那么當(dāng)指針挪動(dòng)時(shí),獲取到的值也就是這個(gè)數(shù)組相對(duì)應(yīng)的值:

     //指針移動(dòng)
    arr_p++;
    //數(shù)組第二個(gè)元素
    printf("arr_p 對(duì)應(yīng)的值為:%d\n", *arr_p);
    //指針挪動(dòng)2
    arr_p += 2;//挪動(dòng)指針指向4
    printf("arr_p 對(duì)應(yīng)的值為:%d\n", *arr_p);
    //挪動(dòng)指針指向1
    arr_p -= 3;
    printf("arr_p 對(duì)應(yīng)的值為:%d\n", *arr_p);

打印的結(jié)果為:

arr_p 對(duì)應(yīng)的值為:2
arr_p 對(duì)應(yīng)的值為:4
arr_p 對(duì)應(yīng)的值為:1

在 Java 中對(duì)于這個(gè)數(shù)組的長(zhǎng)度我們都知道是4,那在 C 語(yǔ)言中這個(gè)數(shù)組的長(zhǎng)度是多少呢?在C語(yǔ)言中,想要獲取 數(shù)組的長(zhǎng)度需要用到 sizeof 這個(gè) api,這里我們打印一下 上面的數(shù)組(arr 以及 int 類(lèi)型)的長(zhǎng)度是多少:

    printf("sizeof arr 的值為:%llu\n", sizeof arr);
    printf("sizeof int 的值為:%llu\n", sizeof(int));

打印的結(jié)果為:

sizeof arr 的值為:16
sizeof int 的值為:4

從打印的日志可以看到 arr 數(shù)組的長(zhǎng)度為 16,int 類(lèi)型的長(zhǎng)度為 4。 這是為什么呢?這是因?yàn)?arr 是個(gè)int 類(lèi)型的數(shù)組,因此 長(zhǎng)度是 arr 長(zhǎng)度 = arr數(shù)組 長(zhǎng)度 x 類(lèi)型長(zhǎng)度 = 4 x 4 = 16。

4.3 數(shù)組 & 遍歷數(shù)組

那么如何通過(guò)指針遍歷數(shù)組呢?我們來(lái)看下面這個(gè)demo:

/**
     * 通過(guò)指針遍歷數(shù)組
     */
    //其他平臺(tái)較嚴(yán)格
    // int i = 0;
    for (int i = 0; i < sizeof arr / sizeof(int); ++i) {
        printf("數(shù)組下標(biāo)為 %d 的值為:%d\n", i, *(arr + i));
        /**
         * 從這里可以看到由于是 int 型數(shù)組,int 的內(nèi)存大小為4.因此地址都是挪動(dòng)4個(gè)位置
         *
         */
        printf("數(shù)組下標(biāo)為 %d 的地址為:%p\n", i, (arr + i));
    }

注意這里,其他編譯器(visiion studio)比較嚴(yán)格的話,不能同上面代碼一樣,需要把 int i = 0; 這一句單獨(dú)寫(xiě)出來(lái)。
打印的結(jié)果為:

數(shù)組下標(biāo)為 0 的值為:1
數(shù)組下標(biāo)為 0 的地址為:000000000061FDE0
數(shù)組下標(biāo)為 1 的值為:2
數(shù)組下標(biāo)為 1 的地址為:000000000061FDE4
數(shù)組下標(biāo)為 2 的值為:3
數(shù)組下標(biāo)為 2 的地址為:000000000061FDE8
數(shù)組下標(biāo)為 3 的值為:4
數(shù)組下標(biāo)為 3 的地址為:000000000061FDEC
4.4 循環(huán)給數(shù)組賦值
    for (int i = 0; i < sizeof arr / sizeof(int); ++i) {
//        * &(arr[i]) = i+100;
//        arr[i] = i+100
        *(arr_p + i) = (i + 200);
        printf("數(shù)組下標(biāo)為 %d 的值重新賦值為:%d\n", i, arr[i]);
    }

打印的日志為:

數(shù)組下標(biāo)為 0 的值重新賦值為:200
數(shù)組下標(biāo)為 1 的值重新賦值為:201
數(shù)組下標(biāo)為 2 的值重新賦值為:202
數(shù)組下標(biāo)為 3 的值重新賦值為:203

在這個(gè)demo中注釋的兩行也是可以實(shí)現(xiàn)重新賦值的方式,在上面三種方式任選其一都是可以的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容