Avatar of fraW
fraW
Flag for Sweden asked on

C Structure to Char pointer, failure

The code below is used in order to try and cast / convert a structure to a char pointer. Why do i want to do this? I want to print the representing bytes so that i afterwards can cast from a string to a structure.

The following code prints the memory address, and not the representation itself. Suggestions?
#include <string.h>
 
typedef struct NetworkFrame_
{
      unsigned ack : 1;
      unsigned tseq : 7;
} NetworkFrame;
 
void main(void)
{
      NetworkFrame nf;
      NetworkFrame * pnf;
      
      char* test;
      
      nf.ack = 1;
      nf.tseq = 12345678;
      
      test = (char*)&nf; // Here you can't the address not the object itself!
 
      pnf = (NetworkFrame *) test;
 
      memcpy(&nf,  test, sizeof(NetworkFrame));
 
	  printf("%x", test);
 
	  system("PAUSE");
}

Open in new window

C

Avatar of undefined
Last Comment
Infinity08

8/22/2022 - Mon
zhuba

You need to references individually, try the following instead:
printf("%x%x", nf.ack, nf.tseq);

Open in new window

ASKER CERTIFIED SOLUTION
Infinity08

THIS SOLUTION ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
GET A PERSONALIZED SOLUTION
Ask your own question & get feedback from real experts
Find out why thousands trust the EE community with their toughest problems.
Infinity08

But, I don't really get what you're trying to achieve - why do things so complicated ? Why do you really need this, and what is the real code you will use this in ?
lucky_james

>>nf.tseq = 12345678;
You assign so big value to a 7 bit memory. if you make all bits to 1, even then it would amount to a meagre 127. I think.

I started with Experts Exchange in 2004 and it's been a mainstay of my professional computing life since. It helped me launch a career as a programmer / Oracle data analyst
William Peck
Infinity08

Oh, and two more things :

1) main HAS to return an int, not void !!!

2) The memcpy you do has the same source and target - ie. you don't really copy the data - you're just overwriting the same data with the same data. Technically, this is even incorrect !
fraW

ASKER
Infinity08, its embedded software, the usage is confidential. sorry :)
Infinity08

I don't need to know the high level usage - just the low-level - ie. why is the cast necessary.
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
fraW

ASKER
Thank you very much, this works!

Can't really understand the whole thing, but thats a later problem! :)
fraW

ASKER
We are implementing a protocol and we receive structures and we just want to cast them to the structs directly, hard to explain but, this is basicly because we want to know how the stuff is placed in memory.

Thanks again
Infinity08

If it's just for sending and receiving data, then you can do something like :

        NetworkFrame nf;
        // fill the frame
        send(sd, &nf, sizeof(NetworkFrame), 0);

and :

        recv(sd, &nf, sizeof(NetworkFrame), 0);
This is the best money I have ever spent. I cannot not tell you how many times these folks have saved my bacon. I learn so much from the contributors.
rwheeler23
Infinity08

>> Can't really understand the whole thing

I'm happy to explain : the code iterates over each byte of the network frame (for a total of sizeof(NetworkFrame) bytes, which is the size of the network frame struct), and shows each of those bytes as a hexadecimal value :

          int i = 0;
          for (i = 0; i < sizeof(NetworkFrame); ++i) {
              printf("%02x ", test[i]);
          }

test[i] is the i-th byte of the network frame.
%02x means to print as hexadecimal with 2 digits, and pre-pended by 0's if needed.


Note that you could have simply done this too :


NetworkFrame nf;
 
int i = 0;
for (i = 0; i < sizeof(NetworkFrame); ++i) {
    printf("%02x ", ((unsigned char*) &nf)[i]);
}
 
/* OR even : */
 
NetworkFrame nf;
 
unsigned char *ptr = &nf;
int i = 0;
for (i = 0; i < sizeof(NetworkFrame); ++i, ++ptr) {
    printf("%02x ", *ptr);
}

Open in new window

fraW

