我似乎再次错过了关于有效类型的几个难题......代码中的注释基本上是我的问题,但这是我能够用适当的上下文来思考这个问题的唯一方法。
#include <stdlib.h>
#include <string.h>
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
答案 0 :(得分:4)
你似乎对什么&#34;有效类型&#34;混淆了。适用于:它适用于malloc的空间,而不是任何指针。和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
函数保留了复制的有效对象类型,因此sizeof(int)
指向的空格的第一个buf
字节的有效类型为int
。< / p>
((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的建议澄清这种情况和严格别名的其他灰色区域。对于你的特定代码,代码在任何解释下仍然是正确的。
答案 1 :(得分:1)
// 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);
是。
答案 2 :(得分:0)
代码是有效的 - 因为必须处理多态数据对象 - 是在C ++之前完成的。
然而,在一个人可能在脚下射击之前,可以从这些手术中推断出更多的东西。这可能发生在Foo
并说出一个不同大小的Foo2
类型,然后访问一个不存在的元素,因为关联的malloc()
不够大。
通常,如果指针类型始终与malloc()相同,则更容易理解并且可能更正确。对于更高级的态射,c ++可能不易出错(只要其警告不被抑制)。