Pratt
Pratt

Reputation: 861

Error while typecasting structure to integer

I am getting error

 error: aggregate value used where an integer was expected

on compiling this code:

#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;
}

Can someone please explain what the error means, and what I am doing wrong.

EDIT: I understand this is a stupid thing to do. What I wanted to know was why the compiler is crying about this. Why cant it just assign the first 32 bits to the integer.

Upvotes: 5

Views: 14175

Answers (5)

Yi_
Yi_

Reputation: 59

Not all typecasting are allowed in C. Per this manual, only the following cases are legal,

  1. Convert an integer to any pointer type.
  2. Convert a pointer to any integer type.
  3. Convert a pointer to an object to a pointer to another object.
  4. Convert a pointer to a function to a pointer to another function.
  5. Correctness of converting null between pointers (either object or function).

Hence, casting a struct to an integer is obviously not a legal conversion.

Upvotes: 1

Jason Martens
Jason Martens

Reputation: 1355

That is not always a stupid thing to do. In my case, I have a struct that I need to send over a network connection. The data must be sent over an SPI bus in byte form, so the struct must be accessed a byte at a time. I used this define to access each byte. You must be aware of the byte ordering of your platform to do this correctly. Also, you must make sure your structs are __PACKED (see also: C/C++: Force Bit Field Order and Alignment), so the compiler does not insert any padding blocks or alignment blocks. This will also not work if any of the bit members fall across the byte boundaries (at least, with the Microchip XC16 compiler it does not).

typedef unsigned char byte;
#define STRUCT_LB(x) ((byte *)(&x))[0]
#define STRUCT_HB(x) ((byte *)(&x))[1]

A nicer way to do this is to define your struct as a union of a bitfield and a byte array like so:

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;

Upvotes: 1

Preetham Nanjappa
Preetham Nanjappa

Reputation: 91

Well, I think one should not mistake the C99 spec by assuming that it is against the language standards.

The standard only says that the results of the conversion may not portable across different machines/architectures.

As long as you have coded the program to work on a particular architecture, it's fine.

For example I group selective members of a data structure DataStr_t that uniquely identifies the object (i.e., the key), packed into another struct, say, DataStrKey_t. I'll make sure that sizeof(DataStrKey_t) is equal to sizeof(uint64) and for all practical purposes use it as uint64 as it is easy to handle.

I also do the below operation often:

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

If you read access or write access the object using the key on a machine the value resulting from conversion is predictable and consistent in bit-wise alignment. Well if you move "only this data" to a different machine (which actually didn't write the data) and try to read it, things may break.

Your program slightly modified for more explanation and successful compilation: Here, as long as LINE_A and LINE_B are execute on the same machine result is always predictable. But if you write the (var_uint64,var_DataStructKey) to a file and read it from a different machine, then execute LINE_B on those populated values, comparison "may" fail.

#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;
}

I guess my answer is too late, but attempted to explain.

Upvotes: 4

Ed Swangren
Ed Swangren

Reputation: 124682

And what do you expect that cast to result in exactly? You could always just cast it's address to a pointer to int and dereference it... but are you sure you can safely do so (no, you can't)? Is structure member alignment going to bite you someday (the answer is "probably, yes, it depends")?

Also, from the C99 styandard:

C99 §6.7.2.1, paragraph 10: "The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined."

Upvotes: 2

ouah
ouah

Reputation: 145839

var = (U32)str;

Because str is an object of a structure type and you cannot convert structure objects to object of arithmetic types. C does not let you perform this kind of conversion.

If you want to access you structure object as an integer you can create an union of your structure and of an U32.

Note that the common construct var = *(U32 *) str; is undefined behavior in C. It violates aliasing and alignment rules.

Upvotes: 6

Related Questions