类型定义如何影响 compile/assembly?

How does typedefing influence compile/assembly?

由于有两种编写枚举、结构、联合或类型的方法,其中一种使用 typedef,或者不使用,我想知道每种方法的优缺点是什么。

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

我个人喜欢第二种变体提供的封装,但是当我为所有枚举、结构和联合编写代码时,我总是会避免使用 typedef 编写第一个示例,但我会使用这种方法:

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

是的,我知道它可以更横向一些 space,但对我来说,它比这样的东西更能详细说明什么是类型:

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

有人可以概述有关使用 typedef 和不使用 typedef 之间差异的事实(不是个人意见,因为这些是可以讨论的)吗?这对编译代码、程序内存大小等有何影响?

当我编译以下没有 typedef 的代码时,我尝试查看程序集差异:

struct TestStruct
{
    int field;
};

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

    return 1;
}

与带有 typedef 的代码对比:

typedef struct
{
    int field;
} TestStruct;

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

    return 1;
}

程序集肯定不一样。我正在进行横向比较:

 ___________________________________________________________________________________________________________
.LC0:                                                                .LC0:
    .string "%d\n"                                                       .string "%d\n"
 main:                                                                main:
    pushq   %rbp                                                          push    rbp
    movq    %rsp, %rbp                                                    mov     rbp, rsp
    subq    , %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    [=16=], %eax                                                      mov     eax, 0
    call    printf                                                        call    printf
    movl    , %eax                                                      mov     eax, 1
    leave                                                                 leave
    ret                                                                   ret

它是使用 godbolt 和 gcc 编译器编译的。当然,我可以看到差异我只是想知道什么时候使用哪种方法更好,具体取决于 benefits/flaws.

注意:我尝试编译成一个 .map 文件,它给出了每个变量的地址、大小、类型等,当使用 typedef 时,.map 文件变得更复杂。

Can someone outline facts (not personal opinions, since these are discussible) about differences between usage of typedef and no typedef?

这与您是否应该 typedef struct 完全相同,主要是主观讨论。大多数人使用 typedef,除了 Linux 世界倾向于输入 struct tag 并且没有明显的对错。

(尽管在非专业文档 Linux 内核编码风格 中试图提供使用后一种风格的理由是可笑的,完全是主观的并且与论点相似因为使用了“匈牙利符号”。)

关于 enum,您通常使用 typedef 只是为了不必在每次使用时都输入 enum tag。没有名字的匿名 enum 通常只在处理本地封装类型时使用。

有几个 non-subjective 键入关键字 + 标签不正确的原因:

  • 在将类型传递给 function-like 宏时使用两个预处理器标记而不是一个可能会有问题,在 X-macros 和类似的地方使用它们。示例:

      #define SUPPORTED_TYPES(X) \
        X(int)                   \
        X(float)                 \
        X(struct foo)            \
    
      enum
      {
        #define ENUM_CONSTANT(type) type ## _val,
        SUPPORTED_TYPES(ENUM_CONSTANT)
      };
    

    此代码试图创建多个与支持类型列表相对应的常量。它将创建 int_valfloat_val,然后在 struct foo_val 时失败,因为该类型中有两个预处理器标记,并且标识符中不能使用空格。另一方面,使用 unsigned int.

    时存在同样的问题
  • 不键入标签符合 C++ 编码风格。 C++ 没有与 C 相同的标记方式,但类型的名称就是类型 - 无需显式键入关键字。


具体来说 typedef unsigned char uint8,这是非常糟糕的做法,但不是因为 typedef,而是因为您正在发明自己的“本地车库标准”而不是使用标准 C stdint.h类型。


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

你做错了什么,比如没有使用相同的优化器设置或忘记在 C90 模式下使用 #include <stdio.h>。有相同的机器码,自己看:https://godbolt.org/z/zd5q7Pc77

Typedef是唯一使C语言语法成为context-sensitive grammar.

的特性

它的作用是改变解析器的环境,将一些符号转换成特殊符号。

如果没有 typedef,C 语言的解析器将不需要环境结构来跟踪类型符号的定义。

注意在ISO/IEC9899:19995.1.1.2 Translation phases中,typedef作用到第7步(语法分析步骤):

  1. White-space 个分隔标记的字符不再重要。每个 预处理令牌转换为令牌。结果标记是 句法和语义分析并作为翻译单元翻译。