bl双性强迫侵犯h_国产在线观看人成激情视频_蜜芽188_被诱拐的少孩全彩啪啪漫画

C語言數(shù)組越界的解決方法

本篇內(nèi)容主要講解“C語言數(shù)組越界的解決方法”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“C語言數(shù)組越界的解決方法”吧!

創(chuàng)新互聯(lián)建站-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價比營口網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式營口網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務覆蓋營口地區(qū)。費用合理售后完善,10余年實體公司更值得信賴。

所謂的數(shù)組越界,簡單地講就是指數(shù)組下標變量的取值超過了初始定義時的大小,導致對數(shù)組元素的訪問出現(xiàn)在數(shù)組的范圍之外,這類錯誤也是 C 語言程序中最常見的錯誤之一。

在 C 語言中,數(shù)組必須是靜態(tài)的。換而言之,數(shù)組的大小必須在程序運行前就確定下來。由于 C 語言并不具有類似 Java 等語言中現(xiàn)有的靜態(tài)分析工具的功能,可以對程序中數(shù)組下標取值范圍進行嚴格檢查,一旦發(fā)現(xiàn)數(shù)組上溢或下溢,都會因拋出異常而終止程序。也就是說,C 語言并不檢驗數(shù)組邊界,數(shù)組的兩端都有可能越界,從而使其他變量的數(shù)據(jù)甚至程序代碼被破壞。

因此,數(shù)組下標的取值范圍只能預先推斷一個值來確定數(shù)組的維數(shù),而檢驗數(shù)組的邊界是程序員的職責。

一般情況下,數(shù)組的越界錯誤主要包括兩種:數(shù)組下標取值越界與指向數(shù)組的指針的指向范圍越界。

數(shù)組下標取值越界

數(shù)組下標取值越界主要是指訪問數(shù)組的時候,下標的取值不在已定義好的數(shù)組的取值范圍內(nèi),而訪問的是無法獲取的內(nèi)存地址。例如,對于數(shù)組 int a[3],它的下標取值范圍是[0,2](即a[0]、a[1] 與 a[2])。如果我們的取值不在這個范圍內(nèi)(如 a[3]),就會發(fā)生越界錯誤。示例代碼如下所示:

1int a[3];  2int i=0;  3for(i=0;i<4;i++)  4{  5    a[i] = i;  6}  7for(i=0;i<4;i++)  8{  9    printf("a[%d]=%d\n",i,a[i]);  0}

很顯然,在上面的示例程序中,訪問 a[3] 是非法的,將會發(fā)生越界錯誤。因此,我們應該將上面的代碼修改成如下形式:

1int a[3];  2int i=0;  3for(i=0;i<3;i++)  4{  5    a[i] = i;  6}  7for(i=0;i<3;i++)  8{  9    printf("a[%d]=%d\n",i,a[i]);  0}

指向數(shù)組的指針的指向范圍越界

指向數(shù)組的指針的指向范圍越界是指定義數(shù)組時會返回一個指向第一個變量的頭指針,對這個指針進行加減運算可以向前或向后移動這個指針,進而訪問數(shù)組中所有的變量。但在移動指針時,如果不注意移動的次數(shù)和位置,會使指針指向數(shù)組以外的位置,導致數(shù)組發(fā)生越界錯誤。下面的示例代碼就是移動指針時沒有考慮到移動的次數(shù)和數(shù)組的范圍,從而使程序訪問了數(shù)組以外的存儲單元。

 1int i;   2int *p;   3int a[5];   4/*數(shù)組a的頭指針賦值給指針p*/   5p=a;   6for(i=0;i<10;i++)   7{   8    /*指針p指向的變量*/   9    *p=i+10;  10    /*指針p下一個變量*/  11    p++;  12}

在上面的示例代碼中,for 循環(huán)會使指針 p 向后移動 10 次,并且每次向指針指向的單元賦值。但是,這里數(shù)組 a 的下標取值范圍是 [0,4](即 a[0]、a[1]、a[2]、a[3] 與 a[4])。因此,后 5 次的操作會對未知的內(nèi)存區(qū)域賦值,而這種向內(nèi)存未知區(qū)域賦值的操作會使系統(tǒng)發(fā)生錯誤。正確的操作應該是指針移動的次數(shù)與數(shù)組中的變量個數(shù)相同,如下面的代碼所示:

 1int i;   2int *p;   3int a[5];   4/*數(shù)組a的頭指針賦值給指針p*/   5p=a;   6for(i=0;i<5;i++)   7{   8    /*指針p指向的變量*/   9    *p=i+10;  10    /*指針p下一個變量*/  11    p++;  12}

