『壹』 c語言指針面試常見問題
c語言指針面試常見問題
指針的使用,一直是c語言面試題中必考的部分,因為指針本身使用的復雜性與普適性,所以考點非常多,而且也可以與其他知識相互結合,因此我們將會使用五篇專題的篇幅來介紹指針。
分析下面的程序,指出程序中的錯誤:
本題解析
沒有正確為str分配內存空間,將會發生異常。問題出在將一個字元串復制進一個字元變數指針所指地址。雖然編譯的時候沒有報錯,但是在運行過程中,因為越界訪問了未被分配的內存,而導致段錯誤。
相關知識點
在處理與指針相關的問題時,首先需要搞明白的就是內存,因為指針操作的就是內存。
第一個,就是內存的分區。這也是經常會被考察的一個考點。
寫出內存分為幾大區域
對於這個問題,有幾種不同的說法,有的說內存分為五大分區,有的說分為四大分區,我們先來看五大分區的說法:
認為內存分為五大分區的人,通常會這樣劃分:
1、BSS段( bss segment )
通常是指用來存放程序中未初始化的'全局變數和靜態變數 (這里注意一個問題:一般的書上都會說全局變數和靜態變數是會自動初始化的,那麼哪來的未初始化的變數呢?變數的初始化可以分為顯示初始化和隱式初始化,全局變數和靜態變數如果程序員自己不初始化的話的確也會被初始化,那就是不管什麼類型都初始化為0,這種沒有顯示初始化的就 是我們這里所說的未初始化。既然都是0那麼就沒必要把每個0都存儲起來,從而節省磁碟空間,這是BSS的主要作用)的一塊內存區域。BSS是英文Block Started by Symbol的簡稱。BSS段屬於靜態內存分配。 BSS節不包含任何數據,只是簡單的維護開始和結束的地址,即總大小。以便內存區能在運行時分配並被有效地清零。BSS節在應用程序的二進制映象文件中並不存在,即不佔用 磁碟空間 而只在運行的時候佔用內存空間 ,所以如果全局變數和靜態變數未初始化那麼其可執行文件要小很多。
2、數據段(data segment)
通常是指用來存放程序中已經初始化的全局變數和靜態變數的一塊內存區域。數據段屬於靜態內存分配,可以分為只讀數據段和讀寫數據段。字元串常量等,但一般都是放在只讀數據段中。
3、代碼段(code segment/text segment)
通常是指用來存放程序執行代碼的一塊內存區域。這部分區域的大小在程序運行前就已經確定,並且內存區域通常屬於只讀, 某些架構也允許代碼段為可寫,即允許修改程序。在代碼段中,也有可能包含一些只讀的常數變數,例如字元串常量等,但一般都是放在只讀數據段中 。
4、堆(heap)
堆是用於存放進程運行中被動態分配的內存段,它的大小並不固定,可動態擴張或 縮減。當進程調用malloc等函數分配內存時,新分配的內存就被動態添加到堆上(堆被擴張); 當利用free等函數釋放內存時,被釋放的內存從堆中被剔除(堆被縮減)
5、棧 (stack)
棧又稱堆棧, 是用戶存放程序臨時創建的局部變數,也就是說我們函數括弧「{}」 中定義的變數(但不包括static聲明的變數,static意味著在數據段中存放變 量)。除此以外, 在函數被調用時,其參數也會被壓入發起調用的進程棧中,並且待到調用結束後,函數的返回值 也會被存放回棧中。由於棧的先進先出特點,所以 棧特別方便用來保存/恢復調用現場。從這個意義上講,我們可以把堆棧看成一個寄存、交換臨時數據的內存區。
而四大分區的說法,則這么認為:
1、堆區:
由程序員手動申請,手動釋放,若不手動釋放,程序結束後由系統回收,生命周期是整個程序運行期間。使用malloc或者new進行堆的申請,堆的總大小為機器的虛擬內存的大小。
說明:new操作符本質上是使用了malloc進行內存的申請,new和malloc的區別如下:
(1)malloc是C語言中的函數,而new是C++中的操作符。
(2)malloc申請之後返回的類型是void*,而new返回的指針帶有類型。
(3)malloc只負責內存的分配而不會調用類的構造函數,而new不僅會分配內存,而且會自動調用類的構造函數。
2、棧區:
由系統進行內存的管理。主要存放函數的參數以及局部變數。在函數完成執行,系統自行釋放棧區內存,不需要用戶管理。整個程序的棧區的大小可以在編譯器中由用戶自行設定,VS中默認的棧區大小為1M,可通過VS手動更改棧的大小。64bits的Linux默認棧大小為10MB,可通過ulimit -s臨時修改。
3、靜態存儲區:
靜態存儲區內的變數在程序編譯階段已經分配好內存空間並初始化。這塊內存在程序的整個運行期間都存在,它主要存放靜態變數、全局變數和常量。
注意:
(1)這里不區分初始化和未初始化的數據區,是因為靜態存儲區內的變數若不顯示初始化,則編譯器會自動以默認的方式進行初始化,即靜態存儲區內不存在未初始化的變數。
(2)靜態存儲區內的常量分為常變數和字元串常量,一經初始化,不可修改。靜態存儲內的常變數是全局變數,與局部常變數不同,區別在於局部常變數存放於棧,實際可間接通過指針或者引用進行修改,而全局常變數存放於靜態常量區則不可以間接修改。
(3)字元串常量存儲在靜態存儲區的常量區,字元串常量的名稱即為它本身,屬於常變數。
(4)數據區的具體劃分,有利於我們對於變數類型的理解。不同類型的變數存放的區域不同。後面將以實例代碼說明這四種數據區中具體對應的變數。
4、代碼區:
存放程序體的二進制代碼。比如我們寫的函數,都是在代碼區的。
通過上面的不同說法,我們也可以看出,這兩種說法本身沒有優劣之分,具體的內存劃分也跟編譯器有很大的關系,因此這兩種說法都是可以接受的,搞明白內存的分區之後,指針的使用才能夠更加的靈活。
;『貳』 C語言指針與返回值
函數A的返回值錯誤。雖然地址和指針很像,但還是不同的兩個東西。而且,一旦出了函數A,返回的地址,也就該被回收,會導致程序出錯。是非常危險的行為。
舉個例子說明下吧。
int *p=a;
這個,在數值上,p是和a的地址相等的,也就是有p==&a。
這時如果a=1;那麼你知道*p==1,是真。
但是此時,p存儲的是a的地址,p還有自己本身的地址。就好比,你(p)坐在座位上,用手指向另一個座位(&a)一樣。那個座位就是a的地址,上面坐著誰(1),就是a的值。你本身就是指針了。
所以說,返回值是指針和地址是完全不同的兩個概念。所以就是錯誤的!
B函數為啥正確呢?因為NULL是一種特殊的指針,叫空指針。所以是可以算作正確的。
『叄』 數組越界訪問的危險性到底有多大
數組(指針)越界訪問,是指使用了超過有效范圍的偏移量。
如只分配了10個元素的空間,但是訪問了第11個元素,就屬於越界。
當出現越界時,由於無法得知被訪問空間存儲的內容及功能,所以會出現不可預知後果。可能程序崩潰,可能運算結果非預期,也可能完全沒有影響。
在編程時要避免指針越界訪問,對於用變數作為參數訪問元素時,需要控制好變數范圍。如果變數值由外部因素決定,那麼訪問前先對合法性做判斷,防止越界。
『肆』 請思考如果在使用數組時,下標越界了,會出現什麼情況
最好的情況是指向一個沒有用到的內存區域,這樣不會對應用程序有影響,最壞的情況是指向的程序的核心區域,會導致系統崩潰。
指針越界,就說明這個指針已經不在數組范圍內了,指向了不屬於數組的區域,再對他進行操作也不是對數組裡面的元素進行操作,而是對一個未知的單元進行操作。
(感覺真恐怖的!)
『伍』 什麼是指針越界,有何後果
)應該特別注意程序的書寫格式,讓它的形式反映出其內在的意義結構。 程序是最復雜的東西(雖然你開始寫的程序很簡單,但它們會逐漸變得復雜起來),是需要用智力去把握的智力產品。良好的格式能使程序結構一目瞭然,幫助你和別人理解它,幫助你的思維,也幫助你發現程序中不正常的地方,使程序中的錯誤更容易被發現。 人們常用的格式形式是:邏輯上屬於同一個層次的互相對齊;邏輯上屬於內部層次的推到下一個對齊位置。請參考本課程的教科書或《C程序設計語言》(The C Programming Language,Brian W. Kernighan & Dennis M. Rirchie,清華大學出版社,大學計算機教育叢書(影印版,英文),1996。) 利用集成開發環境(IDE)或者其他程序編輯器的功能,可以很方便地維護好程序的良好格式。請注意下面這幾個鍵,在寫程序中應該經常用到它們:Enter鍵(換一行),Tab鍵(將輸入游標移到下一個對齊位置——進入新的一個層次),Backspace鍵(回到前一個對齊位置——退到外面的一個層次)。 -------------------------------------------------------------------------------- 2)用最規范的、最清晰的、最容易理解的方式寫程序。注意人們在用C語言寫程序的習慣寫法,例如教科書中解決類似問題時所使用的寫法,《C程序設計語言》一書中有許多極好的程序實例。在這里有一個關於程序模式的相關網頁,裡面也列出了一些常用的模式。 C語言是一個非常靈活的語言,你可能在這里用許多非常隱晦的方式寫程序,但這樣寫出的程序只能是作為一種玩意兒,就像謎語或者智力游戲。這些東西可以用於消磨時間,但通常與實際無緣。在我們的C語言討論組里提到過這種東西。 -------------------------------------------------------------------------------- 3)在編程中,應仔細研究編譯程序給出的錯誤信息和警告信息,弄清楚每條信息的確切根源並予以解決。特別是,不要忽略那些警告信息,許多警告信息源自隱含的嚴重錯誤。我們有許多辦法去欺騙編譯程序,使它不能發現我們程序中的錯誤,但這樣做最終受到傷害的只能是自己。 -------------------------------------------------------------------------------- C語言的運算符很多,優先順序定義也不盡合理,很難完全記清楚,因此要特別注意。需要時查一查(不要怕麻煩,相關網頁有運算符表),或者直接按照自己的需要加上幾個括弧。 -------------------------------------------------------------------------------- scanf("%d %d", i++, a[i]); m = n * n++; -------------------------------------------------------------------------------- 6)總保證一個函數的定義點和它的所有使用點都能看到同一個完整的函數原型說明。 -------------------------------------------------------------------------------- 7)總注意檢查數組的界限和字元串(也以數組的方式存放)的結束。C語言內部根本不檢查數組下標表達式的取值是否在合法范圍內,也不檢查指向數組元素的指針是不是移出了數組的合法區域。寫程序的人需要自己保證對數組使用的合法性。越界訪問可能造成災難性的後果。 例:在寫處理數組的函數時一般應該有一個范圍參數;處理字元串時總檢查是否遇到空字元'\0'。
『陸』 C語言指針越界問題
顯然合法
語法說: 字元串就是以0結尾的, 所以p如果看作一個字元串的話也必然以0結尾
問題是既然你這個數組沒有保證後面是立刻接0的,運行的時候要到什麼地方才能找到p需要的這個0就很難說了, 所以p如果看作字元串,那麼它的內容很可能不只包括這4個字母
『柒』 C語言指針訪問越界
與編譯器無關,只與內存管理機制有關,是操作系統級別的問題,堆棧的讀取方式只是數據結構上的不同,在機器層面,依然是單純的內存讀寫操作;
數組越界訪問的危險性不好評估,但確實是最嚴重的危險之一;
結果基本上會100%崩潰,但是崩潰的原因很可能不一樣,就算是同一段越界代碼跑幾遍,原因也可能是不一樣的;
指針越界問題是不限於數組訪問的,所以全面點的解釋如下:
c語言的編譯時,會跟你的代碼需要,首先申請一塊棧空間和堆空間,棧的優先順序較高,一般時存放程序運行所必須的數據和變數,內存上是連續的,堆空間是程序運行時動態申請的空間,內存上一般是不連續的,這里說的棧與你自己創建的棧不是一個棧,不過數據結構是一樣的,只不過你自己創建的棧是靠你自己寫的代碼動態創建的,所以其實是在你程序的堆空間中的;
下面關鍵問題來了,
以上所有內存空間就是你的程序在跑起來之後,向操作系統申請的所有空間,換句話說,這些內存以外的數據,都是不屬於你這個程序的資源,當你使用指針操作的時候,如果你的指針越界了,那麼接下來你對這個指針的操作就是非法的了,如果這段空間依然是你程序內部的資源,通常會導致你程序自己崩潰,如果是程序之外的資源一般就更糟糕了,甚至會導致更高級別的崩潰,原因很多:
比如你篡改了不屬於你的數據,導致該數據所屬對象的邏輯混亂;
比如越界區域存在保護,內存空間是有讀寫許可權控制的,如果接下來你對只讀的空間進行寫操作,也會導致崩潰,windows下你會看到非常親切的藍屏;
等等...
這也是內存溢出攻擊的基本思想;
『捌』 指針函數和函數指針的返回值各是什麼吖
指針函數返回一個指針,如int *sum (…),返回一個int 型指針,函數指針沒有返回值
『玖』 C語言指針越界問題。
strchr函數就在在字元串中查找指定字元,返回其地址。
這個函數本身就已經把結束符號0,計入遍歷查找范圍,所以沒有把0作為結束位置,所以無所謂。
但字元串最好最後多留一位放0,不然代碼長了,傳參多了,就弄不清了。
『拾』 什麼是指針越界
指針對應於內存。
如:
char a[6]="abcde";
會申請6個位元組;
char i='q';
會申請一個內存空間為1個位元組。
內存會這么存儲。
a b c d e q
a[0]會輸出a,那我把指針指到a[6]那它會怎麼輸出呢??
答案就是q.
指針從字面意思就是指到、指向的意思,就是告訴編譯器怎麼去存取。