我是一年级计算机专业的学生,我的教授说#define
的行业标准,以及被禁止#if
,#ifdef
,#else
,和其他一些预处理指令.由于出乎意料的行为,他使用了"禁止"一词.
这准确吗?如果是这样的话?
事实上,是否存在禁止使用这些指令的任何标准?
首先我听说过它.
没有; #define
等等被广泛使用.有时使用过于广泛,但绝对使用.有些地方C标准要求使用宏 - 你无法轻易避免这些.例如,§7.5 错误
说:
宏是
EDOM EILSEQ ERANGE它扩展为具有类型
int
,不同正值的整数常量表达式,并且适用于#if
预处理指令; ...
鉴于此,很明显并非所有行业标准都禁止使用C预处理器宏指令.但是,各种组织都有"最佳实践"或"编码指南"标准,规定了对C预处理器使用的限制,尽管没有完全禁止使用它 - 它是C的固有部分,无法完全避免.通常,这些标准适用于在安全关键领域工作的人员.
您可以检查MISRA C(2012)标准的一个标准; 这往往会取代某些东西,但即便认识到#define
有时需要等等(第8.20节,规则20.1至20.14涵盖了C预处理器).
美国宇航局GSFC(戈达德太空飞行中心)C编码标准简单地说:
只应在必要时使用宏.过度使用宏会使代码更难以读取和维护,因为代码不再读取或行为类似于标准C.
介绍性声明之后的讨论说明了函数宏的可接受用法.
该CERT C编码标准有很多关于使用预处理程序的指导方针,并暗示你应该尽量少用预处理程序,但并没有禁止其使用.
Stroustrup希望使预处理器在C++中无关紧要,但这还没有发生.正如彼得 指出的那样,一些C++标准,例如2005年左右的JSF AV C++编码标准(联合攻击战斗机,飞行器),决定了C预处理器的最小使用.从本质上讲,JSF AV C++规则将其限制为#include
和#ifndef XYZ_H
/ #define XYZ_H
/ .../#endif
dance,以防止单个标头的多个包含.C++有一些在C中不可用的选项 - 特别是对类型常量的更好支持,然后可以在C不允许使用它们的地方使用它们.另见static const
vs #define
vsenum
对那里问题的讨论.
最好尽量减少预处理器的使用 - 它经常被滥用至少与使用它一样多(参见Boost 预处理器 '库'来说明你可以用C预处理器走多远).
预处理器是C和的一个组成部分#define
和#if
等不能完全避免.在这个问题的教授的说法是不普遍有效的:#define
在行业标准一起被取缔#if
,#ifdef
,#else
,和一些其它的宏是最好过的语句,但可能受到支持并明确提及具体的行业标准(但有关标准不包括ISO/IEC 9899:2011 - C标准.
需要注意的是大卫Hammen已提供的信息,约一个特定的C编码标准-在JPLÇ编码标准 -禁止很多事情,很多人用C使用,其中包括限制使用的C预处理(和限制使用的动态内存分配,并禁止递归 - 阅读它以查明原因,并确定这些原因是否与您相关).
不,禁止使用宏.
事实上,#include
在头文件中使用保护是一种常见的技术,通常是强制性的,并且受到公认的编码指南的鼓励.有些人声称可以#pragma once
替代它,但问题在于#pragma once
- 根据定义,因为编译指示是编译器特定扩展的标准提供的钩子 - 是非标准的,即使它受到许多编译器的支持.
也就是说,#include
由于宏引入的问题(不考虑范围等),有许多行业指南和鼓励的做法主动阻止除了守卫之外的所有宏的使用.在C++开发中,宏的使用比C开发更强烈.
不鼓励使用某些东西与禁止使用它是不同的,因为它仍然可以合法地使用它 - 例如,通过记录理由.
某些编码标准可能会阻止甚至禁止使用#define
创建带有参数的类似函数的宏,例如
#define SQR(x) ((x)*(x))
因为a)这样的宏不是类型安全的,而b)有人会不可避免地写SQR(x++)
,这是不好的juju.
某些标准可能会阻止或禁止使用#ifdef
s进行条件编译.例如,以下代码使用条件编译来正确打印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
s 窝应该将条件代码分解成单独的文件.
但是,我不知道任何公司或行业组织已经完全禁止使用预处理器语句.
宏不能被"禁止".声明是无稽之谈.从字面上看.
例如,部分7.5错误
的的C标准 需要使用宏:
1标题
定义了几个宏,所有宏都与错误条件的报告有关.
2宏是
EDOM EILSEQ ERANGE它扩展为具有类型
int
,不同正值的整数常量表达式,并且适用于#if
预处理指令; 和errno它扩展为具有类型
int
和线程本地存储持续时间的可修改左值,其值由多个库函数设置为正误差数.如果为了访问实际对象而禁止宏定义,或者程序使用名称定义标识符errno
,则行为是未定义的.
因此,不仅宏是C 的必需部分,在某些情况下不使用它们会导致未定义的行为.
不,#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
再一次,我们的想法是通过使用语言的设施而不是预处理器,允许编译器键入check并且(可能)生成更好的代码.
一旦您有足够的编码经验,您就会确切知道何时适合使用#define
.在那之前,我认为你的老师强加某些规则和编码标准是一个好主意,但最好是他们自己应该知道并能够解释原因.全面禁止#define
是荒谬的.
这是完全错误的,宏在C中被大量使用.初学者经常使用它们,但这并不是禁止它们进入行业的理由.一个经典的坏用法是#define succesor(n) n + 1
.如果你期望2 * successor(9)
得到20,那么你错了,因为那个表达式将被翻译为2 * 9 + 1
19而不是20.使用括号来获得预期的结果.
不,它没有被禁止.事实上,如果没有它,就不可能完成非平凡的多平台代码.
没有你的教授错了或你听错了什么.
#define
是一个预处理器宏,条件编译和一些约定需要预处理器宏,这些约束不是简单地用C语言构建的.例如,在最近的C标准,即C99中,增加了对布尔的支持.但它不受语言支持"原生",而是由预处理器支持#define
.请参阅stdbool.h的参考
宏在GNU land C中使用相当多,并且没有条件预处理器命令就无法正确处理相同源文件的多个包含,因此这对我来说似乎是必不可少的语言特性.
也许你的课实际上是在C++上,尽管许多人都没有这样做,但是应该区别于C,因为它是一种不同的语言,我不能在那里代表宏.或许教授意味着他在班上禁止他们.无论如何,我确信SO社区会对他正在谈论的标准感兴趣,因为我非常确定所有C标准都支持使用宏.
与迄今为止的所有答案相反,在高可靠性计算中经常禁止使用预处理器指令.这有两个例外,这些组织的使用是强制性的.这些是#include
指令,以及在头文件中使用包含保护.这种禁令更可能在C++中而不是在C中.
这只是一个例子:16.1.1仅使用预处理器来实现包含保护,并包括包含保护的头文件.
另一个例子,这次是针对C而不是C++:C编程语言的JPL机构编码标准.这种C编码标准并没有完全禁止使用预处理器,但它接近了.具体来说,它说
规则20(预处理器使用)C预处理器的使用应限于文件包含和简单宏.[十条规则8的权力].
我既不宽恕也不谴责这些标准.但是说它们不存在是荒谬的.