c99變長陣列 第九章 C99可變長陣列VLA詳解

2021-10-13 05:01:16 字數 4085 閱讀 7420

c90及c++的陣列物件定義是靜態聯編的,在編譯期就必須給定物件的完整資訊。但在程式設計過程中,我們常常遇到需要根據上下文環境來定義陣列的情況,在執行期才能確知陣列的長度。對於這種情況,c90及c++沒有什麼很好的辦法去解決(stl的方法除外),只能在堆中建立乙個記憶體映像與需求陣列一樣的替代品,這種替代品不具有陣列型別,這是乙個遺憾。c99的可變長陣列為這個問題提供了乙個部分解決方案。

可變長陣列(variable length array,簡稱vla)中的可變長指的是編譯期可變,陣列定義時其長度可為整數型別的表示式,不再象c90/c++那樣必須是整數常量表示式。在c99中可如下定義陣列:

int n = 10, m = 20;

char a[n];

int b[m][n];

a的型別為char[n],等效指標型別是char*,b的型別為int[m][n],等效指標型別是int(*)[n]。int(*)[n]是乙個指向vla的指標,是由int[n]派生而來的指標型別。

由此,c99引入了乙個新概念:可變改型別(variably modified type,簡稱vm)。乙個含有源自vla派生的完整宣告器被稱為可變改的。vm包含了vla和指向vla的指標,注意vm型別並沒有建立新的型別種類,vla和指向vla的指標仍然屬於陣列型別和指標型別,是陣列型別和指標型別的擴充套件。

乙個vm實體的宣告或定義,必須符合如下三個條件:

1。代表該物件的識別符號屬於普通識別符號(ordinary identifier);

2。具有**塊作用域或函式原型作用域;

3。無鏈結性。

ordinary identifier指的是除下列三種情況之外的識別符號:

1。標籤(label);

2。結構、聯合和列舉標記(struct tag、uion tag、enum tag);

3。結構、聯合成員識別符號。

這意味著vm型別的實體不能作為結構、聯合的成員。第二個條件限制了vm不能具有檔案作用域,儲存連續性只能為auto,這是因為編譯器通常把全域性物件存放於資料段,物件的完整資訊必須在編譯期內確定。

vla不能具有靜態儲存週期,但指向vla的指標可以。

兩個vla陣列的相容性,除了滿足要具有相容的元素型別外,決定兩個陣列大小的表示式的值也要相等,否則行為是未定義的。

下面舉些例項來對數種vm型別的合法性進行說明:

#include

int n = 10;

int a[n];        /*非法,vm型別不能具有檔案作用域*/

int (*p)[n];      /*非法,vm型別不能具有檔案作用域*/

struct test

int k;

int a[n];     /*非法,a不是普通識別符號*/

int (*p)[n];   /*非法,p不是普通識別符號*/

int main( void )

int m = 20;

struct test1

int k;

int a[n];         /*非法,a不是普通識別符號*/

int (*p)[n];       /*非法,a不是普通識別符號*/

extern int a[n];       /*非法,vla不能具有鏈結性*/

static int b[n];        /*非法,vla不能具有靜態儲存週期*/

int c[n];             /*合法,自動vla*/

int d[m][n];          /*合法,自動vla*/

static int (*p1)[n] = d;  /*合法,靜態vm指標*/

n = 20;

static int (*p2)[n] = d;  /*未定義行為*/

return 0;

乙個vla物件的大小在其生存期內不可改變,即使決定其大小的表示式的值在物件定義之後發生了改變。有些人看見可變長幾個字就聯想到vla陣列在生存期內可自由改變大小,這是誤解。vla只是編譯期可變,一旦定義就不能改變,不是執行期可變,執行期可變的陣列叫動態陣列,動態陣列在理論上是可以實現的,但付出的代價可能太大,得不償失。考慮如下**:

#include

int main( void )

int n = 10, m = 20;

char a[m][n];

char (*p)[n] = a;

printf( 「%u %u」, sizeof( a ), sizeof( *p ) );

n = 20;

m = 30;

