被有效的类型规则困惑

我似乎再次错过了关于有效类型的几个难题……代码中的注释基本上是我的问题,但这是我能够用适当的上下文来思考这个问题的唯一方法。

#include  #include  typedef struct foo { int n; } Foo; int main(void) { // No effective type yet because there has been no write to the memory. Foo *f = malloc(sizeof(Foo)); // Effective type of `f` is now `Foo *`, except I'm writing to // `f->n`, so shouldn't it be `int *`? Not sure what's going on here. f->n = 1; // No effective type yet because there has been no write to the memory. char *buf = malloc(sizeof(Foo)); // Effective type of `buf` is `Foo *`, despite it being declared as // `char *`. // It's not safe to modify `buf` as a `char` array since the effective type // is not `char`, or am I missing something? memcpy(buf, f, sizeof(Foo)); // The cast here is well defined because effective type of `buf` is // `Foo *` anyway, right? ((Foo *)buf)->n++; // I'm not even sure this is OK. The effective type of `buf` is `Foo *`, // right? Why wouldn't it be OK then? memcpy(f, buf, sizeof(Foo)); // Safe if the last `memcpy` was safe. f->n++; // buf now points to invalid memory. free(buf); // Pointers with different declared types point to the same object, // but they have the same effective type that is not qualified by // `restrict`, so this doesn't violate strict aliasing, right? // This is allowed because `f` was allocated via `malloc`, // meaning it is suitably aligned for any data type, so // the effective type rules aren't violated either. buf = (void *)f; // `f`, and consequently `buf` since it points to the same object as `f`, // now point to invalid memory. free(f); } 

我认为所有这些都没问题,或者在某些情况下我错了吗? 我意识到这是边缘问多个问题,但我基本上都在问我对有效类型和严格别名的理解是否正确。 GCC没有对我进行诊断 – -pedantic-errors -Wextra -Wall -O2 -fstrict-aliasing -Wstrict-aliasing

你似乎对“有效类型”适用的东西感到困惑:它适用于malloc’d空间,而不是任何指针。 与C中一样,指针是一个单独的对象,它具有指向指针可能指向的任何空间的独立属性。

f是(命名)变量,因此其有效类型始终与其声明的类型相同,即Foo * 。 同样, buf的有效类型总是char * 。 有效类型在运行时可能更改的唯一时间是动态分配的空间。

你的要点和代码注释没什么意义,所以我决定重新注释你的代码。 文本是指每种情况下文本上方的代码:

 Foo *f = malloc(sizeof(Foo)); 

好。 已分配未初始化的字节, f指向它们。 动态分配的空间还没有有效类型。

 f->n = 1; 

动态分配空间的第一个sizeof(int)字节的有效类型设置为int 。 (* – 但见脚注)

 char *buf = malloc(sizeof(Foo)); memcpy(buf, f, sizeof(Foo)); 

memcpy函数保留了复制的有效对象类型,因此buf的空间的第一个sizeof(int)字节的有效类型是int

 ((Foo *)buf)->n++; 

首先,转换没有对齐问题,因为malloc空间正确地对齐任何类型。 转到n的访问,这是可以的,因为((Foo *)buf)->nint类型的左值,它指定一个有效类型为int的对象。 所以我们可以毫无问题地进行读写。

 memcpy(f, buf, sizeof(Foo)); 

memcpy总是正常的,因为它设置了有效类型(你的评论暗示memcpy在某些情况下可能不正常)。 此行将f指向的空间的有效类型设置为int (因为源空间的有效类型为int )。

 f->n++; 

((Foo *)buf)->n++相同的基本原理。

 free(buf); buf = (void *)f; 

冗余演员。 f指向的空间仍然是有效类型int ,因为这些行都没有写入该空间。

 free(f); 

没问题。


脚注:有些人对表达式f->n (或((Foo *)buf)->n wrt严格别名采用不同的解释。他们说f->n定义为(*f).n因此相关的有效类型是*f的类型,而不是f->n的类型。我不同意这个观点,所以我不会进一步阐述它。已经有建议让C2X澄清这种情况和其他灰色严格别名的区域。对于您的特定代码,代码在任何解释下仍然是正确的。

 // No effective type yet because there has been no write to the memory. Foo *f = malloc(sizeof(Foo)); 

这里没有访问权限,没有对象。 有效类型无关紧要。

 // Effective type of `f` is now `Foo *`, except I'm writing to // `f->n`, so shouldn't it be `int *`? Not sure what's going on here. f->n = 1; 

地址f处第一个sizeof(Foo)字节的对象具有有效类型Foo,地址f处第一个sizeof(int)个字节处的对象具有有效类型int。

 // No effective type yet because there has been no write to the memory. char *buf = malloc(sizeof(Foo)); 

这里没有访问权限,没有对象。 有效类型无关紧要。

 // Effective type of `buf` is `Foo *`, despite it being declared as // `char *`. // It's not safe to modify `buf` as a `char` array since the effective type // is not `char`, or am I missing something? memcpy(buf, f, sizeof(Foo)); 

对象位于地址的第一个sizeof(Foo)字节但是,有一个有效的类型Foo,并且对象位于地址的第一个sizeof(int)字节但是有效类型为int。

无论其有效类型如何,都可以使用字符类型访问任何对象。 您可以使用char访问buf的字节。

 // The cast here is well defined because effective type of `buf` is // `Foo *` anyway, right? ((Foo *)buf)->n++; 

是。 整个表达式都有效。

 // I'm not even sure this is OK. The effective type of `buf` is `Foo *`, // right? Why wouldn't it be OK then? memcpy(f, buf, sizeof(Foo)); 

还行吧。 memcpy将地址f处的对象类型更改为Foo类型。 即使f之前没有Foo类型,它现在也可以。

 // Safe if the last `memcpy` was safe. f->n++; 

是。

 // buf now points to invalid memory. free(buf); 

是。

 // Pointers with different declared types point to the same object, // but they have the same effective type that is not qualified by // `restrict`, so this doesn't violate strict aliasing, right? // This is allowed because `f` was allocated via `malloc`, // meaning it is suitably aligned for any data type, so // the effective type rules aren't violated either. buf = (void *)f; 

你混合概念。 限制和单个指针的值与别名无关。 访问是。 指针buf现在只指向地址f。

 // `f`, and consequently `buf` since it points to the same object as `f`, // now point to invalid memory. free(f); 

是。

代码是有效的 – 因为必须处理多态数据对象 – 是在C ++之前完成的。

然而,在人们可能在脚下射击之前,从这些手术中可以推断出更多的东西。 这可能是因为拥有Foo并说出一个不同大小的类型Foo2然后访问一个不存在的元素,因为关联的malloc()不够大。

通常,如果指针类型始终与malloc()相同,则更容易理解并且可能更正确。 对于更高级的态射,c ++可能不易出错(只要其警告不被抑制)。