最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C語(yǔ)言編程中常見的五種錯(cuò)誤及對(duì)應(yīng)解決方案

 更新時(shí)間:2021年10月19日 16:19:00   作者:LCTT unigeorge  
這篇文章主要給大家分享的是C語(yǔ)言編程中常見的五種錯(cuò)誤及對(duì)應(yīng)解決方案,詳細(xì)內(nèi)容就請(qǐng)跟小編一起進(jìn)入下面的文章內(nèi)容吧

前言:

C 語(yǔ)言有時(shí)名聲不太好,因?yàn)樗幌窠诘木幊陶Z(yǔ)言(比如 Rust)那樣具有內(nèi)存安全性。但是通過(guò)額外的代碼,一些最常見和嚴(yán)重的 C 語(yǔ)言錯(cuò)誤是可以避免的。

即使是最好的程序員也無(wú)法完全避免錯(cuò)誤。這些錯(cuò)誤可能會(huì)引入安全漏洞、導(dǎo)致程序崩潰或產(chǎn)生意外操作,具體影響要取決于程序的運(yùn)行邏輯。

下文講解了可能影響應(yīng)用程序的五個(gè)錯(cuò)誤以及避免它們的方法:

1. 未初始化的變量

程序啟動(dòng)時(shí),系統(tǒng)會(huì)為其分配一塊內(nèi)存以供存儲(chǔ)數(shù)據(jù)。這意味著程序啟動(dòng)時(shí),變量將獲得內(nèi)存中的一個(gè)隨機(jī)值。

有些編程環(huán)境會(huì)在程序啟動(dòng)時(shí)特意將內(nèi)存“清零”,因此每個(gè)變量都得以有初始的零值。程序中的變量都以零值作為初始值,聽上去是很不錯(cuò)的。但是在 C 編程規(guī)范中,系統(tǒng)并不會(huì)初始化變量。

看一下這個(gè)使用了若干變量和兩個(gè)數(shù)組的示例程序:

#include <stdio.h> 
#include <stdlib.h> 
int 
main() 
{ 
  int i, j, k; 
  int numbers[5]; 
  int *array; 
  puts("These variables are not initialized:"); 
  printf("  i = %d\n", i); 
  printf("  j = %d\n", j); 
  printf("  k = %d\n", k); 
  puts("This array is not initialized:"); 
  for (i = 0; i < 5; i++) { 
    printf("  numbers[%d] = %d\n", i, numbers[i]); 
  } 
  puts("malloc an array ..."); 
  array = malloc(sizeof(int) * 5); 
  if (array) { 
    puts("This malloc'ed array is not initialized:"); 
    for (i = 0; i < 5; i++) { 
      printf("  array[%d] = %d\n", i, array[i]); 
    } 
    free(array); 
  } 
  /* done */ 
  puts("Ok"); 
  return 0; 
} 

這個(gè)程序不會(huì)初始化變量,所以變量以系統(tǒng)內(nèi)存中的隨機(jī)值作為初始值。在我的 Linux 系統(tǒng)上編譯和運(yùn)行這個(gè)程序,會(huì)看到一些變量恰巧有“零”值,但其他變量并沒(méi)有:

These variables are not initialized: 
  i = 0 
  j = 0 
  k = 32766 
This array is not initialized: 
  numbers[0] = 0 
  numbers[1] = 0 
  numbers[2] = 4199024 
  numbers[3] = 0 
  numbers[4] = 0 
malloc an array ... 
This malloc'ed array is not initialized: 
  array[0] = 0 
  array[1] = 0 
  array[2] = 0 
  array[3] = 0 
  array[4] = 0 
Ok 

很幸運(yùn),i j 變量是從零值開始的,但 k 的起始值為 32766。在 numbers 數(shù)組中,大多數(shù)元素也恰好從零值開始,只有第三個(gè)元素的初始值為 4199024。

在不同的系統(tǒng)上編譯相同的程序,可以進(jìn)一步顯示未初始化變量的危險(xiǎn)性。不要誤以為“全世界都在運(yùn)行 Linux”,你的程序很可能某天在其他平臺(tái)上運(yùn)行。例如,下面是在 FreeDOS 上運(yùn)行相同程序的結(jié)果:

These variables are not initialized: 
  i = 0 
  j = 1074 
  k = 3120 
This array is not initialized: 
  numbers[0] = 3106 
  numbers[1] = 1224 
  numbers[2] = 784 
  numbers[3] = 2926 
  numbers[4] = 1224 
malloc an array ... 
This malloc'ed array is not initialized: 
  array[0] = 3136 
  array[1] = 3136 
  array[2] = 14499 
  array[3] = -5886 
  array[4] = 219 
Ok 

永遠(yuǎn)都要記得初始化程序的變量。如果你想讓變量將以零值作為初始值,請(qǐng)額外添加代碼將零分配給該變量。預(yù)先編好這些額外的代碼,這會(huì)有助于減少日后讓人頭疼的調(diào)試過(guò)程。

2. 數(shù)組越界

C 語(yǔ)言中,數(shù)組索引從零開始。這意味著對(duì)于長(zhǎng)度為 10 的數(shù)組,索引是從 0 到 9;長(zhǎng)度為 1000 的數(shù)組,索引則是從 0 到 999。

程序員有時(shí)會(huì)忘記這一點(diǎn),他們從索引 1 開始引用數(shù)組,產(chǎn)生了“大小差一”off by one錯(cuò)誤。在長(zhǎng)度為 5 的數(shù)組中,程序員在索引“5”處使用的值,實(shí)際上并不是數(shù)組的第 5 個(gè)元素。相反,它是內(nèi)存中的一些其他值,根本與此數(shù)組無(wú)關(guān)。

這是一個(gè)數(shù)組越界的示例程序。該程序使用了一個(gè)只含有 5 個(gè)元素的數(shù)組,但卻引用了該范圍之外的數(shù)組元素:

#include <stdio.h> 
#include <stdlib.h> 
int 
main() 
{ 
  int i; 
  int numbers[5]; 
  int *array; 
  /* test 1 */ 
  puts("This array has five elements (0 to 4)"); 
  /* initalize the array */ 
  for (i = 0; i < 5; i++) { 
    numbers[i] = i; 
  } 
  /* oops, this goes beyond the array bounds: */ 
  for (i = 0; i < 10; i++) { 
    printf("  numbers[%d] = %d\n", i, numbers[i]); 
  } 
  /* test 2 */ 
  puts("malloc an array ..."); 
  array = malloc(sizeof(int) * 5); 
  if (array) { 
    puts("This malloc'ed array also has five elements (0 to 4)"); 
    /* initalize the array */ 
    for (i = 0; i < 5; i++) { 
      array[i] = i; 
    } 
    /* oops, this goes beyond the array bounds: */ 
    for (i = 0; i < 10; i++) { 
      printf("  array[%d] = %d\n", i, array[i]); 
    } 
    free(array); 
  } 
  /* done */ 
  puts("Ok"); 
  return 0; 
} 


可以看到,程序初始化了數(shù)組的所有值(從索引 0 到 4),然后從索引 0 開始讀取,結(jié)尾是索引 9 而不是索引 4。前五個(gè)值是正確的,再后面的值會(huì)讓你不知所以:

This array has five elements (0 to 4) 
  numbers[0] = 0 
  numbers[1] = 1 
  numbers[2] = 2 
  numbers[3] = 3 
  numbers[4] = 4 
  numbers[5] = 0 
  numbers[6] = 4198512 
  numbers[7] = 0 
  numbers[8] = 1326609712 
  numbers[9] = 32764 
malloc an array ... 
This malloc'ed array also has five elements (0 to 4) 
  array[0] = 0 
  array[1] = 1 
  array[2] = 2 
  array[3] = 3 
  array[4] = 4 
  array[5] = 0 
  array[6] = 133441 
  array[7] = 0 
  array[8] = 0 
  array[9] = 0 
Ok 

引用數(shù)組時(shí),始終要記得追蹤數(shù)組大小。將數(shù)組大小存儲(chǔ)在變量中;不要對(duì)數(shù)組大小進(jìn)行硬編碼hard-code。否則,如果后期該標(biāo)識(shí)符指向另一個(gè)不同大小的數(shù)組,卻忘記更改硬編碼的數(shù)組長(zhǎng)度時(shí),程序就可能會(huì)發(fā)生數(shù)組越界。

3. 字符串溢出

字符串只是特定類型的數(shù)組。在 C 語(yǔ)言中,字符串是一個(gè)由 char 類型值組成的數(shù)組,其中用一個(gè)零字符表示字符串的結(jié)尾。

因此,與數(shù)組一樣,要注意避免超出字符串的范圍。有時(shí)也稱之為 字符串溢出。

