为什么要打扰预处理程序指令呢?

时间:2009-11-23 20:01:15

标签: c++ c-preprocessor

这个问题可能看起来很基本,但是来自工程(非计算机科学)背景,我不确定'#的片段在某些C ++代码中是什么。

通过快速搜索,我可以看到关于预处理程序指令的简明扼要的cplusplus教程页面。

但是为什么要干扰预处理器指令的概念呢?是否不可能编写可以为常量赋值,定义子程序/函数/宏并处理错误的等效代码?

我想我最终想知道什么时候使用这样的预处理器指令是好的做法,什么时候不是。

13 个答案:

答案 0 :(得分:25)

当您需要执行实际应用程序范围之外的操作时,可以使用预处理程序指令。例如,您将看到完成预处理以包含或不包含基于构建可执行文件的体系结构的代码。例如:

#ifdef _WIN32 // _WIN32 is defined by Windows 32 compilers
#include <windows.h>
#else
#include <unistd.h>
#endif

预处理程序指令也用于保护包含,因此类/函数等不会被定义多次。

#ifndef MY_CLASS_6C1147A1_BE88_46d8_A713_64973C9A2DB7_H_
#define MY_CLASS_6C1147A1_BE88_46d8_A713_64973C9A2DB7_H_
    class MyClass {
    ....
    };
#endif

另一个用途是在代码和库中嵌入版本控制。

在Makefile中你有以下几点:

-D MY_APP_VERSION=4.5.1

在你的代码中

cout << "My application name version: " << MY_APP_VERSION << endl;

答案 1 :(得分:9)

答案1:条件代码必须根据其运行的计算机类型而有所不同。

答案2:启用和禁用编译时看到的语言扩展和兼容性功能。

预处理器来自C,那里有许多你无法表达的东西。好的C ++代码发现使用它的理由比C代码少,但遗憾的是它没有用处。

答案 2 :(得分:7)

因为预处理程序指令在 build 时执行,而您编写的代码将在运行时执行。因此,预处理程序指令有效地使您能够以编程方式修改源代码。

请注意,C预处理器对于这种事情来说是一个相当粗糙的机制; C ++的模板系统为编译时构造代码提供了更强大的框架。其他语言具有更强大的元编程功能(例如,Lisp的宏系统)。

答案 3 :(得分:6)

在编译代码之前进行预处理。它适用于以下情况

#ifdef WIN32
#include <something.h>
#elseif LINUX
#include <somethingelse.h>
#endif

显然包括你想在编译时完成的头文件而不是运行时。我们不能用变量做到这一点。

另一方面。使用C ++是一种很好的做法,并且非常鼓励使用以下示例替换常量表达式

#define PI 3.141592654
with
const double PI=3.141592654;

原因是您获得了正确的类型转换和数据类型处理。

另外

#define MAX(x,y) (x) > (y) ? (x) : (y)

不是很好,因为你可以写

int i = 5
int max = MAX(i++, 6);

预处理器将替换为:

int max = (i++) > (6) ? (i++) : (6);

显然不会给出预期的结果。

相反,MAX应该是一个函数(不是宏)。如果它是一个函数,它也可以在参数中提供类型。

我已经看到预处理器用于各种有趣的事情。像语言关键字声明。在这种情况下,它有助于提高可读性。

简而言之,将预处理器用于必须在编译类型中发生的事情,例如条件包含指令。避免将其用于常量。避免使用宏,而是尽可能使用函数。

答案 4 :(得分:5)

最常用于两件事情,如果没有它会更难组织:

  1. Include guards
  2. 不同平台的不同代码段。

答案 5 :(得分:2)

通常,不应使用预处理程序指令。可悲的是,有时你必须使用C和C ++。

C最初定义语言的方式是,如果不使用预处理器,你真的无法做任何严肃的事情。该语言没有其他内置支持来创建模块化程序,常量,内联代码或进行泛型编程。

C ++摆脱了大部分问题,但设施仍在那里,所以它仍然被使用。 (有趣的是,不是模块化的。我们仍然坚持#include),

如果要与没有预处理器的类似任务的类似抽象级别的语言进行比较,请查看Ada

答案 6 :(得分:2)

许多编程语言都有元编程工具,您可以在其中编写编译器的代码,而不是运行时环境。

例如,在C ++中,我们有模板允许我们指示编译器根据类型甚至编译时常量生成某些代码。 Lisp可能是具有高级元编程功能的语言中最着名的例子。

C预处理程序指令/宏只是“元编程”的另一种形式,虽然形式比其他语言更为粗糙。预处理程序指令指示编译器在编译时执行某些操作,例如忽略某些平台上的某些代码,或者使用另一个字符串查找和替换代码中的字符串。在您的代码已经编译之后,这在运行时是不可能的。

从本质上讲,C预处理器是“元编程”或编译器编程的早期形式。

答案 7 :(得分:1)

不,在所有情况下,如果没有预处理器,实际上是不可能的。我最喜欢的一个宏是

#define IFDEBUG if(DEBUG==1)
//usage:
IFDEBUG{
  printf("Dump of stuff:.,,,");
}else{
  //..do release stuff
}

如果没有宏,我会在最终的可执行文件中浪费(可能很多)空间

而且你必须意识到,C / C ++没有任何类型的包类型require或其他类似的系统。因此,如果没有预处理器,就无法阻止代码重复。 (不能包含头文件)

答案 8 :(得分:1)

  

但是为什么要干扰预处理器指令的概念呢?是否不可能编写可以为常量赋值,定义子程序/函数/宏并处理错误的等效代码?

它在C ++中的使用非常低,创建了该语言的功能以避免与预处理器相关的问题。

  

我想我最终想知道什么时候使用这样的预处理器指令是好的做法,什么时候不是。

在一般的C ++资源中,它通常被认为是不好的做法 - 特别是当使用其他语言功能有意义时。某些东西(即平台/构建依赖程序和生成程序)需要它。简而言之,通常有一个可以很好地扩展的替代品。 (例如常量定义为枚举,或内联模板而不是宏)。如果您发现自己使用了一个并且您不确定,那么只需询问/搜索是否有更好的方法在没有预处理器的情况下在C ++中声明this_code_snippet

答案 9 :(得分:1)

这里有一点历史:C ++是从C开发的,它需要预处理器比C ++更多。例如,要在C ++中定义常量,您可以编写类似const int foo = 4;的内容,而不是#define FOO 4,这是粗略的C等价物。不幸的是,太多人将他们的预处理器习惯从C转换为C ++。

预处理器在C ++中有几种合理的用法。使用#include作为头文件是非常必要的。它对于条件编译也很有用,包括头文件包含防护,因此可以多次#include一个头文件(例如在不同的头文件中),并且只处理一次。 assert语句实际上是一个预处理器宏,并且有一些类似的用法。

除此之外,C ++中很少有合法用途。

答案 10 :(得分:0)

从C ++中获取一些反射功能是一个比一个更好的替代品。

生成具有相同名称的变量和字符串非常有用。

答案 11 :(得分:0)

已回答here

答案 12 :(得分:0)

C预处理器执行许多任务,其中一些但并非所有任务都有更好的C ++替代方案。 C ++有更好的选择使用它。这些替代方案包括模板,内联和const变量(一个矛盾,但这就是标准所称的)代替#define宏。

然而,有些事情你不想没有或根本离不开;例如#include是必不可少的,当编码多个平台或配置时,条件编译仍然有用(尽管在所有情况下都应谨慎使用)。

在某些情况下,通过#pragma控制的编译器特定扩展可能是不可避免的。