类型转换有未定义的行为? [英] Undefined behavior with type casting?

查看:97
本文介绍了类型转换有未定义的行为?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下面的示例为例:

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似乎按照在结构中定义的顺序为结构成员分配内存.这意味着,如果强制转换vector -> array,则在array上执行操作时仍会获得与在vector上执行操作相同的结果,因为它们具有相同的成员和成员顺序.

只要您仅将vector -> array强制转换为arrayvector的通用类型,就不会遇到任何问题.

尽管类型的结构相似,但这种不确定性和不良行为吗?

解决方案

如果您允许类型别名(默认情况下是C或大多数编译器都会这样做,则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;

Take the following example:

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 seems to allocate memory for struct members in the order they're defined in the struct. Which means that if you cast vector -> array you'll still get the same results if you perform operations on array as you would as if you did it on vector since they have the same members and order of members.

As long as you only down cast from vector -> array as if array was a generic type for vector you shouldn't run into any problems.

Is this undefined and bad behavior despite the similar structure of the types?

解决方案

This is well-defined behavior if you permit type aliasing (which C doesn't but most compilers do, either by default or by some compilation flag), and it is undefined behavior if you prohibit this type of type aliasing (which is commonly referred to as "strict aliasing" because the rules are pretty strict). From the N1570 draft of the C standard:

6.5.2.3

6 One special guarantee is made in order to simplify the use of unions: if a union contains several structures that share a common initial sequence (see below), and if the union object currently contains one of these structures, it is permitted to inspect the common initial part of any of them anywhere that a declaration of the complete type of the union is visible. Two structures share a common initial sequence if corresponding members have compatible types (and, for bit-fields, the same widths) for a sequence of one or more initial members.

That section is about unions, but in order for that behavior to be legal in unions, it restricts padding possibilities and thus requires the two structures to share a common layout and initial padding. So we've got that going for us.

Now, for strict aliasing, the standard says:

6.5

7 An object shall have its stored value accessed only by an lvalue expression that has one of the following types:

  • a type compatible with the effective type of the object
  • [...]

A "compatible type" is:

6.2.7

1 Two types have compatible type if their types are the same.

It goes on to explain that more and list a few cases that have a little more "wiggle room" but none of them apply here. Unfortunately for you, the buck stops here. This is undefined behavior.

Now, one thing you could do to get around this would be:

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

typedef struct vector_struct {
    array array;
    size_t reserved;
} vector;

这篇关于类型转换有未定义的行为?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