Convert struct with pointer to char array - c

I am currently trying to find the simplest way to convert a struct which contains a char * to a char array for transmitting a serial data frame.
The struct stores the frame headers, checksum, msg size etc along with the data to be transmit. eg
struct {
unsigned char header;
unsigned char msgSizeL;
unsigned char msgSizeH;
unsigned char *data;
unsigned char checksum;
....
....
....
....
unsinged char endFrame;
} Frame;
The data is a pointer as the amount of data can change with each frame transmit in the range from 1 to 16 bytes.
I am trying to find the simplest way to convert the struct to an array of unsigned chars without copying out each entry in the struct to the array but don't seem to be able to find a way.
Alternately I don't have to use a struct, as i am generating the frame contents excluding the data contents from scratch, I just want to avoid a case of -
unsigned char frame[1000];
frame[0] = 0x01; // header
frame[1] = msgSizeL; // msg size low byte
frame[2] = msgSizeH; // msg size high byte
frame[3] = data[0]; // data contents 0 Over simplification
frame[4] = data[1]; // data contents 1 for data copying -
frame[5] = data[2]; // data contents 2 data length is variable.
frame[6] = data[3]; // data contents 3
frame[7] = data[4]; // data contents 4
frame[8] = data[5]; // data contents 5
frame[9] = data[6]; // data contents 6
frame[10] = data[7]; // data contents 7
frame[3 + msgSize]; = getChecksum(data);
....
....
....
....
frame [3 + msgSize + 20] = 0xFF; // end frame
where i write data to what reads as a random array index rather than something meaningful like frame.checksum = getChecksum(data);.
Open to suggestions and a little bit of flaming for asking a general question rather than a specific one :-)

I am trying to find the simplest way to convert the struct to an array of unsigned chars without copying out each entry in the struct to the array but don't seem to be able to find a way.
If data would contain up to 16 bytes, I would just declare it as 16-char array, like so:
#define MAX_DATA_LEN 16
struct {
unsigned char header;
unsigned char msgSizeL;
unsigned char msgSizeH;
unsigned char data[MAX_DATA_LEN]; // 16-char array instead of pointer
unsigned char checksum;
....
....
....
....
unsinged char endFrame;
} Frame;
If this is done, then all your data inside this struct has a pre-defined size (i.e. number of unsigned char * sizeof(unsigned char), and so on with other data types...) and you can use simple memcpy to copy the struct to an array:
size_t struct_len = /* computing the length of the data */ 100;
memcpy(frame, struct Frame, struct_len);

You have two options:
1. dynamic frame length
struct {
unsigned char header;
unsigned char msgSizeL; // this will be dynamic from 0x06 (empty) to 0x16 (full)
unsigned char msgSizeH; // this will be always 0x00
unsigned char dataSize; // specify the data size here
unsigned char data[16]; // use a static memory area for the package
unsigned char checksum;
unsigned char endFrame;
} Frame;
then you can do this:
Frame xFrame;
unsigned char ucaData[17] = {0}; // your data
unsigned int uiDataLength = 6; // for example
memset(&xFrame, 0, sizeof(Frame)); // just to be sure
xFrame.header = 0x01;
xFrame.msgSizeL = uiDataLength;
xFrame.msgSizeH = 0;
xFrame.dataSize = uiDataLength;
memcpy(xFrame.data, ucaData, uiDataLength);
xFrame.checksum = CRC8(&xFrame, uiDataLength + 4); // crc for filled area
xFrame.endFrame = 0xFF;
then when building the package:
unsigned char xDataFrame[1000];
memcpy(xDataFrame, xFrame, 4 + uiDataLength);
memcpy(xDataFrame + 4 + uiDataLength, &xFrame + 20, 2);
transmit(xDataFrame, 6 + uiDataLength);
This way, you will discard empty bytes from sending and from calculating checksum for.
2. static frame length
Then you can use a struct like this:
struct {
unsigned char header;
unsigned char msgSizeL; // this will be always 0x16 (22)
unsigned char msgSizeH; // this will be always 0x00
unsigned char dataSize; // specify the data size here
unsigned char data[16]; // use a static memory area for the package
unsigned char checksum;
unsigned char endFrame;
} Frame;
This way you can calculate the checksum for the first 20 bytes of this structure, but be aware of that this structure may contain padding areas which may move your data around the defined and padded memory area. sizeof(Frame) may be calculated by hand as 22 bytes but the compiler may decide that it should use 32 bytes or 24 bytes of memory related with the pack setting. You may use
#pragma pack(1) // disable padding
[structure code]
#pragma pack() // back to the original setting
More info about packing:
#pragma pack effect

