Error while typecasting structure to integer
Asked Answered
C

5

5

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.

Cornellcornelle answered 18/9, 2012 at 19:57 Comment(4)
Hm, this is an error for a reason... The reason is that it doesn't make sense. Imagine someone trying to convert a phonebook to an integer...Keaton
The C language is very primitive in its nature. Certain things aren't supported or allowed in it because of that. C does not even try to do work for you somewhere behind the scenes. There's no behind. And just how primitive is C, technically? It's nearly as primitive as assembly language or machine code. For this reason you can't have direct support for dynamic arrays, arbitrary numeric precision, convert things to one another (e.g. to/from strings) easily and many more things. There's gotta be code to do that work, in your program or the libraries it's using.Deaf
Why the downvotes? I found this question helpful, as I was in the same situation. And user529758 is uncalled for - casting is always converting apples to oranges.Sommer
This question is very helpful and completely reasonable, I also don't understand the downvotes.Thromboplastin
F
5
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.

Falconet answered 18/9, 2012 at 19:58 Comment(2)
Can you elaborate a bit more. Why is not possible to do this. The structure only uses 32 bits of memory.Cornellcornelle
@Cornellcornelle You can't not because it's hard or something, you can't because it's disallowed in the language standard.Deaf
S
3

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.

Sharpfreeze answered 18/3, 2013 at 19:31 Comment(1)
This is exactly what I was looking for... A good explanation about how to use this data. I use a struct of aggregate values as flags for processing. I have a lot of flags to process. Iterate over every single produces a lot of overhead. it's better to split they up in group of 8 and check first if one of them is changed. In this way my 120 flag can be checked first with 16 comparison and next the check go to the single flag. As I wrote I don't care about the result of the operation. It's enouth that the value differ from zero.Sandwich
A
2

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."

Ankerite answered 18/9, 2012 at 20:0 Comment(0)
B
1

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;
Beaver answered 10/1, 2014 at 14:22 Comment(0)
B
0

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.

Barbur answered 28/4, 2022 at 18:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.