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);