使用堆上结构的结构初始化语法

Posted

技术标签:

【中文标题】使用堆上结构的结构初始化语法【英文标题】:Use of struct initialization syntax for on-heap struct 【发布时间】:2016-08-13 13:39:08 【问题描述】:

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

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是一个const指针。我在某处发现了这种有效的语法:

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

但我不知道它发生了什么:它是否会创建一个“匿名”结构,然后将其复制到 *entry 所在的位置?使用它是否安全,或者我应该更喜欢创建一个本地结构,然后使用memcpy 复制到正确的位置?

【问题讨论】:

你确定这有效吗,因为它不应该。 entry的类型是什么? entrystruct entry*。我在问题中添加了声明部分。 正如我所怀疑的,它不会编译:ideone.com/2YIgUN 我刚刚注意到您还建议使用 memcpy,所以是的,这是正确的方法。 :-s 奇怪...用你的文件:clang -std=c11 -Weverything -pedantic struct_test.c -o struct_test 对我来说没有错误。 【参考方案1】:

您提交的作业不正确,不应编译。

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

void* mem = malloc( sizeof( struct entry ) );
struct entry temp =  key , data , NULL ;
memcpy( mem , &temp , sizeof( temp ) );
struct entry* e = mem;

【讨论】:

const 字段上使用memcpy 新值真的安全吗? 等等,没关系,你没有这样做,mem 还没有类型。但是留下评论以防其他人对此感到疑惑。 @hyde 是的,memcpy 将有效类型设置为对象。 在您的示例中全程使用e 代替mem 是否有问题? (我猜你使用mem 来完全避免这个问题) @M.M 指向一个对象没有区别。 memcpy 之前的对象没有有效类型。由于这超出了 OP 的理解,我用 void 来暗示它。【参考方案2】:

这一行:

*entry = (struct entry)  .key = key, .data = data, .next = NULL ;

使用赋值运算符。赋值运算符(C11 6.5.16/2)的条件包括:

约束

赋值运算符应该有一个可修改的左值作为它的左操作数。

modifiable lvalue的定义见6.3.2.1/1:

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

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

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


关于问题的第二部分:

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

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

【讨论】:

对没有分配存储持续时间的结构条目类型(或任何其他类型)的对象进行memcpy-ing会导致未定义的行为。 @2501 你能提供标准参考吗 9899:2011 表达式 6.5 第 6 和 7 段 我弄错了,上面的 cmets 不相关。问题在于 const,而不是别名。对不起。 :-/ 对类型结构条目的 memcpy-ing 在 6.7.3 p6 的情况下是 ub,因为其中一个成员是 const。 @2501 6.7.3p6 谈到了使用非常量左值进行编写。 memcpy 不使用左值写入。我见过有人说 memcpy 应该被视为与unsigned char 的一系列分配相同,但我认为标准中的实际文本并不可靠。不清楚的措辞imo,虽然我同意标准应该说的最明智的事情是你不能对声明为const的对象进行memcpy。

以上是关于使用堆上结构的结构初始化语法的主要内容,如果未能解决你的问题,请参考以下文章

[golang]语法基础之构造函数

C++ 如何将一个静态结构体数组初始化?

C#数组js数组json

学习日记——Java循环结构

GO语言struct语法

PHP 数组遍历 foreach 语法结构