为了进一步解释@Chris Dodd的答案,我已经阅读了标准中“语言律师”的详细信息,看起来这段代码是定义良好的:
struct deneme deneme_init = { 15, 20 };
struct deneme *mydeneme = malloc(sizeof(struct deneme));
memcpy(mydeneme, &deneme_init, sizeof(struct deneme));
或者,要动态创建一个完整的const限定的结构体对象:
const struct deneme deneme_init = { 15, 20 };
struct deneme *mydeneme = malloc(sizeof(struct deneme));
memcpy(mydeneme, &deneme_init, sizeof(struct deneme));
const struct deneme *read_only = mydeneme;
原理:
当涉及到所谓的lvalue时,首先需要确定它是否具有类型,如果具有类型,那么该类型是否带有限定符。这在C11 6.3.2.1/1中定义:
lvalue是一个表达式(具有对象类型而不是void),可以潜在地指代对象; 如果一个lvalue在评估时不指代任何对象,则行为未定义。当说一个对象具有特定类型时,类型是由用于指代该对象的lvalue指定的。可修改的lvalue是指没有数组类型、不具有不完整类型和不具有const限定类型的lvalue,如果它是结构体或联合体,则没有任何成员(包括所有包含的聚合体或联合体的所有成员或元素)都具有const限定类型。
因此,显然lvalue不仅具有类型,还具有限定符。如果它具有const限定符或者是具有const限定成员的结构体,则不是可修改的lvalue。
接下来看"严格别名"和有效类型的规则,C11 6.5/7:
访问存储值的对象的有效类型是对象的声明类型(如果有的话)。87)如果通过具有非字符类型的类型的lvalue将值存储到没有声明类型的对象中,则lvalue的类型成为该访问和不修改存储值的后续访问的对象的有效类型。如果使用memcpy
或memmove
将值复制到没有声明类型的对象中,或者将其作为字符类型的数组复制,则如果它有一个,则从值复制的对象是修改后对象的有效类型。对于访问没有声明类型的对象的所有其他访问,所使用的lvalue的有效类型只是该对象的类型。
- 分配的对象没有声明类型。
这意味着由malloc返回的分配块在通过lvalue写入访问(通过赋值或memcpy
)存储在该内存位置中之前没有有效类型。然后,它获得该写访问中使用的lvalue的有效类型。
值得注意的是,指向该内存位置的指针的类型完全无关紧要。它可能与volatile bananas_t*
一样,因为它不用于访问lvalue(至少目前还没有)。仅使用lvalue访问的类型才重要。
现在问题变得有些模糊:如果这个写入访问是通过可修改的左值完成的,那么可能会有影响。有效类型规则不涉及限定符,而“严格别名规则”本身也不关心对象是否有限定符(“类型”可以与“限定类型”别名,反之亦然)。
但是,在其他情况下,如果有效类型是只读的,则可能很重要:特别是如果我们稍后尝试对具有const限定的有效类型的对象进行非限定左值访问。从上面引用的左值部分,有效类型具有限定符是有意义的,即使标准没有明确提到。
因此,为了绝对确定,我们必须正确获取用于左值访问的类型。如果整个对象都应该是只读的,则应该使用本帖子顶部的第二个代码片段。否则,如果它是读/写的(但可能具有限定成员),则应该使用第一个代码片段。然后无论您如何阅读标准,都不会出错。
*(int*)(&mydeneme->a)=15;
- n. m.malloc
导致未定义的行为,因为原型与库函数不兼容,而库函数是void *malloc(size_t);
。 - M.M