壓力才有動(dòng)力,15年中旬就要準(zhǔn)備實(shí)習(xí),學(xué)習(xí)復(fù)習(xí)學(xué)習(xí)復(fù)習(xí)學(xué)習(xí)復(fù)習(xí)學(xué)習(xí)復(fù)習(xí)……無限循環(huán)中,好記性不如爛筆頭……從數(shù)組開始,為主干。
創(chuàng)新互聯(lián)專注于文圣網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠為您提供文圣營(yíng)銷型網(wǎng)站建設(shè),文圣網(wǎng)站制作、文圣網(wǎng)頁設(shè)計(jì)、文圣網(wǎng)站官網(wǎng)定制、小程序定制開發(fā)服務(wù),打造文圣網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供文圣網(wǎng)站排名全網(wǎng)營(yíng)銷落地服務(wù)。
c 的array由一系列的類型相同的元素構(gòu)成,數(shù)組聲明包括數(shù)組元素個(gè)數(shù)和類型,c 中的數(shù)組參數(shù)是引用形式傳參(傳址調(diào)用),而常量標(biāo)量是按值傳遞。
//[]方括號(hào)表示聲明的是數(shù)組,里面的數(shù)字表明了數(shù)組包含的元素?cái)?shù)目 int states[50];//聲明50個(gè)整數(shù)的數(shù)組 double code[365];//聲明365個(gè)浮點(diǎn)數(shù)的數(shù)組 char chr[20];//聲明20個(gè)字符的數(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ù)組聲明的時(shí)候最好是初始化,雖然不會(huì)報(bào)錯(cuò),但是和普通變量一樣,使用沒有初始化的數(shù)組,里面元素的值不定,出現(xiàn)垃圾值。
數(shù)組初始化列表和大小不一致的情況
初始化列表里的元素個(gè)數(shù)和數(shù)組大小不一致,當(dāng)初始化元素?cái)?shù)目少于數(shù)組大小,多余的元素自動(dòng)初始化為0
注意區(qū)分:如果沒有初始化數(shù)組,那么和普通變量一樣,存取的內(nèi)存原來就有的垃圾值。初始化了,只不過部分初始化,那么編譯器會(huì)自動(dòng)初始化余下的元素為0。初始化了,但是初始化列表元素大于數(shù)組大小,那么編譯報(bào)錯(cuò),內(nèi)存溢出。
//初始化沒有完全,編譯器自動(dòng)后面給初始化為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之前就是這兩種方法。
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)為返回一個(gè)整數(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ù)組
c99之后,后兩種方式可以了,并且C99有了一種新的數(shù)組的名詞,叫VLA(variable length array)變長(zhǎng)數(shù)組,VLA。目的是為了讓c更適合做數(shù)值計(jì)算。
數(shù)組初始化小技巧
省略填寫數(shù)組大小,讓編譯器自動(dòng)的去判斷數(shù)組的實(shí)際大小和初始化列表里的項(xiàng)目
//空的方括號(hào)告訴編譯器去初始化列表里判斷數(shù)組實(shí)際大小 char chr[] = {'a', 'b', ' ', '4'}; int i; //這里需要一個(gè)技巧,人工判斷數(shù)組大小容易出錯(cuò),使用sizeof運(yùn)算符計(jì)算 for (i = 0; i < sizeof(chr) / sizeof(chr[0]); i++) { printf("\n%d=%c", i + 1, chr[i]); }
#define NUM 4//采用標(biāo)識(shí)符常量代表數(shù)組大小,推薦的技巧
int days[NUM] = {1, 2, 3, 4};//良好的編程風(fēng)格,如果以后想修改數(shù)組大小,只需要修改開頭的常量即可
知道C99的新特性:對(duì)數(shù)組指定元素初始化
可以對(duì)數(shù)組指定的元素直接初始化,如果對(duì)數(shù)組最后一個(gè)元素初始化,那么傳統(tǒng)語法必須順次初始化到最后一個(gè)元素前,才能對(duì)最后一個(gè)元素初始化。
//對(duì)最后一個(gè)元素初始化為1 float f[3] = {0, 0, 1};
而c99規(guī)定可以直接去初始化
//使用方括號(hào)【】,直接對(duì)數(shù)組某個(gè)元素初始化 int i[3] = {i[2] = 100};//ok!需要加 數(shù)組名【】
對(duì)于一般的數(shù)組初始化,部分初始化之后,余下的自動(dòng)初始化為0(這里vs2010編譯器不是這樣的,全部都是0 了),如果在指定的初始化元素后還有別的初始化值,那么這些數(shù)值自動(dòng)對(duì)數(shù)組后續(xù)元素進(jìn)行初始化,并且指定初始化元素的數(shù)值在第幾個(gè)元素處,那么也會(huì)同樣初始化這個(gè)元素。
int in[7] = {1, 2, in[2] = 10, 3, 4, in[0] = 11}; //首元素=11
只讀數(shù)組
如果只需要對(duì)數(shù)組讀取,而不進(jìn)行修改,那么推薦關(guān)鍵字const,
const int days[NUM] = {1, 2, 3, 4};//數(shù)組中每個(gè)元素都當(dāng)作常量處理,和普通變量一樣,const數(shù)組也需要聲明的時(shí)候初始化
數(shù)組的賦值
使用數(shù)組的下標(biāo)(索引)為數(shù)組元素賦值,c不支持整體賦值,也不支持用花括號(hào)括起來的列表進(jìn)行賦值(初始化除外)這是個(gè)注意點(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器不會(huì)檢測(cè)這種錯(cuò)誤。如果出現(xiàn)非法的索引,那么結(jié)果未知,有時(shí)候會(huì)中斷,但是也有時(shí)候可以執(zhí)行,但是結(jié)果很奇怪(編譯器不同而不同)
編譯器不檢測(cè)數(shù)組邊界,是出于信任程序員,可以節(jié)省時(shí)間和效率。
二維數(shù)組(注意:其實(shí)c 語言中只有一維數(shù)組,英文里沒有多維數(shù)組的說法,僅僅是數(shù)組的數(shù)組)
數(shù)組的數(shù)組,因?yàn)閿?shù)組的內(nèi)容在內(nèi)存是連續(xù)存儲(chǔ)的。
double d[5][12];//定義一個(gè)數(shù)組的數(shù)組(二維數(shù)組),5個(gè)由12個(gè)浮點(diǎn)數(shù)組成的數(shù)組的數(shù)組
d[5] 是一個(gè)包含5個(gè)元素的一維數(shù)組,而其中5個(gè)元素的每一個(gè)元素又是一個(gè)包含12個(gè)浮點(diǎn)數(shù)的數(shù)組,也就是說,數(shù)組d的每個(gè)元素類型都是double[12],這里要注意區(qū)分。首元素就是d[0][0],是double類型的,二維數(shù)組有5行,每行包含12列。改變第二個(gè)下標(biāo)是沿著行移動(dòng),改變第一個(gè)下標(biāo)是沿著列垂直移動(dòng)。且數(shù)組是順序存儲(chǔ)的。
二維數(shù)組的初始化
int i[3][3] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };
數(shù)組的數(shù)組進(jìn)行初始化,外層的每一維數(shù)組初始化列表都用花括號(hào)括起來,而且中間也是用逗號(hào)隔開.對(duì)于一維數(shù)組的初始化問題,同樣適用于二維數(shù)組,比如第一行列表里元素少了,那么剩余的元素自動(dòng)初始化為0,如果多了,同樣報(bào)錯(cuò),而且一行和一行獨(dú)立的,不影響以后的賦值.且 c 的數(shù)組是行優(yōu)先存儲(chǔ)。
也可以省略內(nèi)部的括號(hào),只要最外面那層的括號(hào),但是要保證正確.
int i[3][3] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };//不建議,因?yàn)槎嗔巳菀壮鲥e(cuò)
注意:多維數(shù)組初始化的兩種方式效果不同
//第一種就是內(nèi)部初始化列表也帶括號(hào)的 int i[2][3] = { {4, 5}, {7, 8} }; //很明顯,初始化列表不夠數(shù)組大小
還有一種內(nèi)部不帶花括號(hào)
int i[2][3] = { 4, 5, 7, 8 }; //很明顯,初始化列表不夠數(shù)組大小
多維(二維以上)數(shù)組
double d[2][3][4];//三維數(shù)組的聲明形式,更多維也一樣
通常處理一維數(shù)組用一層循環(huán),二維數(shù)組用兩層循環(huán),多維用多層循環(huán),以此類推,他們的初始化是一樣的,大同小異。不過多維的用的不是太多。
數(shù)組和指針的聯(lián)系
數(shù)組名可以當(dāng)指針用,此時(shí)數(shù)組名的值是一個(gè)指針常量,是數(shù)組的第一個(gè)元素的內(nèi)存地址。針對(duì)這幾個(gè)常見的名詞:常量指針,指針常量,常指針,指向常量的指針,指向常量的常量指針,和指向普通變量的常量指針,說明:經(jīng)個(gè)人的學(xué)習(xí)和理解,查閱資料,我還是認(rèn)同《c++ primer》、<pointers on c>、<c primer>中各位作者的叫法。并不贊同,一些博客或者網(wǎng)友的說法,貌似以前的老師也沒這么教過。
『指針常量』在 c 和 c++中餅沒有專用的寫法,很少直接用到。
肯定的是:數(shù)組名當(dāng)指針用的時(shí)候,這個(gè)指針的內(nèi)容是一個(gè)地址,也就是指針類型的常量,而不是其他的整型或者浮點(diǎn)等基本數(shù)據(jù)類型的常量,不要和數(shù)學(xué)概念混。
比如,有一個(gè)int 類型的變量x,假設(shè)存儲(chǔ)在內(nèi)存的0x11ff位置:
//打算把100這個(gè)常量賦值給變量 x,報(bào)錯(cuò) *0x11ff = 100;
提示間接的需要指針操作符。這是因?yàn)殚g接訪問操作符*,必須跟指針類型的表達(dá)式結(jié)合,而0x11ff是字面值常量,是整型。那么修改為指針類型:
//使用c 的強(qiáng)制類型轉(zhuǎn)換,把0x11ff 從整型轉(zhuǎn)換為了指向整型的指針類型,它唯一的用處就是當(dāng)程序需要訪問內(nèi)存里一些特定的位置時(shí),比如直接訪問硬件的一些特定接口(地址固定)。那么可以使用,其他不行,因?yàn)槲覀儫o法預(yù)測(cè)編譯器會(huì)把變量放在何處。 *(int *)0x11ff = 3.14;
數(shù)組名是這個(gè)數(shù)組首元素的地址,是一個(gè)指向某類型的常指針(或者說成指針常量),但是數(shù)組和指針是不同的!
數(shù)組是包含多個(gè)元素的一種存儲(chǔ)結(jié)構(gòu),通過數(shù)組名來標(biāo)記。指針僅僅是一個(gè)標(biāo)量而已。只有當(dāng)數(shù)組在表達(dá)式(確切的說是數(shù)組在函數(shù)定義頭,函數(shù)聲明(當(dāng)做參數(shù)傳遞),或者類似*(array + size等)使用,編譯器才會(huì)為數(shù)組生成一個(gè)指針常量,用數(shù)組名標(biāo)記這個(gè)常量。故不要把數(shù)組名和指針經(jīng)常性的混為一談。且靜態(tài)存儲(chǔ)的數(shù)據(jù)結(jié)構(gòu),新航道雅思培訓(xùn)程序運(yùn)行期間,內(nèi)存地址固定不變,比如c 和 c++的數(shù)組。
數(shù)租在表達(dá)式里使用的例外情況
使用 sizeof 操作符,數(shù)組名不是指針常量,求的是數(shù)組占據(jù)的內(nèi)存字節(jié)數(shù)
//長(zhǎng)度為3的整型數(shù)組 array int array[3]; printf("%d \n", array);//array 是指針,打印數(shù)組的手地址 printf("%d \n", sizeof(array[0]));//打印的是數(shù)組中的單個(gè)元素的大小,int 類型 4個(gè)字節(jié) 32位 printf("%d \n", sizeof(array));//打印數(shù)組的大?。ú皇窃貍€(gè)數(shù)),占據(jù)的內(nèi)存字節(jié)數(shù),12
1606416396 (如果是%p解析是 :0x7fff5fbff80c )
4
12
使用&操作符,數(shù)組名不是指針常量
int array[10] = {0}; int *p = &array[0]; printf("%p \n", p);//打印的是數(shù)組手地址 int *pp = array; printf("%p \n", pp);//打印的是數(shù)組手地址 //對(duì)數(shù)組明取地址操作,如果數(shù)組名是解釋為了指針常量,那么q 就是指向指針常量的指針 int *q = &array;//那么*q就是指針常量了??? printf("%p \n", *q);//打印的0,不對(duì)
此時(shí)的數(shù)組名,是一個(gè)指向數(shù)組的指針!指向的是一個(gè)地址數(shù)組。
//對(duì)數(shù)組明取地址操作 int (*q)[10] = &array;
指向的是一個(gè)1行10列的二維地址數(shù)組。這還關(guān)系到了多維的數(shù)組和指針的關(guān)系。
先看一維數(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;//有兩種方式
下面的值是等價(jià)的
dates == &dates[+ == &dates[*(dates + ) ==dates[
這些都說明c標(biāo)準(zhǔn)在描述數(shù)組的時(shí)候,借助了指針,他們有密切聯(lián)系
int i[SIZE]; *(i + SIZE);//意思是尋址到內(nèi)存中的i(數(shù)組首元素地址),然后繼續(xù)找SIZE個(gè)單位,找到該地址再取出里面存放的數(shù)值
注意:間接運(yùn)算符*的優(yōu)先級(jí)高于+
*i + 10;//意思是整型數(shù)組i的首元素的值(一個(gè)整數(shù))和整數(shù)10相加的和*(i + 10);//意思是i數(shù)組的第11個(gè)元素的值
c 的數(shù)組不能用=進(jìn)行復(fù)制:只能是循環(huán)遍歷每個(gè)元素進(jìn)行賦值,如果直接賦值數(shù)組名,僅僅是把指針常量的一個(gè)拷貝賦值了。
數(shù)組的下標(biāo)范圍不被編譯器檢測(cè)(不僅僅是 c 和 c++,很多其他語言也是)
數(shù)組的下標(biāo)一般都認(rèn)為是不能為負(fù)數(shù)的,要小于0,也不能大于數(shù)組的維數(shù)(長(zhǎng)度-1),但是有一個(gè)情況是特殊的
int array[5] = {0, 1, 2, 3, 4}; int *p = array + 1;//p 指針指向了 array 的第2個(gè)元素,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)引用和間接訪問方式*是等價(jià)的 p[-10] = 10;//編譯器沒報(bào)錯(cuò),但是這樣確實(shí)是非法操作,編譯器并不檢測(cè)數(shù)組的下標(biāo)范圍 p[100] = 2;//非法
c 的數(shù)組下標(biāo)不被編譯器檢測(cè),如果越界,我們無法得知編譯器把它指向了哪里,可能造成不可估量的錯(cuò)誤。因?yàn)闄z測(cè)的開銷太大了。
另一種數(shù)組下標(biāo)引用的寫法
int a[5] = {0}; 2[a] = 1;
完全正確的,說明編譯器內(nèi)部實(shí)現(xiàn)下標(biāo)的方法邏輯里,是包含這樣的特例的。但是不能那么寫。不規(guī)范。等價(jià)于 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));
第一個(gè)參數(shù)把數(shù)組首地址和數(shù)組的類型傳遞給了函數(shù)sum,第二個(gè)參數(shù)把數(shù)組大小傳遞給函數(shù)
當(dāng)且僅當(dāng)在給函數(shù)原型聲明或者函數(shù)定義的時(shí)候可以用int *i代替 int i[],任何情況下,int *i;都表示i是指向int類型的指針,而int i[];可以表示i是指向int的指針(只在函數(shù)原型聲明和函數(shù)定義的時(shí)候可用),還有可以表示i是數(shù)組i的首地址。
必須掌握如下四個(gè)等價(jià)方式(函數(shù)原型聲明可以省略參數(shù)名字),注意只有在函數(shù)原型聲明或者函數(shù)定義頭的時(shí)候int *i和int i[]是等價(jià)的。
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í)候不能省略參數(shù)名字,必須掌握這兩個(gè)形式
int sum(int *ii, int num){}int sum(int ii[], int num){}
需要明白的是:指針不論在什么情況下,對(duì)于采用四字節(jié)地址的計(jì)算機(jī)系統(tǒng),大小都是4個(gè)字節(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);
一個(gè)問題:判斷優(yōu)先級(jí)
total += *start ++; //一元運(yùn)算符*和++具有相同的優(yōu)先級(jí),結(jié)合從右向坐。注意到這里是后綴自增,那么應(yīng)該是先把指針指向的數(shù)據(jù)加到total,然后指針自增,等價(jià)于循環(huán)體里的兩行代碼效果
如果是前綴自增,那么就是
total += *++start ;
指針先自增,然后再用自增之后指向的值累加到total。如果是
total += (*start )++;
那就是賦值完成之后,指針指向的內(nèi)容自增,而不是指針變量自增,效果是指針指向的地址不變,但里面的元素變了。
C語言中,i[SIZE]和*(i + SIZE)是等價(jià)的
不論i是數(shù)組名還是i是指針變量。不過注意:只有當(dāng)i是一個(gè)指針變量的時(shí)候,才可以使用諸如:i++的運(yùn)算
對(duì)數(shù)組內(nèi)容的保護(hù)
當(dāng)函數(shù)傳入的參數(shù)需要改變,那么就必須傳遞指針(c++引用),處理數(shù)組的時(shí)候,原則上是必須用指針的,雖然也可以用值,那么函數(shù)必須先為原數(shù)組的拷貝分配一個(gè)足夠的內(nèi)存空間,再把原數(shù)組的數(shù)據(jù)寫入到這個(gè)空間的新數(shù)組里,效率很低。故用指針進(jìn)行傳遞,然函數(shù)去直接操作原數(shù)組,方便快捷。
對(duì)形式參數(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)錯(cuò)。注意:這樣定義的話,只是在函數(shù)里對(duì)原數(shù)組不改變,在外面就不是常量對(duì)待了。c 里,const關(guān)鍵字可以定義符號(hào)常量(c99新規(guī)定,以前就是#define),還可以創(chuàng)建數(shù)組常量,指針常量,和指向常量的指針
const int array[2] = {1, 2};//數(shù)組常量array[0] = 2;//error C2166: 左值指定 const 對(duì)象
指向常量的指針
double arrayD[5] = {1, 2, 3, 4, 5};const double *pd = arrayD;//指向常量的指針,指針pd指向了數(shù)組的開頭,pd可變,*pd是常量,不可變。
通常把指向常量的指針作為函數(shù)參數(shù),說明函數(shù)不會(huì)用這個(gè)指針修改數(shù)據(jù)。
注意區(qū)別:普通指針和指向常量的指針
把常量或者非常量的數(shù)據(jù)賦值給指向常量的指針都是可以的(因?yàn)楸旧碇羔樦赶虻氖浅A浚?,但是?duì)于普通指針,只能給它賦值非常量地址
指針和二維數(shù)組的關(guān)系
int arrayInt[4][2];//整型數(shù)組的數(shù)組 //數(shù)組名arrayInt同時(shí)也是數(shù)組首元素地址,而本數(shù)組的首元素又是包含兩個(gè)int元素的數(shù)組 //故數(shù)組名arrayInt也是包含兩個(gè)int類型元素的數(shù)組的地址 arrayInt == &arrayInt[0];//ok的 //而arrayInt[0]又是一個(gè)包含兩個(gè)int元素的數(shù)組 arrayInt[0] == &arrayInt[0][0];//ok
arrayInt【0】是第一行里,第一個(gè)整數(shù)元素的地址,而arrayInt是兩個(gè)整數(shù)元素的地址(第一行的數(shù)組的首地址,其實(shí)也就是第一行里第一個(gè)元素的地址),也就是說,二維數(shù)組的首地址,和里面第一行數(shù)組的首地址相等。
arrayInt == arrayInt[0];//數(shù)組首地址==第一行數(shù)組的首地址 arrayInt == &arrayInt[0][0]; &arrayInt[0] == arrayInt[0]; &arrayInt[0] == &arrayInt[0][0];//他們都是等價(jià)的四個(gè)語句
注意:指針加減之后的變化
指針加一,那么會(huì)對(duì)原來的數(shù)值加一個(gè)和指針指向內(nèi)容的類型大小相對(duì)應(yīng)的一個(gè)數(shù)值(不是單純的加一)。故二維數(shù)組里,arrayInt+1和arrayInt[0] + 1不一樣。
ArrayInt指向的內(nèi)容大小為兩個(gè)int大小,而arrayInt[0]指向的內(nèi)容大小為一個(gè)int。
關(guān)鍵點(diǎn):指針取值的變化
對(duì)一個(gè)指針(地址)使用間接運(yùn)算符取值,可以得到該指針指向的內(nèi)容的數(shù)值。
arrayInt[0]是二維數(shù)組第一行數(shù)組的首元素地址(&arrayInt[0][0])。*arrayInt[0]取出的是arrayInt[0][0],一個(gè)int元素。*arrayInt取出的是這個(gè)二維數(shù)組首元素的值,即arrayInt[0],而arrayInt[0]本身也是一個(gè)地址=&arrayInt[0][0],故*arrayInt其實(shí)還是一個(gè)地址*arrayInt[0][0],故要取出真正的數(shù)值,還需要再*一次,**arrayInt才是一個(gè)int數(shù)arrayInt[0][0]。即,二維數(shù)組名是一個(gè)地址的地址。這是難點(diǎ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代表兩個(gè)int類型元素首地址,0030F924 + 8, printf("%p\n", i[0] + 1);//0030F928,i[0]代表二維數(shù)組里第一行數(shù)組首元素地址,是一個(gè)int類型對(duì)象,故加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,找到最后一個(gè)元素地址,再取值就是實(shí)際存儲(chǔ)的內(nèi)容。 printf("%p\n", (i[1] + 1));// 0030F930 printf("%p\n", *(i + 1) + 1);// 0030F930,最后一個(gè)元素地址
如果有一個(gè)二維數(shù)組,那么如何聲明一個(gè)指向它的指針?
二維數(shù)組首地址是包含兩個(gè)類型大小的,聲明的時(shí)候需要注意具體是指向幾個(gè),再說創(chuàng)建[x]
int (*ptr)[2];//創(chuàng)建一個(gè)指針變量ptr,指向了一個(gè)包含兩個(gè)int類型值的數(shù)組 //說明創(chuàng)建了一個(gè)指向包含兩個(gè)int類型值的數(shù)組的指針
表達(dá)式中[]的優(yōu)先級(jí)高于*
int *p[2];//意思是:[]先和p結(jié)合,則p[2]是包含兩個(gè)int元素的數(shù)組,再和*結(jié)合,則說明p是兩個(gè)指向int類型的指針構(gòu)成的數(shù)組。 //這樣創(chuàng)建得到兩個(gè)指針,都指向int類型,和前面的表達(dá)式不一樣。
int array[2][2] = {{1, 2}, {3, 4}}; int *p[2] = array;// error C2075: “p”: 數(shù)組初始化需要大括號(hào) int (*p)[2] = array;//ok,括號(hào)先運(yùn)算,就是一個(gè)指針,然后再是p[2]數(shù)組 p = array;//ok
指針的賦值類型的兼容性
指針的賦值原則比數(shù)值類型嚴(yán)格的多,數(shù)值類型賦值有類型轉(zhuǎn)換,但是指針沒有。指針本身也是一種類型!
int n = 5; double d = 1.1; int *pi = &n; double *pd = &d; n = d;//隱式的類型轉(zhuǎn)換,ok pi = pd;//vs2010沒有報(bào)錯(cuò),很詭異 pd = pi;
比較復(fù)雜的:
int *pi1;//創(chuàng)建一個(gè)指針pi1,指向一個(gè)int類型元素 int (*pa)[3];//創(chuàng)建一個(gè)指針pa,pa指向包含三個(gè)int類型元素的數(shù)組 int array1[2][3]; int array2[3][2]; int **pi2;//指向指針類型的指針 pi1 = &array1[0][0];//array1[0][0]是一個(gè)int類型的元素 pi1 = array1[0];//和上句等價(jià) pi1 = array1;//非法的操作,array1是包含三個(gè)元素的數(shù)組的地址 pa = array1; pa = array2;//非法,一個(gè)指向的是3個(gè)int,一個(gè)是2個(gè) pi2 = &pi1;//pi2是一個(gè)指針,但是指向一個(gè)指向int類型的指針類型(存儲(chǔ)的是指向int類型的指針類型本身的地址) //pi1是指針,指向了int類型元素,&pi1得到了指向int類型的指針類型本身的地址
對(duì)于 array1【2】【3】,array1是二維數(shù)組的手地址,指向如圖,這個(gè)地址也是指針,是指向了一個(gè)新的數(shù)組,即二維數(shù)組里的數(shù)組,而這個(gè)數(shù)組包含了三個(gè) int類型的 元素。pil 指針,指向了一個(gè) int 類型元素,而 int (*pa)[3];中的 pa,是一個(gè)指針,指向了一個(gè)包含了三個(gè) int 類型元素的數(shù)組。
多重間接運(yùn)算里的const賦值
int *p1; const int *p2;//創(chuàng)建一個(gè)指向常量的指針p2,也就是指向常量的指針 const int **pp2;//創(chuàng)建一個(gè)指向常量的二級(jí)指針pp2
const在誰前面誰就不允許改變。
常量指針p, int * const p;也說作常指針,是說指針 p 本身是常量,不能修改,但是*p,也就是 p 指向的內(nèi)容可以變,它不同于指向常量的指針。指向常量的指針 p 本身不是常量,p 可以 修改,但是 p 指向的內(nèi)容不能被修改,不一定必須指向常量,也可以指向變量,僅僅是不能被賦值。pp2是指向常量的指針,也是一個(gè)二級(jí)指針,指向指針的指針,也就是一個(gè)指向常量的指針的指針。
p1 = p2;//不能把指向常量的指針,賦值給非指向常量的指針 p2 = p1;//反過來可以 pp2 = &p1;//雖然也是把非指向常量的指針賦值給了指向常量的指針,但是在多層的間接運(yùn)算里非法,很有可能出錯(cuò)!
普通指針p1指向了 p2,p2指向的就可以被賦值修改(違法),故不能用普通指針指向。把非指向常量的指針賦值給指向常量的指針的前提是必須是一層間接運(yùn)算。
多層不合法,下面解釋不合法的原因:
int *p1; const int n = 10; const int *p2;//創(chuàng)建一個(gè)指向常量的指針p2 const int **pp2;//創(chuàng)建一個(gè)指向常量的指針的指針pp2
pp2 = &p1;//假設(shè)可以這樣寫,ok! *pp2 = &n;//本局,&n 是常量,*pp2是指向常量的指針,ok,但是呢,*pp2指向p1也成立了,那么此時(shí) *p1不能被修改。而下句的*p1被賦值修改。 *p1 = 1;//發(fā)生了錯(cuò)誤
注意:指向常量的指針是說指針 p 本身可以變,但是指向的內(nèi)容不能被賦值,也可以是指向變量,僅僅是他們不能被賦值。而常量指針,是常指針,指針 p 本身不可變,但是 p 指向的內(nèi)容能被賦值。
函數(shù)和多維數(shù)組的關(guān)系
通??梢允褂脭?shù)組符號(hào)代替指針參量.可以避免使用指針參量,看一個(gè)例子,求二維數(shù)組的和的程序:
1 #define ROWS 3 2 3 #define COLS 4 4 5 //把二維數(shù)組名(指向首個(gè)子數(shù)組的指針)和行數(shù)作為形參 6 7 void sum_rows(int ar[][COLS], int rows);//ar是指向包含4個(gè)int值的數(shù)組的指針 8 9 void sum_cols(int [][COLS], int);//可以省略名稱 10 11 //ar是一個(gè)指向多維數(shù)組的指針 12 13 int sum2d(int (*ar)[COLS], int rows);//當(dāng)且僅當(dāng),ar是函數(shù)的形式參數(shù),int (*ar)[COLS]和int ar[][COLS]等價(jià) 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ù)組的指針,除了第一個(gè)空可以空,其余空的都要填寫大小,因?yàn)榈谝粋€(gè)空表面這是一個(gè)指針。 74 75 {//當(dāng)然,都填上也可以,編譯器編譯會(huì)自動(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 }
c99新內(nèi)容:可以變化長(zhǎng)度的數(shù)組VLA
一般c數(shù)組的長(zhǎng)度事先定義好,且必須是常量,不能是變量.這樣的話,每次更改數(shù)組的長(zhǎng)度,都要重新定義新的函數(shù),麻煩.故C99引入了VLA(變長(zhǎng)數(shù)組),VLA許可動(dòng)態(tài)分配存儲(chǔ)單元,可以在運(yùn)行的時(shí)候指定數(shù)組的大小,常規(guī)的c數(shù)組是靜態(tài)存儲(chǔ)分配的,數(shù)組大小編譯時(shí)已經(jīng)確定,因?yàn)榫S數(shù)(長(zhǎng)度)是常量。VLA(variable length array)可使用變量作為數(shù)組的長(zhǎng)度,這里的變長(zhǎng),不是說數(shù)組的大小以后可以隨時(shí)變,而是說數(shù)組的長(zhǎng),可以用變量來指定,而數(shù)組的大小創(chuàng)建之后不會(huì)改變.
注意:VLA必須是自動(dòng)存儲(chǔ)類的,必須放在函數(shù)內(nèi)部或者作為函數(shù)參量出現(xiàn),否則報(bào)錯(cuò).VLA聲明的時(shí)候不能初始化.
void sum(int rows, int cols, int arr[rows][cols]);//必須是c99及以后標(biāo)準(zhǔn)的編譯器才支持.否則一樣報(bào)錯(cuò).并且參量的順序不要錯(cuò),先定義維數(shù),才有后面.
//c99規(guī)定,可以省略函數(shù)原型聲明的參數(shù)名,作為數(shù)組的維數(shù)需要用*代替void sum(int , int , int arr[*][*]);
數(shù)組的大小和長(zhǎng)度的關(guān)系
變長(zhǎng)數(shù)組是指用整型變量或表達(dá)式聲明或定義的數(shù)組,而不是說數(shù)組的長(zhǎng)度會(huì)隨時(shí)變化,變長(zhǎng)數(shù)組在其生存期內(nèi)的長(zhǎng)度同樣是固定的。因?yàn)樽冮L(zhǎng)數(shù)組一旦被聲明,其大小就會(huì)保持不變直到生命期結(jié)束。其實(shí)就是可以讓普通的 c 數(shù)組,動(dòng)態(tài)的去創(chuàng)建內(nèi)存,分配未知的存儲(chǔ)單元!但是一旦運(yùn)行期間分配完畢,這個(gè)數(shù)組的大小,照樣以后不會(huì)變!直到內(nèi)存釋放。
c語言把數(shù)組歸結(jié)為派生類型,,比如聲明一個(gè)int類型數(shù)組,一個(gè)char類型數(shù)組等
名稱欄目:c/c++函數(shù)、常量、指針和數(shù)組的關(guān)系梳理
網(wǎng)站URL:http://aaarwkj.com/article24/gjohje.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供App設(shè)計(jì)、Google、網(wǎng)站制作、品牌網(wǎng)站建設(shè)、軟件開發(fā)、網(wǎng)站設(shè)計(jì)公司
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)