malloc()缓冲区太小

我问这个问题来磨练我的知识。 我在下面写了一份测试代码。

char *ptr1的大小是malloc(1) 。 我在3行上复制5个字。 共有32个字符长度。 输出很顺利。 我浏览了strncat()函数的代码。 它似乎也没有为复制目的分配额外的内存。 我也没有在ptr1 char指针的末尾添加任何空字符。

这怎么能产生正确的结果? 那是对的吗?

 int main(void) { char *name = "First Set"; char *ptr1; ptr1 = malloc(1); // <<== here the memory is allocated only 1 byte joinWithMe(ptr1, name); // <<== 9 bytes are copied printf("PTR1 : %s\n", ptr1); joinWithMe(ptr1, "-Second Set"); // <<== 11 bytes are copied printf("PTR1 : %s\n", ptr1); joinWithMe(ptr1, "-Third Set"); // <<== 10 bytes are copied printf("PTR1 : %s\n", ptr1); return 0; } void joinWithMe(char *me, const char *him) { strncat(me, him, strlen(him)); } 

OUTPUT

 $ ./ctest PTR1 : First Set PTR1 : First Set-Second Set PTR1 : First Set-Second Set-Third Set 

它提供了“正确”的结果,因为你很幸运。 malloc()函数不能访问内存,它会查找内存并保留您使用它的权限 ,并承诺没有其他代码具有该权限。 无论如何都可以访问它(如果有人知道附近的地址)。 如果未经许可写入缓冲区,则结果未定义。

在这个未定义的情况下,如果缓冲区之外的内存不可写,您可能会崩溃。 如果它是可写的但没有其他代码正在使用它(这可能是你当前的情况),它似乎没有任何问题。 如果其他一些代码正在积极使用该内存,那么该代码将破坏您的代码,或者您的代码会破坏另一个代码,或两者兼而有之。 这种中断的结果是不确定的(因为它取决于代码在做什么),意思是未定义的。

关于strncat有两件事没有人提到你。 (我认为其他人已经很好地解释了缓冲区溢出问题。)

1。

strncat两个字符串参数(事实上,任何str***函数的所有字符串参数)都假定为零终止字符串 。 在你的情况下, malloc分配的内存(字节)显然是0,所以它被解释为一个空字符串。 如果未初始化的内存包含除0以外的其他值,那么它将被解释为一个字符串,只要在其末尾找到一个所需的字符串即可。 所以一般来说,从malloc返回的缓冲区可能是一个非常长的垃圾串,比你分配的内存量要长得多。 如果要分配初始化为0的内存,请使用calloc

2。

strncat的最后一个参数是通过指定已经分配的缓冲区中剩余的空间来防止缓冲区溢出的一种方法。

 // allocate a string buffer const size_t bufSz = 20; char *pBuf = malloc(bufSz); // initialize it with either a short or long string bool shortOrLong = ... strcpy(pBuf, shortOrLong ? "short_str" : "longer_string"); // append a string without buffer overrun const char *pStr = "_hi_there"; strncat(pBuf, pStr, bufSz-strlen(pBuf)-1); 

根据shortOrLong的值, pBuf的内容将是以下之一。

 |0| | | | |5| | | | |10 | | | |15 | | | |20 <-- index +=========+=========+=========+=========+ | | <-- before strcpy +=========+=========+=========+=========+ |s|h|o|r|t|_|s|t|r|0| | <-- before strncat |s|h|o|r|t|_|s|t|r|_|h|i|_|t|h|e|r|e|0| | <-- after strncat +=========+=========+=========+=========+ |l|o|n|g|e|r|_|s|t|r|i|n|g|0 | <-- before strncat |l|o|n|g|e|r|_|s|t|r|i|n|g|_|h|i|_|t|h|0| <-- after strncat +=========+=========+=========+=========+ 

(我使用0表示空终止字符,空白(空格)表示未初始化的值。)

请注意,在"short_str"情况下,附加的字符串适合缓冲区,因此所有字符串都被复制了; 在"longer string"情况下,只有字符串的一部分适合,因此该部分被复制,然后该字符串仍为空终止,以使其保持有效的C字符串而不会超出缓冲区。