C語言為何不改進數組?
今天的作業是編寫有限元程序,剛開始想用C的,後來發現數組大小簡直就是硬傷!!
大一時候就想不通,為什麼C必須在編譯時給定數組大小,這樣感覺限制了很多,是很多代碼編寫很困難。C為什麼不加以改進?不如其他語言可以根據需要指定數組大小。
首先提一嘴:
如果你是想要用一個變數來作為數組聲明時的大小,C99就已經早已實現了。array[size];完全沒有問題,不過數組的大小並不會隨著size變化而變化。
然後:
你知道那些可以任意調節大小的數組是怎麼實現的嗎?其實非常簡單:1.先把數據存在一個固定大小的數組裡。2.地方不夠了,就再開一個更大的固定數組,然後把數據複製進去。好了,現在你還對這些看起來比較聰明的數組感興趣嗎?此問題與「學習彙編語言有什麼好處? 」問題有點關係。C/C++語言的設計和低層的設計有關,作為自動變數的數組對應棧的內存分配,而作為靜態/全局變數的數組是在鏈接時預先分配的,所以不可以實現為自動增長。
其他答案中提到的動態分配棧上空間,C99有variable length array,但在不同架構下未必能實現棧上動態分配,有可能會實現為堆上分配。而在C/C++中也可使用(非標準的)alloca()函數去實現。因為語法的設計是為語言的用途和需求服務的。
如@黑洞逃逸者所說的,
所有語言數組底層的實現都差不多,但是為什麼其他語言用起來那麼方便呢?因為設計它們的理念有根本性的不同。Python 要求易上手,讓程序員不去關注具體實現細節,
更專註於實現軟體的功能,適合快速開發應用程序。Java 被設計為強類型語言,有嚴格的面向對象的編程方法,適合開發跨平台的、易維護的大型軟體。C/C++ 更底層,對內存、棧、堆的控制力強,適合開發需求效率的軟體。舉個簡單的例子,在Java API中可以看到,
當你 New 一個 ArrayList 時,實際上是新建了一個長度為10的的數組對象,你向這個 ArrayList 中添加數據超過10項的時候,它會 new 一個新的數組,長度是原來的3/2(也就是15),然後用 System.arraycopy() 方法將數據從老數組copy到新數組。你在使用 ArrayList.add() 時,這些實現細節是完全看不到的。可以看到,改變數組長度是有開銷的,
在 Java 和 Python 中並沒有很好的辦法控制這些開銷,而用 C/C++ 中那些你看上去特別「硬傷」的使用方法,就能在關鍵性的地方減小這些開銷,增加效率。
換言之,用起來方便是需要犧牲效率作為代價的。這也就是為什麼一些大型 Java 和 Python 程序中,
常常會用 C/C++ 來編寫一些關鍵性的代碼來改善效率。以上什麼?連malloc都沒學到就要寫有限元程序?科技數點錯了好嗎。
C 是高級彙編
int a[100];
實際上就真的「相當於」
int a0, a1, ..., a99;
想要動態數組可以用 malloc/realloc 自己實現。
這是個好問題。C語言自1972年誕生以降的42年間,已經有無數仁人志士在上大學期間被這個問題困擾著。也有無數人解決了這個問題。但是這個問題依然持續困擾著後來的人們。
問題其實在於:在上大學的時候,大家都講究從頭學起、夯實基礎、事必躬親、紙上終淺,基本上所有有理想的計算機專業的學生,都自己現實過從鏈表、棧、隊列到B+樹之類的數據結構或演算法,寫得好不好不說,但是從此被強大的心理暗示作用蹂躪出了這麼一個思維怪圈:一提到C,就覺得我寫東西就要從數組與指針寫起,什麼都不依賴,這樣才叫C的方式,才NB。有句名言:不作死,就不會死。
想想這42年,C語言創造了無數業界神話,Linux Kernel,Git無不是主要用C編寫。如果每一個用C語言編寫軟體的人,寫每一行代碼都要操心下標越界的問題,C語言的使用者們還有可能堅持到現在嗎?
真開始做東西的時候(不是學習),沒人逼你一切從頭做起。已經有無數的先人做好了東西,直接拿來用就可以了。你要相信,只要是已經被任何人解決過一次的問題,在軟體業,就不再是任何問題,如果你不稀罕利用別人的成果,那就是自己給自己製造問題。
所以結論是,C的數組沒有任何問題,你只是需要一個類庫,比如這個,或者你自己隨便找的任何一個:
/ -ccl -
C Containers library
別說C了,C++、C#的數組都不能改大小,可C++有STL,C#有.NET Framework里的各種Container,C只是沒有自帶一個強大萬能的類庫而已,可你不能說它壓根沒有啊。只是得花得時間找找。同時,提前給想評論說「題主問的是C語言本身!」一個回復:語言=語法+語料。類庫,即是編程語言的語料,請不要割裂開來。誰說C/C++的數組一定要指定大小的
int *array1 = (int *) calloc(n, sizeof(int)); // C
int *array2 = new int[n]; // C++
其中n就是運行時指定的大小。其它語言裡面的可變長度數組也是這麼實現的。
為何要改進?固定大小的數組有時非常有用呢!因為 C 是以硬體模型作為自己的編程模型。而硬體還沒辦法同時做到你說的靈活性和數組的所有效率特徵。
樓主需要了解一下C的棧式執行方式,再去看一個叫做malloc的東西
如果只是數組的大小不確定,可以用 VLA - 變長數組
備註:這個變長數組並不是說數組可以變長,而是說,這種數組的長度可以是運行時變數,不需要從一開始就確立。(業餘,如有不當之處歡迎批評指正)早就改進啦~
怕不好用C++還特地整了個庫叫vector
#include
爪機就不粘貼了,百度一下吧,這就是你想要的變長數組!
順便一說這東西屬於stl standard template library,stl里還有很多好玩的結構,想玩還可以搜一下看看。同學,這是一個堆和棧上內存分配不同方式的問題,現在以你的了解程度很難跟你解釋。你先帶著這個疑問學下去吧,慢慢你就明白自己的這個問題有多小白了。
dss886說得對,這是因為C語言的設計理念就是貼近底層的實現。
C/C++的數組在數據結構中應被稱為靜態數組。而其他語言的數組本質上可能是鏈表(javascript的數組應該是鏈表)、動態數組(java的arraylist)或者鏈表和靜態數組的組合。如dss886所說,動態數組也是基於靜態數組的,只不過在容量不夠的時候可以自動申請內存來創建一個更大的靜態數組罷了。而無論這些語言的數組本質上如何實現,這些數組在創建時都有一個固定的初始大小的。
之所以要指定數組的大小,是因為數組其實是一段內存區域,而要用內存的不僅僅是那個數組。(內存是一段有限的連續的線性的存儲區域。)如果不指定那個數組的大小,那麼下一次要用內存的時候,應該用哪裡的內存呢?指定了數組的大小,就明確了數組的邊界,下一次用內存的時候,就可以在數組的邊界外再獲取一段內存。
在許多情況下,數組的長度都是需要改變的,這時候就要用鏈表或者動態數組來實現。但是使用鏈表或者動態數組都會帶來比靜態數組更大的時間上和空間上的開銷。在時間上,在增加容量時都需要動態申請內存,而這往往很耗時,因為內存管理模塊要找出一段可用內存並分割出一段給程序;要訪問鏈表的一個元素,都要從鏈表頭開始一個一個節點地查找。在空間上,動態數組需要有一個變數存儲動態數組的現有長度(可能還有現有容量),鏈表的每個節點都要存儲下一個節點的地址(可能還有上一個節點的地址)。而且動態數組的長度減小的時候,如果縮小容量就要費時,不縮小容量就要浪費空間。任何語言實現的可變長數組,其實都免不了會有這些性能上的額外開銷,所以在數組長度不需要改變、不需要在數組中間插入刪除元素的情況下,C/C++的靜態數組的性能是最高的。
其實C++的STL庫等許多庫已經有現成的用鏈表或動態數組實現好的可變長數組(STL的動態數組vector和鏈表list),你如果不想自己實現,也可以用這些庫。C語言的數組,本身是指針的變形,表示每次到基址的某個偏移量去取值。
所以在C語言里,負下標和數組越界都能編譯通過。題主會不會因此而發狂?
int a = -1;int b[2];int c =-2 ; printf("%d",b[-1]); // 輸出?變長數組怎麼實現的?
要不就是鏈表封裝~
要不就是先給你開一個固定大小的內存區,不夠了?再給你開一個固定大小的,把原來的拷貝過去~語言是底層硬體的模擬~
還有,數組這種東西壓根就是以訛傳訛,所謂的數組
void a[10]的真正含義,其實指的是從地址a往上查10個void類型長度的內存單元
所謂的數組只是好聽而已~首先,C是給了你充分的自由來自己定義你想要的工具,這裡就向題主推薦一個可能可以滿足你的需求的工具:你可以寫一個structure。 裡邊包括一個heap上的array,一個int size,一個int capacity,每次在聲明這個structure的時候對比輸入進來的想要的array 大小和你當前的或者默認的array大小,或者每次往裡push單位的時候對比capacity和size+1 的大小,如果capacity不夠用了或者拿走數據的時候的時候覺得這個array太大了,就複製其中的值並realloc這個array,同時改變你size 和capacity的值。這樣你就包裝好一個可以自動變化大小的dynamic array了。大小的變化幅度可以根據你自己的需求來調整