Search code examples
ctypedefctags

How does typedefing influence compile/assembly?


Since there are two ways of writing enums, structs, unions or types where one uses typedef, or alternatively doesn't, I was wondering what would be the benefits and disadvantages of each approach.

E.g. 1: typedef enum { ENUM_A, ENUM_B } ENUM_OBJECT;
E.g. 2: typedef unsigned char uint8

Personally I like encapsulation provided by the second variant, but when I write code for all enums, structs and unions I would always avoid writing the first example with typedef, but I would use this approach:

E.g. 1: enum ENUM_TAG { ENUM_A, ENUM_B };
        enum ENUM_TAG some_variable;

Yes, I know that it can take a bit more horizontal space, but to me it better details idea of what is the type than something like this:

typedef int matrix_buffer_t[2][5];
matrix_buffer_t some_variable;

Can someone outline facts (not personal opinions, since these are discussible) about differences between usage of typedef and no typedef? How does this influence compiled code, program memory size, etc?

I've tried looking at the assembly diff when I compile the following code with no typedef:

struct TestStruct
{
    int field;
};

int main(void)
{
    struct TestStruct test;
    printf ("%d\n",  test.field);

    return 1;
}

vs code with typedef:

typedef struct
{
    int field;
} TestStruct;

int main(void)
{
    TestStruct test;
    printf ("%d\n",  test.field);

    return 1;
}

Assembly is definitely not the same. I'm giving a side to side comparison:

 ___________________________________________________________________________________________________________
.LC0:                                                                .LC0:
    .string "%d\n"                                                       .string "%d\n"
 main:                                                                main:
    pushq   %rbp                                                          push    rbp
    movq    %rsp, %rbp                                                    mov     rbp, rsp
    subq    $16, %rsp                                                     sub     rsp, 16
    movl    -4(%rbp), %eax                                                mov     eax, DWORD PTR [rbp-4]
    movl    %eax, %esi                                                    mov     esi, eax
    movl    $.LC0, %edi                                                   mov     edi, OFFSET FLAT:.LC0
    movl    $0, %eax                                                      mov     eax, 0
    call    printf                                                        call    printf
    movl    $1, %eax                                                      mov     eax, 1
    leave                                                                 leave
    ret                                                                   ret

It was compiled using godbolt and gcc compiler. For sure I can see the difference I am just wondering when it is better to use which approach depending on benefits/flaws.

Note: I've tried compiling to a .map file which gives addresses of each variable, size, types, etc. and when typedef is used, .map file becomes more compex.


Solution

  • Typedef is the only feature that makes C language grammar a context-sensitive grammar.

    Its effect is to change the environment of the parser, to convert some symbols into special symbols.

    Without typedef a parser for the C language would not need an environmental structure to keep track of the definitions of type symbols.

    Note that in ISO/IEC 9899:1999 5.1.1.2 Translation phases, typedef acts into the 7th step (the syntax analyser step):

    1. White-space characters separating tokens are no longer significant. Each preprocessing token is converted into a token. The resulting tokens are syntactically and semantically analyzed and translated as a translation unit.