溫馨提示×

溫馨提示×

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

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

c/c++ 函數(shù)、常量、指針和數(shù)組的關(guān)系梳理

發(fā)布時間:2020-07-23 19:55:16 來源:網(wǎng)絡(luò) 閱讀:539 作者:張濤澤 欄目:編程語言

壓力才有動力,15年中旬就要準(zhǔn)備實(shí)習(xí),學(xué)習(xí)復(fù)習(xí)學(xué)習(xí)復(fù)習(xí)學(xué)習(xí)復(fù)習(xí)學(xué)習(xí)復(fù)習(xí)……無限循環(huán)中,好記性不如爛筆頭……從數(shù)組開始,為主干。

c 的array由一系列的類型相同的元素構(gòu)成,數(shù)組聲明包括數(shù)組元素個數(shù)和類型,c 中的數(shù)組參數(shù)是引用形式傳參(傳址調(diào)用),而常量標(biāo)量是按值傳遞。

//[]方括號表示聲明的是數(shù)組,里面的數(shù)字表明了數(shù)組包含的元素?cái)?shù)目
    int states[50];//聲明50個整數(shù)的數(shù)組
    double code[365];//聲明365個浮點(diǎn)數(shù)的數(shù)組
    char chr[20];//聲明20個字符的數(shù)組

數(shù)組下標(biāo),不同語言不一定一樣,c 是0開始。

ANSI C 才支持的 數(shù)組的標(biāo)準(zhǔn)初始化

int pow[8] = {1, 2, 3, 4, 5, 6, 7, 8};//只有標(biāo)準(zhǔn)c(ANSI C)支持此初始化語法

注意:數(shù)組聲明的時候最好是初始化,雖然不會報(bào)錯,但是和普通變量一樣,使用沒有初始化的數(shù)組,里面元素的值不定,出現(xiàn)垃圾值。

數(shù)組初始化列表和大小不一致的情況

初始化列表里的元素個數(shù)和數(shù)組大小不一致,當(dāng)初始化元素?cái)?shù)目少于數(shù)組大小,多余的元素自動初始化為0

注意區(qū)分:如果沒有初始化數(shù)組,那么和普通變量一樣,存取的內(nèi)存原來就有的垃圾值。初始化了,只不過部分初始化,那么編譯器會自動初始化余下的元素為0。初始化了,但是初始化列表元素大于數(shù)組大小,那么編譯報(bào)錯,內(nèi)存溢出。

//初始化沒有完全,編譯器自動后面給初始化為0
    double d[4] = {1};//不過vs2010里編譯運(yùn)行,全都是0    //error C2078: 初始值設(shè)定項(xiàng)太多
    double d[4] = {1, 2, 3, 4, 5, 6};

數(shù)組大小可以用整型常量(unsigned)或者字符常量來指定大小,C99之前就是這兩種方法。

c/c++ 函數(shù)、常量、指針和數(shù)組的關(guān)系梳理

    const int const_m = 10;//在c語言(不同于c++)里,const值不被看作是常量
    int n = 100;//定義了變量n

    double d1[10];// ok
    double d2[5 * 2 + 1];//ok
    double d3[];//error,沒有初始化,也沒有大小指定
    double d4[sizeof(int)];//ok,sizeof表達(dá)式在c里被認(rèn)為返回一個整數(shù)常量
    double d5[-10];//error C2118: 負(fù)下標(biāo)
    double d6[0];//error C2466: 不能分配常量大小為 0 的數(shù)組
    double d7[3.14];// error C2058: 常量表達(dá)式不是整型
    double d8[(int)3.14];//ok

    double d9[const_m];// error C2057: 應(yīng)輸入常量表達(dá)式,“d9”未知的大小,不能分配常量大小為 0 的數(shù)組
    double d10[n];//error C2057: 應(yīng)輸入常量表達(dá)式,“d9”未知的大小,不能分配常量大小為 0 的數(shù)組

c/c++ 函數(shù)、常量、指針和數(shù)組的關(guān)系梳理

c99之后,后兩種方式可以了,并且C99有了一種新的數(shù)組的名詞,叫VLA(variable length array)變長數(shù)組,VLA。目的是為了讓c更適合做數(shù)值計(jì)算。

數(shù)組初始化小技巧

省略填寫數(shù)組大小,讓編譯器自動的去判斷數(shù)組的實(shí)際大小和初始化列表里的項(xiàng)目

c/c++ 函數(shù)、常量、指針和數(shù)組的關(guān)系梳理

//空的方括號告訴編譯器去初始化列表里判斷數(shù)組實(shí)際大小
    char chr[] = {'a', 'b', ' ', '4'};    int i;    //這里需要一個技巧,人工判斷數(shù)組大小容易出錯,使用sizeof運(yùn)算符計(jì)算
    for (i = 0; i < sizeof(chr) / sizeof(chr[0]); i++)
    {
        printf("\n%d=%c", i + 1, chr[i]);
    }

