编程参考 - GCC的条件编译

2024-06-23 20:36
文章标签 gcc 编程 编译 参考 条件

本文主要是介绍编程参考 - GCC的条件编译,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

4 Conditionals

条件是一种指令,它指示预处理器选择是否在传递给编译器的最终标记流中包含一段代码。预处理器条件可以测试算术表达式,也可以测试名称是否定义为宏,或者使用特殊的defined操作符同时测试这两种表达式。

A conditional is a directive that instructs the preprocessor to select whether or not to include a chunk of code in the final token stream passed to the compiler. Preprocessor conditionals can test arithmetic expressions, or whether a name is defined as a macro, or both simultaneously using the special defined operator.

C 预处理器中的条件在某些方面类似于 C 语言中的 if 语句,但重要的是要了解它们之间的区别。if 语句中的条件在程序执行过程中进行测试。其目的是让程序在不同的运行过程中,根据所操作的数据表现出不同的行为。预处理条件指令中的条件在编译程序时进行测试。其目的是根据编译时的情况,允许在程序中包含不同的代码。

A conditional in the C preprocessor resembles in some ways an if statement in C, but it is important to understand the difference between them. The condition in an if statement is tested during the execution of your program. Its purpose is to allow your program to behave differently from run to run, depending on the data it is operating on. The condition in a preprocessing conditional directive is tested when your program is compiled. Its purpose is to allow different code to be included in the program depending on the situation at the time of compilation.

不过,这种区别已越来越不明显。现代编译器通常会在编译程序时对 if 语句进行测试,前提是已知其条件在运行时不会发生变化,并消除永远无法执行的代码。如果你能指望编译器做到这一点,那么你可能会发现,如果你使用条件恒定的 if 语句(也许由宏决定),你的程序会更易读。当然,你只能用它来排除代码,而不能排除类型定义或其他预处理指令,而且你只能在代码不使用时语法仍然有效的情况下才能这样做。

However, the distinction is becoming less clear. Modern compilers often do test if statements when a program is compiled, if their conditions are known not to vary at run time, and eliminate code which can never be executed. If you can count on your compiler to do this, you may find that your program is more readable if you use if statements with constant conditions (perhaps determined by macros). Of course, you can only use this to exclude code, not type definitions or other preprocessing directives, and you can only do it if the code remains syntactically valid when it is not to be used.

* Conditional Uses

* Conditional Syntax

* Deleted Code

4.1 Conditional Uses

使用条件式一般有三个原因。

  • 程序可能需要根据运行的机器或操作系统使用不同的代码。在某些情况下,一个操作系统的代码在另一个操作系统上可能是错误的;例如,它可能引用了在另一个系统上不存在的数据类型或常量。出现这种情况时,仅仅避免执行无效代码是不够的。它的存在将导致编译器拒绝该程序。通过预处理条件,可以有效地将无效代码从程序中删除。

  • 你可能希望将同一个源文件编译成两个不同的程序。其中一个版本可能会经常对中间数据进行耗时的一致性检查,或打印这些数据的值以便调试,而另一个版本则不会。

  • 一种方法是将条件总是假的代码从程序中排除,但将其作为注释保留,以供将来参考。

不需要特定系统逻辑或复杂调试钩子的简单程序一般不需要使用预处理条件。

There are three general reasons to use a conditional.

* A program may need to use different code depending on the machine or operating system it is to run on. In some cases the code for one operating system may be erroneous on another operating system; for example, it might refer to data types or constants that do not exist on the other system. When this happens, it is not enough to avoid executing the invalid code. Its mere presence will cause the compiler to reject the program. With a preprocessing conditional, the offending code can be effectively excised from the program when it is not valid.

* You may want to be able to compile the same source file into two different programs. One version might make frequent time-consuming consistency checks on its intermediate data, or print the values of those data for debugging, and the other not.

* A conditional whose condition is always false is one way to exclude code from the program but keep it as a sort of comment for future reference.

Simple programs that do not need system-specific logic or complex debugging hooks generally will not need to use preprocessing conditionals.

4.2 Conditional Syntax

C 预处理器中的条件以条件指令开始:"#if"、"#ifdef "或 "#ifndef"。

A conditional in the C preprocessor begins with a conditional directive: ‘#if’, ‘#ifdef’ or ‘#ifndef’.

* Ifdef

* If

* Defined

* Else

* Elif

* __has_attribute

* __has_cpp_attribute

* __has_c_attribute

* __has_builtin

* __has_feature

* __has_extension

* __has_include

4.2.1 Ifdef

最简单的条件是

The simplest sort of conditional is

#ifdef MACRO

controlled text

#endif /* MACRO */

此块称为条件组。只有在定义了 MACRO 的情况下,预处理器的输出才会包含受控文本。如果定义了 MACRO,我们就说条件成功;如果没有定义,我们就说条件失败。

This block is called a conditional group. controlled text will be included in the output of the preprocessor if and only if MACRO is defined. We say that the conditional succeeds if MACRO is defined, fails if it is not.

条件中的受控文本可以包括预处理指令。只有当条件成功时,它们才会被执行。可以在其他条件组内嵌套条件组,但必须完全嵌套。换句话说,"#endif "总是与最近的 "#ifdef"(或 "#ifndef",或 "#if")相匹配。此外,不能在一个文件中开始一个条件组,而在另一个文件中结束它。

The controlled text inside of a conditional can include preprocessing directives. They are executed only if the conditional succeeds. You can nest conditional groups inside other conditional groups, but they must be completely nested. In other words, ‘#endif’ always matches the nearest ‘#ifdef’ (or ‘#ifndef’, or ‘#if’). Also, you cannot start a conditional group in one file and end it in another.

即使条件失败,其中的受控文本仍会经过初始转换和标记化处理。因此,所有文本都必须是词法上有效的 C 语言。通常情况下,唯一重要的是,失败的条件组中的所有注释和字符串字面量都必须正确结束。

Even if a conditional fails, the controlled text inside it is still run through initial transformations and tokenization. Therefore, it must all be lexically valid C. Normally the only way this matters is that all comments and string literals inside a failing conditional group must still be properly ended.

在 "#endif "后面加上注释并不是必须的,但如果有很多受控文本,这不失为一种好的做法,因为它可以帮助人们将 "#endif "与相应的 "#ifdef "匹配起来。老式程序有时会将 MACRO 直接放在 "#endif "后面,而不加注释。根据 C 标准,这是无效代码。CPP 在接受它时会发出警告。这不会影响 "#endif "与哪一个 "#ifndef "相匹配。

The comment following the ‘#endif’ is not required, but it is a good practice if there is a lot of controlled text, because it helps people match the ‘#endif’ to the corresponding ‘#ifdef’. Older programs sometimes put MACRO directly after the ‘#endif’ without enclosing it in a comment. This is invalid code according to the C standard. CPP accepts it with a warning. It never affects which ‘#ifndef’ the ‘#endif’ matches.

有时,如果宏未被定义,您希望使用某些代码。为此,您可以使用 "#ifndef "而不是 "#ifdef"。#ifndef'的一个常用用法是在第一次包含头文件时才包含代码。请参阅 "仅包含一次的头文件"。

Sometimes you wish to use some code if a macro is not defined. You can do this by writing ‘#ifndef’ instead of ‘#ifdef’. One common use of ‘#ifndef’ is to include code only the first time a header file is included. See Once-Only Headers.