為了加深大家對數(shù)組越界的了解,下面通過一段完整的數(shù)組越界示例來演示編程中數(shù)組越界將會導致哪些問題。

 1#define PASSWORD "123456"   2int Test(char *str)   3{   4    int flag;   5    char buffer[7];   6    flag=strcmp(str,PASSWORD);   7    strcpy(buffer,str);   8    return flag;   9}  10int main(void)  11{  12    int flag=0;  13    char str[1024];  14    while(1)  15    {  16        printf("請輸入密碼:  ");  17        scanf("%s",str);  18        flag = Test(str);  19        if(flag)  20        {  21            printf("密碼錯誤!\n");  22        }  23            else  24            {  25                printf("密碼正確!\n");  26            }  27    }  28    return 0;  29}

上面的示例代碼模擬了一個密碼驗證的例子,它將用戶輸入的密碼與宏定義中的密碼123456進行比較。很顯然,本示例中最大的設(shè)計漏洞就在于 Test() 函數(shù)中的 strcpy(buffer,str) 調(diào)用。

由于程序?qū)⒂脩糨斎氲淖址獠粍拥貜椭频?Test() 函數(shù)的數(shù)組 char buffer[7] 中。因此,當用戶的輸入大于 7 個字符的緩沖區(qū)尺寸時,就會發(fā)生數(shù)組越界錯誤,這也就是大家所謂的緩沖區(qū)溢出Buffer overflow 漏洞。

但是要注意,如果這個時候我們根據(jù)緩沖區(qū)溢出發(fā)生的具體情況填充緩沖區(qū),不但可以避免程序崩潰,還會影響到程序的執(zhí)行流程,甚至會讓程序去執(zhí)行緩沖區(qū)里的代碼。示例運行結(jié)果為:

 1請輸入密碼:12345   2密碼錯誤!   3請輸入密碼:123456   4密碼正確!   5請輸入密碼:1234567   6密碼正確!   7請輸入密碼:aaaaaaa   8密碼正確!   9請輸入密碼:0123456  10密碼錯誤!  11請輸入密碼:

在示例代碼中,flag 變量實際上是一個標志變量,其值將決定著程序是進入密碼錯誤的流程(非 0)還是“密碼正確”的流程(0)。當我們輸入錯誤的字符串1234567或者aaaaaaa,程序也都會輸出“密碼正確”。但在輸入0123456的時候,程序卻輸出“密碼錯誤”,這究竟是為什么呢?

其實,原因很簡單。當調(diào)用 Test() 函數(shù)時,系統(tǒng)將會給它分配一片連續(xù)的內(nèi)存空間,而變量 char buffer[7] 與 int flag 將會緊挨著進行存儲,用戶輸入的字符串將會被復制進 buffer[7] 中。如果這個時候,我們輸入的字符串數(shù)量超過 6 個(注意,有字符串截斷符也算一個),那么超出的部分將破壞掉與它緊鄰著的 flag 變量的內(nèi)容。

當輸入的密碼不是宏定義的123456時,字符串比較將返回 1 或 -1。我們都知道,內(nèi)存中的數(shù)據(jù)按照 4 字節(jié)(DWORD)逆序存儲,所以當 flag 為 1 時,在內(nèi)存中存儲的是0x01000000。如果我們輸入包含 7 個字符的錯誤密碼,如aaaaaaa,那么字符串截斷符 0x00 將寫入 flag 變量,這樣溢出數(shù)組的一個字節(jié) 0x00 將恰好把逆序存放的 flag 變量改為 0x00000000。在函數(shù)返回后,一旦 main 函數(shù)的 flag 為 0,就會輸出“密碼正確”。這樣,我們就用錯誤的密碼得到了正確密碼的運行效果。

而對于0123456,因為在進行字符串的大小比較時,它小于123456,flag的值是 -1,在內(nèi)存中將按照補碼存放負數(shù),所以實際存儲的不是 0x01000000 而是 0xffffffff。那么字符串截斷后符 0x00 淹沒后,變成 0x00ffffff,還是非 0,所以沒有進入正確分支。

