我想弄清楚為什么以多維陣列為形式引數的函式的簽名中,第一維是沒有尺寸的,而其他的則沒有。 事實上,上述宣告的第二部分的答案很清楚:如果不傳遞維度資訊,編譯器將不知道多維陣列在記憶體中如何組織。 困擾我的是這種不一致。
為什么不要求所有的維度資訊在記憶體中組織起來?
為什么不要求明確指定所有尺寸(包括第一個尺寸)?
我想出了一個理論,我想看看這個理論是否正確。
陣列最常見的用法是一維陣列。 鑒于陣列名稱是指向陣列第一個元素的指標,Dennis Ritchie 希望為一維陣列提供完全相同的簽名,無論使用陣列語法還是使用指標語法。
對于指標語法,如果不同時指定大小資訊,就不可能知道在函式中要處理多少個元素。 所以 Dennis Ritchie 強制要求陣列語法也采用同樣的簽名,即讓陣列沒有大小(甚至讓編譯器完全忽略第一維的大小資訊,如果提供的話)。 換句話說,你將讓一個形式引數是一個指標,或者是一個沒有大小的陣列,而第二個形式引數則是陣列的大小。
。uj5u.com熱心網友回復:
當你傳遞一個陣列時,它就會衰減為一個指向第一個元素的指標。這是為了方便。如果沒有它,傳遞一個字串字面意思給一個函式,比如:
void foo(const char* str){}。
會很麻煩:
foo("Hello world"); //the const char[12] decays into .../span>
foo(&"Hello world"[0]); //沒有衰變的話,必須要這樣寫。
如果你想指定所有的尺寸,只需獲取陣列的地址,你將得到一個指向single元素的指標--指定所有尺寸。
示例。這個函式需要一個指向int[2][10]的指標:
void f2d2(int (*)[2][10]){}。
int a2d[2] [10];
f2d2(&a2d); //你可以這樣呼叫它。
在C 中,你可以通過參考陣列來防止陣列衰變為第一個元素的指標。
void f2d2(int (& )[2][10]){}。
int a2d[2] [10]。
f2d2(a2d); // no decay
uj5u.com熱心網友回復:
在C標準中,有兩句話使使用陣列更加清晰。
第一句是(6.3.2.1 L值、陣列和函式指定符)
3 除了當它是sizeof運算子或單數& 運算子的運算元,或者是一個用于初始化陣列的字串字面,否則 型別為 "陣列型別 "的運算式被轉換為 "指標型別 "的運算式。 型別為 "陣列型別 "的運算式被轉換為型別為 "指向型別的指標 "的運算式,該運算式指向陣列物件的初始元素。 指向陣列物件的初始元素,并且不是一個lvalue。如果陣列物件 有暫存器存盤類,該行為是未定義的。
而第二個是(6.7.6.3 函式宣告器(包括原型))
7 將引數宣告為''型別的陣列''應被調整為'合格的指標'。 應調整為''限定型別的指標'',其中型別限定詞(如果有的話 是那些在陣列型別派生的[ 和 ]中指定的。 如果關鍵字static也出現在陣列型別派生的[ 和 ]中,那么對于每一次呼叫 型別推導中,那么對于每次對函式的呼叫,相應的實際引數的值應提供對 相應的實際引數應提供對陣列的第一個 的第一個元素,該元素的數量至少是由 size運算式所指定的元素數的第一個元素。
相對于函式宣告而言,這意味著什么呢?
如果你宣告一個函式,例如
void f( int a[100] ) ;
那么編譯器將以如下方式調整函式引數
void f( int *a )/span>。
因此,例如,這些函式宣告是等價的
void f( int a[100] ) 。
void f( int a[10] )/span>;
void f( int a[1] )/span>;
void f( int a[] ) ;
void f( int *a )/span>;
并且宣告相同的一個函式。你甚至可以在你的程式中包含所有這些宣告,盡管編譯器會發出一個訊息說有多余的宣告。
在這個函式中,變數a的指標型別為int *。
另一方面,你可以通過不同大小的陣列來呼叫函式。陣列代號將被編譯器隱含地轉換為指向其第一個元素的指標。你甚至可以通過一個指標來傳遞一個標量物件。
。所以這些函式的呼叫都是正確的
int a[100] 。
f( a );
int a[10]。
f( a );
int a[1];
f( a );
int a;
f( &a )。
結果是,該函式沒有任何資訊是用什么陣列作為引數的。所以你需要宣告第二個函式引數,它將指定所傳遞的陣列的大小(如果該函式不依賴于陣列中存在的哨兵值)
例如
void f( int a[], size_t n )。
如果你有一個像這樣的多維陣列
T a[N1][N2][N3]...[Nn]。
那么指向其第一個元素的指標將具有這個型別
T ( *a )[N2][N3]...[Nn]。
因此,一個函式宣告如下
void f( T a[N1][N2][N3] ... [Nn] )。
等同于
void f( T ( *a )[N2][N3] ... [Nn] )/span>。
如果N2, N3,...Nn是整數常數運算式,那么這個陣列就不是一個變長陣列。否則它就是一個可變長度的陣列,并且該函式可以像這樣宣告(當宣告不是函式定義的一部分時)
void f( T ( *a ) [*] [*] ... [*] ) ;
對于這樣一個函式,還有一個問題是確定子陣列的大小。所以你需要宣告一些引數來指定這些大小。
例如
void f( size_t n1。size_t n2, size_t n3,. ..., size_t nn, T a[][n2][n3]...[nn] )/span>。
至于C ,那么可變長度陣列并不是一個標準的C 功能。同時,你可以宣告一個函式引數具有一個參考的型別。
例如
void f( int ( & a)[10] )。
在這種情況下的函式中,a不是a的指標。它表示一個陣列,運算式sizeof( a )將產生整個陣列的大小而不是指標的大小。
uj5u.com熱心網友回復:
在C語言中,不可能按值傳遞陣列。你只能傳遞一個指標,然后在函式中,從指標的索引來訪問陣列的內容。
相應地,在函式宣告中,如果你指定了帶有陣列型別的引數,在進一步分析之前,該引數會被調整為指標型別。例如:
void f( int x[][5] ); void f( int (*x)[5]);如果第一種形式被取締,即你必須用指標版本來定義引數,那么這門語言的功能也是一樣的。這只是語法上的糖--事實證明,這給新手帶來了很多困惑,但這是另一個故事。
。所以回答你的問題 -- 所有的陣列尺寸調整后必須被指定,因為否則編譯器在通過傳遞的指標引數訪問陣列時不知道如何索引記憶體。 現在應該很清楚,假的第一個 "維度 "是不相關的,因為它是語法上的糖,會立即被丟棄。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/322435.html
標籤:
