C Reference Manual Reading Notes: 009 Preprocessor Lexical Conventions

C预处理器详解
本文深入探讨了C语言预处理器的工作原理及其语法特性。包括如何识别宏调用、预处理器指令的处理方式、如何处理宏展开时出现的伪预处理器指令等问题,并介绍了如何使用续行符来合并多行代码。

    The preprocessor does not parse the source text, but it does break it up into tokens for the purpose of locating macro calls. The lexical conventions of the preprocessor are somewhat different from the compiler proper; the preprocessor recognize the normal C tokens, and additionally recognize as "tokens" other characters that would not be recognized as valid in C proper. This enables the preprocessor to recognize file names, the presence and absence whitespace, and the location of end-of-line markers.

 

    A line beginning with # is treated as a preprocessor command; the name of the command must follow the # character. Standard C permits whitespace to precede and follow the # character ont the same source line, but some older compilers do not. A line whose only non-whitespace character is a # is termed a null directive in standard C and is treated the same as a blank line. Older implementations may behave differently.

 

    The remainder of the line following the command name may contain arguments for the command if appropriate. If a preprocessor command takes no arguments, then the remainder of the command line should be empty except perhaps for whitespace characters or comments. Many pre-ISO compilers silently ignore all characters following the expected arguments(if any); this can lead to portability problems. The arguments to preprocessor commands are generally subject to macro replacement.

 

    Preprocessor lines are recognized before macro expansion. Therefore, if a macro expands into something that looks like a preprocessor command, that command will not be recognized by the preprocessors in Standard C or in most other C compilers.(Some old UNIX implementations violate this rules.) For example, the result of the following code is not to include the file math.h in the program being compiled:

                     /* This example doesn't work as one might think ! */

                     #define GETMATH #include <math.h>

                     GETMATH

                Instead, the expanded token sequences

                      #include <math.h>

                is merely passed through and compiled as (erroneous) C code

 

    All source lines (including preprocessor command lines) can be continued by preceding the end-of-line markers by a backslash character, /. This happens before scanning for preprocessor commands. Such as the follows:

                The preprocessor

                      #define err(flag,msg) if(flag)/

                                             printf(msg)

                 is the same as

                      #define err(flag,msg) if(flag) printf(msg)

                 If the backslash character below immediately precedes the end-of-line marker, these two lines

                      #define BACKSLASH /

                      #define ASTERISK *

                 will be treated as the single preprocessor command

                      #define BACKSLASH #define ASTERISK *

                 and expanded token sequences is

                      #define ASTERISK *

    The preprocessor treats comments as whitespace, and line breaks within comments do not terminate preprocessor commands. For example:

                      #define COMMENT/* first line

                                                    * second line

                                                    */ "comment"

                  will be treated as:

                      #define COMMENT  "comment"

在编译C++代码时,如果出现错误提示 `C++ preprocessor "/lib/cpp" fails sanity check`,这通常表示系统无法正确调用C++预处理器。以下是可能的原因及对应的解决方法: ### 原因与解决方案 - **缺少或损坏的g++包** 如果系统中没有安装`g++`(GNU C++编译器),或者其安装已损坏,则可能导致预处理器检查失败。可以使用发行版的包管理器来安装或重新安装`g++`。例如,在基于Debian的系统上运行以下命令: ```bash sudo apt-get update sudo apt-get install g++ ``` 在某些Linux发行版中,如Fedora,`g++`可能被命名为`gcc-c++`。在这种情况下,使用`yum`或`dnf`进行安装: ```bash sudo dnf install gcc-c++ ``` - **预处理器路径不正确或环境变量配置问题** 错误信息中的 `/lib/cpp` 可能指向了一个无效的预处理器路径。确保系统的环境变量(如`CC`和`CXX`)正确设置为可用的C/C++编译器路径。例如,可以通过以下命令临时指定编译器路径: ```bash export CC="/usr/bin/gcc" export CXX="/usr/bin/g++" ``` 如果需要长期生效,可以将上述命令添加到全局配置文件 `/etc/profile` 中。 - **libtool版本问题导致的预处理器检查失败** 某些版本的`libtool`存在一个bug,它会导致`configure`脚本尝试检查所有支持的编译器,从而引发预处理器检查失败的问题。这种情况下,安装`g++`通常可以解决问题。如果已经安装了`g++`但问题仍然存在,则考虑升级或降级`libtool`版本以避免该bug。 - **手动验证预处理器功能** 可以通过运行简单的测试命令来确认预处理器是否正常工作。例如: ```bash echo | cpp -v ``` 这条命令会尝试运行C预处理器并输出其版本信息。如果预处理器路径正确且安装无误,应该能看到详细的调试输出;否则,可能会看到类似的错误提示。 - **系统依赖项未满足** 确保系统中所有必要的开发工具和库均已安装。例如,在某些系统上,除了`g++`外,还需要安装`build-essential`、`make`等工具链组件。 ### 验证与后续步骤 完成上述修复后,建议重新运行`configure`脚本以验证问题是否已解决。此外,可以尝试编译一个简单的C++程序来进一步测试编译器和预处理器的功能。 ```cpp #include <iostream> int main() { std::cout << "Hello, World!" << std::endl; return 0; } ``` 使用以下命令编译并运行该程序: ```bash g++ hello.cpp -o hello ./hello ``` 如果程序成功输出“Hello, World!”,则表明编译器和预处理器均正常工作[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值