你遇到的最糟糕的实际宏/预处理滥用是什么?

176

你所遇到的最糟糕的实际宏/预处理器滥用是什么(请不要回答显然的IOCCC*哈哈*)?

如果有一个有趣的小片段或故事,请添加进来。目标是教授一些东西,而不是总是告诉人们“永远不要使用宏”。


p.s .:我以前使用过宏……但通常我最终会摆脱它们,当我有了“真正的”解决方案时(即使真正的解决方案是内联的,因此类似于宏)。


奖励:给出一个示例,其中宏确实比非宏解决方案更好。

相关问题:C++宏何时有利?


+1 为引起人们对我在宏命令手中遭受的普遍虐待的关注。 - i_am_jorf
37
#define true false //开心调试 :) 该行代码的意思是将 true 宏定义为 false,在进行调试时会出现一些奇怪的结果,因此在代码后面加上注释“happy debugging :)”来缓解调试带来的压力。 - n0rd
社区维基意味着没有人会因为对这个问题或其答案的赞/踩而获得(或失去)声望。许多人认为这样的问题是轻松获取声望的廉价方式,因此如果将其标记为社区维基,人们就不太可能因此感到不满并关闭它。 - Graeme Perrow
2
人们很可能会因为一些幽默有趣的内容而感到不悦并将其关闭:您是在暗示您不希望在Stack Overflow上看到任何幽默/有趣的内容吗? - Trevor Boyd Smith
2
只是一个小观点,预处理器是语言的一部分,因此使用它并不邪恶/错误,就像其他任何东西一样。 - Mr. Boy
显示剩余3条评论
70个回答

409

从记忆中,它看起来像这样:

#define RETURN(result) return (result);}

