我是第一年的计算机科学专业的学生,我的教授说#define
在行业标准中被禁止,#if
,#ifdef
,#else
以及其他一些预处理程序指令。由于出乎意料的行为,他使用了“禁止”一词。
这准确吗?如果是这样的话?
事实上,是否存在禁止使用这些指令的任何标准?
答案 0 :(得分:140)
首先我听说过它。
没有; #define
等被广泛使用。有时使用过于广泛,但绝对使用。有些地方C标准要求使用宏 - 你无法轻易避免这些。例如,§7.5错误<errno.h>
表示:
宏是
EDOM EILSEQ ERANGE
扩展为类型为
int
的整数常量表达式,不同的正值,适用于#if
预处理指令; ...
鉴于此,很明显并非所有行业标准都禁止使用C预处理器宏指令。但是,各种组织都有“最佳实践”或“编码指南”标准规定了对C预处理器使用的限制,尽管没有完全禁止使用它 - 它是C的固有部分,无法完全避免。通常,这些标准适用于在安全关键领域工作的人员。
您可以检查MISRA C(2012)标准的一个标准;这往往会取代某些东西,但即使这样也承认有时需要#define
等(第8.20节,规则20.1到20.14涵盖了C预处理器)。
NASA GSFC(戈达德太空飞行中心)C Coding Standards简单地说:
只应在必要时使用宏。过度使用宏会使代码更难以读取和维护,因为代码不再读取或行为类似于标准C.
介绍性陈述之后的讨论说明了函数宏的可接受用途。
CERT C Coding Standard有许多关于预处理器使用的指导原则,并暗示您应尽量减少预处理器的使用,但不禁止使用它。
Stroustrup希望使预处理器在C ++中无关紧要,但这还没有发生。作为Peter notes,一些C ++标准,例如2005年左右的JSF AV C++ Coding Standards(联合攻击战斗机,飞行器),规定C预处理器的使用最少。从本质上讲,JSF AV C ++规则将其限制为#include
和#ifndef XYZ_H
/ #define XYZ_H
/ ... / #endif
舞蹈,以防止单个标头的多个包含。 C ++有一些在C中不可用的选项 - 特别是对类型常量的更好支持,然后可以在C不允许使用它们的地方使用它们。另请参阅static const
vs #define
vs enum
以了解有关问题的讨论。
最好尽量减少预处理器的使用 - 它经常被滥用至少与使用它一样多(请参阅Boost preprocessor'库'来说明你有多远可以使用C预处理器。)
预处理器是C的组成部分,#define
和#if
等不能完全避免。教授在该问题中的陈述通常无效: #define
在行业标准中被禁止与#if
,#ifdef
,#else
以及一些其他宏充其量只是一种过度陈述,但可以通过明确引用特定行业标准来支持(但所涉及的标准不包括ISO / IEC 9899:2011 - C标准)。
请注意,David Hammen有一个provided information关于一个特定的C编码标准 - JPL C Coding Standard - 禁止许多人在C中使用的很多东西,包括限制使用C预处理器(并限制使用动态内存分配,并禁止递归 - 读取它以查明原因,并确定这些原因是否与您相关)。
答案 1 :(得分:33)
不,禁止使用宏。
事实上,在头文件中使用#include
警卫是一种常见的技术,通常是强制性的,并受到公认编码指南的鼓励。有些人声称#pragma once
是另一种选择,但问题是#pragma once
- 根据定义,因为编译指示是编译器特定扩展的标准提供的钩子 - 是非标准的,甚至是如果它得到了许多编译器的支持。
尽管如此,由于宏引入的问题(不考虑范围等),有许多行业指南和鼓励的做法主动阻止除#include
警卫之外的所有宏的使用。在C ++开发中,宏的使用比在C开发中更加强烈。
不鼓励使用某些东西与禁止某些东西不同,因为它仍然可以合法地使用它 - 例如,通过记录理由。
答案 2 :(得分:30)
某些编码标准可能会阻止甚至禁止使用#define
来创建带有参数的类似函数的宏,例如
#define SQR(x) ((x)*(x))
因为a)这样的宏不是类型安全的,并且b)某人不可避免地写SQR(x++)
,这是不好的juju。
某些标准可能会阻止或禁止使用#ifdef
进行条件编译。例如,以下代码使用条件编译来正确打印size_t
值。对于C99及更高版本,您使用%zu
转换说明符;对于C89及更早版本,您使用%lu
并将值转换为unsigned long
:
#if __STDC_VERSION__ >= 199901L
# define SIZE_T_CAST
# define SIZE_T_FMT "%zu"
#else
# define SIZE_T_CAST (unsigned long)
# define SIZE_T_FMT "%lu"
#endif
...
printf( "sizeof foo = " SIZE_T_FMT "\n", SIZE_T_CAST sizeof foo );
某些标准可能会强制执行此操作,而不是执行此操作,而是为C89及更早版本执行一次模块,对C99及更高版本执行一次:
/* C89 version */
printf( "sizeof foo = %lu\n", (unsigned long) sizeof foo );
/* C99 version */
printf( "sizeof foo = %zu\n", sizeof foo );
然后让Make(或Ant,或者您正在使用的任何构建工具)处理编译和链接正确的版本。对于这个荒谬过度的例子,我已经看到代码是#ifdef
的一个无法追踪的老鼠的巢,应该将条件代码考虑在内分成文件。
但是,我不知道有任何公司或行业组织完全禁止使用预处理程序语句。
答案 3 :(得分:17)
宏不能被禁止&#34;。声明是无稽之谈。字面上。
例如,C Standard 的 7.5错误<errno.h>
部分要求使用宏:
1标题
<errno.h>
定义了几个宏,所有宏都与报告错误条件有关。2宏是
EDOM EILSEQ ERANGE
扩展为类型为
int
的不同的整数常量表达式 正值,适用于#if
预处理 指令;和errno
扩展为具有类型
int
和线程的可修改左值 本地存储持续时间,其值设置为正误差 由几个库函数编号。如果是宏定义 抑制为了访问实际对象,或程序定义 名称为errno
的标识符,行为未定义。
因此,不仅宏是必需的 C的一部分,在某些情况下不使用它们会导致未定义的行为。
答案 4 :(得分:14)
不,#define
未被禁止。然而,滥用#define
可能会令人不悦。
例如,您可以使用
#define DEBUG
在您的代码中,以便稍后,您可以使用#ifdef DEBUG
为条件编译指定部分代码,仅用于调试目的。我不认为心智正常的人会想要禁止这样的事情。使用#define
定义的宏也广泛用于可移植程序,以启用/禁用特定于平台的代码的编译。
但是,如果您使用的是
#define PI 3.141592653589793
您的老师可能正确地指出,将PI
声明为具有适当类型的常量会更好,例如,
const double PI = 3.141592653589793;
因为它允许编译器在使用PI
时进行类型检查。
类似地(如上面的John Bode所述),类似函数的宏的使用可能会被拒绝,尤其是在可以使用模板的C ++中。而不是
#define SQ(X) ((X)*(X))
考虑使用
double SQ(double X) { return X * X; }
或者,在C ++中,更好的是,
template <typename T>T SQ(T X) { return X * X; }
再一次,我们的想法是通过使用语言的工具而不是预处理器,允许编译器键入check并且(可能)生成更好的代码。
一旦您有足够的编码经验,您就会确切地知道何时使用#define
。在那之前,我认为你的老师强加某些规则和编码标准是一个好主意,但最好是他们自己应该知道并能够解释原因。全面禁止#define
是荒谬的。
答案 5 :(得分:12)
这是完全错误的,宏在C中被大量使用。初学者经常使用它们很糟糕,但这并不是禁止它们进入行业的理由。经典的不良用法是#define succesor(n) n + 1
。如果您希望2 * successor(9)
给出20,那么您错了,因为该表达式将被翻译为2 * 9 + 1
,即19不是20.使用括号来获得预期结果。
答案 6 :(得分:11)
没有。它没有被禁止。说实话,没有它就不可能做非平凡的多平台代码。
答案 7 :(得分:8)
没有你的教授错了或你听错了。
#define
是一个预处理器宏,条件编译需要预处理器宏,还有一些约定,它们不是简单地用C语言构建的。例如,在最近的C标准,即C99中,增加了对布尔的支持。但它不受支持&#34; native&#34;通过语言,但由预处理器#define
s。见this reference to stdbool.h
答案 8 :(得分:6)
在GNU land C中使用宏非常多,没有条件预处理器命令就无法正确处理相同源文件的多个包含,这使得它们看起来像是我必不可少的语言功能。
也许你的课实际上是在C ++上,尽管许多人没有这样做,但是应该区别于C,因为它是一种不同的语言,我不能在那里代表宏。或许教授意味着他在班上禁止他们。无论如何,我确信SO社区会对他正在谈论的标准感兴趣,因为我非常确定所有C标准都支持使用宏。
答案 9 :(得分:5)
与迄今为止的所有答案相反,在高可靠性计算中经常禁止使用预处理程序指令。这有两个例外,这些组织的使用是强制性的。这些是#include
指令,以及在头文件中使用包含保护。这种禁令更可能是在C ++中而不是在C中。
另一个例子,这次是针对C而不是C ++: JPL Institutional Coding Standard for the C Programming Language 。这种C编码标准并没有完全禁止使用预处理器,但它接近了。具体来说,它说
规则20(预处理器使用) 使用C预处理器 仅限于文件包含和简单宏。 [十条规则8的权力]。
我既不宽恕也不谴责这些标准。但是说它们不存在是荒谬的。
答案 10 :(得分:2)
如果希望C代码与C ++代码互操作,则需要在extern "C"
命名空间中声明外部可见的符号,例如函数声明。这通常使用条件编译来完成:
#ifdef __cplusplus
extern "C" {
#endif
/* C header file body */
#ifdef __cplusplus
}
#endif
答案 11 :(得分:1)
查看任何头文件,您将看到如下内容:
#ifndef _FILE_NAME_H
#define _FILE_NAME_H
//Exported functions, strucs, define, ect. go here
#endif /*_FILE_NAME_H */
这些定义不仅是允许的,而且本质上是关键的,因为每次在文件中引用头文件时它都将单独包含。这意味着如果没有定义,你会多次重新定义防护之间的所有内容,最好的情况是无法编译,更糟糕的情况会让你后悔,为什么你的代码不能按照你想要的方式工作。
编译器还将使用define作为看见here with gcc,它允许您测试编译器的版本,这是非常有用的。我目前正在开发一个需要用avr-gcc编译的项目,但我们有一个测试环境,我们也运行我们的代码。为了防止avr特定文件和寄存器阻止我们的测试代码运行,我们执行以下操作:
#ifdef __AVR__
//avr specific code here
#endif
在生产代码中使用它,补充测试代码可以在不使用avr-gcc的情况下编译,上面的代码只能使用avr-gcc编译。
答案 12 :(得分:1)
如果您刚刚提到#define
,我可能会想到他可能会暗指使用枚举,最好使用enum
来避免愚蠢的错误,例如两次分配相同的数值
请注意,即使对于这种情况,有时最好使用#define
而不是枚举,例如,如果您依赖与其他系统交换的数值,即使添加/,实际值也必须保持不变删除常量(兼容性)。
但是,添加#if
,#ifdef
等不应该使用也只是奇怪。当然,它们可能不应该被滥用,但在现实生活中有很多理由使用它们。
他的意思可能是(在适当的情况下),您不应该在源代码中硬编码行为(这需要重新编译以获得不同的行为),而是使用某种形式的运行时改为配置。
这是我能想到的唯一有意义的解释。