strncpy 导致分段错误 [英] strncpy leading to segmentation fault

查看:29
本文介绍了strncpy 导致分段错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我只是在玩 strncpy.

我的程序是这样的

typedef 结构{char from_str[10];}测试;主要的 (){测试s1;memset(&s1,0,sizeof(test));char src[10]="himansh";字符目的地[10];memset(dest,0,10);src[3]='';printf("src 是 %s,strlen 是 %d 
",源代码,strlen(源代码));fflush(标准输出);strncpy(s1.from_str,src,100);printf("s1.from_str 是 %s , src 是 %s 
",s1.from_str,src);返回 1;}

在我执行 strncpy 之前,我在src"字符串中添加了一个"字符,src"字符串的长度变为 3,目标数组的大小为 10.但在 strncpy 中,我已将字节数设为复制为 100.

这意味着我的源字符串以 NULL 结尾.现在 strncpy 像任何字符串函数一样应该尝试只复制 3 个字节,即使我提供的字节数超过 3(在本例中为 100).它会这样做,但我也遇到了分段错误.

我的结果如下所示

src 是他,strlen 是 3s1.from_str 是他,src 是他分段错误(核心转储)

为什么这里会出现分段错误.

谁能帮帮我.

解决方案

我可以为您指出手册页、网站等,但最终重要的是 C 标准本身.作为标准运行时库的一部分,其用法和行为在 C99-§7.23.2.4 中定义为:

#include <string.h>char *strncpy(char * 限制 s1,const char * 限制 s2,尺寸_t n);

<块引用>

说明strncpy 函数从 s2 指向的数组复制不超过 n 个字符(不复制空字符后面的字符)到 s2 指向的数组s1.如果复制发生在重叠的对象之间,则行为未定义.如果 s2 指向的数组是一个短于 n 个字符的字符串,则将空字符附加到 s1 指向的数组的副本中,直到 n 个字符全部被写入.

退货strncpy 函数返回 s1 的值.

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

此外,虽然标准中明确规定(见上文),但我仍然感到困惑,有多少工程师不知道 strncpy() tail-fills 目的地当源字符串长度小于目标缓冲区大小小于时,字符串缓冲区具有空字符,直到达到指定长度n.由此得出以下不可避免的结论:

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

在你的例子中,因为目标缓冲区只有 10 个字符宽,你在可写内存的定义结束后写入了 90 个额外的字符,从而进入了 未定义行为.

此时你必须问自己那有什么用?" 有一个可以说是基本的用例.它允许您将最多 n 个字符复制到目标缓冲区,并且可以预测您不会超出 n 个字符.时期.但是,最终,您需要一个以空字符结尾的字符串,所以 正确的用法是这样的:

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

其中 Ndst 缓冲区的硬长度,以字符为单位,大于或等于 1.请注意,dst 也可以是动态分配的内存指针:

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

使用上述方法,您将总是dst 处有一个以 null 结尾的字符串.如果源字符串 length 小于指定的目标缓冲区长度,strncpy() 将用空字符尾部填充缓冲区的其余部分,直到总共有 source-chars-copyed + tail-filled-null-characters 等于 n,最后的语句是多余的.如果源字符串长度等于或大于目标缓冲区长度,strncpy() 将在达到 N-1 个字符后停止复制, 最后的语句在缓冲区的末尾设置一个空字符.这会导致原始源的缩减"前缀字符串,但最重要的是,它确保您不会使用稍后扫描终止符的字符串 API 调用超出目标缓冲区的边界.

上述技术的实用性始终值得商榷.我是一个 C++ 人,所以 std::string 将我的快乐自我从所有这些疯狂中拯救出来.但现实是这样的:有时你会关心 src 是否没有将其 整体 复制到 dst;有时你不知道.有用性非常视情况而定.对于在 UI 中呈现字符串数据,这(可能)无关紧要.对于复制用于关键数据的字符串,部分前缀子字符串是不可接受的.当警察向Joseph Johnson Jr."发出逮捕令时,当他的父亲(Joseph Johnson")被拖入监狱时,将会有一些解释要做,因为逮捕令发布软件的名称缓冲区只有 15 个字符.

综上所述,您的分段错误归结为以下语句:

strncpy(s1.from_str,src, 100);//长度参数错误.

回想一下上面的粗体语句: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 字符的动态字符串执行此操作,请参阅先前的用法.

I am just messing around with strncpy.

My program looks like this

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]='';

    printf("src is %s and strlen is %d 
",
            src,strlen(src));

    fflush(stdout);

    strncpy(s1.from_str,src,100);

    printf("s1.from_str is %s , src is %s 
",
            s1.from_str,src);
    return 1;

}

