如果strcat函数中的目标字符串不是以null结尾,那么它是否是未定义的行为?

以下程序

// Code has taken from http://ideone.com/AXClWb #include  #include  #define SIZE1 5 #define SIZE2 10 #define SIZE3 15 int main(void){ char a[SIZE1] = "Hello"; char b[SIZE2] = " World"; char res[SIZE3] = {0}; for (int i=0 ; i<SIZE1 ; i++){ res[i] = a[i]; } strcat(res, b); printf("The new string is: %s\n",res); return 0; } 

有明确的行为。 根据要求,源字符串b以空值终止。 但如果这条线会是什么行为

 char res[SIZE3] = {0}; // Destination string 

被替换为

 char res[SIZE3]; 

标准是否明确说明了目标字符串是否也被终止?

TL; DR是的。


由于这是一个语言律师问题,让我加上我的两分钱。

引用C11 ,章节§7.24.3.1/ 2( 强调是我的

 char *strcat(char * restrict s1,const char * restrict s2); 

strcat函数将s2指向的字符串副本(包括终止空字符)附加s1指向的字符串的末尾。 s2的初始字符会覆盖s1末尾的空字符。[…]

并且,根据定义, 字符串 空值终止,引用§7.1.1/ 1

字符串由第一个空字符终止并包括第一个空字符的连续字符序列

因此,如果源char数组不是以null结尾(即,不是字符串 ),则strcat()可能会超出搜索结束的边界,从而调用未定义的行为 。

根据你的问题, char res[SIZE3]; 作为自动局部变量,将包含不确定的值,如果用作strcat()的目标,将调用UB。

我认为男人 明确地说过

描述

strcat()函数将src字符串附加到dest字符串, 覆盖 dest末尾的终止空字节(’\ 0’) ,然后添加一个终止空字节。 字符串可能不重叠,dest字符串必须有足够的空间用于结果。 如果dest不够大,程序行为是不可预测的; 缓冲区溢出是攻击安全程序的最佳途径。

Enphasis我的

BTW我认为strcat在连接新字符串之前开始在dest字符串中搜索null terminator符,所以它显然是UB,因为dest字符串具有自动存储function。

在提议的代码中

 for (int i=0 ; i 

5字符的a而不是空终止符复制到res字符串,因此从514其他字节未初始化。

标准也说关于safaer实现strcat-s

K.3.7.2.1 strcat_s函数

概要

  #define _ _STDC_WANT_LIB_EXT1_ _ 1 #include  errno_t strcat_s(char * restrict s1, rsize_t s1max, const char * restrict s2); 

运行约束

2 m在进入strcat_s时表示值s1max-strnlen_s(s1,s1max)

我们可以看到strlen_s总是返回dest缓冲区的有效大小。 从我的角度来看,这个实现是为了避免UB的问题而引入的。

如果你保留未初始化的res那么在复制到res (for for循环)之后, res没有NUL终结符。 因此,如果目标字符串不包含NUL字节,则strcat()的行为是未定义的。

基本上strcat()要求它的两个参数都是字符串 (即两者都必须包含终止的NUL字节)。 否则,它是未定义的行为 。 从strcat()的描述中可以strcat()这一点:

§7.23.3.2,strcat()函数

strcat函数将s2指向字符串副本(包括终止空字符)附加到s1指向字符串的末尾。 s2的初始字符会覆盖s1末尾的空字符。

(强调我的)。

如果char res[SIZE3];堆栈中 ,它会有随机/未定义的东西。
你永远不会知道res[SIZE3]是否会有一个零字节,所以是的strcat ting到那个是未定义的。

如果char res[SIZE3]; 是一个未初始化的全局 ,它将全部为零 ,这将使它表现为一个空的c字符串 ,并且strcat到它将是安全的(只要SIZE3足够大,你可以追加)。