0

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.

asked Jun 22, 2024 at 17:14
2
  • 1
    structinstance = (union{struct mystruct m;int i;}){.i=myint}.m; Commented 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. Commented Jun 22, 2024 at 21:12

3 Answers 3

2

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.

Eric Postpischil
234k15 gold badges200 silver badges383 bronze badges
answered Jun 22, 2024 at 18:01
Sign up to request clarification or add additional context in comments.

4 Comments

@ikegami can you explain what happens when you cast a pointer? I thought they were just memory adresses how are they affected by a cast?
Let's say 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.
in c networking(on Windows) you come accross some struct typecasting and it looks similar to the bad practice:" bind(ListenSocket, (SOCKADDR*) &socketaddr_ininstance, sizeof(sockaddr)); " In the second argument of bind() you typecast the instance of the struct "socketaddr_in" to the struct SOCKADDR, i think because this one was in use previously and many functions (including bind) rely on that old struct. Basically socketaddr_in and SOCKADDR both have 16 bytes in total but socketaddr_in has some more variables. Can you explain why here we code this way?
It provides polymorphism. Different structures types are provided for address families. But a 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.
1

Since the 2 types are incompatible, you'd have to be a bit creative. I can think of a bunch of ways:

  1. (削除) int pointer to struct pointer cast (although it works, it's Undefined Behavior, as @ikegami noticed, check [SO]: What is the strict aliasing rule?) (削除ここまで)

  2. Use memcpy

  3. 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:

answered Jun 22, 2024 at 18:07

2 Comments

Note that I didn't say that a pointer cast couldn't be used. (I actually said it could be used.) I said the specific cast you used resulted in UB. There is a way of using casting in this case, which I demonstrated in my answer.
Thanks for the tip! I've done this struct casting several times over the years. always good to learn smth new!
0

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
answered Jun 23, 2024 at 20:47

3 Comments

"Create a pointer of type mystruct and point it at this address" This isn't allowed in C, you will be invoking undefined behavior. So it's not just bad practice, it's a bug. Which may or may not manifest itself depending on compiler & build etc. It is better to avoid writing bugs.
<shrug>. UB doesn't mean that random stuff happens at each call; it simply means that the standard doesn't specify a behavior. The compiler is going to generate something that exhibits predictable behavior. If it works, it works. But as I mentioned, a union is the way to go. I agree that there's no reason to write bad code if you know better. While not a "solution", it is a direct answer to the specific question, which I hope the OP appreciates.
"something that exhibits predictable behavior" Not necessarily. clang for example stops generating the executable at the point where it finds certain UB - you won't get a complete executable, you get half of one. Other forms of UB involves reading random RAM cells which may vary at each read, meaning that the behavior will vary each execution. Etc etc.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.