由于多种原因,不同编译器的宏定义可能会有所不同。下面是一些示例。

  • 有些宏在每种机器上都是预定义的(请参阅特定于系统的预定义宏)。这样,您就可以为特定机器提供专门的代码。

  • 系统头文件定义了更多的宏,与它们实现的功能相关联。你可以使用条件对这些宏进行测试,以避免在未实现系统功能的机器上使用该功能。

  • 在编译程序时,可以使用 -D 和 -U 命令行选项来定义或取消定义宏。你可以选择一个宏名来指定要编译的程序,编写条件来测试该宏是否定义或如何定义,然后使用命令行选项来控制该宏的状态,这些选项可以在 Makefile 中设置,从而将同一个源文件编译成两个不同的程序。请参阅调用。

  • 你的程序可能有一个特殊的头文件(通常称为 config.h),在编译程序时会对其进行调整。它可以定义或不定义宏,这取决于系统的特性和程序所需的功能。调整可以通过 autoconf 等工具自动完成,也可以手工完成。

Macro definitions can vary between compilations for several reasons. Here are some samples.

* Some macros are predefined on each kind of machine (see System-specific Predefined Macros). This allows you to provide code specially tuned for a particular machine.

* System header files define more macros, associated with the features they implement. You can test these macros with conditionals to avoid using a system feature on a machine where it is not implemented.

* Macros can be defined or undefined with the -D and -U command-line options when you compile the program. You can arrange to compile the same source file into two different programs by choosing a macro name to specify which program you want, writing conditionals to test whether or how this macro is defined, and then controlling the state of the macro with command-line options, perhaps set in the Makefile. See Invocation.

* Your program might have a special header file (often called config.h) that is adjusted when the program is compiled. It can define or not define macros depending on the features of the system and the desired capabilities of the program. The adjustment can be automated by a tool such as autoconf, or done by hand.

4.2.2 If

使用 "#if "指令可以测试算术表达式的值,而不仅仅是一个宏的存在。其语法为

The ‘#if’ directive allows you to test the value of an arithmetic expression, rather than the mere existence of one macro. Its syntax is

#if expression

controlled text

#endif /* expression */

表达式是整数类型的 C 表达式,受严格限制。它可以包含

  • 整数常量。

  • 字符常量,其解释与普通代码中的字符常量相同。

  • 算术运算符,用于加、减、乘、除、位运算、移位、比较和逻辑运算(&& 和 ||)。后两种运算符遵守标准 C 语言的通常短路规则。

  • 宏。表达式中的所有宏都会在实际计算表达式值之前展开。

  • 使用defined操作符,可以检查 "#if "中间是否定义了宏。

  • 非宏的标识符都被视为数字 0。如果知道 MACRO 被定义后,其值总是不为零,就可以用 #if MACRO 代替 #ifdef MACRO。在没有函数调用括号的情况下使用的类函数宏也被视为零。

    在某些情况下,这种快捷方式并不可取。当 GCC 在 "#if "中遇到不是宏的标识符时,-Wundef 选项会发出警告。

expression is a C expression of integer type, subject to stringent restrictions. It may contain

* Integer constants.

* Character constants, which are interpreted as they would be in normal code.

* Arithmetic operators for addition, subtraction, multiplication, division, bitwise operations, shifts, comparisons, and logical operations (&& and ||). The latter two obey the usual short-circuiting rules of standard C.

* Macros. All macros in the expression are expanded before actual computation of the expression’s value begins.

* Uses of the defined operator, which lets you check whether macros are defined in the middle of an ‘#if’.

* Identifiers that are not macros, which are all considered to be the number zero. This allows you to write #if MACRO instead of #ifdef MACRO, if you know that MACRO, when defined, will always have a nonzero value. Function-like macros used without their function call parentheses are also treated as zero.

In some contexts this shortcut is undesirable. The -Wundef option causes GCC to warn whenever it encounters an identifier which is not a macro in an ‘#if’.

预处理器对语言中的类型一无所知。因此,"#if "无法识别 sizeof 操作符,也无法识别枚举常量。它们将被视为非宏的标识符,并被 0 替换。在 sizeof 的情况下,这很可能导致表达式无效。

