我只是在搞乱strncpy。
我的程序看起来像这样
typedef struct
{
char from_str[10];
}test;
main ()
{
test s1;
memset(&s1,0,sizeof(test));
char src[10]="himansh";
char dest[10];
memset(dest,0,10);
src[3]='\0';
printf("src is %s and strlen is %d \n",
src,strlen(src));
fflush(stdout);
strncpy(s1.from_str,src,100);
printf("s1.from_str is %s , src is %s \n",
s1.from_str,src);
return 1;
}
在我执行strncpy之前,我在“src”字符串中添加了一个“\ 0”字符,“src”字符串的长度变为3,目标数组的大小为10。但是在strncpy中我将字节数添加到复制为100。
这意味着我的源字符串以NULL结尾。现在strncpy像任何字符串函数应该尝试只复制3个字节,即使我提供的字节数大于3(在这种情况下为100)。它做到了,但我也遇到了分段错误。
我的结果如下所示
src is him and strlen is 3
s1.from_str is him , src is him
Segmentation fault (core dumped)
为什么这个分段错误发生在这里。
任何人都可以帮助我。
答案 0 :(得分:14)
我可以指向您的手册页,网站等,但最重要的是C标准本身。作为标准运行时库的一部分,使用和行为在C99-§7.23.2.4中定义为:
#include <string.h>
char *strncpy(char * restrict s1,
const char * restrict s2,
size_t n);
<强>描述强>
strncpy
函数从s2指向的数组复制不超过n个字符(空字符后面的字符不被复制)到指向的数组 S1。如果在重叠的对象之间进行复制,则行为未定义。 如果s2指向的数组是一个短于n个字符的字符串,则空字符将附加到s1指向的数组中的副本,直到所有字符都被写入。<强>返回强>
strncpy
函数返回s1的值。
此处有重要隐含信息,最重要的是:strncpy()
NOT 如果源字符串长度为
此外,虽然标准中明确规定了(见上文),但仍然让我感到困惑的是,有多少工程师不知道strncpy()
使用空字符填充目标字符串缓冲区当源字符串长度小于而不是目标缓冲区大小时,直到达到指定的长度n
。这得出了以下不可避免的结论:
strncpy()
API始终将n
个字符写入目标缓冲区引用的地址。
在你的情况下,因为目标缓冲区只有10个字符宽,你在可写内存的定义端之外写了90个额外的字符,因此走进了未定义的行为
此时你必须问自己“那么使用什么?” 是一个可论证的基本用例。它允许您将n
个字符复制到目标缓冲区,并且可以预测您不会超过n
个字符。期。但最终,你想要一个以空字符结尾的字符串,所以 的正确用法是:
char dst[ N ];
strncpy(dst, src, N-1);
dst[N-1] = 0;
其中N
是字符中dst
缓冲区的硬性长度,大于或等于1
。请注意,dst
也可以是动态分配的内存指针:
char *dst = malloc( N * sizeof(char) );
strncpy(dst, src, N-1);
dst[N-1] = 0;
通过上述内容,您将始终在dst
处具有以空值终止的字符串。如果源字符串 length 小于指定的目标缓冲区长度,strncpy()
将使用空字符填充缓冲区的其余部分,直到共有源字符复制+尾部 - filled-null-characters等于n
,最终语句是多余的。如果源字符串长度等于或大于目标缓冲区长度,strncpy()
将在达到N-1
个字符时停止复制,并且最终语句在以下位置设置空字符缓冲区的结尾。这会导致原始源的“减少”前缀字符串,但最重要的是,它确保您不会超过目标缓冲区的边界,并使用稍后扫描终结符的字符串API调用。
上述技术的用处总是值得商榷的。我是一个C ++人,所以std::string
让我的快乐自我免于这种疯狂。但实际情况是这样的:有时您会关心src
是否未以整体复制到dst
;有时候你没有。有用性是非常在情境上依赖。为了在UI中呈现字符串数据,这不会(可能)重要。要复制要用于关键数据的字符串,部分前缀子字符串将不可接受。当警察向“小约瑟夫约翰逊”发出逮捕令时,当他的父亲(“约瑟夫约翰逊”)被拖入监狱时会有一些解释,因为保证发行软件的名称缓冲只持有15个字符
所有这些都说明了,你的细分错误归结于这个陈述:
strncpy(s1.from_str,src, 100); // length parameter is wrong.
回想一下上面的粗体声明:“strncpy()
将始终将n
个字符写入目标缓冲区引用的地址。”。这意味着上面的代码将始终将100个字符写入目标缓冲区,在您的情况下只有10个字符宽,因此未定义的行为可能 ker-boom 强>
如果目标缓冲区是固定长度的字符数组,请执行以下操作来纠正此问题:
strncpy(s1.from_str,src, sizeof(s1.from_str)/sizeof(s1.from_str[0])-1);
s1.from_str[ sizeof(s1.from_str)/sizeof(s1.from_str[0])-1 ] = 0;
有关如何对长度为“N chars。”的动态字符串执行此操作,请参阅先前的用法。
答案 1 :(得分:5)
来自http://www.cplusplus.com/reference/cstring/strncpy/
char * strncpy(char * destination,const char * source,size_t num );
从字符串复制字符复制源的前几个num字符 到目的地。如果源C字符串的结尾(表示信号 在复制num个字符之前找到一个空字符) 目标用零填充,直到总共有num个字符 已被写入。
因此,虽然源字符串长度小于目标缓冲区大小的大小,但由于strncpy的传递行为,它会尝试将其余字符覆盖到目标缓冲区大小之外,从而导致分段错误
而不是复制超过100个字符'的大小应该等于目标缓冲区中允许的最大大小,所以,你可以编写
strncpy(s1.from_str,src,sizeof(s1.from_str)/sizeof(s1.from_str[0]) - 1); Actual size -1 to accomodate the null terminator
或更好地编写_countof
宏
#define _countof(s) (sizeof(s)/sizeof(s[0]))
................
strncpy(s1.from_str,src,_countof(s1.from_str) - 1);
答案 2 :(得分:1)
请参阅:http://www.manpagez.com/man/3/strncpy/
stpncpy()和strncpy()函数从s2复制最多n个字符 进入s1。如果s2小于n个字符长,则s1的余数为 填充'\ 0'字符。否则,s1不会被终止。
其余部分已填满......
所以:
strncpy( s1.from_str, src, 10 );
答案 3 :(得分:0)
strncpy(s1.from_str,src,100);
为什么在函数中使用100
,from_str和src都分配了10个连续的字节,但是你要复制100个字节,这会导致seg。故障。
像这样使用,
strncpy(s1.from_str,src,10);