简体   繁体   English

使用sprintf函数后,字符串中的内存和C中的整数数组的存储差异

[英]Difference in storage of memory in string and an integer array in C after using sprintf function

Could you please explain me memory allocation in C for strings and integer array 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]);
}

In the above code, string size is 10 inclusive of null character. 在上面的代码中,字符串大小为10,包括空字符。 How come we access 11 and 12 elements in it? 为什么我们要访问它中的11和12个元素? The program prints 43 该程序打印43

Here 这里

sprintf(str,"%lld",n);

n ie 7564368987643389 converted into character buffer & stored into str . n7564368987643389转换为字符缓冲区并存储到str It looks like 看起来像

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] ..

As you can see str[11] is 4 and str[12] is 3 . 如你所见, str[11]4str[12]3 Hence the printf() statement below prints: 因此下面的printf()语句打印:

printf("%c %c",str[11],str[12]);

4 3 4 3

But since you have declared str of 10 characters and in sprintf() you are trying to store more than 10 characters, it causes undefined behavior . 但是因为你已经声明了10字符的str而在sprintf()你试图存储超过10字符,所以它会导致未定义的行为

You've written past the end of the array into memory you don't own - the behavior on doing so is undefined , and any outcome is possible. 你已经将数组的末尾写入了你不拥有的内存中 - 这样做的行为是未定义的 ,任何结果都是可能的。

C does not require bounds checking on array writes or accesses. C不需要对数组写入或访问进行边界检查。 You won't get an ArrayIndexOutOfBounds-type exception if you index past the end of the array. 如果索引超过数组的末尾,则不会获得ArrayIndexOutOfBounds类型的异常。

In this case you didn't overwrite anything important, so the program ran as you would expect, but it doesn't have to. 在这种情况下,你没有覆盖任何东西重要的是,这样的程序运行你所期望的,但它不就得了。 You could have corrupted data in an adjacent object, or you could have gotten a runtime error from the OS. 您可能在相邻对象中损坏了数据,或者您可能从操作系统中获得了运行时错误。

You are responsible for knowing how big the target buffer is and not reading or writing past the end of it. 您有责任了解目标缓冲区的大小,而不是读取或写入它的末尾。 The language will not protect you here. 这种语言不会保护你。

Could you please explain me memory allocation in C for strings and integer array? 你能解释一下C中的字符串和整数数组的内存分配吗?

Most of the time in C, memory allocation is your responsibility . 大多数时候在C中,内存分配是你的责任 In particular, when you call functions such as strcpy , sprintf and fread , that write potentially arbitrarily many characters to a buffer you supply, it is your responsibility to ensure, somehow, beforehand , that the buffer is big enough. 特别是,当你调用strcpysprintffread这样的函数时,你可以将任意多个字符写入你提供的缓冲区, 你有责任 事先确定缓冲区是否足够大。

Some functions, such as fread , let you say how big your buffer is, so that they can be sure not to overflow it. 一些函数,比如fread ,让你说你的缓冲区有多大,这样它们就可以确保不会溢出它。 Others, such as strcpy , sprintf , and scanf with directives like %s , do not. 其他如strcpysprintf和带有%s等指令的scanf则不然。 You must be especially careful with these functions. 你必须特别小心这些功能。

When you write something like 当你写的东西像

char str[10];
sprintf(str, "%lld", 7564368987643389);

where you supply a buffer that is not big enough for the result, two questions tend to arise: 如果你提供的缓冲区对于结果来说不够大,那么往往会出现两个问题:

  1. Why didn't it work? 它为什么不起作用?

  2. Why did it work? 为什么工作?

If it didn't work, the reason why should be obvious: the destination buffer simply wasn't big enough. 如果它不起作用,原因应该是显而易见的:目标缓冲区根本不够大。 And if despite that problem, it did seem to work, the reason is because C doesn't enforce (doesn't explicitly guard against) buffer overflow. 如果尽管存在这个问题,它似乎确实有效,原因是因为C没有强制执行(没有明确防范)缓冲区溢出。

Suppose I buy some land -- a 1600 square foot plot -- in an undeveloped neighborhood. 假设我在一个未开发的街区买了一块土地 - 一块1600平方英尺的土地。 Suppose my title deed says: 假设我的头衔说:

The property line runs south for 40 feet from an iron stake, then 40 feet west, then 40 feet north, then 40 feet east. 该物业线向南延伸40英尺,距离铁桩40英尺,然后向西40英尺,然后向北40英尺,然后向东40英尺。

So I've got 40 x 40 foot plot of land, but the only feature on the ground that positively identifies where my property is, is an iron stake at one corner. 所以我有40 x 40英尺的土地,但是地面上唯一可以确定我的财产位置的特征是在一个角落的铁柱。 There isn't a crisp black line painted on the soil, or anything, precisely delineating the property lines. 土壤上没有画出清晰的黑色线条,也没有任何东西,正好划定了物业线。

