类型转换的未定义行为?

时间:2016-06-04 15:19:14

标签: 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);

C似乎按照它们在struct中定义的顺序为struct成员分配内存。这意味着如果您投射vector -> array,如果您在array上执行操作,您仍会获得相同的结果,就像您在vector上执行操作一样,因为他们拥有相同的成员和成员的顺序。

只要您从vector -> array向下投射,好像arrayvector的通用类型,就不应该遇到任何问题。

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

1 个答案:

答案 0 :(得分:6)

这是明确定义的行为,如果您允许类型别名(C大多数编译器没有,默认情况下或通过某些编译标志),如果禁止这种类型的别名,则它是未定义的行为(通常被称为“严格别名”,因为规则非常严格。根据C标准的N1570草案:

  

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;