I found a strange behaviour regarding the memory alignment of zero sized arrays when declared inside an unpacked structure. It crossed my mind to throw zero sized arrays to label parts of the structure. I don't think it has a particular use but still i had the feeling some interesting code might be produced.
Is this behaviour compiler specific or will all compilers give the same result (they will not align the zero size array)?
Take the following code for an example:
#include <stdio.h>
struct tagOBJ
{
char Label1[0];
int A;
char Label2[0];
int B;
int C;
};
typedef struct tagOBJ OBJ;
int main()
{
OBJ a;
printf("%d == %d\n", &a.A, a.Label1);
printf("%d == %d\n", &a.B, a.Label2);
return 0;
}
In the above case, the output is as expected:
6422292 == 6422292
6422296 == 6422296
However, if i add in another variable to the structure that implies padding, the given addresses are different:
struct tagOBJ
{
char Label1[0];
int A;
char D;
char Label2[0];
int B;
int C;
};
typedef struct tagOBJ OBJ;
Produces
6422288 == 6422288
6422296 == 6422293 // ! false
CodePudding user response:
Is this behaviour compiler specific or will all compilers give the same result (they will not align the zero size array)?
Arrays with zero size are not allowed by the C language itself. Your code is invalid and the behavior of your code is "undefined". Any compiler can do anything, so, well, the answer would be yes, it is compiler specific, and there is also no requirement that the behavior should be anyhow consistent.
Also:
printf("%d == %d\n", &a.A, a.Label1);
is also invalid. %d
expects an int
- &a.A
is a pointer.
In the spirit of C, would be nicer to add accessor functions:
void *OBJ_public(const OBJ *t) { return (void*)&t->A; }
void *OBJ_private(const OBJ *t) { return (void*)&t->C; }
If you really want to work with macros, well, you can do this:
#define OBJ_PUBLIC A
#define OBJ_PRIVATE C
#define PUBLIC(TYPE, VAR) (void*)(&(VAR).TYPE##_PUBLIC)
#define PRIVATE(TYPE, VAR) (void*)(&(VAR).TYPE##_PRIVATE)
int main() {
OBJ a;
printf("%p\n", PUBLIC(OBJ, a));
}
6422296 == 6422293 // ! false
Gcc has the extension of zero length arrays. And I think - sure it's false. char
member starts when the last struct member ends, so it "glues up" and points to the next byte right after the previous member - so it points to (char*)&a.D 1
, which may or may not be equal to the address of the next member, so it depends on padding. In this case next member is int
, so there is some padding required between char
and int
.