从结构访问数组并将其转换为指针

Accessing an array from a struct and casting it to a pointer

struct message {
  uint8_t start;
  uint16_t length;
  uint8_t data[10];
  uint8_t checkSum;
} __attribute__((packed));

struct message devices[10];

void request(struct message *msg) {
  struct request *req = (struct request *)&msg->data;
  req->operation = 1;
  req->requesterAddress = MASTER_ADDRESS;      
}

request(&devices[0]);

我的问题是,为什么“&msg->data”处有 & 符号?

我的理解是函数 "request" 接收一个指向结构的指针 "msg"。 msg->data从"msg"指向的struct中获取指针"data"("data"是一个数组),然后将其强制转换为另一种指针类型(struct request *)。

所以那部分应该是 (struct request *)msg->data;那么为什么要使用符号 (&)?

好吧,这段代码不是 C 语言,因为 __attribute__((packed)) 仅在 gcc 和其他一些特定实现中有效,但在标准 C 中绝对不存在。

但提供 sizeof(struct message) <= 10,其余代码是 根据 4. Conformance 的正确代码 ,但包含未指定的行为,并且可能包含未定义的行为,具体取决于实施

  • request(&devices[0]);void request(struct message *msg) {...}:好的:request 期望 struct message * 并接收 struct message 数组的第一个元素的地址 - 都是这里很好
  • struct request *req = (struct request *)&msg->data;:这是最有趣的部分。
    • msg->data是一个char数组,也就是一个聚合。聚合的地址是其第一个字节的地址。换句话说 (char *) &msg-> data(聚合的第一个字节的地址)与 msg->data(数组衰减到指向其第一个元素的指针)相同
    • (struct request *)&msg->data; 指向 char 的指针被强制转换为指向 struct request 的指针。根据实现的不同,没有任何东西可以保证指针将正确对齐。如果不是,根据 6.3.2.3 指针 § 7,这是未定义的行为。如果是,根据 6.5 表达式 § 6-7,我们仍然有未定义的行为,因为新指针将用于存储尚未定义的元素char[10] 的声明类型。但这将被所有已知的实现所接受,因为它们在内部处理相同的字符数组(显式类型)和分配的内存(未声明类型)——仅仅因为它们需要实现 malloc(参见

其余代码没有其他问题。但应该注意的是,如果 __attribute__(packed) 被实现认可,字段 data 将是结构的第三个字节,这给出了奇怪的对齐方式。这可能会导致需要对某些类型进行严格对齐的实现崩溃。


来自 C99 的 n1256 草案的参考文献

  1. Conformance
    ...
    2 If a ‘‘shall’’ or ‘‘shall not’’ requirement that appears outside of a constraint is violated, the behavior is undefined...
    3 A program that is correct in all other aspects, operating on correct data, containing unspecified behavior shall be a correct program...

6.3.2.3 Pointers
...
7 A pointer to an object or incomplete type may be converted to a pointer to a different object or incomplete type. If the resulting pointer is not correctly aligned for the pointed-to type, the behavior is undefined. Otherwise, when converted back again, the result shall compare equal to the original pointer. When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object.

6.5 Expressions
...
6 The effective type of an object for an access to its stored value is the declared type of the object, if any. If a value is stored into an object having no declared type through an lvalue having a type that is not a character type, then the type of the lvalue becomes the effective type of the object for that access and for subsequent accesses that do not modify the stored value. If a value is copied into an object having no declared type using memcpy or memmove, or is copied as an array of character type, then the effective type of the modified object for that access and for subsequent accesses that do not modify the value is the effective type of the object from which the value is copied, if it has one. For all other accesses to an object having no declared type, the effective type of the object is simply the type of the lvalue used for the access.

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

  • atype compatible with the effective type of the object,
  • aqualified version of a type compatible with the effective type of the object,
  • a type that is the signed or unsigned type corresponding to the effective type of the object,
  • a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
  • an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
  • a character type.