user28264
user28264

Reputation: 249

using #define for defining struct objects

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

Answers (2)

Sergey Kalinichenko
Sergey Kalinichenko

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

unwind
unwind

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

Related Questions