Link to home
Start Free TrialLog in
Avatar of fraW
fraWFlag 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

Avatar of zhuba
zhuba
Flag of New Zealand image

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

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of Infinity08
Infinity08
Flag of Belgium image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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 ?
>>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.

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 !
Avatar of fraW

ASKER

Infinity08, its embedded software, the usage is confidential. sorry :)
I don't need to know the high level usage - just the low-level - ie. why is the cast necessary.
Avatar of fraW

ASKER

Thank you very much, this works!

Can't really understand the whole thing, but thats a later problem! :)
Avatar of 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
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);
>> 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

Avatar of 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? :)
>> Do you understand? :)

Sure :)
Avatar of fraW

ASKER

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

ASKER

Btw, thank you for the very good explenation :D
>> 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.
Avatar of 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

Avatar of 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.
Avatar of fraW

ASKER

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

Sorry for the multi posts :)
>> 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 !
Avatar of 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

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 ?).
Avatar of 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.
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.
Avatar of 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

>> 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.
Avatar of fraW

ASKER

Heh i did check the link you gave me, but without success :) its the compiler that comes with vs2005
Did you have any further questions about this ?
Avatar of 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
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 :)