c/c++ 函數(shù)、常量、指針和數(shù)組的關(guān)系梳理

#define NUM 4//采用標(biāo)識符常量代表數(shù)組大小,推薦的技巧
    int days[NUM] = {1, 2, 3, 4};//良好的編程風(fēng)格,如果以后想修改數(shù)組大小,只需要修改開頭的常量即可

知道C99的新特性:對數(shù)組指定元素初始化

可以對數(shù)組指定的元素直接初始化,如果對數(shù)組最后一個元素初始化,那么傳統(tǒng)語法必須順次初始化到最后一個元素前,才能對最后一個元素初始化。

//對最后一個元素初始化為1
    float f[3] = {0, 0, 1};

而c99規(guī)定可以直接去初始化

//使用方括號【】,直接對數(shù)組某個元素初始化
    int i[3] = {i[2] = 100};//ok!需要加  數(shù)組名【】

對于一般的數(shù)組初始化,部分初始化之后,余下的自動初始化為0(這里vs2010編譯器不是這樣的,全部都是0 了),如果在指定的初始化元素后還有別的初始化值,那么這些數(shù)值自動對數(shù)組后續(xù)元素進(jìn)行初始化,并且指定初始化元素的數(shù)值在第幾個元素處,那么也會同樣初始化這個元素。

int in[7] = {1, 2, in[2] = 10, 3, 4, in[0] = 11};   //首元素=11

c/c++ 函數(shù)、常量、指針和數(shù)組的關(guān)系梳理

只讀數(shù)組

如果只需要對數(shù)組讀取,而不進(jìn)行修改,那么推薦關(guān)鍵字const,

const int days[NUM] = {1, 2, 3, 4};//數(shù)組中每個元素都當(dāng)作常量處理,和普通變量一樣,const數(shù)組也需要聲明的時候初始化

數(shù)組的賦值

使用數(shù)組的下標(biāo)(索引)為數(shù)組元素賦值,c不支持整體賦值,也不支持用花括號括起來的列表進(jìn)行賦值(初始化除外)這是個注意點(diǎn)

    double d[SIZE] = {1, 2, 3};//ok這是可以的,列表進(jìn)行初始化
    int i[SIZE], count;
    i = d;//error,c不支持?jǐn)?shù)組整體賦值
d[SIZE] = {11, 22, 33};//不起作用,這種形式只有初始化可以使用,賦值必須使用索引

數(shù)組的邊界問題

數(shù)組索引不能越界,因?yàn)榫幾g器不會檢測這種錯誤。如果出現(xiàn)非法的索引,那么結(jié)果未知,有時候會中斷,但是也有時候可以執(zhí)行,但是結(jié)果很奇怪(編譯器不同而不同)

編譯器不檢測數(shù)組邊界,是出于信任程序員,可以節(jié)省時間和效率。

二維數(shù)組(注意:其實(shí)c 語言中只有一維數(shù)組,英文里沒有多維數(shù)組的說法,僅僅是數(shù)組的數(shù)組)

數(shù)組的數(shù)組,因?yàn)閿?shù)組的內(nèi)容在內(nèi)存是連續(xù)存儲的。

double d[5][12];//定義一個數(shù)組的數(shù)組(二維數(shù)組),5個由12個浮點(diǎn)數(shù)組成的數(shù)組的數(shù)組

d[5] 是一個包含5個元素的一維數(shù)組,而其中5個元素的每一個元素又是一個包含12個浮點(diǎn)數(shù)的數(shù)組,也就是說,數(shù)組d的每個元素類型都是double[12],這里要注意區(qū)分。首元素就是d[0][0],是double類型的,二維數(shù)組有5行,每行包含12列。改變第二個下標(biāo)是沿著行移動,改變第一個下標(biāo)是沿著列垂直移動。且數(shù)組是順序存儲的。

二維數(shù)組的初始化

int i[3][3] = {
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9}
    };

數(shù)組的數(shù)組進(jìn)行初始化,外層的每一維數(shù)組初始化列表都用花括號括起來,而且中間也是用逗號隔開.對于一維數(shù)組的初始化問題,同樣適用于二維數(shù)組,比如第一行列表里元素少了,那么剩余的元素自動初始化為0,如果多了,同樣報(bào)錯,而且一行和一行獨(dú)立的,不影響以后的賦值.且 c 的數(shù)組是行優(yōu)先存儲。

也可以省略內(nèi)部的括號,只要最外面那層的括號,但是要保證正確.

int i[3][3] = {        1, 2, 3,        4, 5, 6,        7, 8, 9
    };//不建議,因?yàn)槎嗔巳菀壮鲥e

注意:多維數(shù)組初始化的兩種方式效果不同

