奇怪的TRUE和FALSE宏定义

306

我在一本编程书中看到了以下宏定义。

#define TRUE  '/'/'/'
#define FALSE '-'-'-'

那里没有解释。

请向我解释这些如何作为TRUEFALSE工作。


65
我认为这只是将TRUE定义为1,FALSE定义为0的有趣方式。 - BlackDwarf
164
请注意,在这些表达式周围没有括号的情况下,这是一个糟糕的想法。我的意思是,即使有了括号,这也是个糟糕的想法,但如果没有括号,你只会浪费很长时间来调试代码。 - TartanLlama
70
请问你所参考的编码书籍是哪一本? - artm
48
希望这本书能把这个作为糟糕或者故意晦涩代码的例子。 - Jon Hanna
32
@Daniel: 另一个想法是使用rand()%2来定义MAYBE,这样有时它等于TRUE,有时等于FALSE。 - Kaiserludi
显示剩余27条评论
6个回答

382

让我们看看:'/' / '/' 表示将 char 字面值 / 除以 char 字面值 '/' 本身。结果为一,对于 TRUE 来说听起来很合理。

'-' - '-' 表示减去 char 字面值 '-' 本身。这是零(FALSE)。

这里有两个问题:首先,它不易读懂。使用 10 绝对更好。另外,正如 TartanLlama 和 KerrekSB 指出的那样,如果您要使用这个定义,请在它们周围添加括号,以免出现任何意外:

#include <stdio.h>

#define TRUE  '/'/'/'
#define FALSE '-'-'-'

int main() {
        printf ("%d\n", 2 * FALSE);
        return 0;
}

这将打印出 '-' 这个字符字面值的值(在我的系统上为 45)。

加上括号:

#define TRUE  ('/'/'/')
#define FALSE ('-'-'-')

即使将一个真值与整数相乘并没有太多意义,但该程序正确地打印了零。这只是一种未经括号化的宏可能会咬你的类型的意外错误的示例。


8
将真值与乘法相乘确实是有意义的。例如,对于缩进*should_indent,根据should_indent是否为真,结果将为0或缩进本身,而无需进行分支判断。(我猜这个例子不太好,因为在处理文本时,单一分支并不重要(我见过这种技巧应用在着色器和XPATH中(两者非常不同,我记不清确切的形式了))。 - Alpedar
2
Alpedar - 但从概念上和数学上来看,这样做没有意义 - 在这种情况下,使用“if”语句而不是将“TRUE”乘以整数会更清晰(并且在概念上有意义)。 - Jay
2
逻辑非可以实现为 notx = TRUE - x; 并且可以正常工作。但是,TRUE-FALSE 的结果是-44(假设使用ASCII编码)。 - Hagen von Eitzen
@MarcWittke:那些滥用C预处理器的人,使用这些破碎的宏定义很快就会失去朋友。众所周知,对于没有严格括号保护的简单文本替换可能会导致极其不明显的故障,这就是为什么C++引入了许多不需要使用C预处理器的方法,例如模板和static const bool TRUE = true;。(尽管此定义实际上具有int类型而不是bool。) - Peter Cordes

90

这只是另一种书写方式。

#define TRUE 1
#define FALSE 0
表达式'/'/'/'将把'/'的字符值除以它本身,其结果将为1。
表达式'-'-'-'将从'-'的字符值中减去它本身,其结果将为0。
整个define表达式周围缺少括号,这可能导致使用这些宏的代码出错。Jay's answer很好地解决了这个问题。
一个"现实生活"中忘记括号可能会有害的示例是将这些宏与C风格的转换运算符结合使用。例如,如果有人决定在C++中将这些表达式强制转换为bool
#include <iostream>

#define TRUE  '/'/'/'
#define FALSE '-'-'-'

int main() {
    std::cout << "True: " << (bool) TRUE << std::endl;
    std::cout << "False: " << (bool) FALSE << std::endl;
    return 0;
}

这是我们得到的内容:

True: 0
False: -44

所以(bool) TRUE实际上会评估为false,而(bool) FALSE会评估为true


44

这相当于写作

#define TRUE 1
#define FALSE 0

表达式'/'/'/'实际上是将字符/(无论其数值为何)除以自身,因此结果为1

类似地,表达式'-'-'-'将字符-从自身中减去并计算出0

最好写成:

#define TRUE ('/'/'/')
#define FALSE ('-'-'-')
为了避免在与其他优先级更高的运算符一起使用时发生意外值的变化。

35

Jay已经回答了为什么这些表达式的值是01

出于历史原因,这些表达式'/'/'/''-'-'-'来自1984年第一届国际混淆C代码大赛的一个条目。

int i;main(){for(;i["]<i;++i){--i;}"];read('-'-'-',i+++"hell\
o, world!\n",'/'/'/'));}read(j,i,p){write(j/p+p,i---j,i/i);}

(链接到程序这里,在上面的IOCCC页面中有关于这个程序做什么的提示。)

此外,如果我没记错的话,这些表达式作为混淆的宏定义了TRUEFALSE,并且在Don Libes(1993年)的"Obfuscated C and Other Mysteries"一书中也有涉及。


8

这是一种写宏的滑稽方式,针对TrueFalse

许多解释已经提供了,其中/表示一个1字节数字(根据ASCII),当被自己除时,它会给你1,这将被视为True;同样,-再次是一个字节数字,当减去相同的值时,它会给你0,这将被解释为false

#define TRUE  '/'/'/'
#define FALSE '-'-'-'

因此,我们可以将/-替换为任何我们喜欢的字符,例如:
#define TRUE  '!'/'!'
#define FALSE 'o'-'o'

将保持与原始表达相同的含义。


6
让我们从真值开始。你可以将其读作'/' / '/',表示“字符'/'除以字符'/'”。由于在C语言中,每个字符都是一个数值(一个字节),因此可以将其视为“字符'/'的ASCII值除以相同字符的ASCII值”,这意味着1(因为很明显,x/x等于1)。因此,TRUE是1。
对于FALSE,原理相同:'-'-'-'读作'-' - '-',即“'-'的ASCII值减去'-'的ASCII值”,结果是0。因此,FALSE是0。
这是表述显而易见的一种恶劣方式。

7
这与ASCII无关。 - Kerrek SB
8
@Fabien:这不取决于ASCII。'/'/'/'对于任何有效的字符集都是1,无论'/' == 47(如ASCII中一样)还是'/' == 97(如EBCDIC中一样)或其他任何值。 - Keith Thompson
6
符合规范的C语言实现不能将 '/' 映射为 0。这个值为null字符保留。 - Keith Thompson
4
如果Pawel是在追求细节,那么这个('-'-'-'),因为他的观点基于一个未明示的条件;将Keith的言论描述为追求细节可能更贴切('/')/'/'),但我会称之为"澄清"(加上笑脸后,"追求细节"对我来说肯定是'/'-'/')。把这些评论放在一起可能(' '-'-')可以被称为追求细节,但是,1)在这个领域里这不是义务吗?2)他们让我思考;3)某些事情比以前更清楚了。是的,我想我自己也有点追求细节!(但我比以前更清楚"追求细节"的意思了!;-) - Zhora
3
你说得好像这是一件坏事一样。 - Keith Thompson
显示剩余6条评论

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