[英]Difference in storage of memory in string and an integer array in C after using sprintf function
在使用sprintf
函數后,能否解釋一下C中的字符串和整數數組的內存分配?
#include <stdio.h>
#include <stdlib.h>
int main() {
char str[10];
long long int i = 0, n = 7564368987643389, l;
sprintf(str, "%lld", n); // made it to a string
printf("%c%c", str[11], str[12]);
}
在上面的代碼中,字符串大小為10,包括空字符。 為什么我們要訪問它中的11和12個元素? 該程序打印43
這里
sprintf(str,"%lld",n);
n
即7564368987643389
轉換為字符緩沖區並存儲到str
。 看起來像
str[0] str[2] str[4] str[6] str[8] str[10] str[12] ..
--------------------------------------------------------------------------------------
| 7 | 5 | 6 | 4 | 3 | 6 | 8 | 9 | 8 | 7 | 6 | 4 | 3 | 3 | 8 | 9 | \0 |
--------------------------------------------------------------------------------------
str str[1] str[3] str[5] str[7] str[9] str[11] ..
如你所見, str[11]
為4
, str[12]
為3
。 因此下面的printf()
語句打印:
printf("%c %c",str[11],str[12]);
4
3
但是因為你已經聲明了10
字符的str
而在sprintf()
你試圖存儲超過10
字符,所以它會導致未定義的行為 。
你已經將數組的末尾寫入了你不擁有的內存中 - 這樣做的行為是未定義的 ,任何結果都是可能的。
C不需要對數組寫入或訪問進行邊界檢查。 如果索引超過數組的末尾,則不會獲得ArrayIndexOutOfBounds類型的異常。
在這種情況下,你沒有覆蓋任何東西重要的是,這樣的程序運行你所期望的,但它不就得了。 您可能在相鄰對象中損壞了數據,或者您可能從操作系統中獲得了運行時錯誤。
您有責任了解目標緩沖區的大小,而不是讀取或寫入它的末尾。 這種語言不會保護你。
你能解釋一下C中的字符串和整數數組的內存分配嗎?
大多數時候在C中,內存分配是你的責任 。 特別是,當你調用strcpy
, sprintf
和fread
這樣的函數時,你可以將任意多個字符寫入你提供的緩沖區, 你有責任 事先確定緩沖區是否足夠大。
一些函數,比如fread
,讓你說你的緩沖區有多大,這樣它們就可以確保不會溢出它。 其他如strcpy
, sprintf
和帶有%s
等指令的scanf
則不然。 你必須特別小心這些功能。
當你寫的東西像
char str[10];
sprintf(str, "%lld", 7564368987643389);
如果你提供的緩沖區對於結果來說不夠大,那么往往會出現兩個問題:
它為什么不起作用?
它為什么工作?
如果它不起作用,原因應該是顯而易見的:目標緩沖區根本不夠大。 如果盡管存在這個問題,它似乎確實有效,原因是因為C沒有強制執行(沒有明確防范)緩沖區溢出。
假設我在一個未開發的街區買了一塊土地 - 一塊1600平方英尺的土地。 假設我的頭銜說:
該物業線向南延伸40英尺,距離鐵樁40英尺,然后向西40英尺,然后向北40英尺,然后向東40英尺。
所以我有40 x 40英尺的土地,但是地面上唯一可以確定我的財產位置的特征是在一個角落的鐵柱。 土壤上沒有畫出清晰的黑色線條,也沒有任何東西,正好划定了物業線。
假設我雇用了一名建築師和一名建築工人,我們在我的新土地上建造了一所房子,我們搞砸了我們的測量結果,並將房屋建在10英尺深的地方(我不擁有)。 怎么了?
沒有發生的事情是,我們挖掘第一個基礎或傾倒第一個混凝土或直立第一個牆穿過屬性線的瞬間,一個巨大的錯誤信息出現在天空中說“物業線超出”。
不,不能保證立即檢測到這種錯誤。 在明天,下周或明年之前,可能不會(由建築檢查員或相鄰財產的所有者)注意到這個問題; 在某些情況下,它可能永遠不會被注意到。
而且情況與C內存分配完全相同。 如果你向一個數組寫的數量多於它分配給的數組,那么這個問題可能暫時沒有顯示出來,或者根本不會顯示出來:程序看起來可能完美無缺,盡管它包含了這個合理的可怕錯誤。
出於這個原因,你不僅要小心C中的內存分配,還有一些好的習慣。 它不僅是對你重要聲明數組(或您的malloc緩沖區)夠大,但你也想確保,只要有可能,尺寸檢查 。 例如:
當您調用fread
函數時,它接受目標緩沖區和該緩沖區的大小,請確保您傳遞的大小是准確的。
而不是像sprintf
那樣調用接受目標緩沖區但無法指定其大小的函數,而是選擇像snprintf
這樣的替代函數, 它們允許指定大小,因此可以防止溢出。
如果有一個函數不允許指定緩沖區大小並且沒有更好的替代方法,那么可能根本就不使用該函數。 示例是帶有%s
和%[
指令的strcpy
和scanf
。
當您編寫自己的函數來接受指向緩沖區的指針並將字符或其他數據寫入這些緩沖區時,請確保提供一個參數,調用者可以通過該參數顯式提供緩沖區大小,並確保您的函數符合此限制。
該程序具有未定義的行為,因為:
sprintf
在定義的目標數組str
存儲字符7564368987643389
加上一個空終止符(總共17個字節),其長度僅為10個字節。 sprintf
不接收數組的長度,因此如果輸出太短,則將輸出寫入超出數組末尾的內存。 你應該總是使用snprintf(str, sizeof str, "%lld", n);
避免這種不確定的行為。
printf("%c%c", str[11], str[12]);
讀取超出str
數組末尾的2個字節,即第12個和第13個字節。 這有未定義的行為,但是如果printf
確實將17個字節成功地存儲在從str
的地址開始的內存中,則讀取這些字節可能會產生值'4'
和'3'
,並產生43
的輸出,這可能會或可能會因為你沒有用換行符結束程序的輸出而不可見。
超出數組末尾的寫入和讀取具有未定義的行為,它可能導致程序崩潰或看起來像預期的那樣起作用,但是可能發生不期望的副作用並且暫時不被注意。 在你的系統上似乎沒有什么不好的事情,但在其他系統上,它可能會造成巨大的破壞......想象一下,如果程序作為核動力裝置調節系統的一部分運行,你不會想要以這種方式測試系統的彈性。
我建議創建一些你需要的功能。 像這樣。
#include <stdio.h>
#include <stdlib.h>
char* LL_Int_To_Str(long long int A){
int Lenz=0,i;
int NegFlag=0;
if(A<0){
A=~A+1;
NegFlag=1;
}
long long int B=A;
do{
B/=10;
Lenz++;
}while(B);
char *Result=(char*)malloc(sizeof(char)*(Lenz+1+NegFlag));
Result[Lenz+NegFlag]='\0';
for(i=Lenz-1;i>-1-NegFlag;i--){
Result[i+NegFlag]=(A%10)+48;
A/=10;
}
if(NegFlag){
Result[0]='-';
}
return Result;
}
int main(){
int i;
long long int n=7564368987643389;
//long long int n=-7564368987643389;
char* StrX=LL_Int_To_Str(n);
/*char* Loop;//Debug
for(Loop=StrX;*Loop!='\0';Loop++){
printf("%c\n",*Loop);
}*/
printf("%s\n",StrX);
free(StrX);
return 0;
}
然后你可以確保沒有錯。
有時您認為可以使用某些功能節省您的時間。
但大多數時候,這都是浪費。
另一個暗示
#include <limits.h>
printf("%lld\n",LLONG_MAX);
你可以找到max long long int是9223372036854775807
因此字符串所需的最大大小為19 + 1 + 1(' - 0表示' - '為1)
只需使用char str [21]; 解決所有問題
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.