int myfunction1(args) {
    int x = 0;
    // do something
    RETURN(x)

int myfunction2(args) {
    int y = 0;
    // do something
    RETURN(y)

int myfunction3(args) {
    int z = 0;
    // do something
    RETURN(z)

是的,正确的,所有函数中没有闭合括号。语法高亮混乱,他使用vi进行编辑(不是vim,它有语法着色!)

他是一名俄罗斯程序员,主要从事汇编语言工作。他狂热地追求节省尽可能多的字节,因为他之前曾在内存非常有限的系统上工作过。“那是卫星用的。只有很少的字节,所以我们将每个字节都用于许多事情。”(位操作,重复使用机器指令字节的数值)当我试图了解哪些卫星时,我只能得到“轨道卫星。用于使其进入轨道。”

他还有两个怪癖:在显示器上方安装了一个凸面镜 “用来知道谁在看”,偶尔会突然离开椅子做十个快速俯卧撑。他解释说这最后一个怪癖是“编译器发现代码错误。这是惩罚。”


87
编译器在代码中发现错误。这是一种惩罚。!公司发现了你...对同事们进行了惩罚! - Learning
227
在苏联,程序编译的是你! - Crashworks
53
当我读到关于编译器错误“惩罚”的内容时,我首先想到的是“多比要烫手的情节”。 - Graeme Perrow
124
如果每当编译器在我们的代码中发现错误时,程序员(包括我自己)都做10个俯卧撑,那么我们会更加健康。这可能还会减少通过编译来进行测试的发生。 - MikeyB
5
那个人听起来很不错。但是,我不知道这应该如何改善代码大小。 - jalf
显示剩余17条评论

274

我最糟糕的经历:

#define InterlockedIncrement(x) (x)++
#define InterlockedDecrement(x) (x)--

因为某个白痴把这段代码放在头文件中,我花费了两天时间来追踪一些多线程COM引用计数问题。我不会提及当时我工作的那家公司。

这个故事的寓意是什么?如果你不明白某个东西,阅读文档并学习它,不要只是让它消失。


146
@Joshua:如果您在多线程环境下运行此代码,您可能会无意中这样做。 - 1800 INFORMATION
11
如果你不理解某个东西,就阅读相关文档并学习它。不要只是让它消失。 - 真的! - Paul Alexander
2
@1800 信息:我认为你只会失去选票,这就是为什么我不能给你一个;p - wkf
5
非C++程序员请谅解:这里的主要问题是一个线程安全函数被转换成了非线程安全函数吗?或者是InterlockedIncrement期望一个指针,所以现在你会增加指针而不是指针指向的内容?还是两者都有问题? - Tim Pietzcker
38
问题在于InterlockedIncrement通常是Windows API中定义的一个原子函数。因此,当人们调用InterlockedIncrement时,他们期望调用一个保证原子执行的函数。然而,有人定义了一个同名的宏,该宏被展开为普通的非原子递增操作,这就产生了问题。 - jalf
显示剩余2条评论

166
#define ever (;;)
for ever { 
   ...
}

52
我更喜欢使用<#define forever for(;;)>,这样你就可以写成<forever {...}>。 - paxdiablo
我曾经和一个同学一起上学,他因为EVER这个东西失分了……他很沮丧,因为这个东西在教科书上明明有写 :-) - TofuBeer
6
Pax的建议不就是源自K&R吗?不过,我觉得这并不值得费力去做。 - Jon Ericson
这其实一点也不错。我没有使用for (;;)惯用法,否则我会立即将这个宏添加到我的代码中。 - AnT stands with Russia
1
@hayalci:在Emacs Lisp(和一些Common Lisp实现)中,您可以键入(defmacro ever ()),然后输入(require 'cl (ever)) - Joe D

145
#include <iostream>
#define System S s;s
#define public
#define static
#define void int
#define main(x) main()
struct F{void println(char* s){std::cout << s << std::endl;}};
struct S{F out;};

public static void main(String[] args) {
  System.out.println("Hello World!");
}

挑战:有没有人可以用更少的定义和结构体来完成它?;-)


19
你刚写了一个Java到C语言的转换器!太棒了! - Andreas Petersson
25
被举报为“冒犯”。(开玩笑!) - Annika Backstrom
40
那要么丑得惊人却美丽,要么美得惊人却丑陋。 - Chris Lutz
38
@Mark - 这段代码中,声明了publicstatic与无意义的void,将main(x)替换为main(),因此public static void main(String[] args)变成了int main()。然后将System替换为S s; s,所以System.out.println("Hello World!"); 变成了 S s; s.out.println("Hello World!");,它在S结构体中调用F结构体中的println函数。 - Chris Lutz
2
看一下这个:http://www.mailcom.com/ioccc/chia/chia.c(下载并编译它) - Roberto Bonvallet
显示剩余3条评论

130
#define private public

我以前做过这个。有时候你只需要修改一个成员变量或覆盖一些你无法更改的第三方代码中的函数 - 而且他们没有为你提供访问器。 - Michael Kristofik
我昨天做了这件事,后悔不已。但这只是在一个小的测试应用程序中完成的,该应用程序使用主应用程序的头文件。 - Graeme Perrow
30
对于单元测试来说,这可能会非常有用,尽管对象设计的幽灵会在夜间困扰着你。 - Epaga
12
嗯,未定义行为、轻易违反单一定义规则、潜在的布局差异。是的,这个确实很棒。 - David Thornley
10
因此,我可以访问私有和公共内容,但无法访问受保护的内容,并且无法访问位于“class”关键字和第一个访问修饰符之间的内容。 - Ken Bloom
3
@Ken: #define class struct #define protected public 可以翻译为:将“class”定义为“struct”,将“protected”定义为“public”。 - Yakov Galka

107
#define if while

这是对某人开的玩笑,但受影响的人并不觉得有趣。


22
#define while if会更加隐蔽。 - starblue
7
我们应该澄清一下你的陈述。受影响的人并没有觉得很有趣。 :-) - Andrew Shepherd
6
做作业时,我经常故意做这种事情,只是为了惹恼我的老师。 - isekaijin
15
这个恶作剧不错,但如果有任何“else”语句,它将无法编译。我发现 #define if(x) if(true) 最有效。 - Graphics Noob
32
我一直更喜欢 #define sizeof(x) rand()。 - Jon
显示剩余2条评论

106

丑陋的:

#define begin {
#define end }
/* and so on */

如果你想使用Pascal编码,请购买一款Pascal编译器,不要破坏美丽的C语言。


45
现在你让我想知道,如果有一个聪明的头文件,我能模拟哪些语言。 - Bill the Lizard
47
C语言并不美观,相反它相当丑陋。 - rlbond
27
它的美在于它的简单性。有人说它拥有汇编语言的速度和可读性…… 汇编语言 :-) 我更喜欢它而不是臃肿的C++(尽管在我的日常工作中我更喜欢Java,因为它有巨大的库)。 - paxdiablo
9
确实。找到Bourne shell的原始来源。他就是这样做的,以得到某种类似于混合ALGOL的东西。 - RBerteig
3
#define DO 表示循环两次的宏定义,每次循环用_i表示当前循环次数;IF(cond) 宏定义包含一个条件判断语句,如果条件不成立则跳出循环。给定的代码使用了这两个宏定义,在循环中输出字符"a",并在条件 1==2 不成立时跳出循环。 - Adrian Panasiuk
显示剩余8条评论

