您好,登錄后才能下訂單哦!
壓力才有動力,15年中旬就要準備實習,學習復習學習復習學習復習學習復習……無限循環中,好記性不如爛筆頭……從數組開始,為主干。
c 的array由一系列的類型相同的元素構成,數組聲明包括數組元素個數和類型,c 中的數組參數是引用形式傳參(傳址調用),而常量標量是按值傳遞。
//[]方括號表示聲明的是數組,里面的數字表明了數組包含的元素數目 int states[50];//聲明50個整數的數組 double code[365];//聲明365個浮點數的數組 char chr[20];//聲明20個字符的數組
數組下標,不同語言不一定一樣,c 是0開始。
ANSI C 才支持的 數組的標準初始化:
int pow[8] = {1, 2, 3, 4, 5, 6, 7, 8};//只有標準c(ANSI C)支持此初始化語法
注意:數組聲明的時候最好是初始化,雖然不會報錯,但是和普通變量一樣,使用沒有初始化的數組,里面元素的值不定,出現垃圾值。
數組初始化列表和大小不一致的情況
初始化列表里的元素個數和數組大小不一致,當初始化元素數目少于數組大小,多余的元素自動初始化為0
注意區分:如果沒有初始化數組,那么和普通變量一樣,存取的內存原來就有的垃圾值。初始化了,只不過部分初始化,那么編譯器會自動初始化余下的元素為0。初始化了,但是初始化列表元素大于數組大小,那么編譯報錯,內存溢出。
//初始化沒有完全,編譯器自動后面給初始化為0 double d[4] = {1};//不過vs2010里編譯運行,全都是0 //error C2078: 初始值設定項太多 double d[4] = {1, 2, 3, 4, 5, 6};
數組大小可以用整型常量(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表達式在c里被認為返回一個整數常量 double d5[-10];//error C2118: 負下標 double d6[0];//error C2466: 不能分配常量大小為 0 的數組 double d7[3.14];// error C2058: 常量表達式不是整型 double d8[(int)3.14];//ok double d9[const_m];// error C2057: 應輸入常量表達式,“d9”未知的大小,不能分配常量大小為 0 的數組 double d10[n];//error C2057: 應輸入常量表達式,“d9”未知的大小,不能分配常量大小為 0 的數組
c99之后,后兩種方式可以了,并且C99有了一種新的數組的名詞,叫VLA(variable length array)變長數組,VLA。目的是為了讓c更適合做數值計算。
數組初始化小技巧
省略填寫數組大小,讓編譯器自動的去判斷數組的實際大小和初始化列表里的項目
//空的方括號告訴編譯器去初始化列表里判斷數組實際大小 char chr[] = {'a', 'b', ' ', '4'}; int i; //這里需要一個技巧,人工判斷數組大小容易出錯,使用sizeof運算符計算 for (i = 0; i < sizeof(chr) / sizeof(chr[0]); i++) { printf("\n%d=%c", i + 1, chr[i]); }
#define NUM 4//采用標識符常量代表數組大小,推薦的技巧
int days[NUM] = {1, 2, 3, 4};//良好的編程風格,如果以后想修改數組大小,只需要修改開頭的常量即可
知道C99的新特性:對數組指定元素初始化
可以對數組指定的元素直接初始化,如果對數組最后一個元素初始化,那么傳統語法必須順次初始化到最后一個元素前,才能對最后一個元素初始化。
//對最后一個元素初始化為1 float f[3] = {0, 0, 1};
而c99規定可以直接去初始化
//使用方括號【】,直接對數組某個元素初始化 int i[3] = {i[2] = 100};//ok!需要加 數組名【】
對于一般的數組初始化,部分初始化之后,余下的自動初始化為0(這里vs2010編譯器不是這樣的,全部都是0 了),如果在指定的初始化元素后還有別的初始化值,那么這些數值自動對數組后續元素進行初始化,并且指定初始化元素的數值在第幾個元素處,那么也會同樣初始化這個元素。
int in[7] = {1, 2, in[2] = 10, 3, 4, in[0] = 11}; //首元素=11
只讀數組
如果只需要對數組讀取,而不進行修改,那么推薦關鍵字const,
const int days[NUM] = {1, 2, 3, 4};//數組中每個元素都當作常量處理,和普通變量一樣,const數組也需要聲明的時候初始化
數組的賦值
使用數組的下標(索引)為數組元素賦值,c不支持整體賦值,也不支持用花括號括起來的列表進行賦值(初始化除外)這是個注意點
double d[SIZE] = {1, 2, 3};//ok這是可以的,列表進行初始化
int i[SIZE], count; i = d;//error,c不支持數組整體賦值
d[SIZE] = {11, 22, 33};//不起作用,這種形式只有初始化可以使用,賦值必須使用索引
數組的邊界問題
數組索引不能越界,因為編譯器不會檢測這種錯誤。如果出現非法的索引,那么結果未知,有時候會中斷,但是也有時候可以執行,但是結果很奇怪(編譯器不同而不同)
編譯器不檢測數組邊界,是出于信任程序員,可以節省時間和效率。
二維數組(注意:其實c 語言中只有一維數組,英文里沒有多維數組的說法,僅僅是數組的數組)
數組的數組,因為數組的內容在內存是連續存儲的。
double d[5][12];//定義一個數組的數組(二維數組),5個由12個浮點數組成的數組的數組
d[5] 是一個包含5個元素的一維數組,而其中5個元素的每一個元素又是一個包含12個浮點數的數組,也就是說,數組d的每個元素類型都是double[12],這里要注意區分。首元素就是d[0][0],是double類型的,二維數組有5行,每行包含12列。改變第二個下標是沿著行移動,改變第一個下標是沿著列垂直移動。且數組是順序存儲的。
二維數組的初始化
int i[3][3] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };
數組的數組進行初始化,外層的每一維數組初始化列表都用花括號括起來,而且中間也是用逗號隔開.對于一維數組的初始化問題,同樣適用于二維數組,比如第一行列表里元素少了,那么剩余的元素自動初始化為0,如果多了,同樣報錯,而且一行和一行獨立的,不影響以后的賦值.且 c 的數組是行優先存儲。
也可以省略內部的括號,只要最外面那層的括號,但是要保證正確.
int i[3][3] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };//不建議,因為多了容易出錯
注意:多維數組初始化的兩種方式效果不同
//第一種就是內部初始化列表也帶括號的 int i[2][3] = { {4, 5}, {7, 8} }; //很明顯,初始化列表不夠數組大小
還有一種內部不帶花括號
int i[2][3] = { 4, 5, 7, 8 }; //很明顯,初始化列表不夠數組大小
多維(二維以上)數組
double d[2][3][4];//三維數組的聲明形式,更多維也一樣
通常處理一維數組用一層循環,二維數組用兩層循環,多維用多層循環,以此類推,他們的初始化是一樣的,大同小異。不過多維的用的不是太多。
數組和指針的聯系
數組名可以當指針用,此時數組名的值是一個指針常量,是數組的第一個元素的內存地址。針對這幾個常見的名詞:常量指針,指針常量,常指針,指向常量的指針,指向常量的常量指針,和指向普通變量的常量指針,說明:經個人的學習和理解,查閱資料,我還是認同《c++ primer》、<pointers on c>、<c primer>中各位作者的叫法。并不贊同,一些博客或者網友的說法,貌似以前的老師也沒這么教過。
『指針常量』在 c 和 c++中餅沒有專用的寫法,很少直接用到。
肯定的是:數組名當指針用的時候,這個指針的內容是一個地址,也就是指針類型的常量,而不是其他的整型或者浮點等基本數據類型的常量,不要和數學概念混。
比如,有一個int 類型的變量x,假設存儲在內存的0x11ff位置:
//打算把100這個常量賦值給變量 x,報錯 *0x11ff = 100;
提示間接的需要指針操作符。這是因為間接訪問操作符*,必須跟指針類型的表達式結合,而0x11ff是字面值常量,是整型。那么修改為指針類型:
//使用c 的強制類型轉換,把0x11ff 從整型轉換為了指向整型的指針類型,它唯一的用處就是當程序需要訪問內存里一些特定的位置時,比如直接訪問硬件的一些特定接口(地址固定)。那么可以使用,其他不行,因為我們無法預測編譯器會把變量放在何處。 *(int *)0x11ff = 3.14;
數組名是這個數組首元素的地址,是一個指向某類型的常指針(或者說成指針常量),但是數組和指針是不同的!
數組是包含多個元素的一種存儲結構,通過數組名來標記。指針僅僅是一個標量而已。只有當數組在表達式(確切的說是數組在函數定義頭,函數聲明(當做參數傳遞),或者類似*(array + size等)使用,編譯器才會為數組生成一個指針常量,用數組名標記這個常量。故不要把數組名和指針經常性的混為一談。且靜態存儲的數據結構,新航道雅思培訓程序運行期間,內存地址固定不變,比如c 和 c++的數組。
數租在表達式里使用的例外情況
使用 sizeof 操作符,數組名不是指針常量,求的是數組占據的內存字節數
//長度為3的整型數組 array int array[3]; printf("%d \n", array);//array 是指針,打印數組的手地址 printf("%d \n", sizeof(array[0]));//打印的是數組中的單個元素的大小,int 類型 4個字節 32位 printf("%d \n", sizeof(array));//打印數組的大小(不是元素個數),占據的內存字節數,12
1606416396 (如果是%p解析是 :0x7fff5fbff80c )
4
12
使用&操作符,數組名不是指針常量
int array[10] = {0}; int *p = &array[0]; printf("%p \n", p);//打印的是數組手地址 int *pp = array; printf("%p \n", pp);//打印的是數組手地址 //對數組明取地址操作,如果數組名是解釋為了指針常量,那么q 就是指向指針常量的指針 int *q = &array;//那么*q就是指針常量了!? printf("%p \n", *q);//打印的0,不對
此時的數組名,是一個指向數組的指針!指向的是一個地址數組。
//對數組明取地址操作 int (*q)[10] = &array;
指向的是一個1行10列的二維地址數組。這還關系到了多維的數組和指針的關系。
先看一維數組和指針的關系
//SIZE值是3 short dates[SIZE]; short *pi; short index; double bills[SIZE]; double *pf; //pi = dates;//把數組地址(首元素的地址)賦值給指針變量 pi = &dates[0]; pf = bills;//有兩種方式
下面的值是等價的
dates == &dates[+ == &dates[*(dates + ) ==dates[
這些都說明c標準在描述數組的時候,借助了指針,他們有密切聯系
int i[SIZE]; *(i + SIZE);//意思是尋址到內存中的i(數組首元素地址),然后繼續找SIZE個單位,找到該地址再取出里面存放的數值
注意:間接運算符*的優先級高于+
*i + 10;//意思是整型數組i的首元素的值(一個整數)和整數10相加的和*(i + 10);//意思是i數組的第11個元素的值
c 的數組不能用=進行復制:只能是循環遍歷每個元素進行賦值,如果直接賦值數組名,僅僅是把指針常量的一個拷貝賦值了。
數組的下標范圍不被編譯器檢測(不僅僅是 c 和 c++,很多其他語言也是)
數組的下標一般都認為是不能為負數的,要小于0,也不能大于數組的維數(長度-1),但是有一個情況是特殊的
int array[5] = {0, 1, 2, 3, 4}; int *p = array + 1;//p 指針指向了 array 的第2個元素,1 //p[]也是數組 p[0] = p[3]; printf("%d \n", p[0]);//打印4 //向前偏移了1,那么就是0元素 printf("%d \n", p[-1]);//打印0,因為 c 的數組的下標引用和間接訪問方式*是等價的 p[-10] = 10;//編譯器沒報錯,但是這樣確實是非法操作,編譯器并不檢測數組的下標范圍 p[100] = 2;//非法
c 的數組下標不被編譯器檢測,如果越界,我們無法得知編譯器把它指向了哪里,可能造成不可估量的錯誤。因為檢測的開銷太大了。
另一種數組下標引用的寫法
int a[5] = {0}; 2[a] = 1;
完全正確的,說明編譯器內部實現下標的方法邏輯里,是包含這樣的特例的。但是不能那么寫。不規范。等價于 a[2],*(a + 2)
函數里的數組和指針
int sum(int *i, int n);
或者這樣聲明
int sum(int i[], int n);
main 函數代碼段
int in[5] = {1, 2, 3, 4, 5}; printf("%d", sum(in, 5));
第一個參數把數組首地址和數組的類型傳遞給了函數sum,第二個參數把數組大小傳遞給函數
當且僅當在給函數原型聲明或者函數定義的時候可以用int *i代替 int i[],任何情況下,int *i;都表示i是指向int類型的指針,而int i[];可以表示i是指向int的指針(只在函數原型聲明和函數定義的時候可用),還有可以表示i是數組i的首地址。
必須掌握如下四個等價方式(函數原型聲明可以省略參數名字),注意只有在函數原型聲明或者函數定義頭的時候int *i和int i[]是等價的。
int sum(int i[], int n);int sum(int [], int);//因為可以省略參數名,故數組名可以省去int sum(int *i, int n);int sum(int *, int);//指針名可以省去
而函數定義的時候不能省略參數名字,必須掌握這兩個形式
int sum(int *ii, int num){}int sum(int ii[], int num){}
需要明白的是:指針不論在什么情況下,對于采用四字節地址的計算機系統,大小都是4個字節,和其他無關,只和計算機系統有關。
sum(in, in + SIZE);
語句里in是數組首元素地址,而數組索引從0開始,那么加size之后實際指向的是end元素的下一位。如果指向最后一位,應為:
sum(in, in + SIZE - 1);
一個問題:判斷優先級
total += *start ++; //一元運算符*和++具有相同的優先級,結合從右向坐。注意到這里是后綴自增,那么應該是先把指針指向的數據加到total,然后指針自增,等價于循環體里的兩行代碼效果
如果是前綴自增,那么就是
total += *++start ;
指針先自增,然后再用自增之后指向的值累加到total。如果是
total += (*start )++;
那就是賦值完成之后,指針指向的內容自增,而不是指針變量自增,效果是指針指向的地址不變,但里面的元素變了。
C語言中,i[SIZE]和*(i + SIZE)是等價的
不論i是數組名還是i是指針變量。不過注意:只有當i是一個指針變量的時候,才可以使用諸如:i++的運算
對數組內容的保護
當函數傳入的參數需要改變,那么就必須傳遞指針(c++引用),處理數組的時候,原則上是必須用指針的,雖然也可以用值,那么函數必須先為原數組的拷貝分配一個足夠的內存空間,再把原數組的數據寫入到這個空間的新數組里,效率很低。故用指針進行傳遞,然函數去直接操作原數組,方便快捷。
對形式參數使用const修飾
如果設計操作數組的函數的初衷是不修改原數據,那么可以在函數原型聲明和定義頭里使用const關鍵字
int sum(const int arr[], int n);
告訴編譯器:arr(其實arr是數組首元素地址)指向的數組是包含的常量元素,不能修改,如果修改就報錯。注意:這樣定義的話,只是在函數里對原數組不改變,在外面就不是常量對待了。c 里,const關鍵字可以定義符號常量(c99新規定,以前就是#define),還可以創建數組常量,指針常量,和指向常量的指針
const int array[2] = {1, 2};//數組常量array[0] = 2;//error C2166: 左值指定 const 對象
指向常量的指針
double arrayD[5] = {1, 2, 3, 4, 5};const double *pd = arrayD;//指向常量的指針,指針pd指向了數組的開頭,pd可變,*pd是常量,不可變。
通常把指向常量的指針作為函數參數,說明函數不會用這個指針修改數據。
注意區別:普通指針和指向常量的指針
把常量或者非常量的數據賦值給指向常量的指針都是可以的(因為本身指針指向的是常量),但是對于普通指針,只能給它賦值非常量地址
指針和二維數組的關系
int arrayInt[4][2];//整型數組的數組 //數組名arrayInt同時也是數組首元素地址,而本數組的首元素又是包含兩個int元素的數組 //故數組名arrayInt也是包含兩個int類型元素的數組的地址 arrayInt == &arrayInt[0];//ok的 //而arrayInt[0]又是一個包含兩個int元素的數組 arrayInt[0] == &arrayInt[0][0];//ok
arrayInt【0】是第一行里,第一個整數元素的地址,而arrayInt是兩個整數元素的地址(第一行的數組的首地址,其實也就是第一行里第一個元素的地址),也就是說,二維數組的首地址,和里面第一行數組的首地址相等。
arrayInt == arrayInt[0];//數組首地址==第一行數組的首地址 arrayInt == &arrayInt[0][0]; &arrayInt[0] == arrayInt[0]; &arrayInt[0] == &arrayInt[0][0];//他們都是等價的四個語句
注意:指針加減之后的變化
指針加一,那么會對原來的數值加一個和指針指向內容的類型大小相對應的一個數值(不是單純的加一)。故二維數組里,arrayInt+1和arrayInt[0] + 1不一樣。
ArrayInt指向的內容大小為兩個int大小,而arrayInt[0]指向的內容大小為一個int。
關鍵點:指針取值的變化
對一個指針(地址)使用間接運算符取值,可以得到該指針指向的內容的數值。
arrayInt[0]是二維數組第一行數組的首元素地址(&arrayInt[0][0])。*arrayInt[0]取出的是arrayInt[0][0],一個int元素。*arrayInt取出的是這個二維數組首元素的值,即arrayInt[0],而arrayInt[0]本身也是一個地址=&arrayInt[0][0],故*arrayInt其實還是一個地址*arrayInt[0][0],故要取出真正的數值,還需要再*一次,**arrayInt才是一個int數arrayInt[0][0]。即,二維數組名是一個地址的地址。這是難點。
int i[2][2] = { {1,2}, {3,4} }; printf("%p\n", i);//0030F924,二維數組首元素地址 printf("%p\n", &i[0]);//0030F924 printf("%p\n", i[0]);//0030F924,二維數組第一行數組首元素地址(其實就是二維數組首元素地址) 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]代表二維數組里第一行數組首元素地址,是一個int類型對象,故加4 printf("%p\n",*i);//取出的還是地址 0030F924,是二維數組首地址(第一行數組首地址) printf("%d\n", **i);//1,第一行數組首地址就是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行數組i+1,然后取值*(i+1)得到第二行數組首地址,再加1,找到最后一個元素地址,再取值就是實際存儲的內容。 printf("%p\n", (i[1] + 1));// 0030F930 printf("%p\n", *(i + 1) + 1);// 0030F930,最后一個元素地址
如果有一個二維數組,那么如何聲明一個指向它的指針?
二維數組首地址是包含兩個類型大小的,聲明的時候需要注意具體是指向幾個,再說創建[x]
int (*ptr)[2];//創建一個指針變量ptr,指向了一個包含兩個int類型值的數組 //說明創建了一個指向包含兩個int類型值的數組的指針
表達式中[]的優先級高于*
int *p[2];//意思是:[]先和p結合,則p[2]是包含兩個int元素的數組,再和*結合,則說明p是兩個指向int類型的指針構成的數組。 //這樣創建得到兩個指針,都指向int類型,和前面的表達式不一樣。
int array[2][2] = {{1, 2}, {3, 4}}; int *p[2] = array;// error C2075: “p”: 數組初始化需要大括號 int (*p)[2] = array;//ok,括號先運算,就是一個指針,然后再是p[2]數組 p = array;//ok
指針的賦值類型的兼容性
指針的賦值原則比數值類型嚴格的多,數值類型賦值有類型轉換,但是指針沒有。指針本身也是一種類型!
int n = 5; double d = 1.1; int *pi = &n; double *pd = &d; n = d;//隱式的類型轉換,ok pi = pd;//vs2010沒有報錯,很詭異 pd = pi;
比較復雜的:
int *pi1;//創建一個指針pi1,指向一個int類型元素 int (*pa)[3];//創建一個指針pa,pa指向包含三個int類型元素的數組 int array1[2][3]; int array2[3][2]; int **pi2;//指向指針類型的指針 pi1 = &array1[0][0];//array1[0][0]是一個int類型的元素 pi1 = array1[0];//和上句等價 pi1 = array1;//非法的操作,array1是包含三個元素的數組的地址 pa = array1; pa = array2;//非法,一個指向的是3個int,一個是2個 pi2 = &pi1;//pi2是一個指針,但是指向一個指向int類型的指針類型(存儲的是指向int類型的指針類型本身的地址) //pi1是指針,指向了int類型元素,&pi1得到了指向int類型的指針類型本身的地址
對于 array1【2】【3】,array1是二維數組的手地址,指向如圖,這個地址也是指針,是指向了一個新的數組,即二維數組里的數組,而這個數組包含了三個 int類型的 元素。pil 指針,指向了一個 int 類型元素,而 int (*pa)[3];中的 pa,是一個指針,指向了一個包含了三個 int 類型元素的數組。
多重間接運算里的const賦值
int *p1; const int *p2;//創建一個指向常量的指針p2,也就是指向常量的指針 const int **pp2;//創建一個指向常量的二級指針pp2
const在誰前面誰就不允許改變。
常量指針p, int * const p;也說作常指針,是說指針 p 本身是常量,不能修改,但是*p,也就是 p 指向的內容可以變,它不同于指向常量的指針。指向常量的指針 p 本身不是常量,p 可以 修改,但是 p 指向的內容不能被修改,不一定必須指向常量,也可以指向變量,僅僅是不能被賦值。pp2是指向常量的指針,也是一個二級指針,指向指針的指針,也就是一個指向常量的指針的指針。
p1 = p2;//不能把指向常量的指針,賦值給非指向常量的指針 p2 = p1;//反過來可以 pp2 = &p1;//雖然也是把非指向常量的指針賦值給了指向常量的指針,但是在多層的間接運算里非法,很有可能出錯!
普通指針p1指向了 p2,p2指向的就可以被賦值修改(違法),故不能用普通指針指向。把非指向常量的指針賦值給指向常量的指針的前提是必須是一層間接運算。
多層不合法,下面解釋不合法的原因:
int *p1; const int n = 10; const int *p2;//創建一個指向常量的指針p2 const int **pp2;//創建一個指向常量的指針的指針pp2
pp2 = &p1;//假設可以這樣寫,ok! *pp2 = &n;//本局,&n 是常量,*pp2是指向常量的指針,ok,但是呢,*pp2指向p1也成立了,那么此時 *p1不能被修改。而下句的*p1被賦值修改。 *p1 = 1;//發生了錯誤
注意:指向常量的指針是說指針 p 本身可以變,但是指向的內容不能被賦值,也可以是指向變量,僅僅是他們不能被賦值。而常量指針,是常指針,指針 p 本身不可變,但是 p 指向的內容能被賦值。
函數和多維數組的關系
通常可以使用數組符號代替指針參量.可以避免使用指針參量,看一個例子,求二維數組的和的程序:
1 #define ROWS 3 2 3 #define COLS 4 4 5 //把二維數組名(指向首個子數組的指針)和行數作為形參 6 7 void sum_rows(int ar[][COLS], int rows);//ar是指向包含4個int值的數組的指針 8 9 void sum_cols(int [][COLS], int);//可以省略名稱 10 11 //ar是一個指向多維數組的指針 12 13 int sum2d(int (*ar)[COLS], int rows);//當且僅當,ar是函數的形式參數,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];//按照列的方式進行二維數組的求和 64 65 } 66 67 printf("列%d的和=%d\n", c, tot); 68 69 } 70 71 } 72 73 void sum_rows(int ar[][COLS], int rows)//創建N維數組的指針,除了第一個空可以空,其余空的都要填寫大小,因為第一個空表面這是一個指針。 74 75 {//當然,都填上也可以,編譯器編譯會自動忽略 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];//按照行的方式進行二維數組的求和 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新內容:可以變化長度的數組VLA
一般c數組的長度事先定義好,且必須是常量,不能是變量.這樣的話,每次更改數組的長度,都要重新定義新的函數,麻煩.故C99引入了VLA(變長數組),VLA許可動態分配存儲單元,可以在運行的時候指定數組的大小,常規的c數組是靜態存儲分配的,數組大小編譯時已經確定,因為維數(長度)是常量。VLA(variable length array)可使用變量作為數組的長度,這里的變長,不是說數組的大小以后可以隨時變,而是說數組的長,可以用變量來指定,而數組的大小創建之后不會改變.
注意:VLA必須是自動存儲類的,必須放在函數內部或者作為函數參量出現,否則報錯.VLA聲明的時候不能初始化.
void sum(int rows, int cols, int arr[rows][cols]);//必須是c99及以后標準的編譯器才支持.否則一樣報錯.并且參量的順序不要錯,先定義維數,才有后面.
//c99規定,可以省略函數原型聲明的參數名,作為數組的維數需要用*代替void sum(int , int , int arr[*][*]);
數組的大小和長度的關系
變長數組是指用整型變量或表達式聲明或定義的數組,而不是說數組的長度會隨時變化,變長數組在其生存期內的長度同樣是固定的。因為變長數組一旦被聲明,其大小就會保持不變直到生命期結束。其實就是可以讓普通的 c 數組,動態的去創建內存,分配未知的存儲單元!但是一旦運行期間分配完畢,這個數組的大小,照樣以后不會變!直到內存釋放。
c語言把數組歸結為派生類型,,比如聲明一個int類型數組,一個char類型數組等
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。