c/c++ 函數(shù)、常量、指針和數(shù)組的關(guān)系梳理

//第一種就是內(nèi)部初始化列表也帶括號的
    int i[2][3] = {
    
        {4, 5},
        {7, 8}
    }; //很明顯,初始化列表不夠數(shù)組大小

c/c++ 函數(shù)、常量、指針和數(shù)組的關(guān)系梳理

c/c++ 函數(shù)、常量、指針和數(shù)組的關(guān)系梳理

還有一種內(nèi)部不帶花括號

    int i[2][3] = {    
        4, 5,        7, 8
    }; //很明顯,初始化列表不夠數(shù)組大小

c/c++ 函數(shù)、常量、指針和數(shù)組的關(guān)系梳理

多維(二維以上)數(shù)組

    double d[2][3][4];//三維數(shù)組的聲明形式,更多維也一樣

通常處理一維數(shù)組用一層循環(huán),二維數(shù)組用兩層循環(huán),多維用多層循環(huán),以此類推,他們的初始化是一樣的,大同小異。不過多維的用的不是太多。

數(shù)組和指針的聯(lián)系

數(shù)組名可以當(dāng)指針用,此時數(shù)組名的值是一個指針常量,是數(shù)組的第一個元素的內(nèi)存地址。針對這幾個常見的名詞:常量指針,指針常量,常指針,指向常量的指針,指向常量的常量指針,和指向普通變量的常量指針,說明:經(jīng)個人的學(xué)習(xí)和理解,查閱資料,我還是認(rèn)同《c++ primer》、<pointers on c>、<c primer>中各位作者的叫法。并不贊同,一些博客或者網(wǎng)友的說法,貌似以前的老師也沒這么教過。

『指針常量』在 c 和 c++中餅沒有專用的寫法,很少直接用到。

肯定的是:數(shù)組名當(dāng)指針用的時候,這個指針的內(nèi)容是一個地址,也就是指針類型的常量,而不是其他的整型或者浮點(diǎn)等基本數(shù)據(jù)類型的常量,不要和數(shù)學(xué)概念混。

比如,有一個int 類型的變量x,假設(shè)存儲在內(nèi)存的0x11ff位置:

    //打算把100這個常量賦值給變量 x,報(bào)錯
    *0x11ff = 100;

c/c++ 函數(shù)、常量、指針和數(shù)組的關(guān)系梳理

提示間接的需要指針操作符。這是因?yàn)殚g接訪問操作符*,必須跟指針類型的表達(dá)式結(jié)合,而0x11ff是字面值常量,是整型。那么修改為指針類型:

    //使用c 的強(qiáng)制類型轉(zhuǎn)換,把0x11ff 從整型轉(zhuǎn)換為了指向整型的指針類型,它唯一的用處就是當(dāng)程序需要訪問內(nèi)存里一些特定的位置時,比如直接訪問硬件的一些特定接口(地址固定)。那么可以使用,其他不行,因?yàn)槲覀儫o法預(yù)測編譯器會把變量放在何處。
    *(int *)0x11ff = 3.14;

數(shù)組名是這個數(shù)組首元素的地址,是一個指向某類型的常指針(或者說成指針常量),但是數(shù)組和指針是不同的!

