C语言基本概念理解

 

1.宏定义和函数的区别

-------------------------------------------------------------------------------------------------------------------

宏:宏定义是C提供的三种预处理功能的其中一种,这三种预处理包括:

(1)宏定义

(2)文件包含

(3)条件编译

1.不带参数的宏定义:

格式:#define 标识符 字符串

     标示符就是可以替换字符串的宏名称,编译器在进行预处理过程中对使用宏替换的地方展开,用“字符串”替换宏名称,这样做的好处就是可以对数字或者随机数值指定一个有代表意义的名称,提高程序的可读性和通用性,在后期维护中可以方便的修改宏定义中字符串的数值大小或者其他数值类型,从而可以控制整个代码中所有引用宏名的地方。

     从占用资源的角度看,编译器对宏替换不会占用或者分配内存,和函数比较,调用子函数需要分配内存,增加系统开销,但子函数可以把程序结构模块化,提高程序的可读性和聚合度,对比之下,宏也可以有参数,如果在程序中为了不调用子函数而减小开销,那么所有过程都写在一个函数中,并且没有自注释的名称,程序的可读性就会降低,毕竟代码是处理现实世界中事务数据关系的一种抽象,但不是一个人的,应该是像一首简洁,优美的诗,描述了美好的事务,所以折中之下,宏替换是个不错的选择。

      虽然宏替换占用了编译器的时间,所谓“有得必有失”,减小了程序执行的资源消耗,这未尝不是一种平衡。

     宏的一些特点(引用):

(1)宏名一般用大写

(2)使用宏可提高程序的通用性和易读性,减少不一致性,减少输入错误和便于修改。例如:数组大小常用宏定义 ,可以理解数组大小代表具体含义,便于二次维护。

(3)预处理是在编译之前的处理,而编译工作的任务之一就是语法检查,预处理不做语法检查。

(4)宏定义末尾不加分号;

(5)宏定义写在函数的花括号外边,作用域为其后的程序,通常在文件的最开头。 (6)可以用#undef命令终止宏定义的作用域

(7)宏定义可以嵌套

(8)字符串" "中永远不包含宏

(9)宏定义不分配内存,变量定义分配内存。

带参数的宏:

格式: #define S(r) r*r    

(1)实参如果是表达式容易出问题

area=S(a+b);第一步换为area=r*r;,第二步被换为area=a+b*a+b;

正确的宏定义是#define S(r) ((r)*(r)) 。

(2)宏名和参数的括号间不能有空格

(3)宏替换只作替换,不做计算,不做表达式求解

(4)函数调用在编译后程序运行时进行,并且分配内存。宏替换在编译前进行,不分配内存

(5)宏的哑实结合不存在类型,也没有类型转换。

(6)函数只有一个返回值,利用宏则可以设法得到多个值

(7)宏展开使源程序变长,函数调用不会

(8)宏展开不占运行时间,只占编译时间,函数调用占运行时间(分配内存、保留现场、值传递、返回值。

(9):#define Uint unsigned int 即用 Uint 代替unsigned int 减小了书写的长度,还有就是不同操作系统的通用变量识别可以统一一致。

引用:宏定义其他冷门、重点知识(实际程序设计中也会用到)

       (1) #define NAME "zhangyuncong"

  程序中有"NAME"则,它会不会被替换呢?

  (2) #define 0x abcd

  可以吗?也就是说,可不可以用把标识符的字母替换成别的东西?

  (3) #define NAME "zhang

  这个可以吗?

  (4) #define NAME "zhangyuncong"

  程序中有上面的宏定义,并且,程序里有句:

  NAMELIST这样,会不会被替换成"zhangyuncong"LIST

  四个题答案都是否定的。

===================================================================

        第一个,""内的东西不会被宏替换。这一点应该大都知道。

  第二个,宏定义前面的那个必须是合法的用户标识符

  第三个,宏定义也不是说后面东西随便写,不能把字符串的两个""拆开。

  第四个:只替换标识符,不替换别的东西。NAMELIST整体是个标识符,而没有NAME标识符,所以不替换。

也就是说,这种情况下记住:#define 第一位置第二位置

  (1) 不替换程序中字符串里的东西。

  (2) 第一位置只能是合法的标识符(可以是关键字)

  (3) 第二位置如果有字符串,必须把""配对。

  (4) 只替换与第一位置完全相同的标识符 。

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

  则遇到MAX(1+2,value)则会把它替换成:

  ((1+2)>(value)?(1+2):(value))

  注意事项和无参宏差不多。

  但还是应注意

  #define FUN(a) "a"

  则,输入FUN(345)会被替换成什么?

  其实,如果这么写,无论宏的实参是什么,都不会影响其被替换成"a"的命运。

  也就是说,""内的字符不被当成形参,即使它和一模一样。

  那么,你会问了,我要是想让这里输入FUN(345)它就替换成"345"该怎么实现呢?

  请看下面关于#的用法

3、 有参宏定义中#的用法

  #define STR(str) #str

  #用于把宏定义中的参数两端加上字符串的""

  比如,这里STR(my#name)会被替换成"my#name"

  一般由任意字符都可以做形参,但以下情况会出错:

  STR())这样,编译器不会把“)”当成STR()的参数。

  STR(,)同上,编译器不会把“,”当成STR的参数。

  STR(A,B)如果实参过多,则编译器会把多余的参数舍去。(VC++2008为例)

  STR((A,B))会被解读为实参为:(A,B),而不是被解读为两个实参,第一个是(A第二个是B)。

  4、 有参宏定义中##的用法

  #define WIDE(str) L##str

  则会将形参str的前面加上L

  比如:WIDE("abc")就会被替换成L"abc"

  如果有#define FUN(a,b) vo##a##b()

  那么FUN(id ma,in)会被替换成void main()

  5、 多行宏定义:

  #define doit(m,n) for(int i=0;i<(n);++i)\

  {\

  m+=i;\

  }

其中有参宏定义中 #define  WIDE("abc")  L##str的用法,在高焕堂著作中《UML+OOPC 嵌入式开发精讲》中实现轻量级面向对象的方法中就使用了 ##的用法和多行宏定义用法。

所以根据上述特点使用好宏定义,可以写出优美的代码,防止程序中的不一致错误,提高可移植性,可读性,方便修改维护。只要所有引用宏定义的地方都可以做到一次修改就完成。

另外在很多源代码中有些宏就是函数名称。直接重新定义了函数的名称,增加了代码的可阅读程度。

 条件编译:最开始可能也是出于提高程序设计的方便,对于源程序中某些内容只在特定条件下进行编译,于是乎条件编译就出现了,引用百科“一般情况下,源程序中所有的行都参加编译。但有时希望对其中一部分内容只在满足一定条件下才进行编译,即对一部分内容指定编译条件,这就是“条件编译”(conditional compile)。条件编译语句排版时,需考虑以下三种位置:1)条件编译语句块与函数定义体之间不存在相互嵌套(主要在(.h)文件中);2)条件编译语句块嵌套在函数体之外(主要在(.c)文件中);3)条件编译语句嵌套在函数体内 (主要在(.c)文件中)。条件编译指令将决定那些代码被编译,而哪些是不被编译的。可根据表达式的值或某个特定宏是否被定义来确定编译条件。”

      控制开发可以是某个宏名是否被定义或者表达式的值来确定是否编译某部分代码。

引用:

文件包含命令的一般格式是:#include "文件名"或 #include <文件名>

作用:预处理时,把“文件名”指定的文件内容复制到本文件,再对合并后的文件进行编译。

用途:避免重复劳动

说明:

1.双引号和尖引号的区别:双引号表示系统现在当前目录中寻找file所在的目录,若找不到,再按系统指定的

系统指定的标准方式寻找其它目录;尖引号仅查找按系统标准方式指定的目录.

2.一个#define只能指定一个包含文件,如 果要把多个文件都嵌入到源文件之中,则必须使用多个#define。

3.C语言允许嵌套使用#define。

在file1.c文件中,有文件包含命令#include "file2.c",预处理时,先把file2.c的内容复制到文件

file1.c,再对file1.c进行编译。

从理论上说,#include命令可以包含任何类型的文件,只要这些文件的内容被扩展后符合C语言语法。

2、被包含文件与其所在文件,在预处理后,成为一个文件,因此,如果被包含文件定义有全局变量,在其

它文件中不必用extern关键字声明。但一般不在被包含文件中定义变量。

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值