The preprocessor does not know anything about types in the language. Therefore, sizeof operators are not recognized in ‘#if’, and neither are enum constants. They will be taken as identifiers which are not macros, and replaced by zero. In the case of sizeof, this is likely to cause the expression to be invalid.

预处理器计算表达式的值。它以编译器已知的最宽整数类型进行所有计算;在 GCC 支持的大多数机器上,该类型为 64 位。这与编译器计算常量表达式值的规则不同,在某些情况下可能会得出不同的结果。如果计算出的值不为零,"#if "成功,受控文本被包含在内;否则跳过。

The preprocessor calculates the value of expression. It carries out all calculations in the widest integer type known to the compiler; on most machines supported by GCC this is 64 bits. This is not the same rule as the compiler uses to calculate the value of a constant expression, and may give different results in some cases. If the value comes out to be nonzero, the ‘#if’ succeeds and the controlled text is included; otherwise it is skipped.

4.2.3 Defined

在 "#if "和 "#elif "表达式中使用特殊运算符 defined 来测试某个名称是否被定义为宏。 defined name 和 defined (name) 都是表达式,如果名称在程序的当前点被定义为宏,则其值为 1,否则为 0。因此,#if defined MACRO 与 #ifdef MACRO 完全等价。

The special operator defined is used in ‘#if’ and ‘#elif’ expressions to test whether a certain name is defined as a macro. defined name and defined (name) are both expressions whose value is 1 if name is defined as a macro at the current point in the program, and 0 otherwise. Thus, #if defined MACRO is precisely equivalent to #ifdef MACRO.

defined在同时测试多个宏是否存在时非常有用。例如

defined is useful when you wish to test more than one macro for existence at once. For example,

#if defined (__vax__) || defined (__ns16000__)

would succeed if either of the names __vax__ or __ns16000__ is defined as a macro.

Conditionals written like this:

#if defined BUFSIZE && BUFSIZE >= 1024

通常可以简化为 #if BUFSIZE >= 1024,因为如果没有定义 BUFSIZE,它将被解释为值为零。

can generally be simplified to just #if BUFSIZE >= 1024, since if BUFSIZE is not defined, it will be interpreted as having the value zero.

如果defined运算符作为宏扩展的结果出现,C 标准会指出其行为是未定义的。GNU cpp 将其视为真正的defined运算符,并进行正常评估。如果您使用命令行选项 -Wpedantic,无论您的代码在何处使用此功能,它都会发出警告,因为其他编译器可能会有不同的处理方式。警告也可以通过 -Wextra 启用,也可以通过 -Wexpansion-to-defined 单独启用。

If the defined operator appears as a result of a macro expansion, the C standard says the behavior is undefined. GNU cpp treats it as a genuine defined operator and evaluates it normally. It will warn wherever your code uses this feature if you use the command-line option -Wpedantic, since other compilers may handle it differently. The warning is also enabled by -Wextra, and can also be enabled individually with -Wexpansion-to-defined.

4.2.4 Else

可以在条件中添加 "#else "指令,以便在条件失败时提供替代文本。这就是它的样子:

The ‘#else’ directive can be added to a conditional to provide alternative text to be used if the condition fails. This is what it looks like:

#if expression

text-if-true

#else /* Not expression */

text-if-false

#endif /* Not expression */

如果表达式为非零,则包含text-if-true,跳过text-if-false。如果表达式为零,则相反。

你也可以将 "#else "与 "#ifdef "和 "#ifndef "一起使用。

If expression is nonzero, the text-if-true is included and the text-if-false is skipped. If expression is zero, the opposite happens.

You can use ‘#else’ with ‘#ifdef’ and ‘#ifndef’, too.

4.2.5 Elif

嵌套条件的一种常见情况是用于检查两个以上的可能选择。例如,你可能有

One common case of nested conditionals is used to check for more than two possible alternatives. For example, you might have

#if X == 1

#else /* X != 1 */

#if X == 2

#else /* X != 2 */

#endif /* X != 2 */

#endif /* X != 1 */