使用 gets 函數(shù)讀取數(shù)據(jù)是一種很容易發(fā)生字符串溢出的行為方式。gets 函數(shù)非常危險(xiǎn),因?yàn)樗恢涝谝粋€(gè)字符串中可以存儲(chǔ)多少數(shù)據(jù),只會(huì)機(jī)械地從用戶那里讀取數(shù)據(jù)。如果用戶輸入像 foo 這樣的短字符串,不會(huì)發(fā)生意外;但是當(dāng)用戶輸入的值超過(guò)字符串長(zhǎng)度時(shí),后果可能是災(zāi)難性的。

下面是一個(gè)使用 gets 函數(shù)讀取城市名稱的示例程序。在這個(gè)程序中,我還添加了一些未使用的變量,來(lái)展示字符串溢出對(duì)其他數(shù)據(jù)的影響:

#include <stdio.h> 
#include <string.h> 
int 
main() 
{ 
  char name[10];                       /* Such as "Chicago" */ 
  int var1 = 1, var2 = 2; 
  /* show initial values */ 
  printf("var1 = %d; var2 = %d\n", var1, var2); 
  /* this is bad .. please don't use gets */ 
  puts("Where do you live?"); 
  gets(name); 
  /* show ending values */ 
  printf("<%s> is length %d\n", name, strlen(name)); 
  printf("var1 = %d; var2 = %d\n", var1, var2); 
  /* done */ 
  puts("Ok"); 
  return 0; 
} 

當(dāng)你測(cè)試類似的短城市名稱時(shí),該程序運(yùn)行良好,例如伊利諾伊州的 Chicago 或北卡羅來(lái)納州的Raleigh

var1 = 1; var2 = 2 
Where do you live? 
Raleigh 
<Raleigh> is length 7 
var1 = 1; var2 = 2 
Ok 


威爾士的小鎮(zhèn) Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch 有著世界上最長(zhǎng)的名字之一。這個(gè)字符串有 58 個(gè)字符,遠(yuǎn)遠(yuǎn)超出了 name 變量中保留的 10 個(gè)字符。結(jié)果,程序?qū)⒅荡鎯?chǔ)在內(nèi)存的其他區(qū)域,覆蓋了 var1 var2 的值:

var1 = 1; var2 = 2 
Where do you live? 
Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch 
<Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch> is length 58 
var1 = 2036821625; var2 = 2003266668 
Ok 
Segmentation fault (core dumped) 


在運(yùn)行結(jié)束之前,程序會(huì)用長(zhǎng)字符串覆蓋內(nèi)存的其他部分區(qū)域。注意,var1 var2 的值不再是起始的 1 和 2。

避免使用 gets 函數(shù),改用更安全的方法來(lái)讀取用戶數(shù)據(jù)。例如,getline 函數(shù)會(huì)分配足夠的內(nèi)存來(lái)存儲(chǔ)用戶輸入,因此不會(huì)因輸入長(zhǎng)值而發(fā)生意外的字符串溢出。

4. 重復(fù)釋放內(nèi)存

“分配的內(nèi)存要手動(dòng)釋放”是良好的 C 語(yǔ)言編程原則之一。程序可以使用 malloc 函數(shù)為數(shù)組和字符串分配內(nèi)存,該函數(shù)會(huì)開辟一塊內(nèi)存,并返回一個(gè)指向內(nèi)存中起始地址的指針。之后,程序可以使用 free 函數(shù)釋放內(nèi)存,該函數(shù)會(huì)使用指針將內(nèi)存標(biāo)記為未使用。

但是,你應(yīng)該只使用一次 free 函數(shù)。第二次調(diào)用 free 會(huì)導(dǎo)致意外的后果,可能會(huì)毀掉你的程序。下面是一個(gè)針對(duì)此點(diǎn)的簡(jiǎn)短示例程序。程序分配了內(nèi)存,然后立即釋放了它。但為了模仿一個(gè)健忘但有條理的程序員,我在程序結(jié)束時(shí)又一次釋放了內(nèi)存,導(dǎo)致兩次釋放了相同的內(nèi)存:

#include <stdio.h> 
#include <stdlib.h> 
int 
main() 
{ 
  int *array; 
  puts("malloc an array ..."); 
  array = malloc(sizeof(int) * 5); 
  if (array) { 
    puts("malloc succeeded"); 
    puts("Free the array..."); 
    free(array); 
  } 
  puts("Free the array..."); 
  free(array); 
  puts("Ok"); 
} 