其實,本示例只是用一個字節(jié)淹沒了鄰接變量,導致程序進入密碼正確的處理流程,使設(shè)計的驗證功能失效。

盡量顯式地指定數(shù)組的邊界

在 C 語言中,為了提高運行效率,給程序員更大的空間,為指針操作帶來更多的方便,C 語言內(nèi)部本身不檢查數(shù)組下標表達式的取值是否在合法范圍內(nèi),也不檢查指向數(shù)組元素的指針是不是移出了數(shù)組的合法區(qū)域。因此,在編程中使用數(shù)組時就必須格外謹慎,在對數(shù)組進行讀寫操作時都應當進行相應的檢查,以免對數(shù)組的操作超過數(shù)組的邊界,從而發(fā)生緩沖區(qū)溢出漏洞。

要避免程序因數(shù)組越界所發(fā)生的錯誤,首先就需要從數(shù)組的邊界定義開始。盡量顯式地指定數(shù)組的邊界,即使它已經(jīng)由初始化值列表隱式指定。示例代碼如下所示:

1int a[]={1,2,3,4,5,6,7,8,9,10};

很顯然,對于上面的數(shù)組 a[],雖然編譯器可以根據(jù)始化值列表來計算出數(shù)組的長度。但是,如果我們顯式地指定該數(shù)組的長度,例如:

1int a[10]={1,2,3,4,5,6,7,8,9,10};

它不僅使程序具有更好的可讀性,并且大多數(shù)編譯器在數(shù)組長度小于初始化值列表的長度時還會發(fā)生相應警告。

當然,也可以使用宏的形式來顯式指定數(shù)組的邊界(實際上,這也是最常用的指定方法),如下面的代碼所示:

1#define MAX 10  2&hellip;  3int a[MAX]={1,2,3,4,5,6,7,8,9,10};

除此之外,在 C99 標準中,還允許我們使用單個指示符為數(shù)組的兩段“分配”空間,如下面的代碼所示:

1int a[MAX]={1,2,3,4,5,[MAX-5]=6,7,8,9,10};

在上面的 a[MAX]數(shù)組中,如果 MAX 大于 10,數(shù)組中間將用 0 值元素進行填充(填充的個數(shù)為 MAX-10,并從 a[5] 開始進行 0 值填充);如果 MAX 小于 10,[MAX-5]之前的 5 個元素(1,2,3,4,5)中將有幾個被[MAX-5]之后的 5 個元素(6,7,8,9,10)所覆蓋,示例代碼如下所示:

 1#define MAX 10   2#define MAX1 15   3#define MAX2 6   4int main(void)   5{   6    int a[MAX]={1,2,3,4,5,[MAX-5]=6,7,8,9,10};   7    int b[MAX1]={1,2,3,4,5,[MAX1-5]=6,7,8,9,10};   8    int c[MAX2]={1,2,3,4,5,[MAX2-5]=6,7,8,9,10};   9    int i=0;  10    int j=0;  11    int z=0; 12    printf("a[MAX]:\n");  13    for(i=0;i<MAX;i++)  14    {  15        printf("a[%d]=%d ",i,a[i]); 16    }  17    printf("\nb[MAX1]:\n");  18    for(j=0;j<MAX1;j++)  19    {  20        printf("b[%d]=%d ",j,b[j]);  21    }  22    printf("\nc[MAX2]:\n");  23    for(z=0;z<MAX2;z++)  24    {  25        printf("c[%d]=%d ",z,c[z]);  26    }  27    printf("\n");  28    return 0;  29}

運行結(jié)果為:

1a[MAX]:  2a[0]=1 a[1]=2 a[2]=3 a[3]=4 a[4]=5 a[5]=6 a[6]=7 a[7]=8 a[8]=9 a[9]=10  3b[MAX1]: 4b[0]=1 b[1]=2 b[2]=3 b[3]=4 b[4]=5 b[5]=0 b[6]=0 b[7]=0 b[8]=0 b[9]=0 b[10]=6 b[11]=7 b[12]=8 b[13]=9 b[14]=10  5c[MAX2]:  6c[0]=1 c[1]=6 c[2]=7 c[3]=8 c[4]=9 c[5]=10