另一个条件指令 "#elif "允许将其缩写如下:

Another conditional directive, ‘#elif’, allows this to be abbreviated as follows:

#if X == 1

#elif X == 2

#else /* X != 2 and X != 1*/

#endif /* X != 2 and X != 1*/

"elif"代表 "else if"。与 "#else "一样,它位于条件组的中间并对其进行细分;它不需要与之匹配的 "#endif"。与 "#if "一样,"#elif "指令包含一个要测试的表达式。只有当原来的 "#if "条件失败而 "#elif "条件成功时,才会处理 "#elif "后面的文本。

‘#elif’ stands for “else if”. Like ‘#else’, it goes in the middle of a conditional group and subdivides it; it does not require a matching ‘#endif’ of its own. Like ‘#if’, the ‘#elif’ directive includes an expression to be tested. The text following the ‘#elif’ is processed only if the original ‘#if’-condition failed and the ‘#elif’ condition succeeds.

同一条件组中可以包含多个 "#elif"。然后,只有当 "#elif "条件在原始的 "#if "和其中所有先前的 "#elif "指令失败后成功时,才会处理每个 "#elif "后面的文本。

More than one ‘#elif’ can go in the same conditional group. Then the text after each ‘#elif’ is processed only if the ‘#elif’ condition succeeds after the original ‘#if’ and all previous ‘#elif’ directives within it have failed.

在任意数量的 "#elif "指令之后都可以使用 "#else",但 "#elif "不能紧跟在 "#else "之后。

‘#else’ is allowed after any number of ‘#elif’ directives, but ‘#elif’ may not follow ‘#else’.

4.3 Deleted Code

如果要替换或删除程序的一部分,但又想保留旧代码供将来参考,通常不能简单地将其注释掉。程序块注释不会嵌套,因此旧代码中的第一个注释将结束注释。结果很可能是语法错误泛滥。

If you replace or delete a part of the program but want to keep the old code around for future reference, you often cannot simply comment it out. Block comments do not nest, so the first comment inside the old code will end the commenting-out. The probable result is a flood of syntax errors.

避免这一问题的方法之一是使用一个始终为假的条件。例如,在删除的代码前加上 #if 0,在其后加上 #endif。即使被删除的代码包含条件编译,这种方法也能奏效,但必须是完整的条件(使 "#if "和 "#endif"成对)。

One way to avoid this problem is to use an always-false conditional instead. For instance, put #if 0 before the deleted code and #endif after it. This works even if the code being turned off contains conditionals, but they must be entire conditionals (balanced ‘#if’ and ‘#endif’).

有些人使用 #ifdef notdef 代替。这是有风险的,因为 notdef 可能会被意外定义为宏,然后条件就会成功。#if 0 可认为是条件为假。

Some people use #ifdef notdef instead. This is risky, because notdef might be accidentally defined as a macro, and then the conditional would succeed. #if 0 can be counted on to fail.

不要在非 C 代码中使用 #if 0来作为注释。而应使用真正的注释。#if 0 的内部必须由完整的标记组成;尤其是单引号字符必须注意。注释通常包含不平衡的单引号字符(英语称为撇号)。这些字符会混淆 #if 0,但不会混淆"/*"。

Do not use #if 0 for comments which are not C code. Use a real comment, instead. The interior of #if 0 must consist of complete tokens; in particular, single-quote characters must balance. Comments often contain unbalanced single-quote characters (known in English as apostrophes). These confuse #if 0. They don’t confuse ‘/*’.

参考:

Conditionals (The C Preprocessor)

这篇关于编程参考 - GCC的条件编译的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1088208

相关文章

ESP32 esp-idf esp-adf环境安装及.a库创建与编译

简介 ESP32 功能丰富的 Wi-Fi & 蓝牙 MCU, 适用于多样的物联网应用。使用freertos操作系统。 ESP-IDF 官方物联网开发框架。 ESP-ADF 官方音频开发框架。 文档参照 https://espressif-docs.readthedocs-hosted.com/projects/esp-adf/zh-cn/latest/get-started/index

