一道C語言安全編碼題目

一、前言數組

  最近在網上看到一道C語言題目,用C語言實現一個函數,給定一個int類型的整數,函數輸出逆序的整數,例如輸入123,則輸出字符串"321",,輸入-123,則輸出字符串"-321"。題目要求,不使用標準庫,不得分配內存。當時以爲蠻簡單的,這不就是相似字符串逆轉嘛,本身嘗試作了一下,測試發現,仍是有不少地方考慮不周全。今天在此整理一下基礎知識,做爲一名安全開發人員,時刻須要注意代碼的安全,防止有任何漏洞。題目給出的函數以下:安全

#include <stdio.h>

const char * parseInt(int data)
{
    return "321";
}

int main()
{
    printf("%s\n", parseInt(123));
    return 0;
}

二、思考過程函數

  寫代碼最怕的就是沒有想好,一上來就寫,在寫的過程當中不斷的測試修改,這樣很浪費時間。所以須要先好好想一下,這個題目到底考些什麼呢?測試

(1)int類型的整數分爲正數、0、負數,如何處理這些邊界值編碼

(2)整數與字符串之間的轉換,如何將一個整數轉換爲一個字符spa

(3)如何返回一個const char * 類型的字符串3d

(4)當輸入的整數超過int的範圍如何處理code

三、編碼過程blog

  開始寫代碼的思路以下:定義一個char類型的數組,用於保存結果。使用對10取餘和除法操做依次獲取每一位的數字,而後根據ASSIC碼轉換爲字符。將字符拼接起來,返回字符串數組結果。編碼實現以下:生命週期

const char * parseInt(int data)
{
    char str[16] = {0};
    int i = 0;
    if (data < 0) {
        data = -data;
        str[i++] = '-';
    }
    int tmp = data;
    while (tmp / 10) {
        char ch = tmp % 10 + 48;
        tmp = tmp / 10;
        str[i++] = ch;
    }
    str[i++] = tmp % 10 + 48;return str;
}

當初沒有考慮那麼多,編譯發現出現以下錯誤:

一看編譯錯誤,才意識到本身掉入坑中。題目要求返回一個字符串,並且不用分配內存。當時就想直接定義一個字符數組進行返回,而定義的str屬於函數局部變量。

一個函數的局部變量都是存在stack中的,當這個函數調用過程結束時,這個局部變量都是要釋放掉的,因此就會產生這樣的warning,這個是和變量的life time相關的,因此解決方法有:

1.將char result[16]改成static型

2.使用malloc向heap申請,這些是須要caller用free去釋放的

  因而使用static 類型字符串,代碼改進以下:

 1 const char * parseInt(int data)
 2 {
 3     static char str[16] = {0};
 4     int i = 0;
 5 
 6     if (data < 0) {
 7         data = -data;
 8         str[i++] = '-';
 9     }   
10 
11     int tmp = data;
12     while (tmp / 10 != 0) {
13         char ch = tmp % 10 + 48; 
14         tmp = tmp / 10; 
15         str[i++] = ch; 
16     }   
18     str[i++] = tmp % 10 + 48; 
20     return str;
21 }

int main()
{
  printf("%s\n", parseInt(123));
  printf("%s\n", parseInt(12345678));
  printf("%s\n", parseInt(-89790));
  return 0;
}

測試結果以下:

改成static以後,編譯成功,看輸出的結果上看,前面兩個輸出是正確的,而第三個輸出的結果是錯誤的。尼瑪,再次掉入坑中,對static變量的應用不精通啊。爲何每次到看到結果後纔想起來?

雖然在函數中定義了static局部變量,使得變量的變爲靜態stack存儲區域,生命週期從函數中變成了這個程序的範圍。可是static局部變量在函數第一次調用的時候會初始化,後面調用就不會了,直接使用了。所以致使了剛纔的結果輸出不對,複用了上次遺留的結果。

static靜態局部變量屬於靜態存儲方式,它具備如下特色:
 (1)靜態局部變量在函數內定義 它的生存期爲整個源程序,可是其做用域仍與自動變量相同,只能在定義該變量的函數內使用該變量。退出該函數後, 儘管該變量還繼續存在,但不能使用它。
 (2)容許對構造類靜態局部量賦初值 例如數組,若未賦以初值,則由系統自動賦以0值。
 (3)對基本類型的靜態局部變量若在說明時未賦以初值,則系統自動賦予0值。而對自動變量不賦初值,則其值是不定的。 根據靜態局部變量的特色, 能夠 看出它是一種生存期爲整個源程序的量。雖然離開定義它的函數後不能使用,但如再次調用定義它的函數時,它又可繼續使用, 並且保存了前次被調用後留下的 值。 所以,當屢次調用一個函數且要求在調用之間保留某些變量的值時,可考慮採用靜態局部變量。雖然用全局變量也能夠達到上述目的,但全局變量有時會形成 意外的反作用,所以仍以採用局部靜態變量爲宜。

 第一次調用函數,static變量,初始化。
 第二次,及之後,調用函數,static變量,不會初始化。

繼續改進代碼,在函數中將static變量每次使用for循環進行初始化,改進代碼以下:

 1 #include <stdio.h>
 2 
 3 const char * parseInt(int data)
 4 {
 5     static char str[16] = {0};
 6     int i = 0;
 7 
 8     int t = 0;
 9     for (; t < 16; t++) {
10         str[t] = 0;
11     }
12 
13     if (data < 0) {
14         data = -data;
15         str[i++] = '-';
16     }
17 
18     int tmp = data;
19     while (tmp / 10 != 0) {
20         char ch = tmp % 10 + 48;
21         tmp = tmp / 10;
22         str[i++] = ch;
23     }
24 
25     str[i++] = tmp % 10 + 48;
26 
27     return str;
28 }
29 
30 int main()
31 {
32     printf("%s\n", parseInt(123));
33     printf("%s\n", parseInt(12345678));
34     printf("%s\n", parseInt(-89790));
35     return 0;
36 }

此次輸出結果以下:

終於獲得了正確答案,看似很簡單的題目,折騰的這麼久,才搞出來。擴展一下,你們看看以下這個輸出什麼呢:

printf("%s, %s, %s\n", parseInt(98989),parseInt(-4567), parseInt(123456));

這個結果是什麼呢?爲何會這樣呢?

 printf("%s\n", parseInt(0x8FFFFFFF));
printf("%s\n", parseInt(0xFFFFFFFF));

這個結果是什麼呢?爲何會這樣呢?

int 類型4個字節,32位組成。int的最高位做爲符號位,須要特殊處理。

實際運行結果以下:

相關文章
相關標籤/搜索