如果strncat添加NUL可能会导致数组超出界限



我在使用strncat((时遇到了一些问题。一本名为Pointers On C的书中说,函数strncat((总是在字符串的末尾添加一个NUL。为了更好地理解它,我做了一个实验。

#include<stdio.h>
#include<string.h>
int main(void)
{
char a[14]="mynameiszhm";
strncat(a,"hello",3);
printf("%s",a);
return 0;
}

结果是mynameiszhmhel在这种情况下,数组具有14个字符的内存。除了NUL之外,数组中最初有11个字符。因此,当我再添加三个字符时,所有14个字符都会填充数组的内存。因此,当函数想要添加NUL时,NUL会占用数组外的内存。这会导致数组越界,但上面的程序可以在没有任何警告的情况下运行。为什么?这会引起意想不到的事情吗?那么,当我们使用strncat时,我们是否应该考虑NUL,以防导致数组越界?我还注意到函数strncpy没有添加NUL。为什么这两个字符串函数对同一件事做不同的事情?为什么C的设计师要做这个设计?

这会导致数组越界,但上面的程序可以在没有任何警告的情况下运行。为什么?

也许吧。对于strncat(a,"hello",3);,代码试图写入超过a[]的14。它可能会出界,也可能不会。这是未定义的行为(UB(。任何事情都是允许的。


这会引起意外吗?

也许,行为没有定义。它可能正如你所期望的那样工作-不管是什么。


所以当我们使用strncat时,我们是否应该考虑NUL,以防导致数组越界?

是的,size参数需要考虑附加一个空字符,否则为UB。


我还注意到函数strncpy没有添加NUL。为什么这两个字符串函数对同一件事做不同的事情?为什么C的设计师要做这个设计?

两个函数strncpy()/strncat()简单地共享相似的名称,而不是strcpy()/strcat()的高度相似的配对功能。

考虑到20世纪70年代初,内存的成本要高得多,许多考虑因素可以追溯到一个字节的内存,而不是一小时的工资。功能/名称的统一性不那么重要。


数组中除了NUL之外,最初有11个字符。

更像是"除了3个NUL之外,数组中最初有11个字符&";。这不是C.中的部分初始化

这不是一个真正的答案,而是一个反例。

观察对程序的以下修改:

#include<stdio.h>
#include<string.h>
int main(void)
{
char p[]="***";
char a[14]="mynameiszhm";
char q[]="***";
strncat(a,"hello",3);
printf("%s%s%s", p, a, q);
return 0;
}

a相比,该程序的结果取决于pq在存储器中的位置。如果它们不相邻,结果就不那么清楚,但如果pq紧跟在a之后,则strncat将覆盖第一个*,导致其中一个不再打印,因为它现在将是长度为0的字符串。

因此,结果取决于内存布局,应该清楚的是,编译器可以按照自己喜欢的任何顺序将变量放入内存。它们可以相邻也可以不相邻。

因此,问题是您没有遵守您的承诺,不会在a中放入超过14个字节。编译器按照您的要求执行,只要您遵守承诺,C标准就会保证行为。

现在你有一个程序,可以做也可以不做你想做的事。

最新更新