struct 上的指定初始化程序导致 strcpy 和 realloc 中的段错误

Designated Initialiser on struct causes segfault in strcpy and realloc

我正在使用 WSL Ubuntu-20.04 LTS 并使用 gcc 进行编译。

我正在准备一些示例来向 CS 学生教授 C 语言的基础知识,当我阅读有关指定初始化程序的内容时,以下代码导致了错误,


    typedef enum {male, female} sexe;

    struct human_t {
        char * name;
        char * title;
        sexe gender;
    };

    struct human_t steve = {.name = "Steve", 
                              .title = "Mr.", 
                              .gender = male
                             };

    strcpy(steve.name, "AA");

并阅读有关 strcpy 的手册,目标缓冲区的大小大于 "AA" 我只是不知道为什么这不起作用。

我还观察到以下情况:

    struct human_t steve = {.name = "Steve", 
                              .title = "Mr.", 
                              .gender = male
                             };
    char * new_name = "Gobeldydukeryboo";
    realloc(steve.name, strlen(new_name));
    strcpy(steve.name, new_name);

而这个returns错误realloc(): invalid pointer

阅读 realloc 上的手册,指针必须由 malloc 调用返回才能工作。 我有一种感觉,指定的初始化不会调用 malloc,这可以解释为什么 realloc 失败,但是,它不能解释为什么 strcpy 失败。

这是怎么回事?

struct human_t steve = {.name = "Steve", 
                          .title = "Mr.", 
                          .gender = male
                         };

此处name(和title)使用字符串文字的地址进行初始化。字符串文字不是从堆中分配的,实际上它们(通常)是 read-only。因此,如果您想编写更健壮的代码,应始终将字符串文字分配给 const char*


因此在第一个片段中,strcpy(steve.name, "AA"); 将尝试写入 read-only 内存并崩溃。如果您将 steve.name 设置为 const char,它会产生编译器错误,您就安全了。


然后第二个片段,realloc(steve.name, strlen(new_name)); 将尝试调整未分配给 malloc 的内存块的大小。所以你会崩溃。

此外,这根本不是您使用 realloc 的方式。你需要这样做:

char *tmp = realloc(steve.name, strlen(new_name)+1);
if (!tmp) { perror("realloc error"); exit(1); }
strcpy(tmp, new_name);
steve.name = tmp;

要修复,请存储字符串的副本:

struct human_t steve = {.name = strdup("Steve"), 
                          .title = strdup("Mr."), 
                          .gender = male
                         };

这里strdup是一个POSIX函数,不是标准的C函数,所以可能需要自己定义:

char *strdup(const char *src)
{
    char *dst = src ? malloc(strlen(src)+1) : NULL;
    if (dst) strcpy(dst, src);
    return dst;
}

在此声明中

struct human_t steve = {.name = "Steve", 
                          .title = "Mr.", 
                          .gender = male
                         };

你用字符串文字 "Steve".

的第一个字符的地址初始化了指针 name

那么在这个语句中

strcpy(steve.name, "AA");

您正在尝试更改字符串文字。

任何更改字符串文字的尝试都会导致未定义的行为。

相反你可以写

steve.name = "AA";

之后指针 name 将指向字符串文字 "AA".

在此声明中

realloc(steve.name, strlen(new_name));

您正在尝试重新分配具有静态存储持续时间的字符串文字占用的内存。但是您可以只重新分配以前动态分配的内存。此外,通常您需要将 realloc 调用的返回地址分配给一个指针。否则,重新分配的内存地址将丢失,您将发生内存泄漏。

要么将数据成员名称声明为字符数组,例如

struct human_t {
    char name[10[;
    char * title;
    sexe gender;
};

或者总是动态分配内存来存储字符串,例如

struct human_t steve = {.name = malloc( 10 ), 
                          .title = "Mr.", 
                          .gender = male
                         };

strcpy( steve.name, "Steve" );

另一个数据成员存在同样的问题title