以下计划
// Code has taken from http://ideone.com/AXClWb
#include <stdio.h>
#include <string.h>
#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];
标准是否明确说明目标字符串也将被终止?
答案 0 :(得分:3)
我认为man 明确表示
描述
strcat()函数将src字符串追加到dest字符串,覆盖dest 末尾的终止空字节('\ 0'),然后添加一个终止空字节。字符串可能不重叠,dest字符串必须有足够的空间用于结果。如果dest不够大,程序行为是不可预测的;缓冲区溢出是攻击安全程序的最佳途径。
Enphasis mine
BTW我认为strcat
在连接新字符串之前开始搜索到目标字符串中的null terminator
,所以它显然是UB,只要dest
字符串具有自动存储。
在建议的代码中
for (int i=0 ; i<SIZE1 ; i++){
res[i] = a[i];
}
将5
字符a
而不是空终结符复制到res
字符串,以便从5
到14
的其他字节未初始化。
标准也谈到了safaer实现strcat-s
K.3.7.2.1 strcat_s函数
概要
#define _ _STDC_WANT_LIB_EXT1_ _ 1 #include <string.h> errno_t strcat_s(char * restrict s1, rsize_t s1max, const char * restrict s2);
<强>运行约束强>
2 m 在输入时表示值 s1max - strnlen_s(s1,s1max) 的 strcat_s 强>
我们可以看到strlen_s
总是返回dest
缓冲区的有效大小。从我的观点来看,这个实现是为了避免问题的UB而引入的。
答案 1 :(得分:2)
如果您将res
保留为未初始化,则在将a
复制到res
(for for循环)后,res
中没有NUL终止符。因此,如果目标字符串不包含NUL字节,则strcat()
的行为是未定义的。
基本上strcat()
要求它的两个参数都是字符串(即两者都必须包含终止的NUL字节)。否则,它是undefined behaviour。这个
从strcat()
:
strcat函数附加s2 指向的字符串的副本 (包括终止空字符)到字符串的末尾 由s1指出。 s2的初始字符会覆盖null s1末尾的字符。
(强调我的)。
答案 2 :(得分:2)
TL; DR 是。
由于这是一个语言律师问题,让我加上我的两分钱。
引用C11
,章节§7.24.3.1/ 2(强调是我的)
char *strcat(char * restrict s1,const char * restrict s2);
strcat
函数附加s2
指向的字符串的副本(包括 终止空字符)到s1
指向的字符串的末尾。最初的字符s2
的覆盖范围覆盖s1
末尾的空字符。[...]
,根据定义,字符串 以null结尾,引用§7.1.1/ 1
string 是一个连续的字符序列由第一个null终止并包含 字符。强>
因此,如果源char
数组不是以空值终止的(即,不是字符串),strcat()
可能会超出搜索范围< em>结束,它调用undefined behavior。
根据您的问题,char res[SIZE3];
是一个自动局部变量,将包含不确定的值,如果用作strcat()
的目标,将调用UB。
答案 3 :(得分:1)
如果char res[SIZE3];
位于堆栈上,则会包含随机/未定义的内容。
你永远不会知道res[SIZE3]
中是否会有一个零字节,所以是,strcat
这是未定义的。
如果char res[SIZE3];
是未初始化的全局,那么它将全部为零,这将使其表现为空c-string < / em>,strcat
将是安全的(只要SIZE3足够大,可以附加你的内容)。