this is my idea:
int main(){
struct mystruct {
char a;
char b;
char c;
char d;
};
struct mystruct structinstance;
//1094795585 is in binary: 01000001 01000001 01000001 01000001 -to dez-> 65 65 65 65 -in ASCII-> AAAA
int myint = 1094795585;
//here the gcc compiler gives me this error (it marked "mystruct"): error: conversion to non-scalar type requested
structinstance = (struct mystruct)(myint);
printf("%c,%c,%c,%c",structinstance.a, structinstance.b, structinstance.c, structinstance.d);
}
The expected result from the printf function would be "AAAA". I am having trouble in finding ressources on how to typecast with structs so please share some links if you have any.
-
1structinstance = (union{struct mystruct m;int i;}){.i=myint}.m;Michael Walsh Pedersen– Michael Walsh Pedersen2024年06月22日 20:45:20 +00:00Commented Jun 22, 2024 at 20:45
-
Don't do that, use bit shifting and masking instead. The latter is well-defined and guaranteed to work, as opposed to.n. m. could be an AI– n. m. could be an AI2024年06月22日 21:12:36 +00:00Commented Jun 22, 2024 at 21:12
3 Answers 3
Casting a value with a non-pointer type causes a type conversion. For example, casting the int zero to a float produces a float with value zero. There's no meaningful way to convert an integer into a structure, so (struct mystruct)myint results in a compilation error.
Casting a pointer is different. For example, you could cast &myint to a struct mystruct * and use the resulting pointer. That would have compiled. However, it's a violation of the strict aliasing rules, which means its behaviour is undefined.
The simplest option available to you here is to cast to a unsigned char *. Casting a pointer to an object to a char *, signed char * or unsigned char * is always legal.
So, if we continue to assume the size of myint is four (which is by no means guaranteed even if it's an int), all you need is the following:
char *p = &myint;
printf( "%c%c%c%c\n", p[0], p[2], p[2], p[3] );
We can easily adapt this to objects of any size.
char *p = &myint;
for ( size_t n = sizeof( myint ); n--; )
printf( "%c", *(p++) );
printf( "\n" );
You could also use memcpy or a union, but these are overly complicated for the scenario you presented.
4 Comments
myint is at address 0x1000. &myint produces a pointer of type int * which points to address 0x1000. Casting that to a char * produces a pointer of type char * which points to address 0x1000. So dereferencing that pointer (p) accesses the memory used by myint.void * should have been used as the argument type. I believe that code is technically undefined behaviour (though I have a nagging feeling it might not be because they're both struct pointers). Backwards compatibility sucks sometimes.Since the 2 types are incompatible, you'd have to be a bit creative. I can think of a bunch of ways:
(削除) int pointer to struct pointer cast (although it works, it's Undefined Behavior, as @ikegami noticed, check [SO]: What is the strict aliasing rule?) (削除ここまで)Use memcpy
Wrap the 2 into a union (a common way for checking endianness)
code00.c:
#include <stdint.h>
#include <stdio.h>
#include <string.h>
uint32_t test = 0x41424344; // "ABCD"
typedef struct {
uint8_t c0;
uint8_t c1;
uint8_t c2;
uint8_t c3;
} S;
void cast_pointer()
{
S *ps = (S*)(&test); // !!! UB !!!
printf("Pointer cast (undefined behavior): %c, %c, %c, %c\n", ps->c0, ps->c1, ps->c2, ps->c3);
}
void cast_memcpy()
{
S s;
memcpy(&s, &test, sizeof(test));
printf("Memcpy cast: %c, %c, %c, %c\n", s.c0, s.c1, s.c2, s.c3);
}
typedef union {
S s;
uint32_t i;
} U;
void cast_union()
{
U u;
u.i = test;
printf("Union \"cast\": %c, %c, %c, %c\n", u.s.c0, u.s.c1, u.s.c2, u.s.c3);
}
int main()
{
cast_pointer();
cast_memcpy();
cast_union();
printf("\nDone.\n\n");
return 0;
}
Output:
(qaic-env) [cfati@cfati-5510-0:/mnt/e/Work/Dev/StackExchange/StackOverflow/q078656715]> ~/sopr.sh ### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ### [064bit prompt]> ls main00.c [064bit prompt]> gcc main00.c [064bit prompt]> [064bit prompt]> ls a.out main00.c [064bit prompt]> [064bit prompt]> ./a.out Pointer cast (undefined behavior): D, C, B, A Memcpy cast: D, C, B, A Union "cast": D, C, B, A Done.
Notes:
I replaced int by uint32_t and char by uint8_t so that code is compatible with any system (check [SO]: Difference between int32, int, int32_t, int8 and int8_t)
Structures members may be automatically padded by compiler ([SO]: #pragma pack effect, not the case here)
The output is in reversed order, you probably guessed: because I'm on an Intel CPU (pc064) which is little-endian (although Python related, [SO]: Python struct.pack() behavior (@CristiFati's answer) has some info on the topic)
Needless to say, don't use #1.
2 Comments
The short answer is yes: use a pointer, but it's bad practice. Here's a MRE with your code:
#include <stdio.h>
#include <stdint.h>
int main(){
// to-be-confirmed: big-vs-little endian
struct mystruct {
char d;
char c;
char b;
char a;
};
// struct mystruct structinstance;
//1094795585 is in binary: 01000001 01000001 01000001 01000001 -to dez-> 65 65 65 65 -in ASCII-> AAAA
int myint = 1094795586;
// option 1 -- union
union {
struct mystruct s;
uint32_t i;
} decompose;
decompose.i = myint;
printf("%c,%c,%c,%c\n",decompose.s.a, decompose.s.b, decompose.s.c, decompose.s.d);
// option 2 -- bitstuff
printf("%c,%c,%c,%c\n",(myint >> 24), ((myint >> 16) & 0xff), ((myint >> 8) & 0xff), myint & 0xff);
// option 3 -- bad practice
struct mystruct* s = (struct mystruct*)&myint;
printf("%c,%c,%c,%c\n",s->a, s->b, s->c, s->d);
return 0;
}
Note that byte-ordering is important. In my example I renamed/repositioned your mystruct members.
In option 1 I show you how to do this with a union. This is the cleanest and most-typesafe way of doing this, but you have to track when it's safe to use either the 'int' or the 'struct' member. In this instance it doesn't matter. The nice thing about this is you can change a letter value and the integer also changes.
Option 2 is a more common way of decomposing an integer in my experience. In short, create a 1-byte window (0xff) and bitshift the values you want into place: byte 1 = bits 24-32; byte 2 = 16-24; byte 3 = 8-16; and byte 4 is just the lower 8 bits. In this case you must use unsigned values or you'll have problems with sign extension.
Option 3 is a direct answer to your question. The int is sitting somewhere in memory, at some address X. Create a pointer of type mystruct and point it at this address. Then use it like any other pointer. This is generally risky code but it can be used in niche functions with very limited behavior.
Although it's a bit more typing, I'd recommend using the union for clarity. Always remember to be nice to the poor schmuck who has to maintain your code because it will probably be you.
Output:
A,A,A,B A,A,A,B A,A,A,B