【问题标题】:Is typedef just a string replacement in code or somethings else?typedef 只是代码中的字符串替换还是其他?
【发布时间】:2010-07-16 08:47:00
【问题描述】:

我很想知道 typedef 究竟是如何工作的。

typedef struct example identifier;

identifier x;

在上面的语句中,'标识符'只是在代码中用'结构示例'替换(比如字符串替换)?如果不是,typedef 在这里做什么?

请指教!

【问题讨论】:

标签: c++ c syntax


【解决方案1】:

不,它不是字符串替换 - 那将是宏。它为该类型创建一个别名。

typedefs are preferred over macros 用于自定义类型,部分原因是它们可以正确编码指针类型。

typedef char *String_t;
#define String_d char *
String_t s1, s2;
String_d s3, s4;

s1、s2、s3都被声明为char *,而s4被声明为char,这可能不是本意。

【讨论】:

  • +1 作为答案,尤其是关于字符串替换如何导致其他行为的好例子
  • 好吧,我只是从 c-faq 复制的 :)
  • 感谢@Amar 的简洁解释 :)
  • 编译器如何处理不同的标识符还有其他问题。特别是,类型定义的名称和用户定义的类型保存在不同的标识符空间中。我已将其添加为下面的答案。
【解决方案2】:

typedef 为类型引入了一个同义词。这不是简单的字符串替换,如下所示:

typedef int* int_ptr;
const int* a;    // pointer to const int
const int_ptr b; // const pointer to int

编译器也知道它是一个类型名称,你不能把它放在不允许类型的地方而不会出现编译器错误。

【讨论】:

    【解决方案3】:

    所有人都同意它不是类型替换,并且当指针进入混合时它比它好得多,但也有其他微妙之处。特别是 typedef 的使用会影响代码的解析方式和程序的有效性。

    在 C 和 C++ 中,用户定义的标识符都保存在不同的名称空间中(不是 C++ 意义上的,而是某种标识符空间)。当您使用 typedef 关键字时,您为函数所在的全局名称空间中的类型创建别名。

    // C/C++
    struct test {};
    void test( struct test x ) {} // ok, no collision
    
    // C/C++
    typedef struct test {} test; // test is now both in types and global spaces
    //void test() {}       // compiler error: test is a typedef, cannot be redefined
    

    这里的细微差别是,在 C++ 中,编译器首先会在 全局命名空间 中查找,如果在那里找不到,它也会在类型命名空间中查找:

    // C
    struct test {};
    //void f( test t );      // error, test is not defined in the global namespace
    void f( struct test t ); // ok, we are telling the compiler where to look
    
    // C++
    struct test {};
    void f( test t );        // ok, no test defined in the global name space, 
                             // the compiler looks in the types name space
    void g( struct test t ); // also ok, even if 'struct' not needed here.
    

    但这并不意味着这两个命名空间合并了,只是查找会在这两个地方进行搜索。

    【讨论】:

      【解决方案4】:

      一个重要的区别是typedefs 有范围

      以下是常用的成语

      class Foo: public Bar 
      {
        private:
         typedef Bar inherited;
      
        public:
          Foo(int x) : inherited(x) {};  // preferred to 'Bar(x)' 
      }
      

      您通常会在 .cpp 文件中定义构造函数,并在标题中声明。如果您使用Foo(int x) : Bar(x),如果您更改类层次结构,例如 Foo->Wibble->Bar 而不是 Foo->Bar,那么您很可能会忘记更新构造函数。我个人建议将“继承”的 typedef 添加到每个子类中。

      更多详情请看这里:Using "super" in C++

      【讨论】:

      • @strager:等到你必须从some_template<a, other_template<b>::type, /* ... more? */>继承。
      • @Georg Fritzsche,啊,我见过(并写过),是的。不过,您的示例使它看起来适用于所有情况(尤其是“首选 'Bar(x)'”评论)。
      【解决方案5】:

      Typedef 是为(通常是复杂的)类型创建新名称的快捷方式。它的目的比预处理器的字符串替换更窄。因此,它比预处理器定义(递归解析)更不容易出错。

      【讨论】:

        【解决方案6】:

        使用 typedef 可以创建别名。编译器将别名替换为正确的代码。

        如果你写:

        typedef int company_id;
        company_id mycompany = 100;
        

        编译器得到:

        int mycompany = 100;
        

        【讨论】:

        • 不是字符串替换,与预处理器无关。编译器得到company_id
        • 我没有说它是像#define 这样的预处理器指令。我说那是从一个类型中创建一个别名。
        • 我认为问题在于“编译器获取”这一行。编译器完全按照您编写的方式获取真实代码。它解释它好像原来的类型是在那里使用的——但有一些细微的差别。
        【解决方案7】:

        宏由预处理器完成,纯粹基于文本替换。因此,您可以说他们非常愚蠢。预处理器将接受几乎所有垃圾而无需任何语法检查。

        Typedef 由编译器自己完成,它们通过添加您定义的派生类型来操作编译器自己的类型表。这受制于完整的语法检查,以及专门针对类型的机制。

        您可以认为编译器在执行与声明struct 时类似的“工作”。那里有一个定义,编译器将其转换为类型列表中的一个类型。

        【讨论】:

          【解决方案8】:

          我发现 typedef 使函数签名更易于阅读。假设您要返回一个指向二维数组的指针。这是可读的方式:

          typedef int three_by_three[3][3];
          
          three_by_three* foo();
          

          下面是你如何在没有 typedef 的情况下做到这一点:

          int (*bar())[3][3];
          

          请注意,此签名与应用“字符串替换”的第一个版本完全不同。

          如果 C 声明符语法不是那么难看(Stroustrup:“我认为 C 声明符语法是一个失败的实验”),typedef 可能不会像现在这样被使用。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-01-02
            • 1970-01-01
            相关资源
            最近更新 更多