對數(shù)組做越界檢查,確保索引值位于合法的范圍之內(nèi)

要避免數(shù)組越界,除了上面所闡述的顯式指定數(shù)組的邊界之外,還可以在數(shù)組使用之前進行越界檢查,檢查數(shù)組的界限和字符串(也以數(shù)組的方式存放)的結(jié)束,以保證數(shù)組索引值位于合法的范圍之內(nèi)。例如,在寫處理數(shù)組的函數(shù)時,一般應該有一個范圍參數(shù);在處理字符串時總檢查是否遇到空字符&lsquo;\0&rsquo;。

來看下面一段代碼示例:

 1#define ARRAY_NUM 10   2int *TestArray(int num,int value)   3{   4    int *arr=NULL;   5    arr=(int *)malloc(sizeof(int)*ARRAY_NUM);   6    if(arr!=NULL)   7    {   8        arr[num]=value;   9    }  10    else  11    {  12        /*處理arr==NULL*/  13    }  14    return arr;  15}

從上面的int*TestArray(int num,int value)函數(shù)中不難看出,其中存在著一個很明顯的問題,那就是無法保證 num 參數(shù)是否越界(即當num>=ARRAY_NUM的情況)。因此,應該對 num 參數(shù)進行越界檢查,示例代碼如下所示:

1int *TestArray(int num,int value)   2{  3    int *arr=NULL;   4    /*越界檢查(越上界)*/   5    if(num<ARRAY_NUM)   6    {   7        arr=(int *)malloc(sizeof(int)*ARRAY_NUM);   8        if(arr!=NULL)   9        {  10            arr[num]=value; 11        } 12        else  13        {  14            /*處理arr==NULL*/  15        }  16    }  17    return arr;  18}

這樣通過if(num<ARRAY_NUM)語句進行越界檢查,從而保證 num 參數(shù)沒有越過這個數(shù)組的上界?,F(xiàn)在看起來,TestArray() 函數(shù)應該沒什么問題,也不會發(fā)生什么越界錯誤。

但是,如果仔細檢查,TestArray() 函數(shù)仍然還存在一個致命的問題,那就是沒有檢查數(shù)組的下界。由于這里的 num 參數(shù)類型是 int 類型,因此可能為負數(shù)。如果 num 參數(shù)所傳遞的值為負數(shù),將導致在 arr 所引用的內(nèi)存邊界之外進行寫入。

當然,你可以通過向if(num<ARRAY_NUM)語句里面再加一個條件進行測試,如下面的代碼所示:

1if(num>=0&&num<ARRAY_NUM)  2{  3}

但是,這樣的函數(shù)形式對調(diào)用者來說是不友好的(由于 int 類型的原因,對調(diào)用者來說仍然可以傳遞負數(shù),至于在函數(shù)中怎么處理那是另外一件事情),因此,最佳的解決方案是將 num 參數(shù)聲明為 size_t 類型,從根本上防止它傳遞負數(shù),示例代碼如下所示:

1int *TestArray(size_t num,int value)   2{   3    int *arr=NULL;   4    /*越界檢查(越上界)*/   5    if(num<ARRAY_NUM)   6    {   7        arr=(int *)malloc(sizeof(int)*ARRAY_NUM);   8        if(arr!=NULL)   9        {  10            arr[num]=value;  11        } 12        else  13        {  14            /*處理arr==NULL*/  15        }  16    }  17    return arr;  18}

獲取數(shù)組的長度時不要對指針應用 sizeof 操作符

在 C 語言中,sizeof 這個其貌不揚的家伙經(jīng)常會讓無數(shù)程序員叫苦連連。同時,它也是各大公司爭相選用的面試必備題目。簡單地講,sizeof 是一個單目操作符,不是函數(shù)。其作用就是返回一個操作數(shù)所占的內(nèi)存字節(jié)數(shù)。其中,操作數(shù)可以是一個表達式或括在括號內(nèi)的類型名,操作數(shù)的存儲大小由操作數(shù)的類型來決定。例如,對于數(shù)組 int a[5],可以使用sizeof(a)來獲取數(shù)組的長度,使用sizeof(a[0])來獲取數(shù)組元素的長度。

