C 為何不允許在函式中直接傳遞陣列?

時間 2021-05-06 04:13:44

1樓:張金戈

很簡單呀,陣列是乙個位址加一段記憶體空間。非陣列string要麼在開頭儲存長度,要麼在結尾用\0結束。陣列本身並沒有這些資訊,所以要額外傳長度

2樓:DNFL

關於這個,我說個c語言的例子,對於c++我不太了解。我認為在宣告乙個陣列時,其識別符號有其作用域,那就是乙個函式內。我試過把陣列放到結構體裡包裹著,把結構體本身和位址函式傳參,都能在結構體裡用巨集指令sizeof正確獲取到該陣列的長度。

那麼問題就是,引用結構體的成員,編譯器是如何確定在一塊結構體的記憶體裡各成員相對首位址的偏移量和大小呢(結構體對齊導致的記憶體空隙)。很明顯,使用者在定義結構體時,預編譯時要生成對應的維護資訊用於跨這個結構體變數識別符號的作用域,或者說,本檔案全域性使用。

為了驗證我的猜想,我檢視了關於結構體的巨集指令,其中就巨集定義了結構體的各個成員相對首位址的偏移量和大小,也就是根據這個巨集定義,編譯器才能正確引用內成員。

反過來看陣列,乙個陣列宣告只需指明資料型別和長度,而無需定義乙個固定的陣列型別(固定型別和固定長度)生成對應的維護資訊用於全域性使用,也就是說其識別符號只能在其宣告的函式內作用。

然後說說傳參,函式需要指定形式引數的型別和識別符號名,乙個用於告訴函式如何訪問引數資料,另乙個用於引用訪問引數。那麼指定引數型別為陣列,可以順帶指定陣列的大小嗎,這在二維或多維陣列中指定下一維的長度完全沒有問題的,但問題就是在最高維,或一維陣列的大小是不能指定,或者說指定沒有意義。

簡單來說,在C語言當中,陣列無需定義即可宣告使用(如果是是自定義資料型別,需要先定義資料型別),宣告陣列時,其識別符號內涵的維護資訊如型別,長度,維度等只在其宣告的函式塊內有用(全域性變數就是全域性有效),函式傳參需要函式指明引數型別,不能依靠引數自維護(讓引數告訴你他是啥型別)同時函式不能指明引數陣列的最高維長度(因為陣列在c中,基本就是連續宣告同一型別的變數放在一條線上,然後用識別符號去管理這玩意)

3樓:Xpecya

因為C語言就不允許…

至於為什麼C語言就不允許…我曾經想過這個問題,我覺得乙個可能的原因在於,乙個函式指標在功能上,比乙個陣列結構體要多一點,說穿了就是指標具有運算元組外記憶體的能力,雖然這個能力對於絕大多數程式設計師來說可能都不是必要的,但對於乙個系統語言而言,也許創始人認為是必要的吧…

或者說這是乙個設計哲學的問題,一門語言,究竟應該提供更高的自由度和功能,還是提供更多的安全性。這可能是乙個永遠都爭論不出結果的問題。

4樓:Artorias

效率和通用性的問題吧,變長陣列執行期才能獲得長度,傳參時是沒辦法在棧上拷貝的,因為編譯器不知道長度。定長的話你的乙個函式只能處理乙個長度的陣列,如果你想傳個更長或者更短的陣列都不行。最後傳參直接傳陣列,要拷貝一次全部資料,顯然沒有傳指標快。

5樓:莫名

因為陣列的長度是陣列型別的一部分,c裡面沒有函式過載,所以就把陣列當成指向陣列首元素的指標處理。c++相容c。

c++中其實是可以傳遞陣列,像這樣的

void foo(int (&num)[9]);

這裡用引用就搞定了,不過只能傳遞長度9的int陣列。

想要更通用就只能用模板了。

6樓:

函式傳值,引數的長度得是確定的,c++和c一樣陣列是基本型別,陣列的內容不包含陣列長度資訊,函式無法知道被呼叫時傳過來的陣列長度,你或許可以使用固定長度的陣列,但如果呼叫此函式的地方有意或無意使用不同長度的陣列引數可能導致溢位。

7樓:布客飛龍

好吧,我發現很多人理解成了這個:C 為何不把陣列元素複製乙份來傳遞陣列

我覺得陣列的值語義和引用語義不是個爭議,因為絕大多數語言和絕大多數庫都當成引用語義來處理的。包括Python,Ruby,JS 這樣的指令碼語言,也包括 JVM 和 .NET 全系列語言。

