在C99 6.2.5 P27
中所有指向结构类型的指针都应具有相同的表示和对齐要求 彼此相同。所有指向联合类型的指针都应具有相同的表示形式 对齐要求彼此。指向其他类型的指针不必相同 表示或对齐要求。
这是什么意思?
所有指向结构类型的指针都应具有相同的表示和对齐要求。
这个例外的原因是什么?
指向其他类型的指针不需要具有相同的表示或对齐要求。
我很欣赏相关例子的解释。
答案 0 :(得分:4)
这意味着您可以将任何指针到结构的值存储在任何其他指向结构的变量中,并且在创建有效指针对象的过程中,您可以从中间变量中恢复原始指针。相比之下,您不允许使用不同类别的指针作为中间。例如:
struct Foo * p = &x; // x is a struct Foo
struct Bar * q;
memcpy(&q, &p, sizeof p); // OK, it's allowed to read (but not dereference!) q
memcpy(&p, &q, sizeof q); // OK, p is now the same it was before
union Zip * r;
int * s;
// not allowed to do the same with (p, r) and (p, s)!
memcpy
是正常的,因为p
和q
都具有相同的大小和对齐方式,因为它们都是指向结构的指针。对于两个指向联合或两个指向int的指针也是如此,但是你不能混合类别。
这是另一个非常人为但有效的例子:
struct Foo { int a; };
int f(struct Bar * p)
{
return (struct Foo *)(p)->a;
}
int main()
{
Foo x = { 12 };
return f((struct Bar *)(&x));
}
如果f
的函数参数是不同类别的指针(比如指向联合的指针或指向int的指针),则此程序将不有效。< / p>
任何对象指针可以转换为和返回的唯一指针类型是void *
。 (所以我们可以将参数f
设为void *
。这可以说是最常见的风格。但可以想象,使它成为结构指针更有效,因此在某些平台上更可取。)< / p>
答案 1 :(得分:3)
这意味着将指向结构类型的指针转换为指向另一种结构类型的指针只是一种重新解释。
通常,允许指向不同类型的指针具有不同的大小和不同的对齐要求,例如, char*
的大小和对齐要求可能为16,并且存储地址为big-endian,但unsigned long long*
的大小和对齐要求为8,并将地址存储为little-endian。
但对于struct foo *
和struct bar *
,表示和对齐要求必须相同。 (类似于union
s。)
一个原因是,struct
中指向不完整struct
的指针很常见。如果结构指针的表示和对齐要求不相同,那就不可能了。
答案 2 :(得分:1)
不同的指针类型可能有不同的大小和表示形式; IOW,int *
可能具有与char *
不同的大小和代表,其可能具有与double *
等不同的大小和表示。
任何struct
类型的指针都具有相同的大小和表示形式; IOW,struct T *
和struct Q *
看起来一样。类似地,对于联盟,union T *
和union U *
看起来也是一样的(尽管它们可能与struct
指针看起来不同)。
答案 3 :(得分:1)
在非正式语言中,关于指针表示和转换的C语言语义的重要部分是:
其他一切都取决于实施。
这些特定规则的必要性可以这样证明:
将对象指针转换为字符指针允许按字节顺序访问,而不管类型如何。虽然void指针基本上是伪装的字符指针,但它们带有不同的语义内涵(没有类型关联的通用指针)。
结构分别联合指针共享表示可以指向不完整类型(不透明指针)的指针。
函数指针可能无法转换为void指针,因为它们可能位于完全不同的地址空间中,并且可能无法按字节顺序访问。它们之间是可转换的,因为没有泛型函数指针类型(相当于void指针),并且可能共享一个表示。据我所知,C标准不要求单个表示,即原则上在函数指针类型之间进行转换可能涉及实际转换。但是,由于非原型函数(以及相应的函数指针)需要与各种函数类型兼容,因此可能的转换受到严格限制。
一般而言,C标准在实现方面留下了相当多的内容,以适应广泛的历史优先权,而不会人为地限制未来的发展。
答案 4 :(得分:0)
这意味着你可以,例如,不依赖于函数指针的结构或对齐。我假设这也意味着你在处理指向C ++ class
es或struct
的指针时不能做出这样的假设(虽然我没有检查C ++如何定义这些)。