【问题标题】:Why are #define and typedef operands inverted?为什么 #define 和 typedef 操作数是倒置的?
【发布时间】:2011-02-27 20:25:24
【问题描述】:

以下将A 定义为替换B

#define A B

而这将A 定义为类型 B 的别名:

typedef B A;

为什么?这不是语无伦次吗?

【问题讨论】:

  • PS:我知道它们是完全不同的并且用于非常不同的目的。但就个人而言,我认为 typedef 应该是相反的。
  • 如果有什么安慰的话,Ken Thompson 也有同样的想法。在 Go 中,虽然没有直接等效于 typedef 的仅创建类型别名的方法,但从旧类型创建新类型的方法是 type NewType OldType。我想知道他和 Dennis Ritchie 是否会正面争论 Go 明确做出与 C 相似但不同的语法决策。Go 中的声明顺序基本上是“相反的方式”:-)

标签: c typedef c-preprocessor


【解决方案1】:

简单地说:考虑以下变量声明:

// declare a variable called "myInt" with type int
int myInt;
// declare a variable called "myDouble" with type double
double myDouble;
// declare a variable called "myLong" with type long
long myLong;
// declare a variable called "myFunc" with type pointer to function
void (*myFunc)(char*);

那么typedefs 就很有意义了:

// declare a type alias called "myInt" with type int
typedef int myInt;
// declare a type alias called "myDouble" with type double
typedef double myDouble;
// declare a type alias called "myLong" with type long
typedef long myLong;
// declare a type alias called "myFunc" with type pointer to function
typedef void (*myFunc)(char*);

另一方面,宏可以采用函数式语法:

#define A(B, C) B, C

A(1, 2) // expands out to 1, 2

因此对于宏,“名称”之后的“定义”更有意义。

(顺便说一句,这也适用于 C++。)

【讨论】:

    【解决方案2】:

    是的,宏几乎是一团糟。

    typedef 是在语言的大部分内容完成后的很长一段时间内添加到该语言中的。它使用与存储类相同的语法:

    static int x;
    extern int y;
    typedef int z;
    

    这些将xyz 定义为int——区别在于xyint 类型的对象,而z 基本上是一个int 本身的别名。

    因此,typedef 非常适合该语言,并且(像往常一样)预处理器确实是“奇怪的人”。同时,您可以争辩说语言的其余部分也应该改变。举一个明显的例子,帕斯卡大致颠倒了事情的顺序:

    type
        z = integer;
    
    var
        x : integer;
    

    虽然对于琐碎的示例并没有太大的区别,但我认为这更容易阅读,尤其是当您处理更复杂的声明时。然而,无论好坏,Pascal (大部分)已经失宠,像 Java 这样的新语言保留了 C 语法的这一特定部分(即,他们保留的 C 部分是最需要改变的一件事) .

    【讨论】:

    • +1:很高兴记住声明声明的关键字的概念。在引入仅由句法顺序调用的语言结构之前,请务必三思而后行,无需关键字/运算符。
    • 你为什么说宏(暗示 C 预处理器)是一团糟?与 C 语言相比,它在某些方面是否完全不同?绝对(例如,后期绑定与早期绑定相比)。完全学习会很难吗?是的,它可以。但是是不是一团糟?不是我能看到的。那么你能详细说明一下你所说的“混乱”是什么意思吗?
    • @hlovdal:一些最明显的问题是:1) 缺乏范围,2) 调试器无法获得信息,3) 极易出错(例如,多次评估的参数)。它们很少是可用的最佳解决方案。
    • 1) 缺乏作用域一般适用于宏语言,那么您是否认为所有宏语言都很混乱? 2) 对。但这“仅”使代码难以调试,不会使代码混乱。 3) 这也可以说是关于 C 语言本身(即“if (i = 0) ...”,switch 语句中默认不中断等)。
    • 也许我们对“混乱”这个词赋予了不同的含义,但是如果缺乏秩序和一致性,我认为有些混乱,并且很难获得概述和修改。宏可能会对此做出很好的贡献,但它不会自动这样做(这就是我听到你说的)。使用宏将引入额外的间接级别,这意味着增加了复杂性,但假设宏本身也会带来一些额外的好处,这些好处超过了增加的复杂性。因此:宏是中性的,还不错。
    【解决方案3】:

    因为typedef 中的A 可以是多个符号,例如typedef int Integer, *PInteger;。 这与变量的定义方式一致 (int var, *pvar;)。

    【讨论】:

      【解决方案4】:

      从语言语法的角度来看,Typedef 与externstatic(*) 一起属于storage class specifier 组,因此typedef 与它们的位置相同。它显然不属于这个组,但我想它可能是它最不放错的地方。

      (*) 存储类还包括autoregister,但没有人再使用它们了。

      【讨论】:

        【解决方案5】:

        我不知道为什么就语言决定而言,但 typedef 的方式对我来说很有意义。

        typedef 说明符是语言的一部分。它用作将类型别名为某个名称的一种方式。你总是可以在变量声明中内联类型是什么。

        struct arr { int len; char *chars; } name;
        struct arr another_name;
        

        使用typedef 反映了这种用法,但不是为类型声明变量,而是为类型声明一个名称。

        typedef struct { int len; char *chars; } arr;
        arr name;
        arr another_name;
        

        #define 指令是预处理器的一部分,而不是语言的一部分,因此它不受语言表示某些结构的方式的约束,可以使用更自然的方式来声明它。

        【讨论】:

          【解决方案6】:

          因为预处理器和编译器实际上是两个不同的程序,每个程序都有自己的语法。可以毫不费力地将预处理器与其他语言结合起来(实际上我在较早的时候通过在 dBase III 程序和 AutoLISP 上使用 cpp 来做到这一点,因为这些语言缺乏良好的常量包含机制)。 正如其他人已经指出的那样,typedef 遵循 C 声明系统的语法,#define 是一个简单直接的替换声明。

          【讨论】:

            【解决方案7】:

            是的,typedef 语法也会让我有些失望。我认为您的问题更像是一种抱怨 - C 已经快 40 年了,您不希望 typedef 语法发生变化,对吗?

            【讨论】:

              猜你喜欢
              • 2018-08-14
              • 2010-12-18
              • 2017-04-23
              • 2010-12-12
              • 2012-01-10
              • 2015-03-14
              • 1970-01-01
              • 2013-02-10
              相关资源
              最近更新 更多