ASKER
Its not between methods its over a Radio Transeiver. And the message is encrypted with AES so we need to cast it to a char first and decrypt, then assign the structs. Do you understand? :)
Infinity08

>> Do you understand? :)

Sure :)
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
fraW

ASKER
Oh i see, but,
      unsigned ack : 1;
      unsigned tseq : 7;
should be 1 byte, the loop runs 4 times, is that correct?
fraW

ASKER
Btw, thank you for the very good explenation :D
Infinity08

>> should be 1 byte, the loop runs 4 times, is that correct?

1 bit, not one byte. If you need bytes, then you need this struct :

        typedef struct NetworkFrame_
        {
            unsigned char ack[1];
            unsigned char tseq[7];
        } NetworkFrame;

>> the loop runs 4 times, is that correct?

The loop runs for as many times as there are bytes in the struct. If there's no padding in the struct I just posted, it would run 8 times.
Your help has saved me hundreds of hours of internet surfing.
fblack61
fraW

ASKER
This is the ouput:
05 00 00 00 Press any key to continue . . .

Ack is 1 bit and Tseq is 7bits creating 1 byte. Is a struct padded to 4 bytes? That might be why.. hehe

from the following code:

#include <string.h>
 
typedef struct NetworkFrame_
{
	unsigned ack : 1;
	unsigned tseq : 7;
} NetworkFrame;
 
int main(void)
{
      NetworkFrame nf;
      NetworkFrame * pnf;
      
      unsigned char* test;
      int i = 0;
 
      nf.ack = 1;
      nf.tseq = 2;
      
      test = (unsigned char*)&nf;
 
      pnf = (NetworkFrame *) test;
 
      memcpy(&nf,  test, sizeof(nf));
 
 
      for (i = 0; i < sizeof(nf); ++i) 
      {
		  printf("%02x ", test[i]);
      }
 
	  system("PAUSE");
	  
	  return 0;
}

Open in new window

fraW

ASKER
Follow up question then..

The real networkframe is this:
typedef struct NetworkFrame
{
      unsigned                        ack             : 1;
      unsigned                          tseq            : 7;
      _BYTE                               payload[64];
};

Lets say that i have a long package ( char ) with Hex values: 01ff00ff00ff00ff.... etc..

How do "convert" this to a struct? can i just typecast it? that representation would cause ack to be 1 and tseq to be 0 if im not mistaken.. and the rest in payload.
fraW

ASKER
Oh one more thing, lets say that i have nested structs, is this a problem?

Sorry for the multi posts :)
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
Infinity08

>> Is a struct padded to 4 bytes? That might be why.. hehe

In this case, yes. Structs usually align on word boundaries (and a word is apparently 4 bytes on your system).


>> Lets say that i have a long package ( char ) with Hex values: 01ff00ff00ff00ff.... etc..
>> 
>> How do "convert" this to a struct? can i just typecast it?

If you have padding between ack/tseq and the payload, which is a distinct possibility, then it's not as straightforward as just doing a cast. You can usually force the compiler not to use padding (check your compiler documentation), or you can do an explicit copy (which is obviously more expensive than a cast) :

        NetworkFrame nf;
        unsigned char *msg;                            /* the received message */
        memcpy(&nf, msg, 1);                           /* <--- copy the first byte */
        memcpy(&(nf.payload), msg + 1, 64)    /* <--- copy the payload */


>> that representation would cause ack to be 1 and tseq to be 0 if im not mistaken..

The other way around. If you want ack to be 1 and tseq 0, then you need the byte 0x80 instead of 0x01.


>> Oh one more thing, lets say that i have nested structs, is this a problem?

When done correctly, there's no problem. Remember padding !
fraW

ASKER
Oh thank you for the clearification!

Its padded, *sigh*,  this is the output of the following code:
0005 0042 0040 0000 0000 0000 0000 0000

42 and 40 isnt anything that i assign, so i assume its a padding thing, dont understand why though when my struct is ( should be ) 4 bytes?

Is there a simple solution for this?
#include <string.h>
 
