类型转换的未定义行为?

Fat*_*eep 6 c struct memory-address type-punning

请看以下示例:

typedef struct array_struct {
    unsigned char* pointer;
    size_t length;
} array;

typedef struct vector_struct {
    unsigned char* pointer;
    // Reserved is the amount of allocated memory not being used.
    // MemoryLength = length + reserved;
    size_t length, reserved;
} vector;


// Example Usage:
vector* vct = (vector*) calloc(sizeof(vector), 1);
vct->reserved = 0;
vct->length = 24;
vct->pointer = (unsigned char*) calloc(arr->length, 1);

array* arr = (array*) vct;
printf("%i", arr->length);
free(arr->pointer);
free(arr);
Run Code Online (Sandbox Code Playgroud)

C似乎按照它们在结构中定义的顺序为结构成员分配内存.这意味着如果你投射vector -> array你仍会得到相同的结果如果你执行操作array就像你做的那样,vector因为他们有相同的成员和成员的顺序.

只要你只下跌,从投vector -> array仿佛array是一个通用型的vector,你不应该遇到任何问题.

尽管类型结构相似,这是不确定和不良行为?

Cor*_*lks 6

如果您允许类型别名(C大多数编译器没有,默认情况下或通过某些编译标志执行),这是明确定义的行为,如果禁止此类型的别名(通常引用),则它是未定义的行为作为"严格别名",因为规则非常严格).从N1570 C标准草案:

6.5.2.3

6为了简化联合的使用,我们做了一个特殊的保证:如果一个联合包含几个共享一个共同初始序列的结构(见下文),并且如果联合对象当前包含这些结构中的一个,则允许检查它们中任何一个的共同初始部分,可以看到完整类型的联合声明.如果对应的成员具有一个或多个初始成员的序列的兼容类型(并且对于位字段,具有相同的宽度),则两个结构共享共同的初始序列.

该部分是关于联合的,但为了使这种行为在联合中合法,它限制了填充的可能性,因此需要两个结构共享一个共同的布局和初始填充.所以我们已经为我们做了这件事.

现在,对于严格的别名,标准说:

6.5

7对象的存储值只能由具有以下类型之一的左值表达式访问:

  • 与对象的有效类型兼容的类型
  • [...]

"兼容类型"是:

6.2.7

1如果类型相同,则两种类型具有兼容类型.

它继续解释更多并列出一些具有更多"摆动空间"的案例,但它们都不适用于此.不幸的是,降压在这里停止.这是未定义的行为.

现在,你可以做的一件事就是:

typedef struct array_struct {
    unsigned char* pointer;
    size_t length;
} array;

typedef struct vector_struct {
    array array;
    size_t reserved;
} vector;
Run Code Online (Sandbox Code Playgroud)

  • "我应该补充说,严格的别名规则可能会禁止这一点,这可能只能通过工会来完成,但我不确定." 那是对的.仅仅因为结构在内存中具有相同的布局并不意味着编译器认为类型可以互换.它们仍然是不同的类型,并且仍然未定义通过指向不同类型的指针访问一种类型.让很多人感到高兴的是,类型系统的规则运行的级别高于内存中存在的级别或内存的对齐. (2认同)