嵌入式C语言-预编译命令(#define、#if、#ifdef、#ifndef、#undef)

这篇具有很好参考价值的文章主要介绍了嵌入式C语言-预编译命令(#define、#if、#ifdef、#ifndef、#undef)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

#define

宏定义

#define机制包含了一个规定,允许把参数替换到文本中,这种实现通常称为宏定义。下面是宏的声明方式:

#define name(parameter-list)	stuff

其中,parameter-list(参数列表)是由逗号分割的符号列表,它们可能出现在stuff中。name必须与左括号紧邻。比如下面的例子:

#define MAX(a,b)	(a)>(b)?(a) : (b)

提示
所有用于数值表达式进行求值的宏定义的参数都应该用括号,避免在使用宏时,由于宏展开导致参数中的操作符或邻近操作符之间发生不可预测的情况。

#define 替换

在程序中扩展#define定义符号和宏时,需要注意几个事项:

  • 宏不可以出现递归
  • #define定义符号时,字符串常量的内容不进行检查

带副作用的宏参数

当宏参数的宏定义中出现的次数超过一次时,如果这个参数具有副作用,那么当你使用这个宏时就可能出现危险,导致不可预测的结果。例如,下面的表达式:

x+1;

可以重复执行几百次,它每次获得结果都是一样。这个表达式不具有副作用。但是

i++;

就具有副作用:它增加x的值。MAX宏可以证明具有副作用的参数所引起的问题。观察下列代码,你觉得最终输出的是什么??

#define MAX(a,b)	((a)>(b)?(a):(b))
...
x = 5;
y = 8;
z = MAX(x++,y++);

带副作用参数问题解决并轻松。记住第1个表达式是一个条件表达式,用于确定执行另两个表达式中的那一个,剩余的表达将不会执行。结果是:x=6,y=10,z = 9;
对于这个结果是不是感觉很惊讶,只要检查一下宏替换产生的代码,就一目了然了。
z = (x++)>(y++)?(x++):(y++);
讲解:

  1. 执行x++,y++表达式分别得到x=6,y=9;
  2. 执行>运算表达式;
  3. 执行赋值操作z = 9;
  4. 执行y++得到y=10;

undef

这条预处理指令用于移除一个宏定义。
#undef name
如果一个现存的名字需要被重新定义,那么它的旧的首先必须要用#undef移除。

#ifdef log_e
#undef log_e	//移除之前旧的宏定义log_e
#endif

条件编译

概念

条件编译(conditional compiling)命令指定预处理器依据特定的条件判断保留或删除某段源代码。例如,可以使用条件编译让源代码适用与不同的目标系统,而不需要管理该源代码的各种版本。

条件编译区域以#if、#ifdef或#ifndef等命令作为开头,以#endif命令结尾。条件编译区域可以有任意数量的#elif命令,但最多一个#else命令。下面显示它最简单的语法:

#if constant-expression
	statements
#endif

其中,constant-expression常量表达式)由预处理器进行求值。如果它的值是非零(真),那么statements部分被正常编译,否则预处理器就安静地删除它们。

#if和#elif命令

作为#if或#elif命令条件的表达式,必须是整数常量预处理器表达式。这与普通的整数常量表达式不同,主要的区别在于:

  • 不能在#if或#elif表达式中使用类型转换运算符。
  • 可以使用预处理运算符defined。
  • 在预处理器展开所有宏,并且计算完所有 defined 表达式之后,会使用字符 o 替换掉表达式中所有其他标识符或关键字。

defined运算符

一元运算符defined可以出现在#if或#elif命令的条件中。它的形式如下:

#defined 标识符
#defined (标识符)

如果指定的标识符(identifier)是一个宏名称(也就是说,它被#define命令定义,并且未被#undef命令取消定义),则defined表达式会生产值1。否则,defined表达式会生产值0。

#if和defined相结合

如果defined用宏定义标识符执行,未定义则编译。如下所示:

#define DEBUG

#if defined(DEBUG)
// 宏定义DEBUG执行
#else
// 取消宏定义DEBUG不执行
#endif

defined运算符相对于#ifdef和#ifndef 命令的优点是:你可以在更大型的预处理器表达式中使用它的值。如下例所示:

#if defined( __unix__ ) && defined( __GNUC__ )
/* ... */
#endif

大多数编译器会提供预定义宏,例如上例所使用的宏,它用来识别目标系统和编译器。因此,在 Unix 系统中,通常预先定义好了宏 unix,而 GCC 编译器则会预先定义好了宏 GNUC。类似地,微软 Windows 平台上的 Visual C 编译器会自动定义好宏 _WIN32 和宏 _MSC_VER。

#ifdef 和 #ifndef 命令

通过#ifdef和#ifndef命令测试某个宏定义是否已被定义。它们的语法是:

#ifdef 标识符
#ifndef 标识符

这等同于下面的#if命令:

#if defined 标识符
#if !defined 标识符

如果 identifier(标识符) 不是宏名称,则 #ifndef 标识符后面的条件代码被保留。文章来源地址https://www.toymoban.com/news/detail-471267.html

到了这里,关于嵌入式C语言-预编译命令(#define、#if、#ifdef、#ifndef、#undef)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C语言之预处理命令使用详解----#if、#endif、#undef、#ifdef、#else、#elif

    查了好久才知道的这个原理,记录一下吧! 参考教程 预处理命令 在接触#if、#undef这类预处理指令前,大部分都都接触过#define、#include等预处理命令,通俗来讲预处理命令的作用就是在编译和链接之前,对源文件进行一些文本方面的操作,比如文本替换、文件包含、删除部分

    2024年02月02日
    浏览(31)
  • webrtc交叉编译嵌入式的方法

    背景是我们有嵌入式的需求,需要编译webrtc进入板子上。先说结论,最后是这样config之后就编译通过的: gn gen out/linux-yeshen --args=‘target_os=“linux” target_cpu=“arm64” ffmpeg_branding=“Chrome” proprietary_codecs=true is_debug=true target_sysroot=“…/…/linux/general_yeshen_arm64/aarch64-buildroot-linu

    2024年02月11日
    浏览(29)
  • ARM嵌入式编译器编译优化选项 -O

    Arm嵌入式编译器可以执行一些优化来减少代码量并提高应用程序的性能。不同的优化级别有不同的优化目标,不仅如此,针对某个目标进行优化会对其他目标产生影响。比如想减小生成的代码量,势必会影响到该代码的性能。所以优化级别总是这些不同目标(代码量,程序性

    2024年02月16日
    浏览(52)
  • 嵌入式系统——交叉编译概念与环境搭建

      本文属于嵌入式系统的基础知识,主要介绍编译过程和交叉编译。对于基于ARM内核的微处理器移植操作系统,不可避免的需要使用交叉编译。交叉编译指的是不同平台间编译程序代码的操作,不同平台有两方面:(1)不同的操作系统;(2)不同的处理器平台,如ARM和X

    2024年02月10日
    浏览(33)
  • 嵌入式:ARM常用开发编译软件介绍

    ADS(ARM Developer Suite),是在1993年由Metrowerks公司开发是ARM处理器下最主要的开发工具。 他的前身是SDT,SDT是ARM公司几年前的开发环境软件,目前SDT早已经不再升级。ADS包括了四个模块分别是:SIMULATOR;C 编译器;实时调试器;应用函数库。ADS对汇编、C/C++、java支持的均很好,

    2024年02月06日
    浏览(38)
  • 【ARM 嵌入式 编译系列 2.1 -- GCC 编译参数学习】

    请阅读 【ARM GCC 编译专栏导读】 上篇文章:ARM 嵌入式 编译系列 2 – GCC 编译过程介绍 下篇文章:ARM 嵌入式 C 入门及渐进 3 – GCC attribute ((weak)) 弱符号使用 上篇文章 ARM 嵌入式 编译系列 2 – GCC 编译过程介绍 已经介绍过了具体的编译流程,本篇文章主要介绍变过程中常见的

    2024年02月13日
    浏览(33)
  • 嵌入式Linux Qt交叉编译环境搭建

    TinkerBoard2主板,BuildRoot根文件系统,package自带的Qt版本为5.14.2,所以安装的版本也是5.14.2 安装的组件看个人需求,我都要了 默认安装路径/opt/Qt5.14.2/ 源码路径/opt/Qt5.14.2/5.14.2/Src/ 安装后选定的打包工具路径/opt/Qt5.14.2/5.14.2/(我的默认有gcc_64和android) 这种方法容易导致version `G

    2024年01月25日
    浏览(33)
  • 嵌入式-vim编辑器 gcc编译器

    目录 一.vim编辑器 1.1简介 1.2使用说明 1.2.1复制、粘贴与删除 1.2.2保存 二.gcc编译器 2.1基本介绍 2.2编译原理

    2024年02月09日
    浏览(36)
  • 【ARM 嵌入式 编译系列 3.5 -- gcc 链接参数介绍】

    请阅读 【嵌入式开发学习必备专栏 之 ARM GCC 编译专栏】 上篇文章【ARM 嵌入式 编译系列 3.4 – 查看所依赖库文件的路径 详细介绍】一直在提 链接参数 ,那么链接参数有哪些,它们又有什么作用呢? 如前一篇文章中的的链接参数到底是什么意思呢? -L : 指定了 链接库的路径

    2024年01月17日
    浏览(39)
  • 【ARM 嵌入式 编译系列 10.4 -- 生成二进制文件】

    在嵌入的工作中,经常会使用到二进制文件,那么我们如何自己生成一个二进制文件呢?接下来介绍如何将一个只包含将32位数据的文件转化为二进制文件,原文件如下(数据一共 64bytes): 我们使用 gcc 对齐先进行编译然后再进行反汇编: 具体命令如下: 通过上面命令会生成

    2024年02月02日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包