如果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
字符串,因此从5
到14
其他字节未初始化。
标准也说关于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足够大,你可以追加)。