運(yùn)行這個(gè)程序會(huì)導(dǎo)致第二次使用 free 函數(shù)時(shí)出現(xiàn)戲劇性的失?。?/strong>

malloc an array ... 
malloc succeeded 
Free the array... 
Free the array... 
free(): double free detected in tcache 2 
Aborted (core dumped) 


要記得避免在數(shù)組或字符串上多次調(diào)用 free。將 malloc free 函數(shù)定位在同一個(gè)函數(shù)中,這是避免重復(fù)釋放內(nèi)存的一種方法。

例如,一個(gè)紙牌游戲程序可能會(huì)在主函數(shù)中為一副牌分配內(nèi)存,然后在其他函數(shù)中使用這副牌來(lái)玩游戲。記得在主函數(shù),而不是其他函數(shù)中釋放內(nèi)存。將 malloc free 語(yǔ)句放在一起有助于避免多次釋放內(nèi)存。

5. 使用無(wú)效的文件指針

文件是一種便捷的數(shù)據(jù)存儲(chǔ)方式。例如,你可以將程序的配置數(shù)據(jù)存儲(chǔ)在 config.dat 文件中。Bash shell 會(huì)從用戶家目錄中的 .bash_profile 讀取初始化腳本。GNU Emacs 編輯器會(huì)尋找文件 .emacs 以從中確定起始值。而 Zoom 會(huì)議客戶端使用 zoomus.conf 文件讀取其程序配置。

所以,從文件中讀取數(shù)據(jù)的能力幾乎對(duì)所有程序都很重要。但是假如要讀取的文件不存在,會(huì)發(fā)生什么呢?

在 C 語(yǔ)言中讀取文件,首先要用 fopen 函數(shù)打開文件,該函數(shù)會(huì)返回指向文件的流指針。你可以結(jié)合其他函數(shù),使用這個(gè)指針來(lái)讀取數(shù)據(jù),例如 fgetc 會(huì)逐個(gè)字符地讀取文件。

如果要讀取的文件不存在或程序沒(méi)有讀取權(quán)限,fopen 函數(shù)會(huì)返回 NULL 作為文件指針,這表示文件指針無(wú)效。但是這里有一個(gè)示例程序,它機(jī)械地直接去讀取文件,不檢查 fopen 是否返回了 NULL:

#include <stdio.h> 
int 
main() 
{ 
  FILE *pfile; 
  int ch; 
  puts("Open the FILE.TXT file ..."); 
  pfile = fopen("FILE.TXT", "r"); 
  /* you should check if the file pointer is valid, but we skipped that */ 
  puts("Now display the contents of FILE.TXT ..."); 
  while ((ch = fgetc(pfile)) != EOF) { 
    printf("<%c>", ch); 
  } 
  fclose(pfile); 
  /* done */ 
  puts("Ok"); 
  return 0; 
} 


當(dāng)你運(yùn)行這個(gè)程序時(shí),第一次調(diào)用 fgetc 會(huì)失敗,程序會(huì)立即中止:

Open the FILE.TXT file ... 
Now display the contents of FILE.TXT ... 
Segmentation fault (core dumped) 


始終檢查文件指針以確保其有效。例如,在調(diào)用 fopen 打開一個(gè)文件后,用類似 if (pfile != NULL) 的語(yǔ)句檢查指針,以確保指針是可以使用的。

人都會(huì)犯錯(cuò),最優(yōu)秀的程序員也會(huì)產(chǎn)生編程錯(cuò)誤。但是,遵循上面這些準(zhǔn)則,添加一些額外的代碼來(lái)檢查這五種類型的錯(cuò)誤,就可以避免最嚴(yán)重的 C 語(yǔ)言編程錯(cuò)誤。提前編寫幾行代碼來(lái)捕獲這些錯(cuò)誤,可能會(huì)幫你節(jié)省數(shù)小時(shí)的調(diào)試時(shí)間。

