Home > Back-end >  Why do C macros act as pointers even though I am not passing variables by pointer
Why do C macros act as pointers even though I am not passing variables by pointer

Time:08-20

I am trying to write a series of C macros to provide some generic data type capability for a struct, or grouping of structs that manages a dynamically allocated array. At this point I have written several structs for each data type and am just starting to write a Macro function that can initialize the struct variables and allocated a user defined amount of memory. The idea is to create one init function that can initialize any type of struct, so as long as it has the write form. For this implementation I first have to instantiate a copy of the struct, which is then passed to the macro. The macro ensures that the variables allocated_length and active_length is of type size_t. Then it determines the type of array by de-referencing it and using thetypeof operator. Finally it allocates memory to ptr and then checks to ensure the allocation was not returned as NULL. However, in this example, I do not pass anything back to the main program, and I am not passing variables as a pointer. Yet, somehow, the struct in the structs in the main program are able to see the modifications I made in the Macro. How is this?

#define init_vector(vec_struct, aloc_length) ({size_t _a = (aloc_length);                   \
                                  size_t _b = 0;                    \
                                  typeof(vec_struct.array) ptr = malloc(aloc_length * sizeof(&vec_struct.array));   \
                                  if (ptr == NULL) {                        \
                                    perror("WARNING: ");                        \
                                    exit(0);                                    \
                                  }                                             \
                                  vec_struct.allocated_length = _a;             \
                                  vec_struct.active_length = _b;                    \
                                  vec_struct.array = ptr;       \
                                  })
typedef struct
{
    int *array;
    size_t allocated_length;
    size_t active_length;
} IntVector;

typedef struct
{
    float *array;
    size_t allocated_length;
    size_t active_length;
} FltVector;


int main() {
    IntVector int_vec;
    init_vector(int_vec, 30);
    printf("%ld\n", int_vec.allocated_length);

    FltVector float_vec;
    init_vector(float_vec, 20);
    printf("%ld\n", float_vec.allocated_length);

    return 0;
}

CodePudding user response:

You need to understand that macros are not functions. They simply replace text (or more precise tokens) before the actual C compilation starts.

The compiler will compile this code:

int main() {
    IntVector int_vec;
    ({size_t _a = (30); size_t _b = 0; typeof(int_vec.array) ptr = malloc(30 * sizeof(&int_vec.array)); if (ptr == 
# 32 "/app/example.c" 3 4
   ((void *)0)
# 32 "/app/example.c"
   ) { perror("WARNING: "); exit(0); } int_vec.allocated_length = _a; int_vec.active_length = _b; int_vec.array = ptr; });
    printf("%ld\n", int_vec.allocated_length);

    FltVector float_vec;
    ({size_t _a = (20); size_t _b = 0; typeof(float_vec.array) ptr = malloc(20 * sizeof(&float_vec.array)); if (ptr == 
# 36 "/app/example.c" 3 4
   ((void *)0)
# 36 "/app/example.c"
   ) { perror("WARNING: "); exit(0); } float_vec.allocated_length = _a; float_vec.active_length = _b; float_vec.array = ptr; });
    printf("%ld\n", float_vec.allocated_length);

    return 0;
}

https://godbolt.org/z/ezvKfdn33

Is it something you have expected?

Macros have to be used with great caution and as little as possible.

CodePudding user response:

From https://en.cppreference.com/w/c/preprocessor/replace:

Function-like macros

#define identifier( parameters ) replacement-list

Function-like macros replace each occurrence of a defined identifier with replacement-list, additionally taking a number of arguments, which then replace corresponding occurrences of any of the parameters in the replacement-list.

The syntax of a function-like macro invocation is similar to the syntax of a function call: each instance of the macro name followed by a ( as the next preprocessing token introduces the sequence of tokens that is replaced by the replacement-list. The sequence is terminated by the matching ) token, skipping intervening matched pairs of left and right parentheses.

...

That means (based on your example) that every occurrence of the identifier init_vector is replaced with the code after the last parentheses of the parameter list.

And each occurrence of the parameters vec_struct, aloc_length will also replaced accordingly.

At the end, it is not about functions and function calls, but replacement.

"The preprocessor supports text macro replacement and function-like text macro replacement."

  • Related