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; }
在我执行strncpy之前,我在“src”字符串中添加了一个“\ 0”字符,“src”字符串的长度变为3,目标数组的大小为10。但是在strncpy中我将要复制的字节数复制为100 。
这意味着我的源字符串以NULL结尾。 现在strncpy像任何字符串函数一样,即使我提供的字节数大于3(在本例中为100),也应该尝试仅复制3个字节。 它做到了,但我也得到了分段错误。
我的结果如下所示
src is him and strlen is 3 s1.from_str is him , src is him Segmentation fault (core dumped)
为什么这个分段错误发生在这里。
有人可以帮我从这里出去吗。
我可以指出你的手册页,网站等,但最重要的是C标准本身。 作为标准运行时库的一部分,使用和行为在C99-§7.23.2.4中定义为:
#include char *strncpy(char * restrict s1, const char * restrict s2, size_t n);
描述
strncpy
函数从s2指向的数组复制不超过n个字符(不会复制空字符后面的字符)到s1指向的数组。 如果在重叠的对象之间进行复制,则行为未定义。 如果s2指向的数组是一个短于n个字符的字符串,则将空字符附加到s1指向的数组中的副本,直到写入所有n个字符。返回
strncpy
函数返回s1的值。
这里有重要的隐含信息,最重要的是:如果源字符串长度(不包括其空字符终止符)满足或超过指定的目标缓冲区长度, strncpy()
将不会终止具有空字符的目标字符串。
此外,虽然在标准中明确指出(见上文),但仍然让我感到困惑的是,有多少工程师不知道strncpy()
用空字符填充目标字符串缓冲区,直到达到指定长度n
时源字符串长度小于目标缓冲区大小。 这得出以下不可避免的结论:
strncpy()
API总是将n
字符写入目标缓冲区引用的地址。
在你的情况下,因为目标缓冲区只有10个字符宽,你在可写内存的定义端之外写了90个额外的字符,因此走进了未定义行为的领域 。
在这一点上,你必须问自己“那么使用什么?” 有一个可论证的基本用例。 它允许您将最多n
字符复制到目标缓冲区,并且可以预测您不会超过n
字符。 期。 但是,最终,您需要一个以null结尾的字符串,因此正确的用法是这样的:
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
处具有以null结尾的字符串。 如果源字符串长度小于指定的目标缓冲区长度, strncpy()
将使用空字符尾部填充缓冲区的其余部分,直到源-chars复制的+ tail-filled-null-characters总数等于n
,并且最后的陈述是多余的。 如果源字符串长度等于或大于目标缓冲区长度,则一旦达到N-1
字符, strncpy()
将停止复制,并且最后一个语句在缓冲区的末尾设置一个空字符。 这会导致原始源的“减少”前缀字符串,但最重要的是,它确保您不会超过目标缓冲区的边界,以后会扫描终结符的字符串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的动态字符串执行此操作,请参阅先前的用法。
来自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
或者更好地编写一个_countof
宏
#define _countof(s) (sizeof(s)/sizeof(s[0])) ................ strncpy(s1.from_str,src,_countof(s1.from_str) - 1);
请参阅: http : //www.manpagez.com/man/3/strncpy/
stpncpy()和strncpy()函数最多将s2中的n个字符复制到s1中。 如果s2小于n个字符长,则s1的剩余部分用“\ 0”字符填充。 否则,s1不会终止。
剩下的就是……
所以:
strncpy( s1.from_str, src, 10 );
strncpy(s1.from_str,src,100);
为什么在函数中使用100
,from_str和src都分配了10个连续的字节,但是你要复制100个字节,这会导致seg。 故障。
像这样使用,
strncpy(s1.from_str,src,10);