为什么这个程序用 `YES` 和 `true` 产生不同的结果?

Why is this program produces different result with `YES` and `true`?

这是完整的程序。你能算出它的控制台输出吗?

#import <Foundation/Foundation.h>

#define kEnv YES

#if kEnv
#define x @"abc"
#else
#define x @"xyz"
#endif


#define kVersion true

#if kVersion
#define y @"abc"
#else
#define y @"xyz"
#endif

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"x: %@; y: %@", x, y);
        NSLog(@"%@", kEnv ? @"abc" : @"cba");
        NSLog(@"%@", kVersion ? @"abc" : @"cba");
    }
    return 0;
}

在继续之前,您可以复制粘贴,运行,然后自己检查结果。


输出为:

x: xyz; y: abc
abc
abc

谁能解释一下原因?

如果你添加

#define YES 1

一开始你会得到你期望的输出。原因是 YES 定义可能比这里给出的方式更复杂,这会扰乱预处理器。您可以通过例如将您的测试更改为

#if YES

(并删除我对 YES 的定义),您会得到与之前相同的结果。这只是表明,无论如何定义 YES,它都不会触发

#if YES

测试。

PS

我想如果你想使用常量,例如

#if XXX

#if XXX > 5

您需要将 XXX 定义为数值,否则预处理器无法正确执行计算。我建议你坚持只检查定义,例如只需使用

#ifdef XXX

以及何时需要使用

#if

只使用数值常量,不使用任何表达式。

此处提到的 FWIW https://en.wikipedia.org/wiki/C_preprocessor 预处理器非常有限 - 参考讨论了一些限制。

YES 中被定义为 __objc_yes,这是编译器级别的符号,预处理器不知道。

(/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/objc/objc.h 在我的机器上。)

你可以通过

看到定义
#define T(X) #X
#define S(X) T(X)
printf("YES is: \"%s\"\n", S(YES));

C 预处理器是一个有趣的野兽。 :)

编辑:为什么

#if __objc_yes

表现得好像 __objc_yes 是假的?因为 __objc_yes 对预处理器来说是完全未知的;只有编译器知道。这是从 manual 到 GNU 预处理器,但我相信它反映了标准,关于 #if 之后的表达式可以包含什么:

[...] Identifiers that are not macros, which are all considered to be the number zero. [...]