中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C之指針與數組組合(二十六)

發布時間:2020-07-28 23:55:16 來源:網絡 閱讀:615 作者:上帝之子521 欄目:編程語言

        我們在前面講到數組的本質是一段連續的內存空間,那么它的大小為 sizeof(array_type) * array_size,同時數組名可看做指向數組第一個元素的常量指針。那么問題來了,數組 a + 1 的意義是什么呢?結果又是怎樣呢?指針運算的意義又是什么?結果呢?下來我們看個示例代碼,代碼如下

#include <stdio.h>

int main()
{
    int a[5] = {0};
    int* p = NULL;
    
    printf("a = 0x%X\n", (unsigned int)(a));
    printf("a + 1 = 0x%X\n", (unsigned int)(a + 1));
    
    printf("p = 0x%X\n", (unsigned int)(p));
    printf("p + 1 = 0x%X\n", (unsigned int)(p + 1));
    
    return 0;
}

        編譯結果如下

C之指針與數組組合(二十六)

        我們看到數組 a 相當于一個常量指針,而它便指向的首元素的地址,a + 1 便是首元素的地址加 4,也就是數組第二個元素的地址。因為指針 p int 型,所以  p + 1 相當于加 4。

        指針是一種特殊的變量,它與整數的運算規則為 p + n <==> (unsigned int)p + n*sizeof(*p);那么便是當指針指向同一類型的數組的元素時:p + 1 將指向當前元素的下一個元素;p - 1 將指向當前元素的上一個元素。指針之間只支持減法運算,并且參與減法運算的指針類型必須相同。p1 - p2 <==> ((unsigned int)p1 - (unsigned int)p2)/sizeof(type);注意:a> 只有當兩個指針指向同一個數組中的元素時,指針相減才有意義,其意義為指針所指元素的下標差;b> 當兩個指針指向的元素不在同一個數組中時,結果為定義

        指針也可以進行關系運算(<, <=, >, >=),指針關系運算的前提是同時指向同一個數組中的元素;任意兩個指針之間的比較運算(==,!=),參與比較運算的指針類型必須相同。

        下來我們來看個示例代碼,代碼如下

#include <stdio.h>

#define DIM(a) (sizeof(a) / sizeof(*a))

int main()
{
    char s[] = {'H', 'e', 'l', 'l', 'o'};
    char* pBegin = s;
    char* pEnd = s + DIM(s); // Key point
    char* p = NULL;
    
    printf("pBegin = %p\n", pBegin);
    printf("pEnd = %p\n", pEnd);
    
    printf("Size: %d\n", pEnd - pBegin);
    
    for(p=pBegin; p<pEnd; p++)
    {
        printf("%c", *p);
    }
    
    printf("\n");
   
    return 0;
}

        我們在第3行定義的宏是求這個數組元素的個數,在第9行定義的指針 pEnd 為數組首元素的地址加上數組元素個數,那么它剛好指向數組最后一個元素的臨界。這是 C 語言中的灰色地帶,在 C 語言中是合法的。我們來看看編譯結果

C之指針與數組組合(二十六)

        我們看到結果是如我們所想的那,因為是 char 類型的數組,所以 pEnd = pBegin + 5。

        數組名可以當做常量指針使用,那么指針是否也可以當做數組名來使用呢?我們往后接著說,在數組中的訪問方式有兩種:1、以下標的形式訪問數組中的元素;2、以指針的形式訪問數組中的元素。那么這兩種方式有何區別呢?當指針以固定增量在數組中移動時,效率要高于下標形式。尤其是指針增量為 1 且硬件具有硬件增量模型時效率更高。下標形式與指針形式之間還會相互轉換:a[n] <==> *(a + n) <==> *(n + a) <==> n[a]。這種表示法是不是很奇怪?但經過理論推導完全是成立的,下面我們就來看看是否支持這種寫法

#include <stdio.h>

int main()
{
    int a[5] = {0};
    int* p = a;
    int i = 0;
    
    for(i=0; i<5; i++)
    {
        p[i] = i + 1;
    }
    
    for(i=0; i<5; i++)
    {
        printf("a[%d] = %d\n", i, *(a + i));
    }
    
    printf("\n");
    
    for(i=0; i<5; i++)
    {
        i[a] = i + 10;
    }
    
    for(i=0; i<5; i++)
    {
        printf("p[%d] = %d\n", i, p[i]);
    }
    
    return 0;
}

        我們看到在程序第6行定義指針 p 并將它指向數組 a,接下來就是我們之前說的到底指針是否也可以當做數組名來使用呢,如果可以第11行便不會報錯。在第16行我們以指針的形式來打印數組 a 中的值,第23行則驗證我們上面 i[a] 這種寫法是否正確,第28行則通過下標形式來訪問數組。我么來看看編譯結果

C之指針與數組組合(二十六)

        我們看到程序沒報錯并且完美執行,這就回答了我們上面的問題和疑問。但是得注意:在現代編譯器中,生成代碼優化率已大大提高,在固定增量時,下標形式的效率已經和指針形式相當了;但從代碼的可讀性和維護的角度來看,下標形式更優秀,這就是為什么我們平時見到的代碼中的數組都是以下標形式訪問的啦。

        我們下來再做個實驗,看看數組和指針的區別