93

一个“架构师”,非常谦虚的人,你知道这种类型的,他拥有以下内容:

#define retrun return

因为他喜欢打字速度快。这位脑外科医生过去喜欢对那些比他聪明的人大喊大叫(而几乎每个人都比他聪明),并威胁要动用他的黑带。


我犯了那个错别字太多次,以至于我考虑过它是正确的。 - Joshua
4
最好教你的编辑器自动替换“retrun”为“return”。我已经对我的IRC客户端进行了这样的修改,至少。 - Tetha
1
嘿,我觉得我以前也和那个“架构师”一起工作过。当他需要满足自己的自尊心时,他最终被重新分类为高级架构师。 - BIBD
1
我在bash中将'rn'重新定义为'rm',因为我无法输入,并且'rn'新闻阅读器需要5分钟才能启动并连接到服务器。 - Martin Beckett
2
你不能只是打开一个新的终端(或切换到另一个虚拟终端)并执行 killall rn 吗? - Joe D
@Martin 有一个名为 sl 的程序,可以显示火车动画。(sl 代表“蒸汽机车”。) - Maxpm

69

现实世界中的情况是这样的吗?MSVC在minmax.h中有名为maxmin的宏,每当我想使用标准的std::numeric_limits<T>::max()函数时,都会导致编译器错误。


2
啊,是的,这就是为什么我在微软特定的头文件后面加了一个专门用于恢复正常的 #undef 头文件。 - Pontus Gagge
3
使用 (std::numeric_limits<T>::max)() 解决了问题,但是确实很烦人。 - rlbond
36
在您的项目属性下添加 NOMINMAX,位置为:C / C ++ -> 预处理器 -> 预处理器定义。 - mattnewport
18
иҝҷдәӣе®Ҹе·Із»ҸеӯҳеңЁдәҺMSеӨҙж–Ү件дёӯжҜ”minе’ҢmaxеңЁC++ж ҮеҮҶеә“дёӯеҮәзҺ°зҡ„ж—¶й—ҙжӣҙй•ҝгҖӮ - Richard
4
当你的其他四个外部依赖项也定义了它们自己的min/max时,情况就更糟了,程度各不相同,从括号不当的宏到良好编写的模板。其中一个甚至让未定义或跳过这些定义变得不可能... 在我看来,这部分问题应该归咎于该语言本身,占了50%的责任。 - Roman Starkov
显示剩余4条评论

58

一种介于Pascal语法和法语关键字之间的混合语言:

#define debut {
#define fin }
#define si if(
#define alors ){
#define sinon }else{
#define finsi }

36
#define zut_alors exit(-1)这段代码意思是将一个宏定义为"zut_alors",并将其替换为"exit(-1)"语句。具体来说,当程序执行到宏定义处时,会直接调用exit函数,并传入参数-1表示程序异常结束。 - MikeyB
4
太棒了,让我忍不住大笑出声。所以,这基本上是用 C 实现的本地化法语版 Basic? - Bobby

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接