Here before I do strncpy I have added a "" character in "src" string, length of "src" string becomes 3 , destination array is of size 10 .But in strncpy I have put number of bytes to be copied as 100.

This means my source string is NULL terminated. Now strncpy like any string function should try to copy only 3 bytes even if the number of bytes I provide is more than 3 (in this case 100). It does that, but I get a segmentation fault too.

My result is shown below

src is him and strlen is 3
s1.from_str is him , src is him
Segmentation fault (core dumped)

Why is this segmentation fault happening over here.

Can any one help me out here.

解决方案

I could point you to man pages, websites, etc, but ultimately what matters is the C standard itself. As part of the standard runtime library, the usage and behavior is defined in C99-§7.23.2.4 as:

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

Description The strncpy function copies not more than n characters (characters that follow a null character are not copied) from the array pointed to by s2 to the array pointed to by s1. If copying takes place between objects that overlap, the behavior is undefined. If the array pointed to by s2 is a string that is shorter than n characters, null characters are appended to the copy in the array pointed to by s1, until n characters in all have been written.

Returns The strncpy function returns the value of s1.

There is significant implied information here, the most important being: strncpy() will NOT terminate your destination string with a null character if the source string length (not including its null character terminator) meets or exceeds the specified destination buffer length).

Furthermore, though clearly specified in the standard (see above), it continues to confound me how many engineers are NOT aware that strncpy() tail-fills the destination string buffer with null characters until the specified length n is reached when the source string length is less than the destination buffer size. This draws the following inescapable conclusion:

The strncpy() API will ALWAYS write n characters to the address referenced by the destination buffer.

In your case, because the target-buffer is only 10-chars wide, you're writing 90 additional characters past the defined-end of writable memory, and thus walking into the land of undefined behavior.

At this point you have to be asking yourself "So whats the use?" There is an arguably fundamental use-case. It allows you to copy up to n chars to the target buffer with the predictability of knowing you won't overrun past n chars. Period. Ultimately, though, you want a null-terminated string, so the proper usage is this:

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

where N is the hard-length of the dst buffer in chars and is greater-than-or-equal to 1. Note that dst could just-as-well be a dynamic-allocated memory pointer:

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

With the above, you will always have a null-terminated string at dst. If the source string length is smaller than the specified target buffer length, strncpy() will tail-fill the rest of the buffer with null characters until a total of source-chars-copied + tail-filled-null-characters equals n, and the final statement is redundant. If the source string length is equal to or greater than the target buffer length, strncpy() will stop copying once N-1 chars are reached, and the final statement sets a null character at the end of the buffer. This results in a "cut-down" prefix string of the original source, but most important, it ensures you will NOT exceed the boundaries of your target buffer with a later string-API call that scans for a terminator.

The usefulness of the above technique is always debatable. I'm a C++ guy, so std::string saves my happy-self from all this insanity. But the reality is this: Sometimes you care if src isn't copied in its entirety to dst; sometimes you don't. The usefulness is very situationally dependent. For presenting string-data in a UI this won't (likely) matter. For copying a string to be used for critical data, a partial-prefix-substring isn't going to be acceptable. When the police issue an arrest warrant to "Joseph Johnson Jr.", there will be some explaining to do when his father ("Joseph Johnson") is hauled into jail because the name-buffer of the warrant-issuance software only held 15 chars.

All of that said, your segmentation fault comes down to this statement:

strncpy(s1.from_str,src, 100); // length parameter is wrong.

Recall the bold statement above: "strncpy() will ALWAYS write n characters to the address referenced by the destination buffer.". This means the above code will always write 100 chars to the target buffer, which in your case is only 10-chars wide, thus undefined behavior and likely ker-boom.

Rectify this by doing the following if the target buffer is a fixed-length character array:

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;

See the prior usage for how to do this for dynamic string of length `N chars.

这篇关于strncpy 导致分段错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