Reputation: 249
I came across this simple program somewhere
#include<stdio.h>
#include<stdlib.h>
char buffer[2];
struct globals {
int value;
char type;
long tup;
};
#define G (*(struct globals*)&buffer)
int main ()
{
G.value = 233;
G.type = '*';
G.tup = 1234123;
printf("\nValue = %d\n",G.value);
printf("\ntype = %c\n",G.type);
printf("\ntup = %ld\n",G.tup);
return 0;
}
It's compiling (using gcc) and executing well and I get the following output:
Value = 233
type = *
tup = 1234123
I am not sure how the #define G statement is working. How G is defined as an object of type struct globals ?
Upvotes: 1
Views: 209
Reputation: 726479
First, this code has undefined behavior, because it re-interprets a two-byte array as a much larger struct
. Therefore, it is writing past the end of the allocated space. You could make your program valid by using the size of the struct
to declare the buffer
array, like this:
struct globals {
int value;
char type;
long tup;
};
char buffer[sizeof(struct globals)];
The #define
is working in its usual way - by providing textual substitutions of the token G
, as if you ran a search-and-replace in your favorite text editor. Preprocessor, the first stage of the C compiler, finds every entry G
, and replaces it with (*(struct globals*)&buffer)
.
Once the preprocessor is done, the compiler sees this code:
int main ()
{
(*(struct globals*)&buffer).value = 233;
(*(struct globals*)&buffer).type = '*';
(*(struct globals*)&buffer).tup = 1234123;
printf("\nValue = %d\n",(*(struct globals*)&buffer).value);
printf("\ntype = %c\n",(*(struct globals*)&buffer).type);
printf("\ntup = %ld\n",(*(struct globals*)&buffer).tup);
return 0;
}
Upvotes: 4
Reputation: 399713
The macro simply casts the address of the 2-character buffer buf
into a pointer to the appropriate structure type, then de-references that to produce a struct-typed lvalue. That's why the dot (.
) struct-access operator works on G
.
No idea why anyone would do this. I would think it much cleaner to convert to/from the character array when that is needed (which is "never" in the example code, but presumably it's used somewhere in the larger original code base), or use a union
to get rid of the macro.
union {
struct {
int value;
/* ... */
} s;
char c[2];
} G;
G.s.value = 233; /* and so on */
is both cleaner and clearer. Note that the char
array is too small.
Upvotes: 1