C++工程编译链接错误汇总VisualStudio

目录 一些小的知识点 make工具 可以使用windows下的事件查看器崩溃的地方 dumpbin工具查看dll是32位还是64位的 _MSC_VER .cc 和.cpp 【VC++目录中的包含目录】 vs 【C/C++常规中的附加包含目录】——头文件所在目录如何怎么添加,添加了以后搜索头文件就会到这些个路径下搜索了 include<> 和 include"" WinMain 和

C/C++的编译和链接过程

目录 从源文件生成可执行文件(书中第2章) 1.Preprocessing预处理——预处理器cpp 2.Compilation编译——编译器cll ps:vs中优化选项设置 3.Assembly汇编——汇编器as ps:vs中汇编输出文件设置 4.Linking链接——链接器ld 符号 模块,库 链接过程——链接器 链接过程 1.简单链接的例子 2.链接过程 3.地址和

零基础STM32单片机编程入门(一)初识STM32单片机

文章目录 一.概要二.单片机型号命名规则三.STM32F103系统架构四.STM32F103C8T6单片机启动流程五.STM32F103C8T6单片机主要外设资源六.编程过程中芯片数据手册的作用1.单片机外设资源情况2.STM32单片机内部框图3.STM32单片机管脚图4.STM32单片机每个管脚可配功能5.单片机功耗数据6.FALSH编程时间,擦写次数7.I/O高低电平电压表格8.外设接口

16.Spring前世今生与Spring编程思想

1.1.课程目标 1、通过对本章内容的学习,可以掌握Spring的基本架构及各子模块之间的依赖关系。 2、 了解Spring的发展历史,启发思维。 3、 对 Spring形成一个整体的认识,为之后的深入学习做铺垫。 4、 通过对本章内容的学习,可以了解Spring版本升级的规律,从而应用到自己的系统升级版本命名。 5、Spring编程思想总结。 1.2.内容定位 Spring使用经验

Windwos +vs 2022 编译openssl 1.0.2 库

一 前言 先说 结论,编译64位报错,查了一圈没找到解决方案,最后换了32位的。 使用qt访问web接口,因为是https,没有openssl库会报错 QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());if (reply){if (reply->error() == QNetworkReply::NoError

【服务器运维】CentOS7 minimal 离线安装 gcc perl vmware-tools

0. 本机在有网的情况下,下载CentOS镜像 https://www.centos.org/download/ 1. 取出rpm 有的情况可能不需要net-tools,但是如果出现跟ifconfig相关的错误,就把它安装上。另外如果不想升级内核版本的话,就找对应内核版本的rpm版本安装 perl-Time-Local-1.2300-2.el7.noarch.rpmperl-Tim

SQL Server中,添加数据库到AlwaysOn高可用性组条件

1、将数据添加到AlwaysOn高可用性组,需要满足以下条件: 2、更多具体AlwaysOn设置,参考:https://msdn.microsoft.com/zh-cn/library/windows/apps/ff878487(v=sql.120).aspx 注:上述资源来自MSDN。

装gcc

下载https://anaconda.org/serge-sans-paille/gcc_49/files  需要的gcc版本 把它放到/export/xxx/conda/pkgs 下 source要装的那个环境 到/export/xxx/conda/pkgs 下执行:conda install --use-local gcc_49-4.9.1-6.tar.bz2 装好后,gcc -v 发

青龙面板2.9之Cdle傻妞机器人编译教程

看到有的朋友对傻妞机器人感兴趣,这里写一下傻妞机器人的编译教程。 第一步,这里以linux amd64为例,去官网下载安装go语言安装包: 第二步,输入下方指令 cd /usr/local && wget https://golang.google.cn/dl/go1.16.7.linux-amd64.tar.gz -O go1.16.7.linux-amd64.tar.gz