日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當前位置:首頁 > 單片機 > C語言與CPP編程
[導讀]1 引言 “緩沖區(qū)溢出”對現(xiàn)代操作系統(tǒng)與編譯器來講已經(jīng)不是什么大問題,但是作為一個合格的 C/C++ 程序員,還是完全有必要了解它的整個細節(jié)。 計算機程序一般都會使用到一些內存,這些內存或是程序內部使用,或是存放用戶的輸入數(shù)據(jù),這樣的內存一般稱作緩沖

1 引言

緩沖區(qū)溢出”對現(xiàn)代操作系統(tǒng)與編譯器來講已經(jīng)不是什么大問題,但是作為一個合格的 C/C++ 程序員,還是完全有必要了解它的整個細節(jié)。

計算機程序一般都會使用到一些內存,這些內存或是程序內部使用,或是存放用戶的輸入數(shù)據(jù),這樣的內存一般稱作緩沖區(qū)。簡單的說,緩沖區(qū)就是一塊連續(xù)的計算機內存區(qū)域,它可以保存相同數(shù)據(jù)類型的多個實例,如字符數(shù)組。而緩沖區(qū)溢出則是指當計算機向緩沖區(qū)內填充數(shù)據(jù)位數(shù)時超過了緩沖區(qū)本身的容量,溢出的數(shù)據(jù)覆蓋在合法數(shù)據(jù)上。

2 C/C++中內存分配

任何一個源程序通常都包括靜態(tài)的代碼段(或者稱為文本段)和靜態(tài)的數(shù)據(jù)段,為了運行程序,操作系統(tǒng)首先負責為其創(chuàng)建進程,并在進程的虛擬地址空間中為其代碼段和數(shù)據(jù)段建立映射。但是只有靜態(tài)的代碼段和數(shù)據(jù)段是不夠的,進程在運行過程中還要有其動態(tài)環(huán)境。

一般說來,默認的動態(tài)存儲環(huán)境通過堆棧機制建立。所有局部變量及所有按值傳遞的函數(shù)參數(shù)都通過堆棧機制自動分配內存空間。如下圖。

程序在內存的映射
  • 棧區(qū)(stack):由編譯器自動分配與釋放,存放為運行時函數(shù)分配的局部變量、函數(shù)參數(shù)、返回數(shù)據(jù)、返回地址等。其操作類似于數(shù)據(jù)結構中的棧。
  • 堆區(qū)(heap):一般由程序員自動分配,如果程序員沒有釋放,程序結束時可能有OS回收。其分配類似于鏈表。
  • 全局區(qū)(靜態(tài)區(qū)static):數(shù)據(jù)段,程序結束后由系統(tǒng)釋放。全局區(qū)分為已初始化全局區(qū)(data),用來存放保存全局的和靜態(tài)的已初始化變量和未初始化全局區(qū)(bss),用來保存全局的和靜態(tài)的未初始化變量。
  • 常量區(qū)(文字常量區(qū)):數(shù)據(jù)段,存放常量字符串,程序結束后有系統(tǒng)釋放。
  • 代碼區(qū):存放函數(shù)體(類成員函數(shù)和全局區(qū))的二進制代碼,這個段在內存中一般被標記為只讀,任何對該區(qū)的寫操作都會導致段錯誤(Segmentation Fault)。

需要特別注意的是,堆(Heap)和棧(Stack)是有區(qū)別的,很多程序員混淆堆棧的概念,或者認為它們就是一個概念。簡單來說,它們之間的主要區(qū)別可以表現(xiàn)在如下五個方面。

分配和管理方式不同

堆是動態(tài)分配的,其空間的分配和釋放都由程序員控制。也就是說,堆的大小并不固定,可動態(tài)擴張或縮減,其分配由malloc()等這類實時內存分配函數(shù)來實現(xiàn)。當進程調用malloc等函數(shù)分配內存時,新分配的內存就被動態(tài)添加到堆上(堆被擴張);當利用free等函數(shù)釋放內存時,被釋放的內存從堆中被剔除(堆被縮減)。

而棧由編譯器自動管理,其分配方式有兩種:靜態(tài)分配和動態(tài)分配。靜態(tài)分配由編譯器完成,比如局部變量的分配。動態(tài)分配由alloca()函數(shù)進行分配,但是棧的動態(tài)分配和堆是不同的,它的動態(tài)分配是由編譯器進行釋放,無需手工控制。

申請的大小限制不同

棧是向低地址擴展的數(shù)據(jù)結構,是一塊連續(xù)的內存區(qū)域,棧頂?shù)牡刂泛蜅5淖畲笕萘渴窍到y(tǒng)預先規(guī)定好的,能從棧獲得的空間較小。

堆是向高地址擴展的數(shù)據(jù)結構,是不連續(xù)的內存區(qū)域,這是由于系統(tǒng)是由鏈表在存儲空閑內存地址,自然堆就是不連續(xù)的內存區(qū)域,且鏈表的遍歷也是從低地址向高地址遍歷的,堆的大小受限于計算機系統(tǒng)的有效虛擬內存空間,