就算是擁有值語義陣列的語言,比如 C++ 和 Golang,也有變通的方式。

這樣的好處也很明顯,如果每個函式都把陣列複製乙份,開銷太大。並不是每個函式都要修改陣列,你想修改陣列還不想影響原陣列,手動複製乙份就好了。

這樣看來,C語言隨大流有什麼錯?或者說它的設計方案成了日後的主流(這種情況並不多見),應該說是有遠見才對。

先說 C 裡面的陣列引數。有兩種,第一種就是寫成 f(Type arr),這種直接等價於寫成 f(Type *arr) ,在實參賦給形參的過程中,Type 退化成 Type* ,大家都知道了。

還有乙個就是 C99 加入的f(Type arr[static N]),但 C++ 沒有收錄到其標準裡。C99 加入的很多特性都沒有被 C++ 收錄,比如動態(原生)陣列。

原因可能是根本沒有人提這個提案,不過從 C++ 角度來看,我覺得根本原因是沒必要。因為 C++ 有 STL,並且不提倡使用 C 風格的東西,所以 std::vector 和 std::

array 你自己選乙個吧,這兩個東西都可以直接當引數傳遞的。

8樓:

真有趣,知乎的 c++ 水平被這乙個問題就給鑑定出來了。。

c 的陣列也好, 操作符也好都只是語法糖。a[b]==*

(a+b)

9樓:sdhjkd

如果你是指廣泛意義的陣列的話,在現代C++程式中,這是可以簡單且安全地實現的。

請參考std::array - cppreference.com舉例子,

void

Camera

::setPosition

(const

std::

array

<3,int>

&array

)camera

.setPosition();

//ok

你甚至可以傳遞陣列作為返回值

std::

array

<3,int>

Camera

::getPosition();

}auto

array

=camera

.getPosition

();// ok, array is array<3, int>雖然這仍有爭議, 但我推薦在實際程式設計中,儘量減少使用C-style 陣列,考慮用std::array或std::vector替代

10樓:youngforest

問題不完全正確。C++ 11引入array容器後,就可以將陣列當作函式引數傳遞了。array可以看作是現代版的陣列,overhead也為0。

所以現代C++鼓勵能用array就用array,而不要用原始的陣列,很少出現必須用陣列(指標)的情況了。

至於為什麼原始陣列不能直接傳遞,其他回答已經說的很清楚了。簡而言之就是 C++中原始的陣列只有位址,沒有大小資訊。

11樓:黑洞電網

C++11是允許的。

另外,陣列不是乙個型別,指標才是。

傳乙個指標,再傳乙個整型變數存放這個陣列的長度,理論上就相當於傳了乙個陣列作為引數。

12樓:Doraemon

C 和 C++ 從來都不對陣列做邊界檢查,使用者指定的陣列長度只用作初始化時分配空間用,之後該幹嘛幹嘛,越界了也不管。你 int a[2]; 和 int b[3]; 除了初始化時給兩個位址分配的空間不一樣多以外,後續系統甚至會認為 a 和 b 是同一種型別,都是 int*。所以陣列長度在以函式的形參的形式出現時毫無意義,你指定了長度編譯器也無法確定你傳入的陣列是否超出最大長度了,所以 C 語言標準乾脆就禁止在形參中指定陣列長度了。

其實解決辦法是有的,傳乙個指標,再傳乙個最大長度即可。而且這樣做函式內部也正好可以借第二個引數做邊界檢查。

13樓:

是從 c 遺留下來的問題,而 c 是因為最早的編譯器沒法處理好,所以乾脆禁止了。

intfunc

(int[10

]arr

)這樣的函式,對 c 來說是很麻煩,因為 c 的陣列其實只是指標,長度僅僅在編譯期保留,所以陣列長度沒辦法跟著傳入函式,如果要改成乙個結構,對 c 來說要增加一點開銷,違反了 c 的設計哲學,所以乾脆禁止了,反正對 c 來說也不是什麼大問題。

PS: 印象中以前某個版本的 c ,函式是可以傳入陣列的,但是陣列的大小必須固定。比如 int[10] 的引數,沒辦法傳入 int[9] 的陣列,所以基本沒什麼用處。

14樓:靈劍

這個事情就沒有什麼道理,因為直接寫陣列 int something[10]不可以,但是包到乙個struct裡面就可以了,說明就是設計偏好的問題,底層實現上沒有障礙。無非就是當時設計C語言的人覺得陣列能自動轉換成指標很方便,所以就規定了傳遞陣列永遠等於傳遞指標,而忽略了陣列大小這個隱含的型別引數,導致C變成了現在這個樣子,然後C++就繼承過去了而已。實際上C++中可以傳遞陣列的引用,只是直接按值傳遞陣列不可以而已。

