“緩沖區溢出”對現代操作系統與編譯器來講已經不是什么大問題,但是作為一個合格的 C/C++ 程序員,還是完全有必要了解它的整個細節。
計算機程序一般都會使用到一些內存,這些內存或是程序內部使用,或是存放用戶的輸入數據,這樣的內存一般稱作緩沖區。簡單的說,緩沖區就是一塊連續的計算機內存區域,它可以保存相同數據類型的多個實例,如字符數組。而緩沖區溢出則是指當計算機向緩沖區內填充數據位數時超過了緩沖區本身的容量,溢出的數據覆蓋在合法數據上。
任何一個源程序通常都包括靜態的代碼段(或者稱為文本段)和靜態的數據段,為了運行程序,操作系統首先負責為其創建進程,并在進程的虛擬地址空間中為其代碼段和數據段建立映射。但是只有靜態的代碼段和數據段是不夠的,進程在運行過程中還要有其動態環境。
一般說來,默認的動態存儲環境通過堆棧機制建立。所有局部變量及所有按值傳遞的函數參數都通過堆棧機制自動分配內存空間。如下圖。
程序在內存的映射
需要特別注意的是,堆(Heap)和棧(Stack)是有區別的,很多程序員混淆堆棧的概念,或者認為它們就是一個概念。簡單來說,它們之間的主要區別可以表現在如下五個方面。
堆是動態分配的,其空間的分配和釋放都由程序員控制。也就是說,堆的大小并不固定,可動態擴張或縮減,其分配由malloc()
等這類實時內存分配函數來實現。當進程調用malloc
等函數分配內存時,新分配的內存就被動態添加到堆上(堆被擴張);當利用free
等函數釋放內存時,被釋放的內存從堆中被剔除(堆被縮減)。
而棧由編譯器自動管理,其分配方式有兩種:靜態分配和動態分配。靜態分配由編譯器完成,比如局部變量的分配。動態分配由alloca()
函數進行分配,但是棧的動態分配和堆是不同的,它的動態分配是由編譯器進行釋放,無需手工控制。
棧是向低地址擴展的數據結構,是一塊連續的內存區域,棧頂的地址和棧的最大容量是系統預先規定好的,能從棧獲得的空間較小。
堆是向高地址擴展的數據結構,是不連續的內存區域,這是由于系統是由鏈表在存儲空閑內存地址,自然堆就是不連續的內存區域,且鏈表的遍歷也是從低地址向高地址遍歷的,堆的大小受限于計算機系統的有效虛擬內存空間,
由此空間,堆獲得的空間比較靈活,也比較大。在 32 位平臺下,VC6 下默認為 1M,堆最大可以到 4G;
對堆來說,頻繁執行malloc或free勢必會造成內存空間的不連續,形成大量的碎片,使程序效率降低;而對棧而言,則不存在碎片問題。
假設一個程序的函數調用順序為:主函數main
調用函數func1
,函數func1
調用函數func2
。當這個程序被操作系統調入內存運行時,其對應的進程在內存中的映射結果如下圖所示
例子中的內存映射
進程的棧是由多個棧幀構成的,其中每個棧幀都對應一個函數調用。當調用函數時,新的棧幀被壓入棧;當函數返回時,相應的棧幀從棧中彈出。由于需要將函數返回地址這樣的重要數據保存在程序員可見的堆棧中,因此也給系統安全帶來了極大的隱患。
當程序寫入超過緩沖區的邊界時,就會產生所謂的“緩沖區溢出”
。發生緩沖區溢出時,就會覆蓋下一個相鄰的內存塊,導致程序發生一些不可預料的結果:也許程序可以繼續,也許程序的執行出現奇怪現象,也許程序完全失敗或者崩潰等。
對于緩沖區溢出,一般可以分為4種類型,即棧溢出、堆溢出、BSS溢出與格式化串溢出。其中,棧溢出是最簡單,也是最為常見的一種溢出方式。
void?function(char?*str)?{???char?buffer[10];???strcpy(buffer,str);}
上面的strcpy()
將直接把str
中的內容copy
到buffer
中。這樣只要str
的長度大于 10 ,就會造成buffer
的溢出,使程序運行出錯。存在象strcpy
這樣的問題的標準函數還有strcat(),sprintf(),vsprintf(),gets(),scanf()
等。對應的有更加安全的函數,即在函數名后加上_s
,如scanf_s()
函數。
函數
嚴重性
防范手段
gets() | 最危險 | 使用 fgets(buf, size, stdin) |
strcpy() | 很危險 | 改為使用 strncpy() |
strcat() | 很危險 | 改為使用 strncat() |
sprintf() | 很危險 | 改為使用snprintf(),或者使用精度說明符 |
scanf() | 很危險 | 使用精度說明符,或自己進行解析 |
sscanf() | 很危險 | 使用精度說明符,或自己進行解析 |
fscanf() | 很危險 | 使用精度說明符,或自己進行解析 |
vfscanf() | 很危險 | 使用精度說明符,或自己進行解析 |
vfscanf() | 很危險 | 改為使用 vsnprintf(),或者使用精度說明符 |
vscanf() | 很危險 | 使用精度說明符,或自己進行解析 |
vsscanf() | 很危險 | 使用精度說明符,或自己進行解析 |
streadd() | 很危險 | 使用精度說明符,或自己進行解析 |
#include<stdio.h>int?main(){??signed?int?value1?=?10;??usigned?int?value2?=?(unsigned?int)value1;}
#include<stdio.h>int?main(){??int?a;??int?b;??int?c=a*b;??return?0;}
enum?{TABLESIZE?=?100};int?*table?=?NULL;int?insert_in_table(int?pos,?int?value)?{??if(!table)?{????table?=?(int?*)malloc(sizeof(int)?*TABLESIZE);??}??if(pos?>=?TABLESIZE)?{????return?-1;??}??table[pos]?=?value;??return?0;}
其中:pos
為int
類型,可能為負數,這會導致在數組所引用的內存邊界之外進行寫入,可以將pos
類型改為size_
t避免
例如:
//錯誤char?array[]={'0','1','2','3','4','5','6','7','8'};//正確的寫法應為:char?array[]={'0','1','2','3','4','5','6','7','8',’’};//或者char?array[11]={'0','1','2','3','4','5','6','7','8','9’};
更多案例可以go公眾號:C語言入門到精通
本文由 貴州做網站公司 整理發布,部分圖文來源于互聯網,如有侵權,請聯系我們刪除,謝謝!
網絡推廣與網站優化公司(網絡優化與推廣專家)作為數字營銷領域的核心服務提供方,其價值在于通過技術手段與策略規劃幫助企業提升線上曝光度、用戶轉化率及品牌影響力。這...
在當今數字化時代,公司網站已成為企業展示形象、傳遞信息和開展業務的重要平臺。然而,對于許多公司來說,網站建設的價格是一個關鍵考量因素。本文將圍繞“公司網站建設價...
在當今的數字化時代,企業網站已成為企業展示形象、吸引客戶和開展業務的重要平臺。然而,對于許多中小企業來說,高昂的網站建設費用可能會成為其發展的瓶頸。幸運的是,隨...
卡特大亂斗最強出裝?不祥之刃-卡特里娜颶風的最佳方式發揮在無序的斗爭:1。游戲順序:造谷人的鞋子,魔鬼的擁抱,中亞的虛空之杖,莫雷羅的秘籍2。游戲理念:山谷制造者是游戲中唯一能將總傷害轉化為真實傷害的裝備。這對卡特來說非常有利可圖。如果你想問,你可以選擇惡魔的支持,以提高你的生存能力。如果你想造成高傷害,你可以用破舊來代替??ㄌ卮髞y斗出裝?裝備1:黑暗收割者巫妖金體]提供高傷害并提高移動速度。巫妖...
正能量的歌一直深得人心汪峰-《英雄》北京國安球隊隊歌,聽見這首歌情況下你是不是覺得心潮澎湃?是不是有一種想要去開辟大場面的想法?也許和平時期獲得一場比賽也是英雄。國安隊歌歌詞誰知道???漢化版綠茵場上叫喊著姓名 翠綠色影子也是我們的大牌明星 向著未來 噢 朝著全球 去拼搏大家憧憬的殊榮 獲勝始終屬于你 北京國安永遠爭第一 喔噢…… 北京國安 我們永遠支持你 噢…… 北京國安 大家永遠熱愛你 綠茵場上...
360搶票王還可以搶票嗎?30票搶票王還能用。你可以試試。30搶票提供自動識別驗證碼、預約提醒、自動刷票等功能。搶到火車票的成功率翻倍,幫你買一張回家的火車票??梢酝ㄟ^驗證碼自動識別,自動放票。不過我每次買票都是用12306APP,是官方的搶票軟件,不會有第三方費用。如何用360搶票王搶下鋪火車票?打開軟件后,點擊右上角的“類型”,選擇“下鋪火車票”。30搶票王是360推出的全新搶票工具。是在常用...