行业标准中禁止#定义?
我是计算机系的第一年,我的教授说#define
在#if
, #ifdef
, #else
和其他一些预处理指令中被禁止在行业标准中使用。 他因为意外的行为而使用“禁止”这个词。
这是准确的吗? 如果是这样的话
事实上,是否有禁止使用这些指令的标准?
首先我听说过。
没有; #define
等被广泛使用。 有时使用过于广泛,但绝对使用。 C标准要求使用macros的地方 – 你不能轻易避免。 例如,§7.5 错误<errno.h>
说:
macros是
EDOM EILSEQ ERANGE
它们扩展为
int
types的整型常量expression式,不同的正值,并且适用于#if
预处理指令; …
鉴于此,显然不是所有的行业标准都禁止使用C预处理macros指令。 然而,各种组织的“最佳实践”或“编码准则”标准规定了对C预处理器使用的限制,尽pipe没有完全禁止它的使用 – 这是C的先天部分,不能完全避免。 通常,这些标准适用于在安全关键领域工作的人员。
一个标准可以检查MISRA C (2012)标准; 这往往会禁止事物,但即使这样也认识到有时需要#define
等(第8.20节,规则20.1到20.14涵盖了C预处理器)。
美国宇航局GSFC(戈达德太空飞行中心) C编码标准简单地说:
macros只能在必要时使用。 过度使用macros会使代码难于阅读和维护,因为代码不再像标准C那样读取或行为。
介绍性声明之后的讨论说明了函数macros的可接受使用。
CERT C编码标准有许多使用预处理器的指导原则,意味着你应该尽量减less预处理器的使用,但是不要禁止使用。
Stroustrup希望在C ++中使预处理器无关紧要,但是这还没有发生。 正如Peter所 指出的那样 ,一些C ++标准,如2005年左右的JSF AV C ++编码标准 ( Joint Strike Fighter,Air Vehicle ),决定了C预处理器的最小化使用。 本质上,JSF AV C ++规则将其限制为#include
和#ifndef XYZ_H
/ #define XYZ_H
/ … / #endif
舞蹈,以避免单个标题的多个包含。 C ++有一些在C语言中不可用的选项,特别是对types常量的更好的支持,然后可以在C不允许使用它们的地方使用。 另请参阅static const
与#define
vs enum
讨论那里的问题。
尽量减less预处理器的使用是一个好主意 – 它至less经常被滥用(参见Boost 预处理器 “库”,用于说明C预处理器可以走多远)。
概要
预处理器是C的组成部分, #if
#define
和#if
等不能完全避免。 教授在这个问题上的陈述并不普遍有效: #define
在#if
, #ifdef
, #else
等行业标准中是被禁止的,其他一些macros是最好的说法,但也许可以支持明确提及具体的行业标准(但有关标准不包括ISO / IEC 9899:2011 – C标准)。
请注意, David Hammen 提供了关于一个特定的C编码标准( JPL C编码标准 )的信息,它禁止很多人在C中使用的许多事情,包括限制C预处理器的使用(并限制dynamic内存的使用分配和禁止recursion – 阅读它看看为什么,并决定这些原因是否与你有关)。
不,不禁止使用macros。
事实上,在头文件中使用#include
警卫是一种常见的技术,通常是强制性的,并被公认的编码指南所鼓励。 有些人声称#pragma once
是一种替代scheme,但是问题在于#pragma once
– 根据定义,因为编译指针是标准为编译器指定的扩展提供的钩子 – 是非标准的,即使它被支持由一些编译器。
也就是说,有许多行业指南和鼓励的做法,由于macros引入的问题(不尊重范围等),积极阻止除#include
警卫以外的所有macros的使用。 在C ++开发中,macros的使用比C开发中更加强烈。
不鼓励使用某些东西与禁止使用东西是不一样的,因为仍然有可能合法地使用它 – 例如,通过logging正当性。
一些编码标准可能会阻止甚至禁止使用#define
来创build类似函数的macros ,这些macros需要参数
#define SQR(x) ((x)*(x))
因为a)这样的macros不是types安全的,并且b)有人会不可避免地写SQR(x++)
,这是不好的。
有些标准可能会阻止或禁止#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 ,或者你正在使用的任何构build工具)处理编译和链接正确的版本。 对于这个例子来说,这将是荒谬的矫枉过正,但我已经看到代码是一个不可追踪的#ifdef
鼠巢, 应该有条件代码分解成单独的文件。
但是,我并不知道有任何公司或行业组织已经彻底禁止使用预处理器语句。
macros不能被“禁止”。 这个说法是无稽之谈。 从字面上看。
例如, C标准的 7.5节错误<errno.h>
需要使用macros:
1头
<errno.h>
定义了几个macros,都与错误条件的报告有关。2macros是
EDOM EILSEQ ERANGE
它们扩展为
int
types的整型常量expression式,不同的正值,并且适用于#if
预处理指令; 和errno
它扩展为一个可修改的左值,该值具有
int
types和线程局部存储持续时间,其值由多个库函数设置为正的错误数。 如果为了访问实际对象而抑制macros定义,或者程序定义了名称为errno
的标识符,则行为是不确定的。
所以,不仅macros是C的必需部分,在某些情况下不使用它们会导致未定义的行为。
不,# #define
不被禁止。 然而,滥用#定义可能会被忽视。
例如,你可以使用
#define DEBUG
在您的代码中,以便稍后可以使用#ifdef DEBUG
指定代码的一部分进行条件编译,仅用于debugging目的。 我不认为任何一个正确的人都会想要禁止这样的事情。 使用#define
定义的macros也被广泛用于可移植的程序,以启用/禁用编译特定于平台的代码。
但是,如果你正在使用类似的东西
#define PI 3.141592653589793
你的老师可能会正确地指出,把PI
作为一个常数来声明是合适的,比如,
const double PI = 3.141592653589793;
因为它允许编译器在使用PI
时进行types检查。
类似的(正如上面的John Bode所提到的),类似函数的macros的使用可能会被拒绝,特别是在可以使用模板的C ++中。 所以,而不是
#define SQ(X) ((X)*(X))
考虑使用
double SQ(double X) { return X * X; }
或者,在C ++中,更好的是,
template <typename T>T SQ(TX) { return X * X; }
再一次,这个想法是,通过使用语言的设施而不是预处理器,您可以让编译器input检查,也可以(可能)生成更好的代码。
一旦你有足够的编码经验,你会知道什么时候适合使用#define
。 在那之前,我认为你的老师强加一定的规则和编码标准是一个好主意,但最好是他们自己应该知道并能够解释原因。 全面禁止#define
是无稽之谈。
这是完全错误的,macros在C中被大量使用。初学者经常使用它们,但是这不是禁止它们进入工业的原因。 一个经典的错误用法是#define succesor(n) n + 1
。 如果你期望2 * successor(9)
给20,那么你错了,因为该expression式将被翻译为2 * 9 + 1
即19不是20.使用括号来获得预期的结果。
不,这不是被禁止的。 而且要说的是,如果没有它,就不可能做非平凡的多平台代码。
不,你的教授是错的,或者你误解了一些东西。
#define
是一个预处理器macros,并且需要预处理macros来进行条件编译和一些约定,这些约定不是简单地用C语言编译的。 例如,在最近的C标准,即C99中,增加了对布尔值的支持。 但是这个语言不支持“native”,而是由预处理器#define
。 请参阅对stdbool.h的引用
macros在GNU C语言中使用相当繁重,没有有条件的预处理器命令,就没有办法正确处理同一个源文件的多个包含,所以这使得它们看起来像是我的基本语言特性。
也许你的class级实际上是在C ++上,尽pipe很多人没有这样做,但应该把它与C区别开来,因为它是一种不同的语言,我不能在那里用macros来说话。 或者教授也许意味着他在class上禁止他们。 无论如何,我确信SO社区会有兴趣听到他正在谈论的标准,因为我很确定所有的C标准都支持使用macros。
与迄今为止的所有答案相反,在高可靠性计算中经常禁止使用预处理器指令。 这有两个例外,这些组织使用这些例外。 这些是#include
指令,并且在头文件中使用了include guard。 这些禁令更可能在C ++而不是C中。
这仅仅是一个例子: 16.1.1只使用预处理器来实现包括守卫,并且包括带有守卫的头文件 。
另一个例子,这次是C而不是C ++:C语言的JPL机构编码标准 。 这个C编码标准并没有完全禁止预处理器的使用,而是相当接近。 具体来说,它说
规则20(预处理器使用)C预处理器的使用应限于文件包含和简单的macros。 [十条例八的权力]。
我既不宽容也不谴责这些标准。 但说他们不存在是可笑的。
如果你想让你的C代码与C ++代码进行交互操作,你需要在extern "C"
命名空间中声明外部可见的符号,比如函数声明。 这通常是使用条件编译完成的:
#ifdef __cplusplus extern "C" { #endif /* C header file body */ #ifdef __cplusplus } #endif
看任何头文件,你会看到这样的东西:
#ifndef _FILE_NAME_H #define _FILE_NAME_H //Exported functions, strucs, define, ect. go here #endif /*_FILE_NAME_H */
这些定义不仅是允许的,而且本质上是关键的,因为每次在文件中引用头文件时,它们将被单独包括在内。 这意味着如果没有定义,您将重新定义多次守卫之间最好的情况下不能编译的所有内容,更糟糕的情况是您后来为什么代码不能按照您希望的方式工作。
编译器也会使用gcc这样的定义,让你testing编译器的版本是非常有用的。 我目前正在研究一个需要用avr-gcc进行编译的项目,但是我们有一个testing环境,我们也运行了我们的代码。 为了防止avr特定的文件和寄存器保持我们的testing代码运行,我们做了这样的事情:
#ifdef __AVR__ //avr specific code here #endif
在生产代码中使用这个代码,补充的testing代码可以在不使用avr-gcc的情况下进行编译,上面的代码只用avr-gcc编译。
如果你刚刚提到了#define
,我想也许他暗示了枚举的用法,用enum
来避免愚蠢的错误,例如分配两次相同的数值。
请注意,即使在这种情况下,使用#define
比枚举有时更好,例如,如果您依赖与其他系统交换的数值,并且实际值必须保持不变,即使您添加/删除常量(为了兼容性) 。
但是,加上#if
, #ifdef
等不应该使用,只是奇怪。 当然,他们可能不应该被滥用,但在现实生活中有几十个理由使用它们。
他的意思可能是(在适当的情况下),你不应该在源代码中对行为进行硬编码(这将需要重新编译以获得不同的行为),而是使用某种forms的运行时configuration。
这是我能想到的唯一解释是合理的。