但需要注意的是,sizeof 操作符不能用于函數(shù)類型、不完全類型(指具有未知存儲大小的數(shù)據(jù)類型,如未知存儲大小的數(shù)組類型、未知內(nèi)容的結(jié)構(gòu)或聯(lián)合類型、void 類型等)與位字段。例如,以下都是不正確形式:

1/*若此時max定義為intmax();*/   2sizeof(max)   3/*若此時arr定義為char arr[MAX],且MAX未知*/   4sizeof(arr)   5/*不能夠用于void類型*/   6sizeof(void)   7/*不能夠用于位字段*/   8struct S   9{  10    unsigned int f1 : 1;  11    unsigned int f2 : 5;  12    unsigned int f3 : 12;  13};  14sizeof(S.f1);

了解 sizeof 操作符之后,現(xiàn)在來看下面的示例代碼:

1void Init(int arr[])   2{   3    size_t i=0;   4    for(i=0;i<sizeof(arr)/sizeof(arr[0]);i++)   5    {   6        arr[i]=i;   7    }   8}   9int main(void)  10{  11    int i=0;  12    int a[10];  13    Init(a);  14    for(i=0;i<10;i++)  15    {  16        printf("%d\n",a[i]);  17    }  18    return 0;  19}

從表面看,上面代碼的輸出結(jié)果應該是0,1,2,3,4,5,6,7,8,9,但實際結(jié)果卻出乎我們的意料,如圖 1 所示。

C語言數(shù)組越界的解決方法

圖 1 示例代碼在 VC++2010 中的運行結(jié)果

是什么原因?qū)е逻@個結(jié)果呢?

很顯然,上面的示例代碼在void Init(int arr[])函數(shù)中接收了一個int arr[]類型的形參,并且在main函數(shù)中向它傳遞一個a[10]實參。同時,在 Init() 函數(shù)中通過sizeof(arr)/sizeof(arr[0])來確定這個數(shù)組元素的數(shù)量和初始化值。

在這里出現(xiàn)了一個很大問題:由于 arr 參數(shù)是一個形參,它是一個指針類型,其結(jié)果是sizeof(arr)=sizeof(int*)。在 IA-32 中,sizeof(arr)/sizeof(arr[0])的結(jié)果為 1。因此,最后的結(jié)果如圖 1 所示。

對于上面的示例代碼,我們可以通過傳入數(shù)組的長度的方式來解決這個問題,示例代碼如下:

 1void Init(int arr[],size_t arr_len)   2{   3    size_t i=0;   4    for(i=0;i<arr_len;i++)   5    {   6        arr[i]=i;   7    }   8}   9int main(void)  10{  11    int i=0;  12    int a[10];  13    Init(a,10);  14    for(i=0;i<10;i++)  15    {  16        printf("%d\n",a[i]);  17    }  18    return 0;  19}

除此之外,我們還可以通過指針的方式來解決上面的問題,示例代碼如下所示:

1void Init(int (*arr)[10])   2{   3    size_t i=0;   4    for(i=0;i< sizeof(*arr)/sizeof(int);i++)   5    {   6        (*arr)[i]=i;   7    }   8}  9int main(void)  10{  11    int i=0;  12    int a[10];  13    Init(&a);  14    for(i=0;i<10;i++)  15    {  16        printf("%d\n",a[i]);  17    }  18    return 0;  19}

現(xiàn)在,Init() 函數(shù)中的 arr 參數(shù)是一個指向arr[10]類型的指針。需要特別注意的是,這里絕對不能夠使用void Init(int(*arr)[])來聲明函數(shù),而是必須指明要傳入的數(shù)組的大小,否則sizeof(*arr)無法計算。但是在這種情況下,再通過 sizeof 來計算數(shù)組大小已經(jīng)沒有意義了,因為此時數(shù)組大小已經(jīng)指定為 10 了。

到此,相信大家對“C語言數(shù)組越界的解決方法”有了更深的了解,不妨來實際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學習!

本文名稱:C語言數(shù)組越界的解決方法
當前路徑:http://vcdvsql.cn/article34/pdedpe.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供企業(yè)網(wǎng)站制作網(wǎng)站改版、網(wǎng)站制作、移動網(wǎng)站建設(shè)小程序開發(fā)外貿(mào)網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

網(wǎng)站優(yōu)化排名