Safe use of structural alignment

I am new to a company where the following use of the structure is performed:

#include <stdio.h> #include <string.h> typedef unsigned char uint8; typedef signed char int8; typedef unsigned short int uint16; typedef signed short int int16; typedef struct padded_t { int8 array0[11]; int8 array1[4]; uint16 len2; int8 array2[25]; uint16 len3; int8 array3[6]; // many more len/arrays follow } padded_t; int main(int argc, char** argv) { padded_t foo; memset((void*)&foo, 0, sizeof(padded_t)); int8* str = "foobar"; int16 size = (int16)strlen(str); int8* ptr = (int8*)&foo.len2; // please note that the memcpy references only the pointer to len // are the following couple of lines safe? memcpy ((void*)ptr, &size, 2); memcpy ((void*)&ptr[2], (void*)str, size); printf("%d\n", foo.len2); printf("%s\n", foo.array2); return 0; } 

I know some things about alignment and padding, and I assume that the compiler (gnu C99 for ARM9 device) will add some paddings to align the structure.

But is this code safe?
As far as I understand, it will be safe if uint16 len variables are immediately followed by int8 array[] variables, regardless of other elements of the structure.

Will it only add pads before uint16 when the size before it is odd?
Is it correct? And more importantly, is it safe?

+5
source share
2 answers

But is this code safe?

As I understand it, it will be safe if the uint16 len variables immediately follow the int8 array[] variables independently of other elements of the structure.

This is not safe in the sense that compilers are allowed to freely insert any number of additions between or after structure elements, so you cannot be sure that &ptr[2] points to the first byte of foo.array2 . However, if uint16 does have a width of two bytes (which is in no way guaranteed by the language), you can be sure that if size less than 25, then

 memcpy ((void*)&ptr[2], (void*)str, size); 

neither the bytes of foo.len2 nor the last byte of foo.array2 will be changed. Since foo was previously filled with zeros, this leaves foo.array2 as a correctly completed C string. Thus, you can safely print it with printf() . C, on the other hand, does not guarantee that the result of this will be the same as the result of printing str .

Will he only add paddings before uint16 when the size before it is odd?

This is at the discretion of the compiler. It can be influenced by pragmatics, command-line options, configuration options, language extensions (although none of them are used in this example), the target architecture, or anything else the compiler wants to use to make such decisions.

Is it used correctly?

As far as I can tell, the program is consistent if that is what you mean.

And more importantly, is it safe?

The output of the program is not predictable only from its code, therefore, in this sense, no, it is unsafe.

+2
source

You do not need to write code that works on every system. What you have to do is write code with predictable behavior. Either it works as designed, or if your assumptions are not fulfilled, the static statement interrupts the compilation.

The second line of memcpy cannot do this. It is assumed that offsetof(struct padded_t, len2) + 2 == offsetof(struct padded_t, array2) . An assumption that is often fulfilled, but completely stupid.

Why just write

 foo.len2 = strlen(str); memcpy (foo.array2, str, foo.len2); //possibly, foo.array2[foo.len2] = '\0'; 

The code is readable. No unnecessary variables. There are no extra throws. No unpredictable behavior. The original is not like code from which you learn a lot, the mess is not like what I expect from someone who can write C.

To respond to your comment, making them packaged is a wrong fix. Because it will lead to misuse of the members and just open up another possibility of worms .

Also be careful with custom fixed typedef sizes. I once had fun debugging typedef char int8; on a system with char unsigned ...

+3
source

All Articles