C 对堆上结构使用结构初始化语法

C 对堆上结构使用结构初始化语法,c,struct,initialization,heap-memory,C,Struct,Initialization,Heap Memory,我有一个简单的结构,我想在堆上初始化,并在函数中作为指针返回 struct entry { const char* const key; // We don't want the key modified in any way const void* data; // But the pointer to data can change struct entry* next; }; 有一个问题,我不能calloc逐个初始化它和成员,因为key是常量指针。我在某

我有一个简单的结构,我想在堆上初始化,并在函数中作为指针返回

struct entry {
    const char* const key; // We don't want the key modified in any way
    const void* data;      // But the pointer to data can change
    struct entry* next;
};
有一个问题,我不能
calloc
逐个初始化它和成员,因为
key
是常量指针。我在某个地方发现了这种语法:

struct entry* entry = calloc(1, sizeof(struct entry));
*entry = (struct entry) { .key = key, .data = data, .next = NULL };

但我不知道这是怎么回事:它是否创建了一个“匿名”结构,然后复制到
*entry
所在的位置?这样使用安全吗?还是我更喜欢创建一个本地结构,然后用
memcpy
复制到正确的位置?

您提供的赋值不正确,不应编译

初始化具有常量成员的已分配结构的正确方法是分配一些内存,创建一个临时结构条目对象,然后使用memcpy将该对象复制到已分配的内存中:

void* mem = malloc( sizeof( struct entry ) );
struct entry temp = { key , data , NULL };
memcpy( mem , &temp , sizeof( temp ) );
struct entry* e = mem;
这一行:

*entry = (struct entry) { .key = key, .data = data, .next = NULL };
使用赋值运算符。赋值运算符(C116.5.16/2)的条件包括:

约束

赋值运算符应具有可修改的左值作为其左操作数

可修改左值的定义见6.3.2.1/1:

可修改左值是一个左值,它不具有数组类型、不完整类型、不具有常量限定类型,并且如果它是结构或联合,则不具有任何具有常量限定类型的成员(递归地包括所有包含的聚合或联合的任何成员或元素)

因此,
*entry
不是一个可修改的左值,因为它的类型是一个具有const限定类型的成员的结构。因此,在赋值运算符的左侧显示
*条目
是一种违反约束的行为

clang编译器(我尝试过的所有版本)似乎没有对此约束冲突给出任何诊断消息;这显然是一个编译器错误。gcc确实给出了一个诊断


关于问题的第二部分:

我是否应该创建一个本地结构,然后使用memcpy将其复制到正确的位置


正如2501所解释的,当写入
malloc
系列分配的空间时,可以在C中执行此操作。(如果您声明了一个命名的
struct entry
对象,则不太清楚是否允许
memcpy
覆盖该对象)。

您确定它有效吗,因为它不应该有效。
条目的类型是什么?
条目
是一个
结构条目*
。我在问题中添加了声明部分。正如我所怀疑的,它不会编译:我只是注意到你也建议使用memcpy,所以是的,这是正确的方法-sStrange。。。使用您的文件:
clang-std=c11-Weverything-pedantic struct_test.c-o struct_test
对我来说不会产生错误。在
const
字段上使用
memcpy
新值真的安全吗?等等,没关系,您没有这样做,
mem
还没有类型。但留下注释以防其他人对此感到疑惑。@hyde是的,memcpy将有效类型设置为对象。在示例中使用
e
而不是
mem
是否有问题?(我猜你使用了
mem
来完全避免这个问题)@M.M指向一个对象没有什么区别。memcpy之前的对象没有有效类型。因为这远远超出了OP的理解,所以我使用void来暗示它。对struct entry类型(或任何其他类型)的对象进行memcpy-ing操作时,如果没有分配存储持续时间,则会导致未定义的行为。@2501您能为9899:2011表达式6.5第6和第7段提供标准引用吗?我犯了一个错误,上述评论与此无关。问题在于常量,而不是别名。对不起。:-/由于6.7.3 p6中有一个成员是常量,因此在类型结构项上进行memcpy是ub。@2501 6.7.3p6谈到使用非常量左值进行写入
memcpy
不使用左值写入。我看到有人说memcpy应该被视为一系列赋值的
无符号字符
,但我认为标准中的实际文本并不可靠。虽然我同意标准应该说的最明智的事情是,你不能对声明的const对象进行memcpy。