9

我只是在搞乱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 个字节,即使我提供的字节数超过 3(在本例中为 100)。它这样做了,但我也遇到了分段错误。

我的结果如下所示

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

为什么这里会发生这种分段错误。

有谁可以帮我离开这里吗。

4

4 回答 4

18

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

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

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

返回函数返回 s1 的strncpy值。

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

strncpy() 此外,尽管在标准中明确规定(见上文),但它继续让我感到困惑,有多少工程师不知道当源字符串n长度小于目标缓冲区大小。由此得出以下不可避免的结论:

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

在您的情况下,因为目标缓冲区只有 10 个字符宽,所以您在可写内存的定义结束后写入了 90 个额外的字符,从而进入了未定义行为的领域。

在这一点上,您必须问自己“那有什么用?” 有一个可以说基本的用例。它允许您将最多n字符复制到目标缓冲区,并且可以预测您不会超出过去的n字符。时期。但最终,您需要一个以 null 结尾的字符串,因此正确用法是:

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

其中N是 chars 中缓冲区的硬长度dst并且大于或等于1. 请注意,dst也可以是动态分配的内存指针:

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 中呈现字符串数据,这(可能)无关紧要。对于复制用于关键数据的字符串,部分前缀子字符串是不可接受的。当警察向“Joseph Johnson Jr.”发出逮捕令时,当他的父亲(“Joseph Johnson”)被拖入监狱时,将会有一些解释要做,因为逮捕令发布软件的名称缓冲区只有 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 字符的动态字符串执行此操作。

于 2012-12-28T08:59:41.707 回答
6

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

char * strncpy ( char * 目标, const char * 源, size_t num );

从字符串复制字符 将源的前 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);
于 2012-12-28T06:16:48.147 回答
1

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

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

剩下的都填满了......

所以:

strncpy( s1.from_str, src, 10 );
于 2012-12-28T06:15:28.283 回答
0

strncpy(s1.from_str,src,100);

你为什么100在你的函数中使用,from_str 和 src 都分配了 10 个连续的字节,但是你正在复制 100 个字节,这导致了 seg。过错。

像这样使用,

strncpy(s1.from_str,src,10);

于 2012-12-28T06:20:08.197 回答