C中的结构和铸造

Ano*_*non 10 c struct pointers casting

我在想:

如果我有结构定义,例如,像这样:

struct Base {
  int foo;
};

struct Derived {
  int foo; // int foo is common for both definitions
  char *bar;
};
Run Code Online (Sandbox Code Playgroud)

我可以这样做吗?

void foobar(void *ptr) {
  ((struct Base *)ptr)->foo = 1;
}

struct Derived s;

foobar(&s);
Run Code Online (Sandbox Code Playgroud)

例如,当它的类型实际上时,将void指针强制转换Base *为访问它?fooDerived *

Chr*_*oph 11

你应该做

struct Base {
  int foo;
};

struct Derived {
  struct Base base;
  char *bar;
};
Run Code Online (Sandbox Code Playgroud)

避免破坏严格的别名; C允许指针类型的任意转换是一种常见的误解:尽管它在大多数实现中都会按预期工作,但它是非标准的.

这也避免了由于使用pragma指令而导致的任何对齐不兼容性.


zwo*_*wol 7

许多真实世界的C程序假设您显示的构造是安全的,并且对C标准(特别是"公共初始序列"规则,C99§6.5.2.3p5)进行了解释,符合该标准.不幸的是,自从我最初回答这个问题以来的五年里,我可以很容易地得到的所有编译器(即GCC和Clang)都集中在对常见的初始序列规则的不同的,更窄的解释上,根据该规则,你展示的构造引起了挑战未定义的行为.具体来说,试验这个程序:

#include <stdio.h>
#include <string.h>

typedef struct A { int x; int y; }          A;
typedef struct B { int x; int y; float z; } B;
typedef struct C { A a;          float z; } C;

int testAB(A *a, B *b)
{
  b->x = 1;
  a->x = 2;
  return b->x;
}

int testAC(A *a, C *c)
{
  c->a.x = 1;
  a->x = 2;
  return c->a.x;
}

int main(void)
{
  B bee;
  C cee;
  int r;

  memset(&bee, 0, sizeof bee);
  memset(&cee, 0, sizeof cee);

  r = testAB((A *)&bee, &bee);
  printf("testAB: r=%d bee.x=%d\n", r, bee.x);

  r = testAC(&cee.a, &cee);
  printf("testAC: r=%d cee.x=%d\n", r, cee.a.x);

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

在启用优化(没有-fno-strict-aliasing)的情况下进行编译时,GCC和Clang都会假设两个指针参数testAB 不能指向同一个对象,所以我得到的输出就像

testAB: r=1 bee.x=2
testAC: r=2 cee.x=2
Run Code Online (Sandbox Code Playgroud)

他们没有做出这样的假设testAC,但是 - 以前一直被认为testAB需要编译的印象好像它的两个论点可能指向同一个对象 - 我对自己对标准的理解不再有足够的信心来说明是否还是不为保证继续工作.