使用堆上结构的结构初始化语法
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
的类型是什么?
entry
是struct 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
的对象,那么是否允许 memcpy
ing 就不太清楚了)。
【讨论】:
对没有分配存储持续时间的结构条目类型(或任何其他类型)的对象进行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。以上是关于使用堆上结构的结构初始化语法的主要内容,如果未能解决你的问题,请参考以下文章