typedef struct NetworkFrame_
{
	unsigned ack : 1;
	unsigned tseq : 7;
	unsigned char payload[3];
} NetworkFrame;
 
int main(void)
{
      NetworkFrame nf;
      NetworkFrame *pnf;
      
      unsigned char* test;
      int i = 0;
 
      nf.ack = 1;
      nf.tseq = 2;
		
	  nf.payload[0] = 0;
	  nf.payload[1] = 0;
	  nf.payload[2] = 0;
	  nf.payload[3] = 0;
      
      test = (unsigned char*)&nf;
 
      pnf = (NetworkFrame *) test;
 
      memcpy(&nf,  test, sizeof(nf));
 
      for (i = 0; i < sizeof(nf); ++i) {
		  printf("%04x ", test[i]);
      }
 
	  system("PAUSE");
	  
	  return 0;
}

Open in new window

Infinity08

My compiler adds no padding in this case, but a compiler is allowed to add padding if it thinks that that would improve performance or so.


Note that this :

          nf.payload[3] = 0;

is a buffer overflow : the payload array is only 3 bytes, not 4.

Also note that you still have the faulty memcpy there ...


>> 42 and 40 isnt anything that i assign

Padding can contain random data, yes.


>> dont understand why though when my struct is ( should be ) 4 bytes?

The C standard doesn't say that it has to be 4 bytes. Check your compiler documentation - usually there is a way to force the compiler not to add padding (which compiler are you using ?).
Experts Exchange has (a) saved my job multiple times, (b) saved me hours, days, and even weeks of work, and often (c) makes me look like a superhero! This place is MAGIC!
Walt Forbes
fraW

ASKER
Oh okay!

At the moment i am using Visual Studio 2005 but this scenario is supposed to be moved to HiTIDE and using their C Compiler for PIC16 chip.
Infinity08

If I'm not mistaken, for Visual Studio, you need to add this line right before the struct definition :

        #pragma pack(1)

which will force alignment on byte boundaries.

        http://msdn2.microsoft.com/en-us/library/2e70t5y1(VS.80).aspx

For the other compiler, you'll have to check in its documentation.
fraW

ASKER
The following code still results:  0005 0042 0040 0000 0000 0000 0000

Can't understand why?
#include <string.h>
#include <stddef.h>
 
#pragma pack(1)
typedef struct NetworkFrame_
{
	unsigned ack : 1;
	unsigned tseq : 7;
	unsigned char payload[3];
} NetworkFrame;
 
int main(void)
{
      NetworkFrame nf;
      NetworkFrame *pnf;
      
      unsigned char* test;
      int i = 0;
 
      nf.ack = 1;
      nf.tseq = 2;
		
	  nf.payload[0] = 0;
	  nf.payload[1] = 0;
	  nf.payload[2] = 0;
	  nf.payload[3] = 0;
      
      test = (unsigned char*)&nf;
 
      pnf = (NetworkFrame *) test;
 
      memcpy(&nf,  test, sizeof(nf));
 
      for (i = 0; i < sizeof(nf); ++i) {
		  printf("%04x ", test[i]);
      }
 
	  system("PAUSE");
	  
	  return 0;
}

Open in new window

⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
Infinity08

>> Can't understand why?

Well, bit fields are highly implementation dependent, and I always advise not to use them for interpreting byte streams, especially if the code needs to be portable.


It seems that your compiler insists on allocating an entire unsigned int even though only the first byte is used. Usually compilers have ways of changing the behavior, but I don't know your compiler very well, so I can't tell you how. Check the compiler documentation.
fraW

ASKER
Heh i did check the link you gave me, but without success :) its the compiler that comes with vs2005
Infinity08

Did you have any further questions about this ?
All of life is about relationships, and EE has made a viirtual community a real community. It lifts everyone's boat
William Peck
fraW

ASKER
Hehe still no success in packing it, but i guess theres no more you can do for me, you've given me all the help you can!

Thank you
Infinity08

Apart from telling you that it's not recommended to depend on bitfields for things like this, because they're extremely platform dependent, I can't say much, no, because I don't know the compilers you're using :)