代码之家  ›  专栏  ›  技术社区  ›  Andrew Garrison

为什么C中没有宏?

  •  48
  • Andrew Garrison  · 技术社区  · 16 年前

    当我第一次学习C语言时,我惊讶地发现,他们对C/C++中相同的宏没有支持。我意识到,在我的C语言中存在着定义关键字,但与我在C/C++中所喜欢的相比,它是非常缺乏的。有人知道为什么吗 真实的 C中缺少宏。

    如果这个问题已经以某种形式被问到了,我很抱歉-我保证在发帖前花了整整5分钟的时间寻找副本。

    11 回复  |  直到 9 年前
        1
  •  67
  •   DouglasH    16 年前

    来自C常见问题解答。

    http://blogs.msdn.com/CSharpFAQ/archive/2004/03/09/86979.aspx

    为什么不支持定义宏? 在C++中,我可以定义一个宏,例如:

    #define PRODUCT(x, y, z) x * y * z

    然后在代码中使用它:

    int a = PRODUCT(3, 2, 1);

    C不允许你这样做。为什么?

    原因有几个。第一个是可读性。

    我们的主要设计目标之一是保持代码的可读性。拥有编写宏的能力使程序员能够创建自己的语言——这种语言不一定与下面的代码有任何关系。要理解代码的作用,用户不仅必须理解语言的工作方式,而且还必须理解当时有效的所有定义宏。这使得代码更难阅读。

    在C中,您可以使用方法而不是宏,并且在大多数情况下,JIT将内联这些方法,从而为您提供相同的性能方面。

    还有一个更微妙的问题。宏是以文本方式完成的,这意味着如果我写:

    int y = PRODUCT (1 + 2, 3 + 4, 5 + 6)

    我希望能得到一些能给我 3 * 7 *11 = 231 但事实上,我定义的扩展给出了:

    int y = 1 + 2 * 3 + 4 * 5 + 6;

    给了我33。我可以通过巧妙地应用括号来解决这个问题,但是编写一个在某些情况下工作而在其他情况下不工作的宏是非常容易的。

    虽然严格来说C没有预处理器,但它确实有条件编译符号,可用于影响编译。这些可以在代码中定义,也可以用编译器的参数定义。C语言中的“预处理”指令(尽管没有单独的预处理步骤,但仅与C/C++的一致性)是(从ECMA规范中获取的文本):

    #define and #undef 用于定义和取消定义条件编译符号

    #if, #elif, #else and #endif

    用于有条件地跳过源代码的部分

    #line 用于控制为错误和警告发出的行号。

    #error and #warning 用于发出错误和警告。

    #region and #endregion

    用于显式标记源代码的部分。

    有关上述内容的更多信息,请参见ECMA规范第9.5节。条件编译也可以使用方法的Conditional属性来实现,因此只有在定义了适当的符号时,才会编译对方法的调用。有关更多信息,请参见ECMA规范第24.4.2节。

    作者:Eric Gunnerson

        2
  •  35
  •   bencooley    16 年前

    这样你就可以一次又一次地在上面打字。

    // Windows presetation foundation dependency property.
    public class MyStateControl : ButtonBase
    {
      public MyStateControl() : base() { }
      public Boolean State
      {
        get { return (Boolean)this.GetValue(StateProperty); }
        set { this.SetValue(StateProperty, value); } 
      }
      public static readonly DependencyProperty StateProperty = DependencyProperty.Register(
        "State", typeof(Boolean), typeof(MyStateControl),new PropertyMetadata(false));
    }
    

    显然,C和.NET的设计者从未真正使用他们创建的任何库或框架。如果他们这样做,他们就会意识到,某种形式的卫生句法宏观体系是绝对有序的。

    不要让C和C++的跛脚宏的缺点使你对编译时间解析代码的能力产生影响。编译时解析和代码生成允许您更有效地表达代码的含义和意图,而无需拼写出源代码的所有琐碎细节。例如,如果您可以将上面的内容替换为:

    public class MyStateControl : ButtonBase
    {
      public MyStateControl() : base() { }
    
      [DependencyProperty(DefaultValue=true)] 
      bool State { get; set; }
    }
    

    嘘有他们,OcamML(至少Meta ML)有他们,C和C++有他们(以一种恶劣的形式,但总比没有他们好)。C没有。

        3
  •  27
  •   Jon Skeet    16 年前

    在我的经验中,C++风格的宏增加了大量的复杂性,没有相应的好处。我当然没有在C或Java中漏掉它们。(在C语言中,我很少使用预处理器符号,但偶尔我很高兴它们在那里。)

    现在很多人都要求 Lisp-style macros 这一点我几乎不知道,但肯定比C++风格更悦耳。

    你特别想做什么 用宏?我们也许能帮助你用更习惯的方式思考…

        4
  •  9
  •   David Jeske    9 年前

    C是针对更广泛的受众(或在其他术语,消费者基础)比C +,C或ASM。实现这一目标的唯一方法是让程序员获得相当不熟练的技能。因此,所有强大但危险的工具都被拿走了。即宏、多重继承、对象生存期控制或类型无关编程。

    同样地,火柴、刀子和钉枪是有用和必要的,但它们必须放在儿童够不到的地方。(可悲的是,纵火、谋杀、内存泄漏和无法读取的代码仍然会发生)。

    在指责我不思考“C”之前,你写过多少次:

    protected int _PropOne;
    public int PropOne
    {
        get
        {
            return _PropOne;
        }
        set
        {
            if(value == _PropOne) { return; }
            NotifyPropertyChanging("PropOne");
            _PropOne = value;
            NotifyPropertyChanged("PropOne");
        }
    }
    

    对于宏,每次这16行看起来都是这样:

    DECLARE_PROPERTY(int, PropOne)
    DECLARE_PROPERTY(string, PropTwo)
    DECLARE_PROPERTY(BitmapImage, PropThree)
    
        5
  •  4
  •   Seth    16 年前

    使用C/C++中的宏定义常量,生成小的内联函数,以及直接与编译代码有关的各种事物(α-IFDEF)。

    在C中,您有强类型常量,它是一个足够智能的编译器,可以在必要时内联函数,并且知道如何以正确的方式编译内容(没有预编译头的废话)。

    但是如果你真的想的话,没有特别的理由不能首先通过C预处理器运行你的CS文件。

        6
  •  2
  •   Drew Noakes    13 年前

    作为一个长期从事C++学习的程序员,我现在错过了对元编程C语言的丰富支持。至少,我现在对元编程的含义有了更广泛的理解。

    我真的很想看到那种注入的宏观支持 Nemerle 在C中。它似乎为语言添加了非常自然和强大的扩展能力。如果你还没看过,我真的建议你这样做。

    有一些很好的例子 Wikipedia .

        7
  •  1
  •   Community Mohan Dere    9 年前

    宏在中使用过度 C++ 但他们仍然有 their uses 但是,这些用途中的大多数与 C# 由于反射和更好地集成使用异常进行错误报告。

        8
  •  1
  •   Matthew Lock fge    16 年前

    本文比较了Perl和Lisp宏,但这一点仍然是相同的:文本级宏(PerL/C++)与源级宏(LISP)相比引起了大量的问题。

    http://lists.warhead.org.uk/pipermail/iwe/2005-July/000130.html

    比我更勇敢的人在C语言中使用了他们自己的宏系统。# http://www.codeproject.com/KB/recipes/prepro.aspx

        9
  •  0
  •   Imagist    16 年前

    宏是当今大多数程序员比编译器更聪明的工具。在C/C++中,仍然有一些情况是这样的。

    现在,大多数程序员都不如C编译器/运行时聪明。

        10
  •  0
  •   Kevin Williams    10 年前

    任何同意宏是坏的想法的人都应该阅读这本书,“双手合十”。 http://en.wikipedia.org/wiki/With_Folded_Hands 它讲述了一个故事,讲的是我们如何阻止人们做愚蠢的事情,一直到阻止他们做非常明智的事情。

    虽然我喜欢C,但我真的很讨厌它助长了实际软件工程师的愚蠢。所以,是的,把宏留给专业人员。当我们这样做的时候,把变量的命名也留给专业人员。这会导致一些真正无法读取的代码。要遵循“代码必须最终可读”的完整语句,所有变量都应命名为a-z,后跟a-z(或其他任意结构,如仅名词)。因为一些不熟练的人可能会将他们的变量命名为“someThingsEFult,但由于某些用户使用了模块化的东西,所以文件不允许这样做”。

        11
  •  -1
  •   Vidya Sagar plebksig    10 年前

    您可以对宏(如propertychanged)执行某些操作,方法如下 this

    如果这比宏更好? 这是一个你必须决定的问题:)