標籤:c
Do you master on array in C ?
由於新標準C99的支援變長數組, 幾乎C的標準特性就是看著gcc來的(Linux 核心嚴重依賴GCC)
int mani(){ const int a = 10; int array[a]; return 0;}
這段代碼能過編譯嗎?
在2012年是過不了的,2014年就可以了(時間改變一切啊~)
在VC++6.0上做的測試結果
-
sdev98\bin\hello.c(7) : error C2057: expected constant expressionf:\vc++6.0\microsoft visual studio\common\msdev98\bin\hello.c(7) : error C2466: cannot allocate an array of constant size 0f:\vc++6.0\microsoft visual studio\common\msdev98\bin\hello.c(7) : error C2133: ‘array‘ : unknown sizeError executing cl.exe.
-
-
過不了。選VC6.0的原因是因為這傢伙的編譯器的規範和早期的ANSI是符合的很好的.早期的ANSI標準不支援變長數組的特性
-
-
但是!C99明文規定, C語言加入了變長數組的特性
#include <stdio.h>#include <stdlib.h>int main(){ int a = 10;char b[a];int *p = &a;(*p)++;printf("%d %d\n", sizeof(b), a);return 0;}
能過嗎?能!
下面的能過嗎?不能!
int a = 10;int array[a] ;int main(){ return 0l}
上面三個問題能不能答對都不重要了, 下面搞定數組的時候到了,逃避不了新特性——數組變長
一直說數組變長,這裡其實有兩種類型的“變長”—— VLA (varible length array) & VM (varible modified,that is, a pointer to a VLA type)
節選自C99標準(數組的就下面這部分)
Array declarators
Constraints
回答,何為數組?
1 In addition to optional type qualifiers and the keyword static, the [ and ] may delimit an expression or *.
下面if就開始分析情況了...
If they delimit an expression (which specifies the size of an array), the expression shall have an integer type. If the expression is a constant expression, it shall have a value greater than zero.
如果數組的長度是限定的,那麼array[exp]中的exp應該是一個整形數
如果是個常量運算式,這個運算式的值要大於0(知道這裡肯定有人會說struct裡array[0]的用法,別急後面會分析)
The element type shall not be an incomplete or function type. The optional type qualifiers and the keyword static shall appear only in a declaration of a function parameter with an array type, and then only in the outermost array type derivation.
什麼樣的數組可以變長?
2 Only an ordinary identifier (as defined in 6.2.3) with both block scope or function prototype scope and no linkage shall have avariably modified type. If an identifier is declared to be an object with static storage duration, it shall not have a variable length array type.
我還是把6.2.3截屏出來,這樣好論證,反正比較短
這裡的ordinary identifier 最後一項“all other identifiers called ordinary identifiers” ,這裡就注意這裡的ordinary identifier就是四個選項“—” 的最後一項,而不包括前面三項,那麼值得注意的就是struct不屬於ordinary identifier
int a = 10;int array[a];int main(){ return 0;}
這段代碼是過不了gcc的編譯的,數組變長發生在block(就是main函數的{} )之外,
Semantics
3 If, in the declaration ‘‘T D1’’, D1 has one of the forms:
D[ type-qualifier-list opt assignment-expression opt ]
D[ static type-qualifier-list opt assignment-expression ]
D[ type-qualifier-list static assignment-expression ]
D[ type-qualifier-list opt * ]
and the type specified for ident in the declaration ‘‘T D’’ is ‘‘derived-declarator-type-list T ’’, then the type specified for ident is ‘‘derived-declarator-type-list array of T ’’. 121)
數組兩大類型,incomplete type 和complete type
4
先介紹了什麼是incomplete type.
If the size is not present, the array type is anincomplete type.If the size is * instead of being an expression, the array type is a variable length array type of unspecified size, which can only be used in declarations with function prototype scope;
注意,這裡的*,是省略的意思,不是真的在[ ]寫個*
element_type name[size];
這裡size缺失,形成element_type name[]; 叫varible length array可變長數組,其實我們經常用
char string[] = "hello world!\n";
什麼是complete type
such arrays are nonetheless complete types. If the size is an integer constant expression and the element When several ‘‘array of’’ specifications are adjacent, a multidimensional array is declared. Thus, * can be used only in function declarations that are not definitions (see 6.7.5.3). type has a known constant size, the array type is not a variable length array type; otherwise, the array type is a variable length array type.
5 If the size is an expression that is not an integer constant expression: if it occurs in a declaration at function prototype scope, it is treated as if it were replaced by *; otherwise, each time it is evaluated it shall have a value greater than zero.
如果size是一個值不是整形常量運算式的時候,如果這種情況發生在函式宣告的時候,把這個不是常量運算式的size當作*,就是省略掉了,不外乎就是數組傳參數是以指標的形式傳遞的,這裡道出了數組傳參的本質來源.
如果不是發生在函式宣告的參數中,那麼這個時候size必須是個大於0的整數.
The size of each instance of a variable length array type does not change during its lifetime. Where a size expression is part of the operand of asizeof operator and changing the value of the size expression would not affect the result of the operator, it is unspecified whether or not the size expression is evaluated.
任何varible length array的大小在其整個生命週期都不會發生變化.
#include <stdio.h>#include <stdlib.h>int main(){int a = 10;char b[a];int *p = &a;(*p)++;printf("%d %d\n", sizeof(b), a);return 0;}
列印什嗎?10 11的原因就在於上面的解釋
@凱旋衝鋒 發現了一種極贊的做法
#include <stdio.h>#include <stdlib.h>int main(){const int a = 10;char b[a];printf("%d %d\n", sizeof(b), a);int *p = &a;(*p)++;printf("%d %d\n", sizeof(b), a);return 0;}
看!輸出結果是
10 10
11 11
為什嗎?怎麼就變了不是說好整個生命週期數組長度不變的麼,不是說好sizeof都不受影響嗎?wait,看看那個const,不能再贊的技巧,這裡涉及到了一次指標的強制類型轉換
看到int *p = &a; 這裡&a 是const int a的地址,極其對於這個地址怎麼解釋呢?看它的資料類型——const int
類似的,char c;&c 就會把這個地址解釋為儲存的是一個char類型的變數.
Just think about it.
int*p = &a; p 指向的是一個int類型的變數,&a 指向的是一個const int的變數,上帝,如果你對p 解引用,然後你是可以改變p指向的值的,為什嗎?因為p告訴你它指向的類型是int類型 你想怎麼改怎麼改,而const int不行!
說白了就是同一個記憶體位址,你使用了不同的方法去解釋它,一種解釋方法是p,一種解釋方法是&a
把這兩者結合起來會發生很奇妙的作用,hold住!
當你通過a去訪問這塊記憶體的時候是read only的(const),當你去通過p去訪問這塊記憶體的時候是read&write的,int類型嘛.
a++;是不允許的,你嘗試通過read only的方式去改變a標記的記憶體的資料
(*p)++;是允許的,p的訪問方式是 r w
你會很淡定的發現只有一個error!warning和我推測的一樣,是有強制類型轉換.
但是不確定這個做法的後果,沒有研究它對stack其他儲存位置的影響,這幾乎是一個“魔法師的魔術",但是你要知道它發生在stack上面,而且之前array的大小其實定下來了,我懷疑這種做法是一種”詐騙“,並沒有實質上的去改變數組的大小,可以說是一種“合法的數組越界” (關於這點我們可以繼續討論,這種做法對於stack記憶體布局的影響)
6 For two array types to be compatible, both shall have compatible element types, and if both size specifiers are present, and are integer constant expressions, then both size specifiers shall have the same constant value. If the two array types are used in a context which requires them to be compatible, it is undefined behavior if the two size specifiers evaluate to unequal values.
這裡多維陣列的compatible問題我還不是很明白,以至於後面圖片的的那個demo我也不是很明白,這裡還要請
Essential On C & linux 的teammate多多指教
下面開始放例子了,介紹數組的用法,以及違法情況
7 EXAMPLE 1
float fa[11], *afp[17];
declares an array of float numbers and an array of pointers to float numbers.
8 EXAMPLE 2
Note the distinction between the declarations
extern int *x;
extern int y[];
The first declares x to be a pointer to int; the second declares y to be an array of int of unspecified size (an incomplete type), the storage for which is defined elsewhere.
9EXAMPLE 3
The following declarations demonstrate the compatibility rules for variably modified types.
extern int n;extern int m;void fcompat(void){int a[n][6][m];int (*p)[4][n+1];int c[n][n][6][m];int (*r)[n][n][n+1];p = a; // invalid: not compatible because 4 != 6r = c; // compatible, but defined behavior only if n == 6 and m == n+1 這裡不是很明白,感覺r 和c 和不來啊,總覺得維數都不一樣}
10 EXAMPLE 4
All declarations of variably modified (VM) types have to be at either block scope or function prototype scope.
這句話就死死的把指向變長數組的指標——VM 的使用範圍僅限於block 內部
Array objects declared with the static or extern storage-class specifier cannot have a variable length array (VLA) type.
任何具有靜態或者外部連結特性的objects,都不能VLA(這幾乎可以理解為,VLA只發生在棧上,如果我的觀點有誤,希望能夠交流討論)
However, an object declared with the static storage-class specifier can have a VM type(that is, a pointer to a VLA type). Finally, all identifiers declared with a VM type have to be ordinary identifiers and cannot, therefore, be members of structures or unions.
VM最後不能是結構體member
圖中的 int(*s)[m] 就是 VM
最後不忘記討論數組size == 0的情況,這裡只能用在結構體裡面
這是一種很經典的做法,以至於C99標準裡面有特別聲明,不是所謂的“奇蹟淫巧”,這裡時有正規說明的!哈哈
對於使用方法,下面的link我有總結
http://blog.csdn.net/cinmyheart/article/details/28985843
As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called aflexible array member. With two exceptions, the flexible array member is ignored. First, the size of the structure shall be equal to the offset of the last element of an otherwise identical structure that replaces the flexible array member with an array of unspecified length. 106) Second, when a . (or ->)
operator has a left operand that is (a pointer to) a structure with a flexible array member and the right operand names that member, it behaves as if that member were replaced with the longest array (with the same element type) that would not make the structure larger than the object being accessed; the offset of the array shall remain that of the flexible array member, even if this would differ from that of the replacement array. If this array would have no elements, it behaves as if it had one element but the behavior is undefined if any attempt is made to access that element or to generate a pointer one past it.
這種是flexible array member,注意區分 VLA和 VM
林蔭道 霍貝瑪 荷蘭 1689年 140 × 103cm 油畫 英國倫敦國家美術館
此圖展現了鄉野的美麗風光,畫面上寧靜的鄉間景緻看似平淡,卻耐人尋味,使觀者心曠神怡。對稱的小樹於平穩中見動感,隨小道向前遠望還能看到左旁的教堂尖頂,右旁兩幢高頂茅屋,車轍印在泥濘的村道上,表現出一種正在延續著的平靜而艱難的生活,佔有大部分畫面的天空則雲蒸霞蔚,美得令人陶醉。這幅畫中還帶著一種憂傷的諷刺意味,長長的林蔭大道在灰暗的地平線處消失到一點,畫中這種憂傷的諷喻也是霍貝瑪現實生活的真實反映。
Do you master on array in C ?