被有效的类型规则困惑
我似乎再次错过了关于有效类型的几个难题……代码中的注释基本上是我的问题,但这是我能够用适当的上下文来思考这个问题的唯一方法。
#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)->n
是int
类型的左值,它指定一个有效类型为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 ++可能不易出错(只要其警告不被抑制)。