C語言函式中定義大陣列耗盡堆疊的問題

2021-08-22 18:34:42 字數 2632 閱讀 4029

今晚上一同學請我幫忙看乙個c程式,gcc編譯時一直抱錯,說是段錯誤。

程式本身寫的比較差,但編譯能通過,只是有幾十個警告。

兩個小時過去了,在eclipse+gcc下沒有找到問題(這個環境還不熟悉),換到了vc下面,逐步除錯,才發現問題出在被main呼叫的乙個叫做readmctal()的函式的前面幾行中。該函式如下:

voidreadmctal(void)

else

......

同時#definenum_cell9999

#defineegroup175

定義的二位陣列實在是太大了。看了反彙編之後感覺貌似是堆疊的問題,試著將

float

temp_spectra[num_cell][egroup],temp_error[num_cell][egroup];

float

temp_flux[num_cell],temp_flux_error[num_cell];

移動到函式體外,大功告成!

到linux下用gcc編譯,「段錯誤」的提示消失了。

經過分析,我認為乙個函式分配的記憶體是有限的,在函式體內定義的二維陣列太大了,耗盡了堆疊,因此報錯。

ps:論壇達人的觀點:

養成良好的程式設計習慣,一般公司都有coding style,裡面應該有規定:

函式內部(區域性變數)禁止定義大陣列,而應使用動態記憶體;如陣列需傳入函式應使用指標作為引數;

其實就算你呼叫這個函式不出錯,但是如果函式巢狀很多的話也會發生segment error

在某些資源有限的系統下,更需要注意這個問題,比如51微控制器

函式內是在棧分配記憶體,棧大小一般限制在1m到2m

函式外則是全域性變數,在data段分配記憶體

很早之前寫的了,現在發到c版來。

關於c語言記憶體方面的話題要真說起來的話那恐怕就沒頭了,所以本文僅僅是乙個**。

關於記憶體問題不同平台之間有一定的區別。本文所指的平台是x86的linux平台

用c語言做程式(其實其他語言也一樣),不僅要熟悉語法,其實很多相關的背景知識也很重要。在學習和研究c語言中記憶體分配的問題前,首先要了解一下linux分配給程序(執行中的程式)的位址空間是什麼樣的。

總的來說有3個段,即**段,資料段和堆疊段(學過彙編的朋友一定很熟悉了)。**段就是儲存程式文字的,所以有時候也叫做文字段,指令指標中的指令就是 從這裡取得。這個段一般是可以被共享的,比如你在linux開了2個vi來編輯文字,那麼一般來說這兩個vi是共享乙個**段的,但是資料段不同(這點有 點類似c++中類的不同物件共享相同成員函式)。資料段是儲存資料用的,還可以分成初始化為非零的資料區,bss,和堆(heap)三個區域。初始化非零 資料區域一般存放靜態非零資料和全域性的非零資料。bss是block started by symbol的縮寫,原本是組合語言中的術語。該區域主要存放未初始化的全域性資料和靜態資料。還有就是堆了,這個區域是給動態分配記憶體是使用的,也就是用 malloc等函式分配的記憶體就是在這個區域裡的。它的位址是向上增長的。最後乙個堆疊段(注意,堆疊是stack,堆是heap,不是同乙個東西),堆 棧可太重要了,這裡存放著區域性變數和函式引數等資料。例如遞迴演算法就是靠棧實現的。棧的位址是向下增長的。具體如下:

*****===高位址 *****==

程式棧 堆疊段

向下增長

「空洞」 *****==

向上增長 堆

------ 資料段

bss------

非零資料

*****====低位址 *****==

*****==== *****==

** **段

*****==== *****==

需要注意的是,**段和資料段之間有明確的分隔,但是資料段和堆疊段之間沒有,而且棧是向下增長,堆是向上增長的,因此理論上來說堆和棧會「增長到一起」,但是作業系統會防止這樣的錯誤發生,所以不用過分擔心。

有了以上理論做鋪墊,下面就說動態記憶體的分配。上面說了,動態記憶體空間是在堆中分配的。實現動態分配的也就是下面幾個函式:

stdlib.h :

void *malloc(size_t size);

void *calloc(size_t nmemb, size_t size);

void *realloc(void *ptr, size_t size);

void free(void *ptr);

乙個乙個說吧。malloc就是分配乙個size大小的記憶體空間,並且用乙個void型別的指標指向這個空間,然後返回這個指標。也就是說,malloc 返回了乙個指向size大小的空間的void型別的指標,如果要使用這個空間,還得把void*型別轉換成乙個你需要的型別,比如int*之類。 calloc和malloc基本一樣,不同的是有兩點,一是calloc分配的空間大小是由nmemb*size決定的,也就是說nmemb是條目個數, 而size可以看成是條目的大小,計算總空間任務由calloc去做。二是calloc返回的空間都用0填充,而malloc則不確定記憶體中會有什麼東 西。realloc是用來改變已經分配的空間的大小。指標ptr是void型別的,它應該指向乙個需要重新分配大小的空間,而size引數則是重新分配之 後的整個空間大小,而不是增加的大小。同樣,返回的是乙個指向新空間的指標。free用來釋放由上面3個函式分配的空間,其引數就是指向某空間的指標。

基本就這些了,這些都是比較基礎的話題,高階話題和細節問題還有很多,這裡就不進行說明了,有機會我會繼續總結一番的

C語言函式呼叫中堆疊知識

c語言的程式執行可以說就是不斷的呼叫函式,從主入口的main函式到各種各樣的庫函式,再到使用者自定義的完成特定功能的函式。程式中關於乙個函式的操作主要包括三個方面。函式宣告,函式定義,函式呼叫。簡而言之,函式宣告顧名思義就是告訴編譯器有乙個這樣的函式,同時告訴編譯器它的返回值型別和引數型別 引數預設...

C語言定義動態陣列

今天在unbutu寫一道簡單的約瑟夫環的問題 有n個人,迴圈報數,報到3的人淘汰,求最後剩下的人,我就想定義乙個動態陣列來記錄他們是否被淘汰 如果淘汰了就把他對應陣列中的值改為0,淘汰n 1個人之後,陣列中值為1的人就是最後剩下的人。int n printf 請輸入總人數 n scanf d n i...

c語言陣列定義(詳細)

陣列 什麼是陣列 變數的組合,是一種定義變數的手段。定義 型別 陣列名 數量 陣列定義後,預設值同樣不確定,因此也需要初始化。int arr 5 int num1,num2,num3,num4,num5 使用 陣列名 編號 編號 下標 從0開始,範圍 0,數量 1 遍歷 配合for迴圈從頭到尾顯示,...