Suppose I hire an architect and a builder, and we build a house on my new plot of land, and we screw up our measurements, and we build the house 10 feet into a neighboring parcel of land (that I don't own). 假设我雇用了一名建筑师和一名建筑工人,我们在我的新土地上建造了一所房子,我们搞砸了我们的测量结果,并将房屋建在10英尺深的地方(我拥有)。 What happens? 怎么了?

What does not happen is that the instant we dig that first footing or pour that first concrete or erect that first wall that crosses over the property line, a giant error message appears in the sky saying "PROPERTY LINE EXCEEDED". 没有发生的事情是,我们挖掘第一个基础或倾倒第一个混凝土或直立第一个墙穿过属性线的瞬间,一个巨大的错误信息出现在天空中说“物业线超出”。

No, this kind of error is not guaranteed to get detected right away. 不,不能保证立即检测到这种错误。 The problem might not be noticed (by a building inspector, or by the owner of the adjacent property) until tomorrow, or next week, or next year; 在明天,下周或明年之前,可能不会(由建筑检查员或相邻财产的所有者)注意到这个问题; and under some circumstances it might never get noticed at all. 在某些情况下,它可能永远不会被注意到。

And the situation is just about exactly the same with C memory allocation. 而且情况与C内存分配完全相同。 If you write more to an array than it's allocated to hold, the problem might not reveal itself for a while, or it might not reveal itself at all: the program might seem to work perfectly, despite this reasonably dire error it contains. 如果你向一个数组写的数量多于它分配给的数组,那么这个问题可能暂时没有显示出来,或者根本不会显示出来:程序看起来可能完美无缺,尽管它包含了这个合理的可怕错误。

For this reason, not only do you have to be careful with memory allocation in C, there are some good habits to get into. 出于这个原因,你不仅要小心C中的内存分配,还有一些好的习惯。 Not only is it important to declare you arrays (or malloc your buffers) big enough, but you also want to make sure that, whenever possible, the size is checked. 它不仅是对你重要声明数组(或您的malloc缓冲区)够大,但你也想确保,只要有可能,尺寸检查 For example: 例如:

  • When you call functions like fread , that accept a destination buffer and the size of that buffer, make sure the size you pass is accurate. 当您调用fread函数时,它接受目标缓冲区该缓冲区的大小,请确保您传递的大小是准确的。

  • Instead of calling functions like sprintf that accept a destination buffer but with no way to specify its size, prefer alternative functions like snprintf that do allow the size to be specified and that therefore can guard against overflow. 而不是像sprintf那样调用接受目标缓冲区但无法指定其大小的函数,而是选择像snprintf这样的替代函数, 它们允许指定大小,因此可以防止溢出。

  • If there's a function that doesn't allow the buffer size to be specified and for which there's no better alternative, maybe just don't use that function at all. 如果有一个函数不允许指定缓冲区大小并且没有更好的替代方法,那么可能根本就不使用该函数。 Examples are strcpy and scanf with the %s and %[ directives. 示例是带有%s%[指令的strcpyscanf

  • When you write your own functions that accept pointers to buffers and that write characters or other data into those buffers, make sure you provide an argument by which the caller can explicitly supply the buffer size, and make sure your function honors this limit. 当您编写自己的函数来接受指向缓冲区的指针并将字符或其他数据写入这些缓冲区时,请确保提供一个参数,调用者可以通过该参数显式提供缓冲区大小,并确保您的函数符合此限制。

The program has undefined behavior because: 该程序具有未定义的行为,因为:

  • sprintf stores the characters 7564368987643389 plus a null terminator (a total of 17 bytes) in the destination array str defined with a length of only 10 bytes. sprintf在定义的目标数组str存储字符7564368987643389加上一个空终止符(总共17个字节),其长度仅为10个字节。 sprintf does not receive the length of the array, hence writes the output to memory beyond the end of the array if it is too short. sprintf不接收数组的长度,因此如果输出太短,则将输出写入超出数组末尾的内存。 You should always use snprintf(str, sizeof str, "%lld", n); 你应该总是使用snprintf(str, sizeof str, "%lld", n); to avoid such undefined behavior. 避免这种不确定的行为。

  • printf("%c%c", str[11], str[12]); reads 2 bytes beyond the end of the str array, namely the 12th and the 13th bytes. 读取超出str数组末尾的2个字节,即第12个和第13个字节。 This has undefined behavior, but if printf did successfully store the 17 bytes into the memory starting at the address of str , reading these bytes may yield the values '4' and '3' , and produce an output of 43 , which may or may not be visible as you did not end the program's output with a newline. 这有未定义的行为,但是如果printf确实将17个字节成功地存储在从str的地址开始的内存中,则读取这些字节可能会产生值'4''3' ,并产生43的输出,这可能会或可能会因为你没有用换行符结束程序的输出而不可见。

Writing and reading beyond the end of an array has undefined behavior, it may cause the program to crash or may seem to function as expected, but undesirable side effects can occur and go unnoticed for a while. 超出数组末尾的写入和读取具有未定义的行为,它可能导致程序崩溃或看起来像预期的那样起作用,但是可能发生不期望的副作用并且暂时不被注意。 On your system it seems nothing bad happened, but on some other system, it may cause tremendous damage... imagine if the program were running as part of a nuclear powerplant regulation system, you would not want to test the system's resilience this way. 在你的系统上似乎没有什么不好的事情,但在其他系统上,它可能会造成巨大的破坏......想象一下,如果程序作为核动力装置调节系统的一部分运行,你不会想要以这种方式测试系统的弹性。

I suggest creating some function you need. 我建议创建一些你需要的功能。 Like this. 像这样。

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

Then you can make sure nothing mistake. 然后你可以确保没有错。

Sometimes you think you can use some function save your time. 有时您认为可以使用某些功能节省您的时间。

But most of time ,it's waste. 但大多数时候,这都是浪费。


Another suggest 另一个暗示

#include <limits.h>
printf("%lld\n",LLONG_MAX);

You can find max of long long int is 9223372036854775807 你可以找到max long long int是9223372036854775807

So max size of string requires is 19+1+1 (1 for '\\0' 1 for '-') 因此字符串所需的最大大小为19 + 1 + 1(' - 0表示' - '为1)

Just use char str[21]; 只需使用char str [21]; solve all problem 解决所有问题

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM