玖叶教程网

前端编程开发入门

嵌入式C语言常用的5类预处理(嵌入式c语言编程规范)

在嵌入式系统编程中不管是内核的驱动程序还是应用程序的编写,涉及到大量的预处理与条件编译,这样做的好处主要体现在代码的移植性强以及代码的修改方便等方面。因此引入了预处理与条件编译的概念。在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高手等你来。

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言