到此這篇關(guān)于C語(yǔ)言編程中常見的五種錯(cuò)誤及對(duì)應(yīng)解決方案的文章就介紹到這了,更多相關(guān)C 語(yǔ)言編程常見錯(cuò)誤及對(duì)應(yīng)解決方案內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • c++中的內(nèi)聯(lián)函數(shù)inline用法實(shí)例

    c++中的內(nèi)聯(lián)函數(shù)inline用法實(shí)例

    在本篇文章里小編給大家整理的是關(guān)于c++中的內(nèi)聯(lián)函數(shù)inline用法實(shí)例以及相關(guān)知識(shí)點(diǎn),有需要的朋友們學(xué)習(xí)下。
    2019-09-09
  • C語(yǔ)言實(shí)現(xiàn)linux網(wǎng)卡檢測(cè)精簡(jiǎn)版

    C語(yǔ)言實(shí)現(xiàn)linux網(wǎng)卡檢測(cè)精簡(jiǎn)版

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)linux網(wǎng)卡檢測(cè)的精簡(jiǎn)版,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-06-06
  • c++并查集優(yōu)化(基于size和rank)

    c++并查集優(yōu)化(基于size和rank)

    這篇文章主要介紹了c++并查集優(yōu)化(基于size和rank),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • C++實(shí)現(xiàn)景區(qū)旅游信息管理系統(tǒng)

    C++實(shí)現(xiàn)景區(qū)旅游信息管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)景區(qū)旅游信息管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • C++?異常處理機(jī)制與自定義異常體系處理方式

    C++?異常處理機(jī)制與自定義異常體系處理方式

    本節(jié)將詳細(xì)介紹C++異常處理的相關(guān)概念、用法以及如何通過(guò)自定義異常體系來(lái)滿足程序的需求,同時(shí),我們將對(duì)比C語(yǔ)言的傳統(tǒng)錯(cuò)誤處理方式,分析C++異常機(jī)制的優(yōu)缺點(diǎn),并探討標(biāo)準(zhǔn)庫(kù)中提供的異常體系,幫助開發(fā)者更好地理解和使用C++的異常處理功能,感興趣的朋友一起看看吧
    2024-12-12
  • 詳解C++?左值引用與?const?關(guān)鍵字

    詳解C++?左值引用與?const?關(guān)鍵字

    這篇文章主要介紹了C++?左值引用與?const?關(guān)鍵字,左值引用是已定義的變量的別名,其主要用途是用作函數(shù)的形參,將?const?關(guān)鍵字用于左值引用時(shí),其在初始化時(shí)可接受的賦值形式變得更加廣泛了,這里來(lái)總結(jié)一下,需要的朋友可以參考下
    2022-09-09
  • C語(yǔ)言中全局變量,局部變量,靜態(tài)局部變量的區(qū)分方式

    C語(yǔ)言中全局變量,局部變量,靜態(tài)局部變量的區(qū)分方式

    這篇文章主要介紹了C語(yǔ)言中全局變量,局部變量,靜態(tài)局部變量的區(qū)分方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • iostream與iostream.h的區(qū)別詳細(xì)解析

    iostream與iostream.h的區(qū)別詳細(xì)解析

    以下是對(duì)C++中iostream與iostream.h的區(qū)別進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過(guò)來(lái)參考下
    2013-09-09
  • C++ Boost Coroutine使用協(xié)程詳解

    C++ Boost Coroutine使用協(xié)程詳解

    通過(guò)Boost.Coroutine,可以在C++中使用協(xié)程。協(xié)程是其他編程語(yǔ)言的一個(gè)特性,通常使用關(guān)鍵字yield來(lái)表示協(xié)程。在這些編程語(yǔ)言中,yield可以像return一樣使用
    2022-11-11
  • C 語(yǔ)言中實(shí)現(xiàn)環(huán)形緩沖區(qū)

    C 語(yǔ)言中實(shí)現(xiàn)環(huán)形緩沖區(qū)

    本文主要是介紹 C語(yǔ)言實(shí)現(xiàn)環(huán)形緩沖區(qū),并附有詳細(xì)實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,希望能幫助有需要的小伙伴
    2016-07-07

最新評(píng)論

库尔勒市| 子洲县| 米泉市| 清徐县| 基隆市| 新丰县| 营山县| 交城县| 信宜市| 宁阳县| 建瓯市| 云浮市| 固始县| 辰溪县| 崇阳县| 高碑店市| 都江堰市| 尉犁县| 衡水市| 峡江县| 武义县| 远安县| 中方县| 延长县| 灵武市| 尼勒克县| 达日县| 常山县| 连城县| 凌海市| 顺义区| 文化| 新宾| 台北县| 普安县| 宜州市| 安岳县| 剑阁县| 呼和浩特市| 常熟市| 奎屯市|