printf( 「/n」 );

printf( 「%u %u」, sizeof( a ), sizeof( *p ) );

return 0;

雖然n和m的值在隨後的**中被改變,但a和p所指向物件的大小不會發生變化。

上述**使用了運算子sizeof,在c90/c++中,sizeof從運算元的型別去推演結果,不對運算元進行實際的計算,運算子的結果為整數常量。當sizeof的運算元是vla時,情形就不同了。sizeof必須對vla進行計算才能得出vla的大小,運算結果為整數,不是整數常量。

vm除了可以作為自動物件外,還可以作為函式的形參。作為形參的vla,與非vla陣列一樣,會調整為與之等效的指標,例如:

void func( int a[m][n] ); 等效於void func( int (*a)[n] );

在函式原型宣告中,vla形參可以使用*標記,*用於中,表示此處宣告的是乙個vla物件。如果函式原型宣告中的vla使用的是長度表示式,該表示式會被忽略,就像使用了*標記一樣,下面幾個函式原型宣告是一樣的:

void func( int a[m][n] );

void func( int a[*][n] );

void func( int a[ ][n] );

void func( int a[*][*] );

void func( int a[ ][*] );

void func( int (*a)[*] );

*標記只能用在函式原型宣告中。再舉個例:

#include

void func( int, int, int a[*][*] );

int main(void)

int m = 10, n = 20;

int a[m][n];

int b[m][m*n];

func( m, n, a );     /*未定義行為*/

func( m, n, b );

return 0;

void func( int m, int n, int a[m][m*n] )

printf( "%u/n", sizeof( *a ) );

除了*標記外,形參中的陣列還可以使用型別限定詞const、volatile、restrict和static關鍵字。由於形參中的vla被自動調整為等效的指標,因此這些型別限定詞實際上限定的是乙個指標,例如:

void func( int, int, int a[const][*] );

等效於void func( int, int, int ( *const a )[*] );

它指出a是乙個const物件,不能在func內部直接通過a修改其代表的物件。例如:

void func( int, int, int a[const][*] );

void func( int m, int n, int a[const m][n] )

int b[m][n];

a = b;        /*錯誤,不能通過a修改其代表的物件*/

static表示傳入的實參的值至少要跟其所修飾的長度表示式的值一樣大。例如:

void func( int, int, int a[const static 20][*] );

int m = 20, n = 10;

int a[m][n];

int b[n][m];

func( m, n, a );

func( m, n, b );     /*錯誤,b的第一維長度小於static 20*/

型別限定詞和static關鍵字只能用於具有陣列型別的函式形參的第一維中。這裡的用詞是陣列型別,意味著它們不僅能用於vla,也能用於一般陣列形參。

總的來說,vla雖然定義時長度可變,但還不是動態陣列,在執行期內不能再改變,受制於其它因素,它只是提供了乙個部分解決方案。

c99變長陣列 c語言程式設計之可變長陣列

是不是看著標題進來了,認為自己看錯了?因為平常我們c程式設計的時候資料長度都是固定好的,沒有聽說過c還能實現變長陣列?不過你沒看錯,c99中確實是有柔性陣列的概念,也就是可變長陣列,c99中允許結構體中的最後乙個成員是長度未知的陣列,其定義格式如下 typedef struct int a char...

C99中的變長陣列(VLA)

處理二維陣列的函式有一處可能不太容易理解,陣列的行可以在函式呼叫的時候傳遞,但是陣列的列卻只能被預置在函式內部。例如下面這樣的定義 define cols 4 int sum3d int ar cols int rows 現在假定了如下的陣列 int array1 5 4 int array2 10...

C99新增的柔性陣列

我總結了一下用指標和用變長結構體的區別 1.在位置方面 指標可以放在任何地方,但是變長結構體的變長部分一定要放在結構體的最後。2.在記憶體占用方面 指標會佔乙個指標的大小的記憶體空間,但是變長陣列是不佔記憶體的,它只是乙個佔位符。3.在記憶體布局方面 指標指向的記憶體和結構體的記憶體可以是不連續的,...