使用字符数组初始化C结构体

21

我有一个定义如下的C语言结构体:

struct Guest {
   int age;
   char name[20];
};

当我创建一个Guest变量并使用以下方式进行初始化时:

int guest_age = 30;
char guest_name[20] = "Mike";
struct Guest mike = {guest_age, guest_name};

我得到了有关第二个参数初始化的错误,它告诉我guest_name不能用于初始化成员变量char name[20]

我可以这样做来完成所有初始化:

struct Guest mike = {guest_age, "Mike"};

但这不是我想要的。我希望通过变量初始化所有字段。在 C 中该如何做到?


2
你不能复制数组,应该使用 std::string - chris
1
我知道我可以使用std :: string。但是,如果我想坚持使用C风格的字符数组呢? - tonga
5
你说过C或者C++,现在你只说C了。 - chris
我应该明确一下,我想在C字符数组中完成它。我已经编辑了我的问题。这可行吗? - tonga
4个回答

29

mike.name 是结构体中20个字节的保留内存。guest_name 是指向另一个内存位置的指针。试图将guest_name 分配给结构体成员是不可能的。

如果你需要将数据复制到结构体中,你需要使用memcpy和相关函数。在这种情况下,你需要处理\0终止符。

memcpy(mike.name, guest_name, 20);
mike.name[19] = 0; // ensure termination

如果你有以\0结束的字符串,你也可以使用strcpy,但由于name的大小为20,我建议使用strncpy

strncpy(mike.name, guest_name, 19);
mike.name[19] = 0; // ensure termination

这样,我就不能使用Guest mike = {...}的形式来初始化此结构的其他成员。如果我有更多的字段,我必须使用mike.age = ...,; mike.id=...; memcpy(mike.name, guest_name, 20); - tonga
如果你不事先(在编译时)知道字符串,那么是的。 - Scolytus
如果*guest_name的大小小于20,那么使用memcpy()解决方案将会产生未定义行为。 - undefined
根据问题,尺寸是20。 - undefined

6

mike.name是一个字符数组。你不能只使用=运算符复制数组。

相反,你需要使用strncpy或类似的方法来复制数据。

int guest_age = 30;
char guest_name[20] = "Mike";
struct Guest mike = { guest_age };
strncpy(mike.name, guest_name, sizeof(mike.name) - 1);

您将此问题标记为C ++,因此我想指出,在这种情况下,您几乎总是应该优先使用std :: string而不是char []


谢谢。所以在结构体声明中使用C结构体初始化来初始化字符数组成员是不可能的,对吗? - tonga
通常情况下,在使用 strncpy 函数时,您需要添加空终止符。 - M.M
在这个例子中,我使用括号表示法初始化了struct,它将零化指定成员之后的所有内容。然而,在strncpy调用中存在相关的偏移错误,还有一些错别字,我很惊讶没有人指出来。;) 谢谢 - Steve Howard

0

实际上,您可以静态初始化此结构:

struct Guest {
   int age;
   char name[20];
};

Guest guest = { 30, {'M','i','k','e','\0'}};

数组的每个元素必须显式设置,不能使用c-字符串完成。如果结构体定义了char* name,则可以这样做:

struct Guest {
   int age;
   char* name;
};

Guest guest = { 30, "Mike"};

-2

在C语言中,您可以静态分配一个带有固定char[]数组的结构体。例如,gcc允许以下操作:

#include <stdio.h>

typedef struct {
    int num;
    char str[];
} test;

int main(void) {
    static test x={.num=sizeof("hello"),.str="hello"};

    printf("sizeof=%zu num=%d str=%s\n",sizeof(x),x.num,x.str);
    return 0;
}

它会执行正确的操作(但要注意sizeof(x):在我的机器上返回4,而不是总静态分配内存的长度)。

对于从堆栈分配的结构体,这种方法不起作用,正如你可能猜到的那样。


(1) 我不相信GCC会如此糟糕,以至于sizeof("hello")返回6——但也许你指的是sizeof(x),在这种情况下,4是正确的答案。(2) 这被称为结构体中的柔性数组成员。(3) 初始化是GCC的扩展,尽管您必须调用-pedantic来获取它的“坦白”:initialization of a flexible array member [-Werror=pedantic]static test x={.num=sizeof("hello"),.str="hello"}; — 编译器选项 gcc -O3 -g -std=c11 -Wall -Wextra -Werror -pedantic xx37.c。我正在使用Mac OS X 10.11.6上的GCC 6.1.0。 - Jonathan Leffler
@JonathanLeffler 这个扩展是否还会扩展分配的空间以适应初始化器的大小? - M.M
@M.M:我不确定,但我认为是这样。我不太擅长阅读x86_64汇编语言,但size xx37.o的输出(其中xx37.c包含本答案中代码的副本)给出了一个大小为10的数据段,这对应于如果将x分配为单个单位(这是FAM必须处理的方式)时应该具有的大小。因此,我推断GCC正确地完成了这项工作(这并不令人惊讶,因为它确实完成了这项工作),但我愿意接受证明我错误的观点。 - Jonathan Leffler
虽然这个习惯用法是gcc的扩展[1],但我没有看到ISO C99 [2]中禁止它的任何内容。 [1] https://gcc.gnu.org/onlinedocs/gcc-4.3.5/gcc/Zero-Length.html [2] ISO/IEC 9899第6.7.8段第20、22段。如果您想复习[],第31段也值得一看。 - Daniel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接