c-为什么将结构地址强制转换为int指针、取消引用并将其用作语句中的LVALUE会使我的微控制器崩溃



以下代码在运行时使我的微处理器崩溃。

struct dummytype dummy;
struct crummytype crummy;
*(unsigned int*)&dummy = *(unsigned int*)&crummy;

假设两个结构的大小相同,那么这段代码中是否存在无效的C?它的有效性取决于什么特殊情况吗?

只有当两个结构都有一个unsigned int作为第一个成员时,这才有效。

C99§6.7.2.1/13

在结构对象中,非位字段成员和位字段所在的单位resident的地址按声明的顺序增加。指向结构对象,适当转换,指向其初始成员(或者如果该成员是位字段,然后到它所在的单元),反之亦然。可能有未命名的在结构对象中填充,但不在其开头。

简单地说,给定一个结构对象的地址,您可以将该地址强制转换为指向第一个成员类型的指针:

struct A
{
    unsigned int n;
    char junk[5];
};
struct A a;
unsigned int *p = (unsigned int *)&a;  // OK. pointer is proper for first member type
unsigned long*x = (unsigned long *)&a; // BAD

简而言之,只有两个结构类型的第一个成员都有unsigned int时,您的代码才是合法的任何其他都是未定义的行为(void *尽管如此,但由于它是不可取消引用的,因此它在这里真的不适用)。每种结构类型都"足够大"以容纳一个unsigned int是不够的。他们的第一个成员实际上必须unsigned int

即使两个结构具有相同的大小,重叠行为也是未定义的。

如果结构是这样的,上面的语句将给出您正在寻找的结果。

struct dummytype
{
int a;
};
struct crummytype
{
int b;
};    

如果结构是这样的,你不能说结果会是什么。

struct dummytype
{
char name[20];
int a;
};
struct crummytype
{
char name1[20];
int b;
};

根据int的对齐要求和别名规则(通过不同于对象有效类型的左值访问对象),它是无效的C。

C99标准草案状态(附件J):

J.2未定义的行为

在以下情况下行为未定义:

[…]对象被指定给不精确重叠的对象或完全重叠的对象具有不兼容类型的对象(6.5.16.1)。

那么,关于兼容类型:

6.2.7兼容型和复合型

如果两个类型的类型相同,则它们具有兼容的类型。的附加规则6.7.2中针对类型说明符描述了确定两种类型是否兼容,6.7.3中用于类型限定符,6.7.5中用于声明符。此外,在单独的转换单元中声明的并集或枚举类型是兼容的,如果它们标记和成员满足以下要求:如果用标记声明一个,则其他应使用相同标签进行声明。如果两者都是完整类型,则以下附加要求适用:成员,以使每对相应的成员声明为兼容类型,并且如果用名称声明对应对中的一个成员,则另一个成员用相同的名称声明。对于两个结构委员须按相同次序宣布。对于两个结构或联合体,对应位字段应具有相同的宽度。对于两个枚举,对应的成员应具有相同的值。

使两个structs大小相同不足以使它们成为兼容类型,因此行为是未定义的。

编辑:为了完整起见,我将@PascalCuoq在评论中引用的摘录添加到本线程中的另一个答案中,这也是相关的:

6.5表达式

[…]

7

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

  • 与对象的有效类型兼容的类型
  • 与对象的有效类型兼容的类型的合格版本
  • 一个类型,它是与对象
  • 一个类型,它是与的限定版本相对应的有符号或无符号类型对象的有效类型
  • 一种聚合或并集类型,在其成员(递归地,包括子集合或包含联盟的成员),或
  • 字符类型

最新更新