由此空間,堆獲得的空間比較靈活,也比較大。在 32 位平臺下,VC6 下默認為 1M,堆最大可以到 4G;

申請效率不同

  • 棧由系統(tǒng)自動分配,速度快,但是程序員無法控制。
  • 堆是有程序員自己分配,速度較慢,容易產(chǎn)生碎片,不過用起來方便。

產(chǎn)生碎片不同

對堆來說,頻繁執(zhí)行malloc或free勢必會造成內存空間的不連續(xù),形成大量的碎片,使程序效率降低;而對棧而言,則不存在碎片問題。

內存地址增長的方向不同

  • 堆是向著內存地址增加的方向增長的,從內存的低地址向高地址方向增長;
  • 棧的增長方向與之相反,是向著內存地址減小的方向增長,由內存的高地址向低地址方向增長。

假設一個程序的函數(shù)調用順序為:主函數(shù)main調用函數(shù)func1,函數(shù)func1調用函數(shù)func2。當這個程序被操作系統(tǒng)調入內存運行時,其對應的進程在內存中的映射結果如下圖所示

例子中的內存映射

進程的棧是由多個棧幀構成的,其中每個棧幀都對應一個函數(shù)調用。當調用函數(shù)時,新的棧幀被壓入棧;當函數(shù)返回時,相應的棧幀從棧中彈出。由于需要將函數(shù)返回地址這樣的重要數(shù)據(jù)保存在程序員可見的堆棧中,因此也給系統(tǒng)安全帶來了極大的隱患。

當程序寫入超過緩沖區(qū)的邊界時,就會產(chǎn)生所謂的“緩沖區(qū)溢出”。發(fā)生緩沖區(qū)溢出時,就會覆蓋下一個相鄰的內存塊,導致程序發(fā)生一些不可預料的結果:也許程序可以繼續(xù),也許程序的執(zhí)行出現(xiàn)奇怪現(xiàn)象,也許程序完全失敗或者崩潰等。

緩沖區(qū)溢出

對于緩沖區(qū)溢出,一般可以分為4種類型,即棧溢出、堆溢出、BSS溢出與格式化串溢出。其中,棧溢出是最簡單,也是最為常見的一種溢出方式。

沒有保證足夠的存儲空間存儲復制過來的數(shù)據(jù)

void function(char *str) 
{
   char buffer[10];
   strcpy(buffer,str);
}

上面的strcpy()將直接把str中的內容copybuffer中。這樣只要str的長度大于 10 ,就會造成buffer的溢出,使程序運行出錯。存在象strcpy這樣的問題的標準函數(shù)還有strcat(),sprintf(),vsprintf(),gets(),scanf()等。對應的有更加安全的函數(shù),即在函數(shù)名后加上_s,如scanf_s()函數(shù)。

  • 嚴格檢查輸入長度和緩沖區(qū)長度。
  • 常見的高危函數(shù)
函數(shù) 嚴重性 防范手段
gets() 最危險 使用 fgets(buf, size, stdin)
strcpy() 很危險 改為使用 strncpy()
strcat() 很危險 改為使用 strncat()
sprintf() 很危險 改為使用snprintf(),或者使用精度說明符
scanf() 很危險 使用精度說明符,或自己進行解析
sscanf() 很危險 使用精度說明符,或自己進行解析
fscanf() 很危險 使用精度說明符,或自己進行解析
vfscanf() 很危險 使用精度說明符,或自己進行解析
vfscanf() 很危險 改為使用 vsnprintf(),或者使用精度說明符
vscanf() 很危險 使用精度說明符,或自己進行解析
vsscanf() 很危險 使用精度說明符,或自己進行解析
streadd() 很危險 使用精度說明符,或自己進行解析

整數(shù)溢出

  1. 寬度溢出:把一個寬度較大的操作數(shù)賦給寬度較小的操作數(shù),就有可能發(fā)生數(shù)據(jù)截斷或符號位丟失
#include<stdio.h>

int main()
{
  signed int value1 = 10;
  usigned int value2 = (unsigned int)value1;
}
  1. 算術溢出,該程序即使在接受用戶輸入的時候對a、b的賦值做安全性檢查,a+b 依舊可能溢出:
#include<stdio.h>

int main()
{
  int a;
  int b;
  int c=a*b;
  return 0;
}

數(shù)組索引不在合法范圍內

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;
}

其中:posint類型,可能為負數(shù),這會導致在數(shù)組所引用的內存邊界之外進行寫入,可以將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',’\0’};
//或者
char array[11]={'0','1','2','3','4','5','6','7','8','9’};


點【在看】是最大的支持 

免責聲明:本文內容由21ic獲得授權后發(fā)布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關機構授權發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內容真實性等。需要轉載請聯(lián)系該專欄作者,如若文章內容侵犯您的權益,請及時聯(lián)系本站刪除( 郵箱:macysun@21ic.com )。
換一批
延伸閱讀
關閉