我正在问这个问题来磨练我的知识。我在下面写了一个测试代码。
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));
}
输出
$ ./ctest
PTR1 : First Set
PTR1 : First Set-Second Set
PTR1 : First Set-Second Set-Third Set
答案 0 :(得分:3)
它提供&#34;权利&#34;结果,因为你很幸运。 malloc()
函数不会使可能访问内存,它会找到内存并保留权限使用它,并承诺没有其他代码具有该内存允许。无论如何都可以访问它(如果有人知道附近的地址)。如果未经许可写入缓冲区,则结果未定义。
在这种未定义的情况下,如果缓冲区之外的内存不可写,您可能会崩溃。如果它是可写的但没有其他代码正在使用它(这可能是你当前的情况),它似乎没有任何问题。如果某些其他代码正在积极使用该内存,那么该代码将破坏您的代码,或者您的代码会破坏另一个代码,或者两者兼而有之。这种中断的结果是不确定的(因为它取决于代码在做什么),意思是未定义。
答案 1 :(得分:3)
关于strncat
,没有人提及过你。 (我认为其他人在解释缓冲区溢出问题方面做得很好。)
strncat
的两个字符串参数(事实上,任何str***
函数的所有字符串参数)都假定为零终止字符串。在您的情况下,由malloc
分配的内存(字节)显然发生为0,因此它被解释为空字符串。如果未初始化的内存包含除0以外的其他一些值,则只要需要在其末尾找到a,它就会被解释为一个字符串。所以一般来说,从malloc
返回的缓冲区可能是一个非常长的垃圾串,比你分配的内存量要长得多。如果要分配初始化为0的内存,请使用calloc
。
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字符串而不会超出缓冲区。