行业标准中禁止#定义?

我是计算机系的第一年,我的教授说#define#if#ifdef#else和其他一些预处理指令中被禁止在行业标准中使用。 他因为意外的行为而使用“禁止”这个词。

这是准确的吗? 如果是这样的话

事实上,是否有禁止使用这些指令的标准?

首先我听说过。

没有; #define等被广泛使用。 有时使用过于广泛,但绝对使用。 C标准要求使用macros的地方 – 你不能轻易避免。 例如,§7.5 错误<errno.h>说:

macros是

  EDOM EILSEQ ERANGE 

它们扩展为inttypes的整型常量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 

它们扩展为inttypes的整型常量expression式,不同的正值,并且适用于#if预处理指令; 和

 errno 

它扩展为一个可修改的左值,该值具有inttypes和线程局部存储持续时间,其值由多个库函数设置为正的错误数。 如果为了访问实际对象而抑制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。

这是我能想到的唯一解释是合理的。