2012-12-28 110 views
8

我只是用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”字符串,长度的“SRC”字符串变成3增加了一个“\ 0”字符,目的地阵列大小的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

你认为它应该尝试做什么,它做什么是两个完全不同的东西。 –

回答

13

我可以指出你的男人网页,网站等,但最终重要的是C标准本身。作为标准运行时库的一部分,使用和行为在C99-§7.23.2.4定义为:

#include <string.h> 
char *strncpy(char * restrict s1, 
     const char * restrict s2, 
     size_t n); 

说明strncpy功能拷贝不超过n个字符(即遵循一个字符空字符不会被复制)从s2指向的数组转到 s1指向的数组。如果在重叠对象之间进行复制,则行为不确定。 如果s2指向的数组的长度小于n个字符,则在s1指向的数组副本中会添加空字符,直到写入全部n个字符为止。

返回 strncpy函数返回s1的值。

显著隐含的信息在这里,最重要的是:strncpy()以空字符终止您的目标字符串,如果源字符串长度(不包括其空字符终止)达到或超过指定的目标缓冲区长度)。

此外,虽然在标准中明确规定(见上文),它继续混淆了我很多的工程师怎么都没有意识到,strncpy()尾填充空字符到指定的长度n目标字符串缓冲区到达时源字符串长度为,比目标缓冲区大小小。这得出了以下不可避免的结论:

strncpy() API将始终将n字符写入目标缓冲区引用的地址。

在你的情况,因为目标缓冲区只有10字符宽,你写90个其他字符过去写存储器的定义高端,因此走进的未定义行为土地。

在这一点上,你必须问自己:“那么最新的使用?”有一个可以说是最基本的用例。它允许你复制最多n字符到目标缓冲区,并且知道你不会超过n字符。期。不过说到底,你想有一个空值终止字符串,因此正确用法是这样的:

char dst[ N ]; 
strncpy(dst, src, N-1); 
dst[N-1] = 0; 

其中Ndst缓冲区的硬长度字符,是大于,或相等到1。需要注意的是dst可能只是-AS-不失为一个动态分配的内存指针:

char *dst = malloc(N * sizeof(char)); 
strncpy(dst, src, N-1); 
dst[N-1] = 0; 

通过上述,您将总是必须在dst一个空结尾的字符串。如果源字符串长度为小于指定的目标缓冲区长度,则strncpy()将用空字符尾部填充缓冲区的其余部分,直到源字符复制+尾部填充空字符总数等于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; 

参见如何做到这一点对于长度`Ñ字符的动态字符串的先前使用。

+1

thnx @WhozCraig ..非常详细和描述性..我只是与strncpy鬼混,我知道如何安全地使用strncpy。我只是想知道如果'src'字符串是NULL终止的,那么如果'n'大于'src'字符串的大小并且'dest'字符串的大小足够大以容纳多少个字符被复制到'dest'字符串中'src'字符串但小于'n'。关于strncpy的MAN页面描述对我来说还不够清楚。 –

+1

'strncpy'是可预测的固定大小的字段。它不是**“字符串处理函数”。 – vonbrand

1

参见:http://www.manpagez.com/man/3/strncpy/

的stpncpy()和函数strncpy()函数至多n个字符复制从S2 到S1。如果s2长度小于n个字符,则s1的其余部分为 ,填充'\ 0'字符。否则,s1不会终止。

其余填充....

所以:

strncpy(s1.from_str, src, 10); 
+0

那么这与这个问题有什么关系? – SomeWittyUsername

+0

但是这里src字符串的大小小于dest字符串的大小,并且n大于src和dest字符串的大小。 –

+0

@icepack,因为from_str后的90个字节(可能内存不属于您)将被覆盖。然后它取决于编译器设置/操作系统。 ...什么事情发生 –

5

http://www.cplusplus.com/reference/cstring/strncpy/

字符*函数strncpy(字符*目的地,为const char *源,为size_t NUM ) ;

从字符串复制字符将源 的前几个字符复制到目标。如果在复制num数字字符之前发现源C字符串(它由空字符发信号为 )的末尾,则将 目标使用零填充,直到写入总数为 的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); 
+1

这个seg错误发生在一台LINUX机器上,但它不在另一台UNIX机器上发生。为什么这样 ? –

+1

@HimanshuGupta:在分配的内存之外写入是一个未定义的行为(UB),分段错误是许多UB中的一个[包括恶魔从你的鼻子飞出](http://www.catb.org/jargon/html/N /nasal-demons.html) – Abhijit

+0

thnx @Abhijit .. –

0

strncpy(s1.from_str,src,100);

你为什么要使用你的函数,from_str均被和src都已经连续10个字节分配100,但你要复制100个字节,这是导致赛格。故障。

使用这样的,

strncpy(s1.from_str,src,10);

+0

我只是在鬼混。这发生在LINUX机器上,但我没有在另一台UNIX机器上发生任何seg错误。 –

+0

由于这会导致“未定义的行为”,因此您可能无法在其他LINUX机器上发生seg故障,但在您的情况下最好使用'strcpy'。 –

+0

Thnx @Adeel Ahmed –