test.c 代碼

#include <stdio.h>

int main()
{
    extern int a[];
    
    printf("&a = %p\n", &a);
    printf("a = %p\n", a);
    printf("*a = %d\n", *a);

    
    return 0;
}


ext.c 代碼

int a[] = {1, 2, 3, 4, 5};

        我們看到在 ext.c 中定義了一個數組,我們先以數組的方式在 test.c 中訪問,看看打印結果

C之指針與數組組合(二十六)

        我們看到的結果和我們想的是一致的,&a 就代表數組的地址,a 就代表數組首元素的地址,兩個是相同的。*a 的值便是數組中第一個元素的值啦。我們再來將 test.c 中的第5行改成 extern int* a; 這樣呢,我們來看看編譯結果

C之指針與數組組合(二十六)

        我們看到發生段錯誤了,這是什么情況呢?數組 a 的值為 1,*a 發生段錯誤了,在內存中,數組的值是這樣存儲的 0001 0002 ... 0005(大端機器)。那么 a 自然也就為 1了,計算機中的 1 地址處為內核態,用戶態的程序想要訪問內核態的地址,計算機當然會報錯。

        那么 a 和 &a 有何區別呢? a 為數組首元素的地址,&a 為整個數組的地址。a 和 &a 的區別在于指針運算。a + 1 ==> (unsigned int)a + sizeof(*a);&a + 1 ==> (unsigned int)(&a) + sizeof(*&a) ==> (unsigned int)(&a) + sizeof(a);

        下來我們來看個經典的指針運算問題,同時也是一道筆試面試題

#include <stdio.h>

int main()
{
    int a[5] = {1, 2, 3, 4, 5};
    int* p1 = (int*)(&a + 1); 
    int* p2 = (int*)((int)a + 1);
    int* p3 = (int*)(a + 1);
    
    printf("%d, %d, %d\n", p1[-1], p2[0], p3[1]);
    
    return 0;
}

        我們看下編譯結果

C之指針與數組組合(二十六)

        我們來分析下,第一個 p1[-1] ==> p1[&a +1 - 1] =>p1[&a],自然它的值也就為 5 了。p3[1] ==> (a + 1 +2) ==> (a + 2),自然也就為 3 啦。第二個的數感覺是隨機數,但我們仔細分析下,它是首地址加 1,也就是往后移一位。這個數組在小端系統中,就是 1000 2000 ... 5000這樣分布的,后移一位就變成了 0002,便是 0x02000000 轉成十進制便是 33554432 啦。

        數組作為函數參數時,編譯器將其編譯成對應的指針。如:void f(int a[]) <==> void f(int* a);void f(int a[5]) <==> void f(int* a);在一般情況下,當定義的函數中有數組參數時,需要定義另一個參數來標定數組的大小。

        我們來看個示例代碼

#include <stdio.h>

void func1(char a[5])
{
    printf("In func1: sizeof(a) = %d\n", sizeof(a));
    
    *a = 'a';
    
    a = NULL;
}

void func2(char b[])
{
    printf("In func2: sizeof(b) = %d\n", sizeof(b));
    
    *b = 'b';
    
    b = NULL;
}

int main()
{
    char array[10] = {0};
    
    func1(array);
    
    printf("array[0] = %c\n", array[0]);
    
    func2(array);
    
    printf("array[0] = %c\n", array[0]);
    
    return 0;
}

        我們在 func1 中打印它的參數大小,并且以指針方式進行賦值和指向 NULL,如果是數組的話便會報錯。我們來看看編譯結果

C之指針與數組組合(二十六)

        我們發現兩函數的數組參數都被當成指針來處理了。 通過本節對指針和數組的學習,總結如下:1、數組聲明時編譯器自動分配一片連續的內存空間,指針聲明時只分配了用于容納地址值的4字節空間;2、指針和整數可以進行運算,其結果為指針。指針之間只支持減法運算,其結果為數組元素下標差;3、指針之間支持比較運算,其類型必須相同;4、數組名和指針僅使用方式相同,數組名的本質不是指針,指針的本質不是數組;5、數組名并不是數組的地址,而是數組首元素的地址;6、函數的數組參數化為指針。


        歡迎大家一起來學習 C 語言,可以加我QQ:243343083

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

乡宁县| 固镇县| 江油市| 宿松县| 武清区| 合江县| 浮山县| 普安县| 栖霞市| 湛江市| 博爱县| 汝南县| 花莲县| 平江县| 溧水县| 贞丰县| 西贡区| 汤原县| 土默特左旗| 瑞金市| 广汉市| 成武县| 顺平县| 望都县| 车险| 肃北| 五家渠市| 四子王旗| 丰城市| 西乌珠穆沁旗| 沂水县| 寿阳县| 元氏县| 仁化县| 霍州市| 绥阳县| 锦州市| 宁南县| 宜兴市| 亚东县| 龙泉市|