Him*_*pta 8 c segmentation-fault strncpy
我只是乱搞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;
}
Run Code Online (Sandbox Code Playgroud)
在我执行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)
Run Code Online (Sandbox Code Playgroud)
为什么这个分段错误发生在这里.
有人可以帮我从这里出去吗.
Who*_*aig 14
我可以指向你的手册页,网站等,但最重要的是C标准本身.作为标准运行时库的一部分,使用和行为在C99-§7.23.2.4中定义为:
#include <string.h>
char *strncpy(char * restrict s1,
const char * restrict s2,
size_t n);
Run Code Online (Sandbox Code Playgroud)
描述 该
strncpy函数从s2指向的数组复制不超过n个字符(空字符后面的字符不被复制)到s1指向的数组.如果在重叠的对象之间进行复制,则行为未定义.如果s2指向的数组是一个短于n个字符的字符串,则将空字符附加到s1指向的数组中的副本,直到写入所有n个字符.返回 该
strncpy函数返回s1的值.
这里有重要的隐含信息,最重要的是:如果源字符串长度(不包括其空字符终止符)满足或超过指定的目标缓冲区长度,strncpy()则不会使用空字符终止目标字符串.
此外,虽然在标准中明确规定(见上文),它继续混淆了我很多的工程师怎么都没有意识到,strncpy() 尾填充空字符的目标字符串缓冲区指定的长度,直到n达到当源字符串长度小于比目标缓冲区大小.这得出以下不可避免的结论:
该strncpy()API将永远写入n字符由目标缓冲区中引用的地址.
在你的情况下,因为目标缓冲区只有10个字符宽,所以你要在可写内存的定义端之外写入90个附加字符,从而进入未定义行为的范围.
在这一点上,你必须问自己"那么使用什么?" 这里是一个可以说是基本用例.它允许您将nchars 复制到目标缓冲区,并且可以预测您不会超出过去的n字符.期.不过说到底,你想有一个空值终止字符串,所以在正确用法是这样的:
char dst[ N ];
strncpy(dst, src, N-1);
dst[N-1] = 0;
Run Code Online (Sandbox Code Playgroud)
其中N是dstchars中缓冲区的硬性长度,大于或等于1.请注意,它dst也可以是动态分配的内存指针:
char *dst = malloc( N * sizeof(char) );
strncpy(dst, src, N-1);
dst[N-1] = 0;
Run Code Online (Sandbox Code Playgroud)
通过上面的内容,您将始终使用以null结尾的字符串dst.如果源字符串长度小于指定的目标缓冲区长度,strncpy()则将使用空字符尾部填充缓冲区的其余部分,直到源-chars复制的+ tail-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.
Run Code Online (Sandbox Code Playgroud)
回想一下上面的粗体声明:" 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;
Run Code Online (Sandbox Code Playgroud)
有关如何为长度为`N chars的动态字符串执行此操作,请参阅先前的用法.
来自http://www.cplusplus.com/reference/cstring/strncpy/
char*strncpy(char*destination,const char*source,size_t num);
从字符串复制字符将源的第一个num字符复制到destination.如果在复制num个字符之前找到源C字符串的结尾(由空字符表示),则使用零填充目标,直到已向其写入总共num个字符.
因此,尽管源字符串长度小于目标缓冲区大小的大小,但由于strncpy的传递行为,它会尝试将其余字符覆盖到目标缓冲区大小之外,从而导致Segmentation fault
而不是复制超过100个字符'的大小应该等于目标缓冲区中允许的最大大小,所以,你可以写
strncpy(s1.from_str,src,sizeof(s1.from_str)/sizeof(s1.from_str[0]) - 1); Actual size -1 to accomodate the null terminator
Run Code Online (Sandbox Code Playgroud)
或者更好地写一个_countof宏
#define _countof(s) (sizeof(s)/sizeof(s[0]))
................
strncpy(s1.from_str,src,_countof(s1.from_str) - 1);
Run Code Online (Sandbox Code Playgroud)