¿Cómo puedo acceder a los miembros de una estructura cuando no está alineada correctamente?

I'm afraid that I'm not very good at low level C stuff, I'm more used to using objects in Obj-c, so please excuse me if this is an obvious question, or if I've completely misunderstood something...

I am attempting to write an application in Cocoa/Obj-C which communicates with an external bit of hardware (a cash till.) I have the format of the data the device sends and receives - and have successfully got some chunks of data from the device.

For example: the till exchanges PLU (price data) in chunks of data in the following format: (from the documentation)

Name Bytes Type

Name             Bytes    Type

PLU code h        4      long   
PLU code L        4      long
desc              19     char
Group             3      char
Status            5      char 
PLU link code h   4      long 
PLU link code l   4      long
M&M Link          1      char
Min. Stock.       2      int
Price 1           4      long 
Price 2           4      long

Total 54 Bytes

So I have a struct in the following form in which to hold the data from the till:

typedef struct MFPLUStructure { 
 UInt32   pluCodeH; 
 UInt32   pluCodeL; 
 unsigned char description[19]; 
 unsigned char group[3]; 
 unsigned char status[5]; 
 UInt32   linkCodeH; 
 UInt32   linkCodeL; 
 unsigned char mixMatchLink; 
 UInt16   minStock; 
 UInt32   price[2];  
} MFPLUStructure;  

I have some known sample data from the till (below) which I have checked by hand and is valid

00 00 00 00 4E 61 BC 00 54 65 73 74 20 50 4C 55 00 00 00 00 00 00 00 00 00 00 00 09 08 07 17 13 7C 14 04 00 00 00 00 09 03 00 00 05 BC 01 7B 00 00 00 00 00 00 00

es decir,

  • bytes 46 to 50 are <7B 00 00 00> == 123 as I would expect as the price is set to '123' on the till.

  • byte 43 is <05> == 5 as I would expect as the 'mix and match link' is set to 5 on the till.

  • bytes 39 to 43 are <09 03 00 00> == 777 as I would expect as the 'link code' is set to '777' on the till.

  • Bytes 27,28,29 are <09 08 07> which are the three groups (7,8 & 9) that I would expect.

The problem comes when I try to get some of the data out of the structure programmatically: The early members work correctly right up to, and including the five 'status' bytes. However, members after that don't come out properly. (see debugger screenshot below.)

Imagen 1 - http://i.stack.imgur.com/nOdER.png

I assume that the reason for this is because the five status bytes push later members out of alignment - i.e. they are over machine word boundaries. Is this right?

Image 2 - i.imgur.com/ZhbXU.png

Am I right in making that assumption?
And if so, how can I get the members in and out correctly?

Gracias por cualquier ayuda.

preguntado el 08 de enero de 11 a las 18:01

summing all the number in column 'Bytes' gives 70, not 54; however, if you replace the 8s for the 8-byte long types with 4s, you get the 54 you mentioned... -

PLU code h should be 8 bytes long but in your structure you are using UInt32 wich is only 4 bytes long, are you sure that is correct? Shouldn't it be UInt64? (the same case happens in other PLUs and Prices) -

Oh yes, sorry - that was a copying error by me when formatting the table for this site. Fixed now. -

Por qué Médica you using separate high and low words? We have 64-bit types now. You can use the offsetof macro to check the position of each member within the structure. kernel.org/doc/man-pages/online/pages/man3/offsetof.3.html También puedes tomar el sizeof the structure and compare to the total you arrive at by summing up the sizes by hand. Don't forget to also check that it isn't a mismatch between the endianness of the data and the endianness of your machine. -

I'm using high and low words in the structure in order to keep the structure in exactly the same format as the external hardware is providing/expecting. As for endian-ness, I'm using the NSEndian macros EndianU32_LtoN etc... to make sure that numbers going back and forth between the two bits of hardware stay in the correct form. -

2 Respuestas

If you're sure that endianness of host and wire agree, you could also use a packed structure to read the data in a single pass. However, this is compiler-specific and will most likely impact performance of member access.

Asumiendo gcc, you'd use the following declarations:

struct __attribute__ ((__packed__)) MFPLUStructure { ... };
typedef struct MFPLUStructure MFPLUStructure;

If you decide to use a packed structure, you should also verify that it is of correct size:

assert(sizeof (MFPLUStructure) == 54);

Respondido el 08 de enero de 11 a las 21:01

Hmm - Thanks also. Looks like I've got some more reading to do about structs. - Diggory

Fantastic! I'll take your caveats into account, but that works very well. Thanks. - Diggory

Either access the data a byte at a time and assemble it into larger types, or memcpy it into an aligned variable. The latter is better if the data is known to be in a format specific to the host's endianness, etc. The former is better if the data follows an external specification that might not match the host.

Respondido el 08 de enero de 11 a las 21:01

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.