15樓:otomn

怎麼沒有人提stack的問題。通過parameter傳遞的數值本質上是複製到了函式所處的stack上。其在stack上本質和在函式內宣告的變數一樣,只不過被caller賦值了。

因為其大小必須是固定的,所以不能把不定長的陣列複製進去。

至於為什麼不允許傳遞定長陣列,可能是沒必要特地為了定長陣列加乙個這樣的語法。

16樓:yc znone

C陣列是常量值(區別於,陣列的元素可以是變數),不允許改變,也不可以複製,因此無法通過值傳遞的方式把乙個陣列傳入函式。

int a[10];

int b[10]=a; //這句是錯誤的。

17樓:采薇東籬下

這看起來是乙個簡單的問題,但是無論使用哪個方法,總是不能同時兼顧幾個問題。

函式的引數在編譯時,引數臨時變數等大小已經決定(棧空間大小),因此無法傳入長度可能不定的乙個引數。

void DoSth(int a[4

即便你這樣傳入,你的編譯器也會認為你傳入的是乙個int *。

int arr[5] = ;

DoSth(arr);

編譯器也不會判你為錯誤,用乙個詞叫退化(decay),你的int a[4]會退化成為乙個int *a

兩者究竟有什麼區別?

區別就是,沒退化(decay)之前,你可以知道它的長度,退化之後,便不得而知,通過以下case你可以檢驗我上面說的這句話

int array[5];

std::cout << sizeof(array) << "\n"; // 20 (在我的電腦上)

int *ptr; // 手動退化

std::cout << sizeof(ptr) << "\n"; // 8 (在我的電腦上)

為啥要說(在我的電腦上):int在我的電腦上是4個位元組,int *是8位元組,也許在32位的電腦上會有所不同。

言歸正傳,回到問題本身,C++為何不允許在函式中直接傳遞陣列:我大概明白你的意思,就是你想在不退化的情況下,直接傳遞乙個陣列包含的全部資訊到函式中。

先說能不能做,再說為什麼不推薦。

可以

如果僅僅是不退化是可以做到的:

void Dosth1(int (&arr)[4]){}

這樣就可以完成題目的願望,或者你可以仍然認為我沒有完成因為:

void Dosth1(int (&arr)[4])

其實,他仍然是傳送了乙個指標到函式中,只不過保留了長度這個資訊。因此我們可以在函式中使用range語句來實現迭代操作

void Dosth1(int (&arr)[4])

}不推薦

我們本來可以傳入int a[5]或者int a[N]到函式裡,只需要多傳乙個長度引數即可,然而我們提前指定了長度,就失去了這個權利。

為什麼c++想方設法的都要讓你傳入乙個指標,而不是陣列本身,那時因為棧空間非常昂貴,函式呼叫的時候要拷貝引數到暫存器,棧空間…………(說多了答主也不懂了)。

TP Link為何不允許使用者諮詢硬體配置?難道是欺詐?

烏鴉守夜人 我猜是市場部筆誤。腦洞一下 研發內部肯定是寫4Gb的,上面說商家都是按GB,那是因為不是晶元廠。晶元廠給的都是Gb單位。市場部拿到規格之後一看,哦4G的,於是就寫4G了 WolfBoy tp link 的風格是同一款產品,會隨著時間的推移,逐漸推出不同的硬體版本。因此現在告訴你確切引數,...

聯盟允許哈登聯手杜蘭特 歐文,當年為何不允許科比和保羅聯手?

栗子閒談 當時的紐奧良黃蜂隊是聯盟託管的,聯盟有處理球隊事物的權力,再加上當時報團之風不太盛行,所以聯盟否決了這筆交易。而籃網三巨頭雖然聯盟不太支援,卻也無可奈何,這是性質問題! 當時的黃蜂是聯盟託管 現在的鵜鶘 聯盟相當於老闆,但是不能一直是老闆,為了脫手球隊,手裡得有個當家球星。否則重建時間那麼...

為何C Rust都不允許靜態函式是虛的?

丟貓 可以在一門語言上實現,但是C Rust選擇不實現這種動態派發啊,Rust連編譯時的多派發都沒有 方法過載 但是如果你在Rust裡需要這種派發,你可以用trait來實現。 Glavo 其實我覺得題主想要的是 Kotlin Scala 裡 Companion Object 這樣的東西。想模擬的話放...