數(shù)組是包含多個元素的一種存儲結(jié)構(gòu),通過數(shù)組名來標(biāo)記。指針僅僅是一個標(biāo)量而已。只有當(dāng)數(shù)組在表達(dá)式(確切的說是數(shù)組在函數(shù)定義頭,函數(shù)聲明(當(dāng)做參數(shù)傳遞),或者類似*(array + size等)使用,編譯器才會為數(shù)組生成一個指針常量,用數(shù)組名標(biāo)記這個常量。故不要把數(shù)組名和指針經(jīng)常性的混為一談。且靜態(tài)存儲的數(shù)據(jù)結(jié)構(gòu),新航道雅思培訓(xùn)程序運(yùn)行期間,內(nèi)存地址固定不變,比如c 和 c++的數(shù)組。

數(shù)租在表達(dá)式里使用的例外情況

使用 sizeof 操作符,數(shù)組名不是指針常量,求的是數(shù)組占據(jù)的內(nèi)存字節(jié)數(shù)

    //長度為3的整型數(shù)組 array
    int array[3];
    printf("%d \n", array);//array 是指針,打印數(shù)組的手地址
    printf("%d \n", sizeof(array[0]));//打印的是數(shù)組中的單個元素的大小,int 類型 4個字節(jié)  32位
    printf("%d \n", sizeof(array));//打印數(shù)組的大?。ú皇窃貍€數(shù)),占據(jù)的內(nèi)存字節(jié)數(shù),12

1606416396 (如果是%p解析是 :0x7fff5fbff80c )

12 

使用&操作符,數(shù)組名不是指針常量

c/c++ 函數(shù)、常量、指針和數(shù)組的關(guān)系梳理

    int array[10] = {0};    int *p = &array[0];
    printf("%p \n", p);//打印的是數(shù)組手地址
    
    int *pp = array;
    printf("%p \n", pp);//打印的是數(shù)組手地址    
    //對數(shù)組明取地址操作,如果數(shù)組名是解釋為了指針常量,那么q 就是指向指針常量的指針
    int *q = &array;//那么*q就是指針常量了???
    printf("%p \n", *q);//打印的0,不對

c/c++ 函數(shù)、常量、指針和數(shù)組的關(guān)系梳理

此時的數(shù)組名,是一個指向數(shù)組的指針!指向的是一個地址數(shù)組。

    //對數(shù)組明取地址操作
    int (*q)[10] = &array;

指向的是一個1行10列的二維地址數(shù)組。這還關(guān)系到了多維的數(shù)組和指針的關(guān)系。

先看一維數(shù)組和指針的關(guān)系

c/c++ 函數(shù)、常量、指針和數(shù)組的關(guān)系梳理

    //SIZE值是3
    short dates[SIZE];    short *pi;    short index;    double bills[SIZE];    double *pf;    //pi = dates;//把數(shù)組地址(首元素的地址)賦值給指針變量
    pi = &dates[0];
    pf = bills;//有兩種方式

c/c++ 函數(shù)、常量、指針和數(shù)組的關(guān)系梳理

下面的值是等價的

    dates == &dates[+  == &dates[*(dates + ) ==dates[

這些都說明c標(biāo)準(zhǔn)在描述數(shù)組的時候,借助了指針,他們有密切聯(lián)系

    int i[SIZE];    *(i + SIZE);//意思是尋址到內(nèi)存中的i(數(shù)組首元素地址),然后繼續(xù)找SIZE個單位,找到該地址再取出里面存放的數(shù)值

注意:間接運(yùn)算符*的優(yōu)先級高于+

*i + 10;//意思是整型數(shù)組i的首元素的值(一個整數(shù))和整數(shù)10相加的和*(i + 10);//意思是i數(shù)組的第11個元素的值

c 的數(shù)組不能用=進(jìn)行復(fù)制:只能是循環(huán)遍歷每個元素進(jìn)行賦值,如果直接賦值數(shù)組名,僅僅是把指針常量的一個拷貝賦值了。

數(shù)組的下標(biāo)范圍不被編譯器檢測(不僅僅是 c 和 c++,很多其他語言也是)

數(shù)組的下標(biāo)一般都認(rèn)為是不能為負(fù)數(shù)的,要小于0,也不能大于數(shù)組的維數(shù)(長度-1),但是有一個情況是特殊的

c/c++ 函數(shù)、常量、指針和數(shù)組的關(guān)系梳理

    int array[5] = {0, 1, 2, 3, 4};    int *p = array + 1;//p 指針指向了 array 的第2個元素,1    //p[]也是數(shù)組
    p[0] = p[3];
    printf("%d \n", p[0]);//打印4    //向前偏移了1,那么就是0元素
    printf("%d \n", p[-1]);//打印0,因?yàn)?nbsp;c 的數(shù)組的下標(biāo)引用和間接訪問方式*是等價的
    p[-10] = 10;//編譯器沒報(bào)錯,但是這樣確實(shí)是非法操作,編譯器并不檢測數(shù)組的下標(biāo)范圍
    p[100] = 2;//非法

c/c++ 函數(shù)、常量、指針和數(shù)組的關(guān)系梳理

c 的數(shù)組下標(biāo)不被編譯器檢測,如果越界,我們無法得知編譯器把它指向了哪里,可能造成不可估量的錯誤。因?yàn)闄z測的開銷太大了。

另一種數(shù)組下標(biāo)引用的寫法

   int a[5] = {0};    2[a] = 1;

完全正確的,說明編譯器內(nèi)部實(shí)現(xiàn)下標(biāo)的方法邏輯里,是包含這樣的特例的。但是不能那么寫。不規(guī)范。等價于 a[2],*(a + 2)

函數(shù)里的數(shù)組和指針

int sum(int *i, int n);

或者這樣聲明

int sum(int i[], int n);

main 函數(shù)代碼段

int in[5] = {1, 2, 3, 4, 5};
printf("%d", sum(in, 5));

第一個參數(shù)把數(shù)組首地址和數(shù)組的類型傳遞給了函數(shù)sum,第二個參數(shù)把數(shù)組大小傳遞給函數(shù)

當(dāng)且僅當(dāng)在給函數(shù)原型聲明或者函數(shù)定義的時候可以用int *i代替 int i[],任何情況下,int *i;都表示i是指向int類型的指針,而int i[];可以表示i是指向int的指針(只在函數(shù)原型聲明和函數(shù)定義的時候可用),還有可以表示i是數(shù)組i的首地址。

必須掌握如下四個等價方式函數(shù)原型聲明可以省略參數(shù)名字),注意只有在函數(shù)原型聲明或者函數(shù)定義頭的時候int *i和int i[]是等價的。

int sum(int i[], int n);int sum(int [], int);//因?yàn)榭梢允÷詤?shù)名,故數(shù)組名可以省去int sum(int *i, int n);int sum(int *, int);//指針名可以省去

而函數(shù)定義的時候不能省略參數(shù)名字,必須掌握這兩個形式

int sum(int *ii, int num){}int sum(int ii[], int num){}

需要明白的是:指針不論在什么情況下,對于采用四字節(jié)地址的計(jì)算機(jī)系統(tǒng),大小都是4個字節(jié),和其他無關(guān),只和計(jì)算機(jī)系統(tǒng)有關(guān)。

sum(in, in + SIZE);

語句里in是數(shù)組首元素地址,而數(shù)組索引從0開始,那么加size之后實(shí)際指向的是end元素的下一位。如果指向最后一位,應(yīng)為:

sum(in, in + SIZE - 1);

一個問題:判斷優(yōu)先級

total += *start ++; //一元運(yùn)算符*和++具有相同的優(yōu)先級,結(jié)合從右向坐。注意到這里是后綴自增,那么應(yīng)該是先把指針指向的數(shù)據(jù)加到total,然后指針自增,等價于循環(huán)體里的兩行代碼效果

如果是前綴自增,那么就是

total += *++start ;

指針先自增,然后再用自增之后指向的值累加到total。如果是

total += (*start )++;

那就是賦值完成之后,指針指向的內(nèi)容自增,而不是指針變量自增,效果是指針指向的地址不變,但里面的元素變了。

C語言中,i[SIZE]和*(i + SIZE)是等價的

不論i是數(shù)組名還是i是指針變量。不過注意:只有當(dāng)i是一個指針變量的時候,才可以使用諸如:i++的運(yùn)算

對數(shù)組內(nèi)容的保護(hù) 

當(dāng)函數(shù)傳入的參數(shù)需要改變,那么就必須傳遞指針(c++引用),處理數(shù)組的時候,原則上是必須用指針的,雖然也可以用值,那么函數(shù)必須先為原數(shù)組的拷貝分配一個足夠的內(nèi)存空間,再把原數(shù)組的數(shù)據(jù)寫入到這個空間的新數(shù)組里,效率很低。故用指針進(jìn)行傳遞,然函數(shù)去直接操作原數(shù)組,方便快捷。

對形式參數(shù)使用const修飾

如果設(shè)計(jì)操作數(shù)組的函數(shù)的初衷是不修改原數(shù)據(jù),那么可以在函數(shù)原型聲明和定義頭里使用const關(guān)鍵字

int sum(const int arr[], int n);

告訴編譯器:arr(其實(shí)arr是數(shù)組首元素地址)指向的數(shù)組是包含的常量元素,不能修改,如果修改就報(bào)錯。注意:這樣定義的話,只是在函數(shù)里對原數(shù)組不改變,在外面就不是常量對待了。c 里,const關(guān)鍵字可以定義符號常量(c99新規(guī)定,以前就是#define),還可以創(chuàng)建數(shù)組常量,指針常量,和指向常量的指針

const int array[2] = {1, 2};//數(shù)組常量array[0] = 2;//error C2166: 左值指定 const 對象

指向常量的指針

double arrayD[5] = {1, 2, 3, 4, 5};const double *pd = arrayD;//指向常量的指針,指針pd指向了數(shù)組的開頭,pd可變,*pd是常量,不可變。

通常把指向常量的指針作為函數(shù)參數(shù),說明函數(shù)不會用這個指針修改數(shù)據(jù)。

注意區(qū)別:普通指針和指向常量的指針

把常量或者非常量的數(shù)據(jù)賦值給指向常量的指針都是可以的(因?yàn)楸旧碇羔樦赶虻氖浅A浚?,但是對于普通指針,只能給它賦值非常量地址

指針和二維數(shù)組的關(guān)系

c/c++ 函數(shù)、常量、指針和數(shù)組的關(guān)系梳理

     int arrayInt[4][2];//整型數(shù)組的數(shù)組    //數(shù)組名arrayInt同時也是數(shù)組首元素地址,而本數(shù)組的首元素又是包含兩個int元素的數(shù)組    //故數(shù)組名arrayInt也是包含兩個int類型元素的數(shù)組的地址
    arrayInt == &arrayInt[0];//ok的    //而arrayInt[0]又是一個包含兩個int元素的數(shù)組
    arrayInt[0] == &arrayInt[0][0];//ok

c/c++ 函數(shù)、常量、指針和數(shù)組的關(guān)系梳理

arrayInt【0】是第一行里,第一個整數(shù)元素的地址,而arrayInt是兩個整數(shù)元素的地址(第一行的數(shù)組的首地址,其實(shí)也就是第一行里第一個元素的地址),也就是說,二維數(shù)組的首地址,和里面第一行數(shù)組的首地址相等。

    arrayInt == arrayInt[0];//數(shù)組首地址==第一行數(shù)組的首地址
    arrayInt == &arrayInt[0][0];    &arrayInt[0] == arrayInt[0];    &arrayInt[0] == &arrayInt[0][0];//他們都是等價的四個語句

注意:指針加減之后的變化

指針加一,那么會對原來的數(shù)值加一個和指針指向內(nèi)容的類型大小相對應(yīng)的一個數(shù)值(不是單純的加一)。故二維數(shù)組里,arrayInt+1和arrayInt[0] + 1不一樣。

ArrayInt指向的內(nèi)容大小為兩個int大小,而arrayInt[0]指向的內(nèi)容大小為一個int。

關(guān)鍵點(diǎn):指針取值的變化

對一個指針(地址)使用間接運(yùn)算符取值,可以得到該指針指向的內(nèi)容的數(shù)值。

arrayInt[0]是二維數(shù)組第一行數(shù)組的首元素地址(&arrayInt[0][0])。*arrayInt[0]取出的是arrayInt[0][0],一個int元素。*arrayInt取出的是這個二維數(shù)組首元素的值,即arrayInt[0],而arrayInt[0]本身也是一個地址=&arrayInt[0][0],故*arrayInt其實(shí)還是一個地址*arrayInt[0][0],故要取出真正的數(shù)值,還需要再*一次,**arrayInt才是一個int數(shù)arrayInt[0][0]。即,二維數(shù)組名是一個地址的地址。這是難點(diǎn)。

c/c++ 函數(shù)、常量、指針和數(shù)組的關(guān)系梳理

int i[2][2] = {
        {1,2},
        {3,4}
    };
    printf("%p\n", i);//0030F924,二維數(shù)組首元素地址
    printf("%p\n", &i[0]);//0030F924
    printf("%p\n", i[0]);//0030F924,二維數(shù)組第一行數(shù)組首元素地址(其實(shí)就是二維數(shù)組首元素地址)
    printf("%p\n", &i[0][0]);//0030F924
    printf("%p\n", i + 1);//0030F92C,i代表兩個int類型元素首地址,0030F924 + 8,
    printf("%p\n", i[0] + 1);//0030F928,i[0]代表二維數(shù)組里第一行數(shù)組首元素地址,是一個int類型對象,故加4
    printf("%p\n",*i);//取出的還是地址    0030F924,是二維數(shù)組首地址(第一行數(shù)組首地址)
    printf("%d\n", **i);//1,第一行數(shù)組首地址就是1的地址
    printf("%d\n", *i[0]);//1
    printf("%d\n", i[0][0]);//1
    printf("%d\n", i[1][1]);//4
    printf("%d\n", *(*(i + 1) + 1));//4,尋址過程:先找到第2行數(shù)組i+1,然后取值*(i+1)得到第二行數(shù)組首地址,再加1,找到最后一個元素地址,再取值就是實(shí)際存儲的內(nèi)容。
    printf("%p\n", (i[1] + 1));//    0030F930
    printf("%p\n", *(i + 1) + 1);//    0030F930,最后一個元素地址

c/c++ 函數(shù)、常量、指針和數(shù)組的關(guān)系梳理

如果有一個二維數(shù)組,那么如何聲明一個指向它的指針?

二維數(shù)組首地址是包含兩個類型大小的,聲明的時候需要注意具體是指向幾個,再說創(chuàng)建[x]

int (*ptr)[2];//創(chuàng)建一個指針變量ptr,指向了一個包含兩個int類型值的數(shù)組    //說明創(chuàng)建了一個指向包含兩個int類型值的數(shù)組的指針

表達(dá)式中[]的優(yōu)先級高于*

int *p[2];//意思是:[]先和p結(jié)合,則p[2]是包含兩個int元素的數(shù)組,再和*結(jié)合,則說明p是兩個指向int類型的指針構(gòu)成的數(shù)組。    //這樣創(chuàng)建得到兩個指針,都指向int類型,和前面的表達(dá)式不一樣。
    int array[2][2] = {{1, 2}, {3, 4}};    int *p[2] = array;// error C2075: “p”: 數(shù)組初始化需要大括號
    int (*p)[2] = array;//ok,括號先運(yùn)算,就是一個指針,然后再是p[2]數(shù)組
    p = array;//ok

指針的賦值類型的兼容性

指針的賦值原則比數(shù)值類型嚴(yán)格的多,數(shù)值類型賦值有類型轉(zhuǎn)換,但是指針沒有。指針本身也是一種類型!

c/c++ 函數(shù)、常量、指針和數(shù)組的關(guān)系梳理

    int n = 5;    double d = 1.1;    int *pi = &n;    double *pd = &d;
    n = d;//隱式的類型轉(zhuǎn)換,ok
    pi = pd;//vs2010沒有報(bào)錯,很詭異
    pd = pi;

c/c++ 函數(shù)、常量、指針和數(shù)組的關(guān)系梳理

比較復(fù)雜的:

c/c++ 函數(shù)、常量、指針和數(shù)組的關(guān)系梳理

    int *pi1;//創(chuàng)建一個指針pi1,指向一個int類型元素
    int (*pa)[3];//創(chuàng)建一個指針pa,pa指向包含三個int類型元素的數(shù)組
    int array1[2][3];    int array2[3][2];    int **pi2;//指向指針類型的指針
    pi1 = &array1[0][0];//array1[0][0]是一個int類型的元素
    pi1 = array1[0];//和上句等價
    pi1 = array1;//非法的操作,array1是包含三個元素的數(shù)組的地址
    pa = array1;

    pa = array2;//非法,一個指向的是3個int,一個是2個
    pi2 = &pi1;//pi2是一個指針,但是指向一個指向int類型的指針類型(存儲的是指向int類型的指針類型本身的地址)    //pi1是指針,指向了int類型元素,&pi1得到了指向int類型的指針類型本身的地址

c/c++ 函數(shù)、常量、指針和數(shù)組的關(guān)系梳理

c/c++ 函數(shù)、常量、指針和數(shù)組的關(guān)系梳理

對于 array1【2】【3】,array1是二維數(shù)組的手地址,指向如圖,這個地址也是指針,是指向了一個新的數(shù)組,即二維數(shù)組里的數(shù)組,而這個數(shù)組包含了三個 int類型的 元素。pil 指針,指向了一個 int 類型元素,而 int (*pa)[3];中的 pa,是一個指針,指向了一個包含了三個 int 類型元素的數(shù)組。

多重間接運(yùn)算里的const賦值

    int *p1;    const int *p2;//創(chuàng)建一個指向常量的指針p2,也就是指向常量的指針
    const int **pp2;//創(chuàng)建一個指向常量的二級指針pp2

const在誰前面誰就不允許改變。

常量指針p, int * const p;也說作常指針,是說指針 p 本身是常量,不能修改,但是*p,也就是 p 指向的內(nèi)容可以變,它不同于指向常量的指針。指向常量的指針 p 本身不是常量,p 可以 修改,但是 p 指向的內(nèi)容不能被修改,不一定必須指向常量,也可以指向變量,僅僅是不能被賦值。pp2是指向常量的指針,也是一個二級指針,指向指針的指針,也就是一個指向常量的指針的指針。

    p1 = p2;//不能把指向常量的指針,賦值給非指向常量的指針
    p2 = p1;//反過來可以
    pp2 = &p1;//雖然也是把非指向常量的指針賦值給了指向常量的指針,但是在多層的間接運(yùn)算里非法,很有可能出錯!

普通指針p1指向了 p2,p2指向的就可以被賦值修改(違法),故不能用普通指針指向。把非指向常量的指針賦值給指向常量的指針的前提是必須是一層間接運(yùn)算。

多層不合法,下面解釋不合法的原因:

    int *p1;    const int n = 10;    const int *p2;//創(chuàng)建一個指向常量的指針p2
    const int **pp2;//創(chuàng)建一個指向常量的指針的指針pp2
    pp2 = &p1;//假設(shè)可以這樣寫,ok!
    *pp2 = &n;//本局,&n 是常量,*pp2是指向常量的指針,ok,但是呢,*pp2指向p1也成立了,那么此時 *p1不能被修改。而下句的*p1被賦值修改。
    *p1 = 1;//發(fā)生了錯誤

 注意:指向常量的指針是說指針 p 本身可以變,但是指向的內(nèi)容不能被賦值,也可以是指向變量,僅僅是他們不能被賦值。而常量指針,是常指針,指針 p 本身不可變,但是 p 指向的內(nèi)容能被賦值。

函數(shù)和多維數(shù)組的關(guān)系

通常可以使用數(shù)組符號代替指針參量.可以避免使用指針參量,看一個例子,求二維數(shù)組的和的程序:

c/c++ 函數(shù)、常量、指針和數(shù)組的關(guān)系梳理

  1 #define ROWS 3  2   3 #define COLS 4  4   5 //把二維數(shù)組名(指向首個子數(shù)組的指針)和行數(shù)作為形參  6   7 void sum_rows(int ar[][COLS], int rows);//ar是指向包含4個int值的數(shù)組的指針  8   9 void sum_cols(int [][COLS], int);//可以省略名稱 10  11 //ar是一個指向多維數(shù)組的指針 12  13 int sum2d(int (*ar)[COLS], int rows);//當(dāng)且僅當(dāng),ar是函數(shù)的形式參數(shù),int (*ar)[COLS]和int ar[][COLS]等價 14  15 int main() 16  17 { 18  19     int array[ROWS][COLS] = { 20  21        {1, 2, 3, 4}, 22  23        {5, 6, 7, 8}, 24  25        {9, 10, 11, 12} 26  27     }; 28  29     sum_rows(array, ROWS);//按照行方式求和 30  31     sum_cols(array, ROWS);//按照列方式求和 32  33     printf("%d\n", sum2d(array, ROWS)); 34  35     36  37     system("pause"); 38  39     return 0; 40  41 } 42  43 void sum_cols(int ar[][COLS], int rows) 44  45 { 46  47     int r; 48  49     int c; 50  51     int tot; 52  53     for (c = 0; c < COLS; c ++) 54  55     { 56  57        tot = 0; 58  59        for (r = 0; r < rows; r++) 60  61        { 62  63            tot += ar[r][c];//按照列的方式進(jìn)行二維數(shù)組的求和 64  65        } 66  67        printf("列%d的和=%d\n", c, tot); 68  69     } 70  71 } 72  73 void sum_rows(int ar[][COLS], int rows)//創(chuàng)建N維數(shù)組的指針,除了第一個空可以空,其余空的都要填寫大小,因?yàn)榈谝粋€空表面這是一個指針。 74  75 {//當(dāng)然,都填上也可以,編譯器編譯會自動忽略 76  77     int r; 78  79     int c; 80  81     int tot; 82  83     for (r = 0; r < rows; r ++) 84  85     { 86  87        tot = 0; 88  89        for (c = 0; c < COLS; c++) 90  91        { 92  93            tot += ar[r][c];//按照行的方式進(jìn)行二維數(shù)組的求和 94  95        } 96  97        printf("行%d的和=%d\n", r, tot); 98  99     }100 101 }102 103 int sum2d(int (*ar)[COLS], int rows)104 105 {106 107     int r;108 109     int c;110 111     int tot = 0;112 113     for (r = 0; r < rows; r ++)114 115     {116 117        for (c = 0; c < COLS; c++)118 119        {120 121            tot += ar[r][c];122 123        }124 125     }126 127     return tot;128 129 }

c/c++ 函數(shù)、常量、指針和數(shù)組的關(guān)系梳理

 c/c++ 函數(shù)、常量、指針和數(shù)組的關(guān)系梳理

 

c99新內(nèi)容:可以變化長度的數(shù)組VLA

一般c數(shù)組的長度事先定義好,且必須是常量,不能是變量.這樣的話,每次更改數(shù)組的長度,都要重新定義新的函數(shù),麻煩.故C99引入了VLA(變長數(shù)組),VLA許可動態(tài)分配存儲單元,可以在運(yùn)行的時候指定數(shù)組的大小,常規(guī)的c數(shù)組是靜態(tài)存儲分配的,數(shù)組大小編譯時已經(jīng)確定,因?yàn)榫S數(shù)(長度)是常量。VLA(variable length array)可使用變量作為數(shù)組的長度,這里的變長,不是說數(shù)組的大小以后可以隨時變,而是說數(shù)組的長,可以用變量來指定,而數(shù)組的大小創(chuàng)建之后不會改變.

注意:VLA必須是自動存儲類的,必須放在函數(shù)內(nèi)部或者作為函數(shù)參量出現(xiàn),否則報(bào)錯.VLA聲明的時候不能初始化.

void sum(int rows, int cols, int arr[rows][cols]);//必須是c99及以后標(biāo)準(zhǔn)的編譯器才支持.否則一樣報(bào)錯.并且參量的順序不要錯,先定義維數(shù),才有后面.
//c99規(guī)定,可以省略函數(shù)原型聲明的參數(shù)名,作為數(shù)組的維數(shù)需要用*代替void sum(int , int , int arr[*][*]);

數(shù)組的大小和長度的關(guān)系

變長數(shù)組是指用整型變量或表達(dá)式聲明或定義的數(shù)組,而不是說數(shù)組的長度會隨時變化,變長數(shù)組在其生存期內(nèi)的長度同樣是固定的。因?yàn)樽冮L數(shù)組一旦被聲明,其大小就會保持不變直到生命期結(jié)束。其實(shí)就是可以讓普通的 c 數(shù)組,動態(tài)的去創(chuàng)建內(nèi)存,分配未知的存儲單元!但是一旦運(yùn)行期間分配完畢,這個數(shù)組的大小,照樣以后不會變!直到內(nèi)存釋放。

c語言把數(shù)組歸結(jié)為派生類型,,比如聲明一個int類型數(shù)組,一個char類型數(shù)組等


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI