嵌入式C语言常用的5类预处理
yuyutoo 2024-12-14 15:53 2 浏览 0 评论
在嵌入式系统编程中不管是内核的驱动程序还是应用程序的编写,涉及到大量的预处理与条件编译,这样做的好处主要体现在代码的移植性强以及代码的修改方便等方面。因此引入了预处理与条件编译的概念。在C语言的程序中可包括各种以符号#开头的编译指令,这些指令称为预处理命令。预处理命令属于C语言编译器,而不是C语言的组成部分。通过预处理命令可扩展C语言程序设计的环境。预处理的行为是由指令控制的。这些指令是由#字符开头的一些命令。
一、预处理指令
大多数预处理器指令属于下面3种类型:
●宏定义:#define 指令定义一个宏,#undef指令删除一个宏定义。
●文件包含:#include指令导致一个指定文件的内容被包含到程序中。
●条件编译:#if,#ifdef,#ifndef,#elif,#else和#endif指令可以根据编译器可以测试的条件来将一段文 本包含到程序中或排除在程序之外。
剩下的#error,#line和#pragma指令更特殊的指令,较少用到。
二、宏定义
1、无参宏
#define 宏名 字符串
●宏名在源程序只能够若用引号括起来,则预处理程序不对其作宏替换。
●宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名。在宏展开时由预处理程序 层层替换。
●习惯上宏名可用大写字母表示,以方便与变量区别。但也允许用小写字母。
2、带参数的宏
#define 宏名(形参表) 字符串
在定义带参数的宏时,宏名和形参表之间不能有空格出现,否则,就将宏定义成为无参数形 式, 而导致程序出错。
Eg:#define ABS(x) (x)<0?-(x):(x) 这个宏定义中,如果x的值小于0,则使用一元运算符(-)对其 取负,得到正数。
3、预处理操作符#和##
在使用#define定义宏时,可使用操作符#在字符串中输出实参。Eg:
#define AREA(x,y) printf(“长为“#x”,宽为“#y”的长方形的面积:%d\n”,(x)*(y));
##与操作符#类似,操作符##也可用在带参宏中替换部分内容。该操作符将宏中的两个部分连 接成一个内容。例如,定义如下宏:
#define VAR(n) v##n
当使用一下方式引用宏:
VAR(1)
三、文件包含
#include< 文件名>
#include“文件名”
其扩展名可以是“.c”,表示包含普通C语言源程序。也可以是 “.h”,表示C语言程序的头文件。C语言 系统中大量的定义与声明是以头文件形式提供的。
用<>括起文件名,一般指系统头文件,编译程序将到C语言开发环境中设置好的 include文件中去 找指定的文件。
用""括起文件名则是自定义头文件
四、条件编译
1、单条件判断
#if
程序段
#else
程序段
#endif
该条件编译命令的执行过程为:若常量表达式的值为真(非0),则对程序段1进行编译,否则对程序 段2进行编译。因此可以使程序在不同条件下完成不同的功能。
2、多条件判断
#if 常量表达式 1
程序段 1
#elif 常量表达式 2
程序段 2
… …
#elif 常量表达式 n
程序段 n
#else
程序段 m
#endif
3、使用#ifdef和#ifndef
判断符号常量定义
#ifdef命令的使用格式如下:
#ifdef 标识符
程序段 1
#else
程序段 2
#endif
4、使用#defined和#undef
与#ifdef类似的,可以在#if命令中使用define来判断是否已定义指定的标识符。
#if defined 标识符
程序段 1
#endif
与下面的标示方式意义相同。
#ifdef 标识符
程序段 1
#endif
也可使用逻辑运算符,对defined取反。例如:
#if ! define 标识符
程序段 1
#endif
与下面的标示方式意义相同。
#ifndef 标识符
程序段 1
#endif
在#ifdef和#ifndef命令后面的标识符是使用#define进行定义的。在程序中,还可以使用#undef取 消对标识符的定义,其形式为:
#undef 标识符
Eg:
#define MAX 100
#undef MAX
五.其他预处理命令
1、预定义的宏名
ANSI C标准预定义了五个宏名,每个宏名的前后均有两个下画线,避免与程序员定义相同的宏名 (一般都不会定义前后有两个下划线的宏)。这5个宏名如下:
●__DATE__:当前源程序的创建日期。
● __FILE__:当前源程序的文件名称(包括盘符和路径)。
● __LINE__:当前被编译代码的行号。
● __STDC__:返回编译器是否位标准C,若其值为1表示符合标准C,否则不是标准C.
● __TIME__:当前源程序的创建时间。
Eg:
#include<stdio.h>
int main()
{
int j;
printf("日期:%s\n",__DATE__);
printf("时间:%s\n",__TIME__};
printf("文件名:%s\n",__FILE__);
printf("这是第%d行代码\n",__LINE__);
printf("本编译器%s标准C\n",(__STD__)?"符合":"不符合");
return 0;
}
2、行号和文件名命令#line
使用__LINE__预定义宏名编译的程序行号。使用#line命令可改变预定义宏__LINE__与__FILE__的内容,该命令的基本形如下:
#line number[“filename”]
其中的数字为一个正整数,可选的文件名为有效文件标识符。行号为源代码中当前行号,文件名为源文件的名字。命令为#line主要用于调试以及其他特殊应用。
Eg:
1:#include<stdio.h>
2:#include<stdlib.h>
4:#line 1000
6:int main()
7:{
8: printf("当前行号:%d\n",__LINE__);
9: return 0;
10:}
3、修改编译器设置命令 #pragma
#pragma命令的作用是设定编译器的状态,或者指示编译器完全一些特定的动作。#pragma命令对每个编译器给出了一个方法,在保持与C语言完全兼容的情况下,给出主机或者操作系统专有的特征。其格式一般为:
#pragma Para
其中,Para为参数,可使用的参数很多,下面列出常用的参数:
Message参数,该参数能够在编译信息输出窗口中输出对应的信息,这对于源代码信息的控制是非常重要的,其使用方法是:
#pragma message(消息文本)
当编译器遇到这条指令时,就在编译输出窗口中将消息文本显示出来。
另外一个使用比较多得pragma参数是code_seg.格式如:
#pragma code_seg([“section_name”[,section_class]])
它能够设置程序中函数代码存放的代码段,在开发驱动程序的时候就会使用到它。
参数once,可保证头文件被编译一次,其格式为:
#pragma once
只要在头文件的最开始加入这条指令就能够保证头文件被编译一次。
4、产生错误信息命令 #error
#error命令强制编译器停止编译,并输出一个错误信息,主要用于程序调试。其使用如下:
#error 信息错误
注意,错误信息不用双括号括起来。当遇到#error命令时,错误信息将显示出来。
例如,以下编译预处理器命令判断预定义宏__STDC__,如果其值不为1,则显示一个错误信息,提示程序员该编译器不支持ANSI C标准。
#if __STDC__!=1
#error NOT ANSI C
#endif
六、内联函数
在使用#define定义带参数宏时,在调用函数时,一般需要增加系统的开销,如参数传递,跳转控制,返回结果等额外操作需要系统内存和执行时间。而使用带参数宏时,通过宏替换可再编译前将函数代码展开导源代码中,使编译后的目标文件含有多段重复的代码。这样做,会增加程序的代码量,都可以减少执行时间。
在C99标准钟,还提供另外一种解决方法:使用内联函数。
在程序编译时,编译器将程序中出现的内联函数的调用表达式用内联函数的函数体来进行替代。显然,这种做法不会产生转去转回得问题。都是由于在编译时将函数体中的代码被替代到程序中,因此会增加目标代码量,进而增加空间的开销,而在时间开销上不像函数调用时那么大,可见它是以增加目标代码为代码来换取时间的节省。
定义内联函数的方法很简单,只要在定义函数头的前面加上关键字inline即可。内联函数的定义与一般函数一样。例如,定于一个两个整数相加的函数:
#include<stdio.h>
#include<stdlib.h>
inline int add(int x,int y);
inline int add(int x,int y)
{
return x+y;
}
int main()
{
int i,j,k;
printf("请输入两个整数的值:\n");
scanf("%d %d",&i,&j);
k=add(i,j);
printf("k=%d\n",k);
return 0;
}
在程序中,调用函数add时,该函数在编译时会将以上代码复制过来,而不是像一般函数那样是运行时被调用。
内联函数具有一般函数的特性,它与一般函数所不同之处在于函数调用的处理。一般函数进行调用时,要讲程序执行权转导被调函数中,然后再返回到调用到它的函数中;而内联函数在调用时,是将调用表达式用内联函数体来替换。在使用内联函数时,应该注意如下几点:
● 在内联函数内部允许用循环语句和开关语句。
● 内联函数的定义必须出现在内联函数第一次被调用之前。
其实,在程序中声明一个函数为内联时,编译以后这个函数不一定是内联的,
即程序只是建议编译器使用内联函数,但是编译器会根据函数情况决定是否使用内联,所以如果编写的内联函数中出现循环或者开关语句,程序也不会提示出错,但那个函数已经不是内联函数了。
一般都是讲一个小型函数作为内联函数。
大家如果对编程感兴趣,想了解更多的编程知识,解决编程问题,咨询编程学习,可以关注我们的微信公众号:程序员互动联盟(coder_online),这里有java高手、C++/C高手、windows/Linux高手等你来。
相关推荐
- Mysql和Oracle实现序列自增(oracle创建序列的sql)
-
Mysql和Oracle实现序列自增/*ORACLE设置自增序列oracle本身不支持如mysql的AUTO_INCREMENT自增方式,我们可以用序列加触发器的形式实现,假如有一个表T_WORKM...
- 关于Oracle数据库12c 新特性总结(oracle数据库19c与12c)
-
概述今天主要简单介绍一下Oracle12c的一些新特性,仅供参考。参考:http://docs.oracle.com/database/121/NEWFT/chapter12102.htm#NEWFT...
- MySQL CREATE TABLE 简单设计模板交流
-
推荐用MySQL8.0(2018/4/19发布,开发者说同比5.7快2倍)或同类型以上版本....
- mysql学习9:创建数据库(mysql5.5创建数据库)
-
前言:我也是在学习过程中,不对的地方请谅解showdatabases;#查看数据库表createdatabasename...
- MySQL面试题-CREATE TABLE AS 与CREATE TABLE LIKE的区别
-
执行"CREATETABLE新表ASSELECT*FROM原表;"后,新表与原表的字段一致,但主键、索引不会复制到新表,会把原表的表记录复制到新表。...
- Nike Dunk High Volt 和 Bright Spruce 预计将于 12 月推出
-
在街上看到的PandaDunk的超载可能让一些球鞋迷们望而却步,但Dunk的浪潮仍然强劲,看不到尽头。我们看到的很多版本都是为女性和儿童制作的,这种新配色为后者引入了一种令人耳目一新的新选择,而...
- 美国多功能舰载雷达及美国海军舰载多功能雷达系统技术介绍
-
多功能雷达AN/SPY-1的特性和技术能力,该雷达已经在美国海军服役了30多年,其修改-AN/SPY-1A、AN/SPY-1B(V)、AN/SPY-1D、AN/SPY-1D(V),以及雷神...
- 汽车音响怎么玩,安装技术知识(汽车音响怎么玩,安装技术知识视频)
-
全面分析汽车音响使用或安装技术常识一:主机是大多数人最熟习的音响器材,有关主机的各种性能及规格,也是耳熟能详的事,以下是一些在使用或安装时,比较需要注意的事项:LOUDNESS:几年前的主机,此按...
- 【推荐】ProAc Response系列扬声器逐个看
-
有考牌(公认好声音)扬声器之称ProAcTablette小音箱,相信不少音响发烧友都曾经,或者现在依然持有,正当大家逐渐掌握Tablette的摆位设定与器材配搭之后,下一步就会考虑升级至表现更全...
- #本站首晒# 漂洋过海来看你 — BLACK&DECKER 百得 BDH2000L无绳吸尘器 开箱
-
作者:初吻给了烟sco混迹张大妈时日不短了,手没少剁。家里有了汪星人,吸尘器使用频率相当高,偶尔零星打扫用卧式的实在麻烦(汪星人:你这分明是找借口,我掉毛是满屋子都有,铲屎君都是用卧式满屋子吸的,你...
- 专题|一个品牌一件产品(英国篇)之Quested(罗杰之声)
-
Quested(罗杰之声)代表产品:Q212FS品牌介绍Quested(罗杰之声)是录音监听领域的传奇品牌,由英国录音师RogerQuested于1985年创立。在成立Quested之前,Roger...
- 常用半导体中英对照表(建议收藏)(半导体英文术语)
-
作为一个源自国外的技术,半导体产业涉及许多英文术语。加之从业者很多都有海外经历或习惯于用英文表达相关技术和工艺节点,这就导致许多英文术语翻译成中文后,仍有不少人照应不上或不知如何翻译。为此,我们整理了...
- Fyne Audio F502SP 2.5音路低音反射式落地音箱评测
-
FyneAudio的F500系列,有新成员了!不过,新成员不是新的款式,却是根据原有款式提出特别版。特别版产品在原有型号后标注了SP字样,意思是SpecialProduction。Fyne一共推出...
- 有哪些免费的内存数据库(In-Memory Database)
-
以下是一些常见的免费的内存数据库:1.Redis:Redis是一个开源的内存数据库,它支持多种数据结构,如字符串、哈希表、列表、集合和有序集合。Redis提供了快速的读写操作,并且支持持久化数据到磁...
- RazorSQL Mac版(SQL数据库查询工具)
-
RazorSQLMac特别版是一款看似简单实则功能非常出色的SQL数据库查询、编辑、浏览和管理工具。RazorSQLformac特别版可以帮你管理多个数据库,支持主流的30多种数据库,包括Ca...
你 发表评论:
欢迎- 一周热门
-
-
前端面试:iframe 的优缺点? iframe有那些缺点
-
带斜线的表头制作好了,如何填充内容?这几种方法你更喜欢哪个?
-
漫学笔记之PHP.ini常用的配置信息
-
其实模版网站在开发工作中很重要,推荐几个参考站给大家
-
推荐7个模板代码和其他游戏源码下载的网址
-
[干货] JAVA - JVM - 2 内存两分 [干货]+java+-+jvm+-+2+内存两分吗
-
正在学习使用python搭建自动化测试框架?这个系统包你可能会用到
-
织梦(Dedecms)建站教程 织梦建站详细步骤
-
【开源分享】2024PHP在线客服系统源码(搭建教程+终身使用)
-
2024PHP在线客服系统源码+完全开源 带详细搭建教程
-
- 最近发表
-
- Mysql和Oracle实现序列自增(oracle创建序列的sql)
- 关于Oracle数据库12c 新特性总结(oracle数据库19c与12c)
- MySQL CREATE TABLE 简单设计模板交流
- mysql学习9:创建数据库(mysql5.5创建数据库)
- MySQL面试题-CREATE TABLE AS 与CREATE TABLE LIKE的区别
- Nike Dunk High Volt 和 Bright Spruce 预计将于 12 月推出
- 美国多功能舰载雷达及美国海军舰载多功能雷达系统技术介绍
- 汽车音响怎么玩,安装技术知识(汽车音响怎么玩,安装技术知识视频)
- 【推荐】ProAc Response系列扬声器逐个看
- #本站首晒# 漂洋过海来看你 — BLACK&DECKER 百得 BDH2000L无绳吸尘器 开箱
- 标签列表
-
- mybatis plus (70)
- scheduledtask (71)
- css滚动条 (60)
- java学生成绩管理系统 (59)
- 结构体数组 (69)
- databasemetadata (64)
- javastatic (68)
- jsp实用教程 (53)
- fontawesome (57)
- widget开发 (57)
- vb net教程 (62)
- hibernate 教程 (63)
- case语句 (57)
- svn连接 (74)
- directoryindex (69)
- session timeout (58)
- textbox换行 (67)
- extension_dir (64)
- linearlayout (58)
- vba高级教程 (75)
- iframe用法 (58)
- sqlparameter (59)
- trim函数 (59)
- flex布局 (63)
- contextloaderlistener (56)