【发布时间】:2016-02-13 01:08:13
【问题描述】:
如果我将宏定义为#define LOGIC_ONE 1 并希望在case 语句中使用LOGIC_ONE,那么LOGIC_ONE 考虑什么类型?
它是否被识别为 int,因为我将它定义为值 1?
【问题讨论】:
-
要查看预处理器的作用,请将
-E传递给编译器。 (msvc, gcc, clang 都接受-E)
如果我将宏定义为#define LOGIC_ONE 1 并希望在case 语句中使用LOGIC_ONE,那么LOGIC_ONE 考虑什么类型?
它是否被识别为 int,因为我将它定义为值 1?
【问题讨论】:
-E 传递给编译器。 (msvc, gcc, clang 都接受-E)
C++ 宏是简单的文本替换。
在编译器启动时,您的LOGIC_ONE 已经被预编译器替换为1。就像您马上写1一样。 (在这种情况下,它是一个 int 文字......)
编辑以将讨论包含在 cmets 中:
如果您(或其他有权访问您的代码的人)将您的 #define LOGIC_ONE 1 更改为 #define LOGIC_ONE "1",它将在您的程序中更改其行为并成为 const char[] 文字。
编辑:
由于这篇文章得到的关注超出了我的预期,我想我为那些好奇的人添加了对C++ 14 Standard 的引用:
2.2 翻译阶段 [lex.phases]
(...)
4. 执行预处理指令,扩展宏调用,并 _Pragma 一元运算符表达式被执行。 (...) 然后删除所有预处理指令。
(...)
7. 分隔标记的空白字符不再重要。每个预处理令牌都被转换为一个令牌。 (2.6)。生成的标记在句法和语义上进行分析并作为翻译单元进行翻译。
如上所述,宏在第 4 阶段被替换,之后不再存在。 “句法和语义”分析发生在第 7 阶段,代码被编译(“翻译”)。
整数字面量在
中指定2.13.2 整数字面量 [lex.icon]
(...)
整数文字是没有句点或指数部分的数字序列,可选的分隔单引号在确定其值时会被忽略。整数文字可能有一个指定其基数的前缀和一个指定其类型的后缀。
(...)表 5 - 整数字面量的类型
Suffix | Decimal literal | Binary, octal, or hexadecimal literal ----------------------------------------------------------------------------- none | int | int | long int | unsigned int | long long int | long int | | unsigned long int | | long long int | | unsigned long long int ----------------------------------------------------------------------------- u or U | unsigned int | unsigned int | unsigned long int | unsigned long int | unsigned long long int | unsigned long long int ----------------------------------------------------------------------------- l or L | long int | long int | long long int | unsigned long int | | long long int | | unsigned long long int ----------------------------------------------------------------------------- Both u or U | unsigned long int | unsigned long int and l or L | unsigned long long int | unsigned long long int ----------------------------------------------------------------------------- ll or LL | long long int | long long int | unsigned long long int ----------------------------------------------------------------------------- Both u or U |unsigned long long int | unsigned long long int and ll or LL | |
字符串字面量在
中指定2.13.5 字符串字面量 [lex.string]
(...)
1 字符串文字是由双引号包围的字符序列(如 2.13.3 中所定义),可选前缀为 R、u8、u8R、u、uR、U、UR、L、rLR,如“... ", R"(...)", u8"...", u8R"**(...)**", u"...", uR"*~(...)*~" 、U"..."、UR"zzz(...)zzz"、L"..." 或 LR"(...)"。
(...)
6 在翻译阶段 6 之后,不以编码前缀开头的字符串文字是普通字符串文字,并使用给定的字符进行初始化。
7 以 u8 开头的字符串文字,例如 u8"asdf",是 UTF-8 字符串文字。
8 普通字符串文字和 UTF-8 字符串文字也称为窄字符串文字。窄字符串字面量的类型为“array of nconst char”,其中 n 是字符串的大小,定义如下,并且具有 静态存储持续时间 (3.7)。
【讨论】:
LOGIC_ONE 当作int 对待?
"1"
1的方式对待它——所以是的,它是一个int字面量。
"1"?
const char []。
预处理器定义没有类型——它们基本上只是“粘贴”到它们出现的代码中。例如,如果你在语句中使用它;
int foo = LOGIC_ONE;
然后它将被解释为整数。 (在预处理器之后运行的编译器只会将该代码视为int foo = 1;)您甚至可以在诸如;
int foo##LOGIC_ONE;
然后您将创建一个变量foo1。呸!
举一个宏定义的替代例子;
#define LOGIC_ONE hello
int LOGIC_ONE = 5;
printf("%d\n", hello);
这是完全有效的,并声明了一个名为 hello 的 int,但表明没有用于定义的“类型”——hello 只是在代码中遇到 LOGIC_ONE 的地方被替换。
除非绝对必要,否则避免使用预处理器宏。专业编码标准经常禁止或严格限制预处理器的使用。通常总有比使用宏更好的方法来做事。例如,考虑这些替代方案;
static const int LOGIC_ONE = 1;
enum { LOGIC_ONE = 1 };
预处理器是学习者在 C 语言中陷入真正混乱的一种快速方法。
【讨论】:
LOGIC_ONE 在它出现的任何地方都被 1 替换。就编译器而言,LOGIC_ONE 不存在,它只看到 1。所以你的问题是“1 是 int 吗?”。答案是 -> 这取决于你在哪里输入 1
【讨论】:
1 还是LOGIC_ONE。感谢您的澄清!
宏是文本替换。 1 的类型为 constexpr int。
【讨论】: