将结构体强制转换为整数时发生错误

5

我遇到了错误

 error: aggregate value used where an integer was expected

编译此代码时:

#include <stdio.h>
typedef unsigned long U32; 
typedef struct hello_s
{
  U32   a:8;
  U32   b:24;
}hello_t;

int main()
{
  hello_t str;
  U32 var;

  str.a = 0xAA;
  str.b = 0xAAA;
  var = (U32)str;
  printf("var : %lX\n", var);
  return 0;
}

有人能解释一下这个错误的意思吗?我做错了什么。

编辑:我知道这是一个愚蠢的行为。我想知道的是编译器为什么会抱怨这个。为什么不能只将前32位分配给整数。


3
嗯,这个错误是有原因的......原因是它没有意义。想象一下有人试图将电话簿转换为整数...... - user529758
2
C语言在其本质上非常原始。由于这个原因,某些事情不被支持或允许在其中。C甚至不会在幕后为您做任何工作。没有幕后。那么,从技术上讲,C有多原始呢?它几乎和汇编语言或机器码一样原始。因此,您不能直接支持动态数组、任意数字精度、轻松地将事物彼此转换(例如,转换为/从字符串)以及许多其他事情。必须有代码来完成这项工作,在您的程序或其使用的库中。 - Alexey Frunze
3
为什么会有人踩这个问题?我觉得这个问题非常有用,因为我也处于同样的情况。而且user529758说话毫无必要——类型转换就像把苹果变成橘子一样。 - Vorac
这个问题非常有帮助,而且完全合理,我也不明白为什么会被踩。 - pgibbons
5个回答

5
var = (U32)str;

因为str是一个结构类型的对象,您不能将结构对象转换为算术类型的对象。C语言不允许执行这种类型的转换。

如果您想将结构对象视为整数,则可以创建您的结构和U32的联合体。

请注意,C语言中常见的构造var = *(U32 *) str;是未定义行为。它违反了别名和对齐规则。


1
你能详细说明一下吗?为什么不能这样做?该结构只使用32位内存。 - Pratt
@Pratt,你不能这样做不是因为它很难或其他什么原因,而是因为它在语言标准中被禁止了。 - Alexey Frunze

3

我认为人们不应该错误地认为C99规范违反了语言标准。

该标准只是说,转换结果可能在不同的机器/架构之间不可移植。

只要您已经编写程序以在特定架构上运行,那就没问题。

例如,我将数据结构DataStr_t的选择性成员分组,用另一个结构体DataStrKey_t进行打包,这个结构体唯一地标识对象(即键),我会确保sizeof(DataStrKey_t)等于sizeof(uint64),并且出于所有实际目的,将其作为uint64使用,因为它很容易处理。

我也经常执行以下操作:

memcmp(&var_uint64, &var_DataStructKey, sizeof(uint64));

如果您使用键在一台机器上读取或写入对象,则转换后的值在位对齐方面是可预测且一致的。 但是,如果您将“仅此数据”移动到另一台实际没有写入数据的计算机上并尝试读取它,可能会出现问题。
为了更好的解释并保证成功编译,您的程序进行了轻微修改: 只要LINE_A和LINE_B在同一台机器上执行,结果总是可预测的。 但是,如果您将(var_uint64,var_DataStructKey)写入文件并从另一台机器上读取,然后在那些填充的值上执行LINE_B,比较“可能”失败。
#include <stdio.h>
#include <string.h>

typedef unsigned long U32;
typedef struct hello_s
{
  U32   a:8;
  U32   b:24;
}hello_t;

int main()
{
  hello_t str;
  U32 var;

  str.a = 0xAA;
  str.b = 0xAAA;
  var = *(U32 *)(&str); //LINE_A

  if(0 == memcmp(&var, &str, sizeof(U32))) //LINE_B
      printf("var : %lu\n", var);

  return 0;
}

我想我的答案可能有些晚了,但我还是试着解释一下。

这正是我一直在寻找的...一个关于如何使用这些数据的好解释。我使用一个聚合值的结构作为处理标志。我有很多标志要处理。迭代每个标志会产生很多开销。最好将它们分成8组,并首先检查它们中是否有一个被更改。通过这种方式,我的120个标志可以通过16次比较首先进行检查,然后检查转到单个标志。正如我所写的,我不关心操作的结果。只要值与零不同即可。 - andyinno

2

您期望将其转换为什么类型?您可以将其地址转换为指向int的指针并进行解引用,但是您确定可以安全地这样做吗(不,您不能)?结构成员对齐会在某一天咬你吗?答案是“可能,是的,这取决于情况”。

此外,从C99标准:

C99 §6.7.2.1第10段:“位字段分配的顺序(高位到低位或低位到高位)是由实现定义的。”


1
那并不总是一件愚蠢的事情。在我的情况下,我有一个需要通过网络连接发送的结构体。数据必须以字节形式通过SPI总线发送,因此必须逐个字节访问结构体。我使用了这个定义来访问每个字节。您必须了解您平台的字节顺序才能正确执行此操作。此外,您必须确保您的结构体是__PACKED(另请参见:C/C++:强制位域顺序和对齐),以便编译器不会插入任何填充块或对齐块。如果任何位成员跨越字节边界(至少在Microchip XC16编译器中是这样),这也将无法工作。
typedef unsigned char byte;
#define STRUCT_LB(x) ((byte *)(&x))[0]
#define STRUCT_HB(x) ((byte *)(&x))[1]

更好的方法是将结构体定义为位域和字节数组的联合体,如下所示:
typedef unsigned char byte;
typedef struct {
    union {
        struct __PACKED {
            byte array[2];
        } bytes;

        struct __PACKED {
            byte b0: 1;
            byte b1: 1;
            byte b2: 1;
            byte b3: 1;
            byte b4: 1;
            byte other: 3;
            byte more: 6;
            byte stuff: 2;
        } fields;
    };
} MyData;

0

C语言中并不允许所有类型转换。根据this manual,只有以下情况是合法的:

  1. 将整数转换为任何指针类型。
  2. 将指针转换为任何整数类型。
  3. 将对象的指针转换为另一个对象的指针。
  4. 将函数的指针转换为另一个函数的指针。
  5. 在指针之间(无论是对象还是函数)正确地转换null。

因此,将结构体强制转换为整数显然是不合法的转换。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接