Related

Using a pointer to access typedef struct

Objective: Writing to an internal buffer from the values of members of a structure.
I have a structure that contains members of type Uint16 (unsigned int); here is a small portion of it:
typedef unsigned int Uint16;
typedef struct
{
Uint16 ee_Speed_Control_Mode;
Uint16 ee_Motor_Type;
Uint16 ee_Max_Electrical_Speed;
Uint16 ee_Carrier_Frequency;
Uint16 ee_Rated_Motor_Frequency;
Uint16 ee_Rated_Motor_Current;
Uint16 ee_Rs; // extern
Uint16 ee_Rr; // extern
Uint16 ee_L_lkg; // extern
Uint16 ee_Lm; // extern
Uint16 ee_No_Load_Current;
Uint16 ee_Phase_Reversal;
.....
.....
} EEPROM_PARAMETERS;
EEPROM_PARAMETERS eepromParameters;
My attempt:
Here is a function that is intended to write to eeprom: (Most of it is not shown for simplicity; the focus is occurring in the 'for' loop
void eeprom_write(Uint16 address, Uint32 *data, Int16 len)
{
Uint16 i;
// Multiple bytes will be written
// Page Write operation will be used
// Page Write bits to be sent:
// bit 0: Start condition, a high-to-low transition of SDA with SCL high
startCondition();
// bits 1-8: Device address
I2caRegs.I2CDXR = DEVICE_ADDRESS_WRITE;
// bit 9: EEPROM outputs 0 as ACK bit
// Check for ACK bit
while (I2caRegs.I2CDRR != 0)
{
wait();
}
// bits 10-17, bit 18 (ACK) and bits 19-26: two 8-bit word addresses
I2caRegs.I2CDXR = address;
// After setting the address, page write is capable of writing 64 bytes without stopping
// The EEPROM will respond with a zero after each data word has been received
// The data word address lower 6 bits are internally incremented following the receipt of each data word
// If more than 64 data words are written, data word addresses will "roll over" and previous data will be overwritten
for (i = 0; i < len; i++)
{
// How to increment address in data structure?
I2caRegs.I2CDXR = *data++;
}
// After page write operation is complete, execute stop condition
stopCondition();
}
When I try to call this function with my parameters..
eeprom_write(0, &eepromParameters, sizeof(eepromParameters) );
I get a incompatible type error:
error #169: argument of type "EEPROM_PARAMETERS *" is incompatible with parameter of type "Uint16 *"
My next thought would be that I need a middle man to bring them together and make it a compatible match. Any tips please on what I can try? Thanks
The problem is the declaration and usage of data. If you declare it as
void eeprom_write(Uint16 address, EEPROM_PARAMETERS* data, Int16 len);
and call it as
eeprom_write(0, &eepromParameters, sizeof(eepromParameters));
It will fall over in
*data++
since it will increment by the size of EEPROM_PARAMTERS. If the prototype is declared as
void eeprom_write(Uint16 address, UInt16* data, Int16 len);
It needs to be called as
eeprom_write(0, &eepromParameters.ee_Speed_Control_Mode, sizeof(eepromParameters) / sizeof(Uint16));
This assumes that everything in EEPROM_PARAMETERS is Uint16. Another way of doing this is to use enums.
enum EEOffsets
{
ee_Speed_Control_Mode,
ee_Motor_Type,
ee_Max_Electrical_Speed,
ee_Carrier_Frequency,
...
ee_Max
};
// Initialize the parameters
Uint16 eepromParameters[ee_Max] = { ... };
// If you need to assign
eepromParameters[ee_Carrier_Frequency] = 85;
...
eeprom_write(0, eepromParameters, ee_Max);

Serialize struct to byte array with pointer as struct member

I am trying to copy a byte array to my struct, then serialize my struct to a byte array again.
But, after I serialize my struct array, I cant get my data value (0x12, 0x34, 0x56) again, instead i get some rubbish data.
What is wrong here?
#pragma pack(push, 1)
typedef struct {
uint8_t length;
uint8_t *data;
} Tx_Packet;
#pragma pack(pop)
static void create_tx_packet(uint8_t *packet, uint8_t *src, int length);
int main(void)
{
uint8_t packet[32];
uint8_t data[] = { 0x12, 0x34, 0x56 };
create_tx_packet(packet, data, 3);
//i check using debugger, i cant get the data value correctly
//but i could get length value correctly
return 0;
}
static void create_tx_packet(uint8_t *packet, uint8_t *src, int length)
{
Tx_Packet *tx_packet = malloc(sizeof(*tx_packet ));
tx_packet->length = length;
tx_packet->data = (uint8_t *)malloc(length);
memcpy(tx_packet->data, src, length);
memcpy(packet, tx_packet, sizeof(*tx_packet));
}
Right now, your create_tx_packet() function copies a Tx_Packet struct created in the function to a uint8_t array. That struct contains the length and a pointer to the data, but not the data itself. It's actually not necessary to use the struct as an intermediate step at all, particularly for such a simple packet, so you could instead do:
static void create_tx_packet(uint8_t *packet, uint8_t *src, int length)
{
*packet = length; /* set (first) uint8_t pointed to by packet to the
length */
memcpy(packet + 1, src, length); /* copy length bytes from src to
the 2nd and subsequent bytes of
packet */
}
You still need to make sure packet points to enough space (at least length + 1 bytes) for everything (which it does). Since the version above doesn't dynamically allocate anything, it also fixes the memory leaks in your original (which should have freed tx_packet->data and tx_packet before exiting).
--
If you do want to use a struct, you can (since the data is at the end) change your struct to use an array instead of a pointer for data -- then extra space past the size of the struct can be used for the data, and accessed through the data array in the struct. The struct might be:
typedef struct {
uint8_t length;
uint8_t data[];
} Tx_Packet;
and the function becomes (if a temporary struct is used):
static void create_tx_packet(uint8_t *packet, uint8_t *src, int length)
{
/* allocate the temporary struct, with extra space at the end for the
data */
Tx_Packet *tx_packet = malloc(sizeof(Tx_Packet)+length);
/* fill the struct (set length, copy data from src) */
tx_packet->length = length;
memcpy(tx_packet->data, src, length);
/* copy the struct and following data to the output array */
memcpy(packet, tx_packet, sizeof(Tx_Packet) + length);
/* and remember to free our temporary struct/data */
free(tx_packet);
}
Rather than allocate a temporary struct, though, you could also use struct pointer to access the byte array in packet directly and avoid the extra memory allocation:
static void create_tx_packet(uint8_t *packet, uint8_t *src, int length)
{
/* Set a Tx_Packet pointer to point at the output array */
Tx_Packet *tx_packet = (Tx_Packet *)packet;
/* Fill out the struct as before, but this time directly into the
output array so we don't need to allocate and copy so much */
tx_packet->length = length;
memcpy(tx_packet->data, src, length);
}
If you use memcpy(packet, tx_packet, sizeof(*tx_packet)); you are copying the memory representation of tx_Packet into packet, starting with tx_packet->length.
Additionally when mallocating tx_packet that size should be sizeof(*packet)+sizeof(uint8_t) (length of packet plus length field)
And again when copying the tx_packet back to packet you are writing out of the boundaries of packet.
EDIT:
I forgot to mention that depending on your compiler memory alignment parameter you could get any length for the fields (including tx_packet->length) to accelerate memory operation. On 32bits machine it could be 4 and padded with rubbish.
When you serialize your struct with
memcpy(packet, tx_packet, sizeof(*tx_packet));
you're copying the length and the pointer to the data, but not the data itself. You'll probably need two memcpy calls: one of sizeof(uint8_t) to copy the length field, and one of length to copy the data.
This line:
Tx_Packet *tx_packet = malloc(sizeof(*packet));
only allocates one byte for the packet header, which you then immediately write off the end of, causing undefined behavior. You probably meant
Tx_Packet *tx_packet = malloc(sizeof(*tx_packet));

Length of char array (where "int" values are stored)

I need to store the some integer values in the char array with same size of memory .
like int32 should fit in char[4] .
so below is the thing I tried
int val = 123457;
char *data = val;
this stores properly, but If I allocate like this , I can not get the length or free it.
so I tried like this.
char *data = malloc(sizeof(val);
but How do I copy that int value .
Due to some requirement I have to store in the char array and get back int value from the char array.
Yes, you can do this, but it's not wise to mingle your data types like this unless you actually know what you're doing.
This is fraught with peril, as they say.
Still, if you wish to do it, and then copy the data, you can do it like this:
int val = 123457;
char *data = malloc(sizeof(val);
memcpy(data, &val, sizeof(val));
Another way you can do this is by using union(one more useless thing of c++ if you ask me).
union utyp {
int i;
char ch[sizeof(int)];
};
union utyp x;
x.i=324255;
Union is making all of its variables share the same memory space
324255 is 100 11110010 10011111 to binary
so for example the (int)x.ch[2] is 4
memcpy will solve your problem. You do something like this:
int val = 123457;
char data[sizeof int];
memcpy(data, &val, sizeof int);
Just look up man page for memcpy for more details. Basically it copies specified number of bytes from memory pointed to by second argument to that of first argument.
Note about endian-ness: If your machine is little-endian then data above will contain the bytes in reverse byte-order of what your would see if you represented the value 123457 in binary or hex. If you want to see bytes in data in big-endian then you may want use following code:
int val = htonl(123457);
char data[sizeof int];
memcpy(data, &val, sizeof int);
Here htonl converts whatever byte-order your machine is, into network order, i.e. big-endian.
with
int val = 123457;
char *data = val;
you probably meant
int val = 123457;
char *data = (char*)&val; // note the & to give the address of val
now data is pointing to the int, data is not holding the value, just pointing to it.
but if you want to copy the int value to the allocated memory:
char *data = malloc(sizeof(int));
int val = 123457;
memcpy( data, &val, sizeof(int));
now the int value is both stored in data and in val
you can also use a union
typedef union
{
int myint;
char mychars[sizeof(int)];
} u;
...
u myunion;
myunion.myint = 123456;
now you have the value 123456 in the int and you can get the characters by copying from myunion.mychars
You can do it easily using type casts e.g.
char data[8];
uint32_t a,b; // ATTENTION! int can be 64 bit depending on architecture and compiler !
a=12345;
b=678910;
*(uint32_t *)data=a;
*(uint32_t *)(data+4)=b;
// to retrieve the values
uint32_t c=*(uint32_t *)(data+4); // we'll get b
In C Char having the size of 1 word or byte means you can only store one character either a number..!!! While you trying to store a int32 number it is having 6 chars in terms of character. What are you saying it can't be possible with char in C.

Structures with dynamic memory allocation in C

I need to write data into a structure where the length of the data depends on the command I want to send to a device. For that I have defined the following structure:
typedef struct {
uint8 len; // Command length (cmd ... crc)
uint8 cmd; // Command code
uint8 data_length; // Data length
uint8 data[12]; // Data: max 12 Byte
uint8 crc_h; // CRC value MSB
uint8 crc_l; // CRC value LSB
}CMD_TYPE;
Note: the members cmd, *data_length* and crc that are always present, instead member data can be empty or contains up to 12 Bytes.
I have created a function that returns a initialized command according to the parameters passed to the function:
CMD_TYPE Device::get_cmd(uint8 cmd, uint8 data_len, uint8 *data)
{
CMD_TYPE cmd;
cmd.len = (4 + data_len) * sizeof(uint8);
cmd.cmd = cmd;
cmd.data_length = data_len;
cmd.data = (uint8 *)realloc(cmd.data, data_len*sizeof(uint8));
if(data_len > 0) memcpy(cmd.data, data, data_len);
add_crc16((uint8*)&cmd);
return cmd;
}
The function get_cmd() is used like this:
uint8 cmd_code = 0x01;
uint8 data[2] = {0xAB, 0xCD};
CMD_TYPE cmd = local_device->get_cmd(cmd_code, 2, data);
retVal = local_device->send(cmd);
When I try to compile this code I get an error from the compiler for that line:
cmd.data = (uint8 *)realloc(cmd.data, data_len*sizeof(uint8));
and the compiler error is:
error: lvalue required as left operand of assignment
The aim of using realloc() is to re-size the array data or to remove it at all from my new command structure. What is wrong in my code? Is that the right way to initialize structures with dynamic memory allocation?
What you want is the infamous struct hack:
typedef struct
{
uint8 len; // Command length (cmd ... crc)
uint8 cmd; // Command code
uint8 data_length; // Data length
uint8 crc_h; // CRC value MSB
uint8 crc_l; // CRC value LSB
uint8 data[1]; // Data: max 12 Byte
} CMD_TYPE;
The trick is to allocate enough room for all of the members of the struct up to data[], then add enough bytes for the data[] member:
CMD_TYPE * allocCmd(int dataSize)
{
int len;
CMD_TYPE * p;
len = sizeof(CMD_TYPE) + (dataSize-1)*sizeof(uint8);
p = (CMD_TYPE *) malloc(len);
memset(p, 0, len);
p->data_length = dataSize;
return p;
}
Here, len is calculated to be the size of the struct, minus the size of the empty data member, plus however many elements dataSize specifies for the data array.
The catch is that you have to be careful never to access any elements of p->data[] beyond what is actually allocated in it (inside the struct).
Your CMD_TYPE.data is an array, not a pointer. Since you want it to track dynamically allocated memory, it has to be a pointer:
uint8_t * data;
Just don't forget to initialize it with malloc() (or by setting it to zero before realloc()) and to clean up after yourself
By the way, do not cast the result of malloc() and co.
array defined as a[..] are immutable, you can't assign anything to them. Instead you should use pointers.

Read binary data (from file) into a struct

I'm reading binary data from a file, specifically from a zip file. (To know more about the zip format structure see http://en.wikipedia.org/wiki/ZIP_%28file_format%29)
I've created a struct that stores the data:
typedef struct {
/*Start Size Description */
int signatute; /* 0 4 Local file header signature = 0x04034b50 */
short int version; /*  4 2 Version needed to extract (minimum) */
short int bit_flag; /*  6 2 General purpose bit flag */
short int compression_method; /*  8 2 Compression method */
short int time; /* 10 2 File last modification time */
short int date; /* 12 2 File last modification date */
int crc; /* 14 4 CRC-32 */
int compressed_size; /* 18 4 Compressed size */
int uncompressed_size; /* 22 4 Uncompressed size */
short int name_length; /* 26 2 File name length (n) */
short int extra_field_length; /* 28 2 Extra field length (m) */
char *name; /* 30 n File name */
char *extra_field; /*30+n m Extra field */
} ZIP_local_file_header;
The size returned by sizeof(ZIP_local_file_header) is 40, but if the sum of each field is calculated with sizeof operator the total size is 38.
If we have the next struct:
typedef struct {
short int x;
int y;
} FOO;
sizeof(FOO) returns 8 because the memory is allocated with 4 bytes every time. So, to allocate x are reserved 4 bytes (but the real size is 2 bytes). If we need another short int it will fill the resting 2 bytes of the previous allocation. But as we have an int it will be allocated plus 4 bytes and the empty 2 bytes are wasted.
To read data from file, we can use the function fread:
ZIP_local_file_header p;
fread(&p,sizeof(ZIP_local_file_header),1,file);
But as there're empty bytes in the middle, it isn't read correctly.
What can I do to sequentially and efficiently store data with ZIP_local_file_header wasting no bytes?
In order to meet the alignment requirements of the underlying platform, structs may have "padding" bytes between members so that each member starts at a properly aligned address.
There are several ways around this: one is to read each element of the header separately using the appropriately-sized member:
fread(&p.signature, sizeof p.signature, 1, file);
fread(&p.version, sizeof p.version, 1, file);
...
Another is to use bit fields in your struct definition; these are not subject to padding restrictions. The downside is that bit fields must be unsigned int or int or, as of C99, _Bool; you may have to cast the raw data to the target type to interpret it correctly:
typedef struct {
unsigned int signature : 32;
unsigned int version : 16;
unsigned int bit_flag; : 16;
unsigned int compression_method : 16;
unsigned int time : 16;
unsigned int date : 16;
unsigned int crc : 32;
unsigned int compressed_size : 32;
unsigned int uncompressed_size : 32;
unsigned int name_length : 16;
unsigned int extra_field_length : 16;
} ZIP_local_file_header;
You may also have to do some byte-swapping in each member if the file was written in big-endian but your system is little-endian.
Note that name and extra field aren't part of the struct definition; when you read from the file, you're not going to be reading pointer values for the name and extra field, you're going to be reading the actual contents of the name and extra field. Since you don't know the sizes of those fields until you read the rest of the header, you should defer reading them until after you've read the structure above. Something like
ZIP_local_file_header p;
char *name = NULL;
char *extra = NULL;
...
fread(&p, sizeof p, 1, file);
if (name = malloc(p.name_length + 1))
{
fread(name, p.name_length, 1, file);
name[p.name_length] = 0;
}
if (extra = malloc(p.extra_field_length + 1))
{
fread(extra, p.extra_field_length, 1, file);
extra[p.extra_field_length] = 0;
}
C structs are just about grouping related pieces of data together, they do not specify a particular layout in memory. (Just as the width of an int isn't defined either.) Little-endian/Big-endian is also not defined, and depends on the processor.
Different compilers, the same compiler on different architectures or operating systems, etc., will all layout structs differently.
As the file format you want to read is defined in terms of which bytes go where, a struct, although it looks very convenient and tempting, isn't the right solution. You need to treat the file as a char[] and pull out the bytes you need and shift them in order to make numbers composed of multiple bytes, etc.
The solution is compiler-specific, but for instance in GCC, you can force it to pack the structure more tightly by appending __attribute__((packed)) to the definition. See http://gcc.gnu.org/onlinedocs/gcc-3.2.3/gcc/Type-Attributes.html.
It's been a while since I worked with zip-compressed files, but I do remember the practice of adding my own padding to hit the 4-byte alignment rules of PowerPC arch.
At best you simply need to define each element of your struct to the size of the piece of data you want to read in. Don't just use 'int' as that may be platform/compiler defined to various sizes.
Do something like this in a header:
typedef unsigned long unsigned32;
typedef unsigned short unsigned16;
typedef unsigned char unsigned8;
typedef unsigned char byte;
Then instead of just int use an unsigned32 where you have a known 4-byte vaule. And unsigned16 for any known 2-byte values.
This will help you see where you can add padding bytes to hit 4-byte alignment, or where you can group 2, 2-byte elements to make up a 4-byte alignment.
Ideally you can use a minimum of padding bytes (which can be used to add additional data later as your expand the program) or none at all if you can align everything to 4-byte boundaries with variable-length data at the end.
Also, the name and extra_field will not contain any meaningful data, most likely. At least not between runs of the program, since these are pointers.

Resources