《C语言参悟之旅》-读书笔记(七)


                                                                                 第八章 预处理

cpp源文件——(预编译)》预处理文件——(编译)》目标文件——(连接)》可执行文件

预处理:源文件在进行编译时第一遍扫描之前做的工作(词法分析和语法分析)

程序员与预处理器进行交互的工具是一种被称作预处理器指示的命令(一些以“#”号开头的单行命令)

编译的源文件test.cpp

#include <stdio.h>
#define  PI  3.1415926
int main( )
{
	int r = 3;
	float girth, area;
	girth = 2*PI*r;
	area = PI*r*r;
	printf("周长为:%f 面积为: %f\n",girth, area );
}

与预编译文件test.i文件

#line 713 "c:\\program files\\microsoft visual studio 10.0\\vc\\include\\stdio.h"

__declspec(deprecated("The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: " "_tempnam" ". See online help for details.")) __declspec(dllimport) char * __cdecl tempnam(   const char * _Directory,    const char * _FilePrefix);



#line 719 "c:\\program files\\microsoft visual studio 10.0\\vc\\include\\stdio.h"

 __declspec(deprecated("The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: " "_fcloseall" ". See online help for details.")) __declspec(dllimport) int __cdecl fcloseall(void);
 __declspec(deprecated("The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: " "_fdopen" ". See online help for details.")) __declspec(dllimport) FILE * __cdecl fdopen(  int _FileHandle,    const char * _Format);
 __declspec(deprecated("The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: " "_fgetchar" ". See online help for details.")) __declspec(dllimport) int __cdecl fgetchar(void);
 __declspec(deprecated("The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: " "_fileno" ". See online help for details.")) __declspec(dllimport) int __cdecl fileno(  FILE * _File);
 __declspec(deprecated("The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: " "_flushall" ". See online help for details.")) __declspec(dllimport) int __cdecl flushall(void);
 __declspec(deprecated("The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: " "_fputchar" ". See online help for details.")) __declspec(dllimport) int __cdecl fputchar(  int _Ch);
 __declspec(deprecated("The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: " "_getw" ". See online help for details.")) __declspec(dllimport) int __cdecl getw(   FILE * _File);
 __declspec(deprecated("The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: " "_putw" ". See online help for details.")) __declspec(dllimport) int __cdecl putw(  int _Ch,    FILE * _File);
 __declspec(deprecated("The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: " "_rmtmp" ". See online help for details.")) __declspec(dllimport) int __cdecl rmtmp(void);

#line 731 "c:\\program files\\microsoft visual studio 10.0\\vc\\include\\stdio.h"


}
#line 735 "c:\\program files\\microsoft visual studio 10.0\\vc\\include\\stdio.h"

#pragma pack(pop)

#line 739 "c:\\program files\\microsoft visual studio 10.0\\vc\\include\\stdio.h"

#line 3 "c:\\users\\hzp\\documents\\visual studio 2010\\projects\\test03\\test03\\test03.cpp"

int main( )
{
	int r;
	float girth, area;
	girth = 2*3.1415926*r;
	area = 3.1415926*x*x;
	printf("周长为:%d 面积为: %f\n",girth, area );
}

对比两个文件的差别,得出预编译所做的工作:

1)引入:删除#include指示,并将stdio.h的内容加入到程序中来对其进行响应,

2)替换:删除恶劣#define指示,并将该文件所有PI替换为指定值

3)把注释替换成一个空格字符

4)删除一些不必要的空格

Ps:这个.i文件有助于调试错误程序

一、宏定义:

 I.无参宏定义

#define  PI  3.1415926  //指定PI的值为3.1415926
#define  ARRAY_LEN 80  //指定数组长度为80
#define  CR  '\r'      //指定CR表示换行符

作用:无参宏定义经常被用于给数值、字符、和字符串命名

优点:

           A.简化输入

           B.可读性

           C.一改全改

 II.带参宏定义

#include <stdio.h>
#define  MAX(x,y)   (((x)>(y)? (x):(y)))
int main( )
{
	int i = 0;
	int j = 1;
	printf("the larger of two:%d", MAX(i,j));
}

作用:带参宏定义经常被用作模板:如上例中  (((x)>(y)? (x):(y)))如果要经常使用(或是某些语句)
若使用宏替换可以设法得到更多的结果

#include <stdio.h>
#define  MAX_MIN(x,y,max,min)   max =(((x)>(y)? (x):(y))); min = (((x)<(y)? (x):(y)));
int main( )
{
	int i = 0;
	int j = 1;
	int max  = 0;
	int min  = 0;
	
	MAX_MIN(i, j, max,min);
	printf("两个数中的最大数是:%d\n",max);
	printf("两个数中的最小数是:%d\n",min);
}

 

说明

 1)  函数调用是在程序运行时处理的,必须为形参分配临时空间,而宏的展开则是编译前进行(不分配存储单元,也不进行值传递);

 2)宏名没有类型,其参数也没有类型,而函数的参数和返回值都是有特定类型的;

 3)无法用一个指针来指向一个宏,但可以使用一个指针指向一个函数;

III.使用宏时注意事项

1)宏定义中的各种符号都被视为替换文本,可能会引起一些错误(编译器会直接找出错误的地方,而不会找到错误的根源——宏定义本身)

#define  LENGTH  = 100;//加入了分号
int a[LENGTH];
预编译后:

int a[= 100;];

2)#define的有效范围是从其定义命令开始到此源文件结束。但可以使用#undef指示宏定义的作用域

#undef  PI //取消前面定义的圆周率的宏但如果前面没有定义,那么此句不起任何作用

3)宏可以嵌套定义

#define R     1.5
#define PI    3.1415926
#define L     2*PI*R

4)宏名作为整体被替换

#define   LENGTH   10
int  ARRAY_LENGTH   //只会替换LENGTH;而不会替换ARRAY_LENGTH

5) 必要的括号

#define  MUL(x, y)    x*y
result = MUL(5+3,6);
//被替换成5+3*6
//正确的写法加括号
#define  MUL(x, y)    ((x)*(y))//因为圆括号的运算级别最高,这样替换可以按照编程者的思路进行,而不会因为其他运算符的优先级而出现非预期的结果                                      //且参数的每一出现都要加括号,而不是只给替换文本加括号
     
#define PER(x)     (x/100)
 j = PER(i+1 );
//预编译后:
 j = i+1/100;

6)太长的情况下使用“\”:

          #define  PF(one, two,three,four,five)         printf("%d,%d,%d,%d,%d,%d"\n),  \

          one ,two,three, four,five)

7)  宏不允许重复定义

 

IV.控制版本的宏

#include <stdio.h>
int main( )
{
   printf("编译日期为: %d\n", __LINE__);
   printf("编译日期为: %s\n", __FILE__);
   printf("编译日期为: %s\n", __DATE__);
   printf("编译日期为: %s\n", __TIME__);
}

二.条件编译

1.第一种形式

#ifdef  标识符  
   程序片段1
#else
    程序片段2
#endif

说明  如果 标识符已经被#define指示定义过,则对程序片段1进行编译,否则对程序片段2进行编译

2.第二种形式

#ifndef  标识符
  程序片段1
#else
   程序片段2
#endif

说明:如果标识符未被define命令定义过,则对程序片段1进行编译,否则对程序片段2进行编译

3.第三种形式

#if 常量表达式1
    程序片段1
#elif 常量表达式2
    程序片段2
#else
     程序片段3
#endif

 说明:如果常量表达式的值为真,则编译程序片段1.......

4.条件编译的作用:

  1)编写在多台机器或多种操作系统之间可移植的程序

#if   defined(Windows)
  .......
#elif  defined (DOS)
   .....
#else  defined(UNIX)
    .....
#endif

  2)编写在不同的编译器上进行编译的程序

#if __STDC__
  标准C函数原型
#else
  其他C函数声明
#endif

  3)防止重复包含

三.文件包含
文件包含指一个源文件可以包含另一个源文件的全部或部分内容

#include<文件名>


使用尖括号时,预处理器到存放库函数头文件所在的目录中寻找要包含的文件

#indclude"文件名"


使用双括号时,系统先在用户当前的目录中寻找要包含的文件,如果找不到,则再到存放库函数所在的文件中查找

注意事项:

1)一个include只能包含一个文件

#include<file1.h>       <file2.h>//一个include只能包含一个文件

2)嵌套包含时使用条件编译来防止重复包含:

#ifndef     FIRSTINCLUDE_H
#define     FIRSTINCLUDE_H
.....
#endif

计算器的实例

limit.h:提供一些对于运算对象的限制和转换

#ifndef  LIMT_H
#define  LIMT_H
#define  TO_LONG(x)   (long)x               //将参数转换为long型
#define  IS_PLUS(x)   (x >= 0 ? 1:0)        //判断参数是否为正数
#define  CHECK_ZERO(divisor)  (divisor) == 0?1:0 //判断除数是否为0的宏

#endif

count.h:提供各种形式运算的宏定义以及运算函数原型

#ifndef  COUNT_H
#define  COUNT_H
#include <math.h>

#define   SUM(x,y)  ((x)+(y))    //计算加法的宏
#define   SUB(x,y)  ((x)-(y))
#define   MUL(x,y)  ((x)*(y))
#define   DIV(x,y)  ((x)/(y))
#define   MOD(x,y)  ((x)%(y))
#define   SQUARE(x)  ((x)*(x))
#define   CUBE(x)  ((x)*(x)*(x))
#define   SQRT(x)  sqrt(x)

void count(char);

#endif 
myio.h:提供各种形式的输入和输出操作
#ifndef  MYIO_H 
#define  MYIO_H 
#ifdef  _cplusplus
#include <iostream>
#else
#include <stdio.h>
#endif

#define  CLUE_OPE   printf("请输入一个运算符: +表示加法,-表示减法,*表示乘法,/表示除法,\n                  %%表示求余,2表示乘方,3表示立方,s表示开方\n")//输出提示信息的宏
#define  CLUE_ONE   printf("请输入一个运算对象:\n")  //提示输入两个运算对象的宏
#define  CLUE_TWO   printf("请输入两个个运算对象:\n")  //提示输入一个运算对象的宏
#define  CLUE_ZERO  printf("你输入的被除数为0,请重新输入:\n")//提示被除数不能为0的宏
#define  CLUE_PLUS  printf("开平方的书必须是正数,请重新输入:\n") //提示被开方的数必须为正数
#define  IN_OP(x)      x = getchar()               //输入运算符的宏
#define  IN_O(X)      scanf("%lf",&x)     //输入一个数据的宏
#define  IN_T(x,y)    scanf("%lf%lf", &x,&y) //输入两个数据的宏
#define  OUT_D(x)   printf("计算结果为:%f\n",x)  //输出double类型的宏
#define  OUT_L(x)   printf("计算结果为:%lf\n",x)  //输出double类型的宏
#endif

demo.c:演示各种运算

#include "count.h"
#include "limt.h"
#include "myio.h"
int main()
{
	char c;
	
	while(1)
	{
		CLUE_OPE;
	        count(IN_OP(c));
		fflush(stdin);
	}
	return 0;
}

void count(char c)
{
	double  x ,y;
	switch(c)
  {

	
	
	case '+':
		CLUE_TWO;
		IN_T(x,y);
		OUT_D(SUM(x,y));
		break;
	case '-':
		CLUE_TWO;
		IN_T(x,y);
		OUT_D(SUB(x,y));
		break;
	case'*':
		CLUE_TWO;
		IN_T(x,y);
		OUT_D(MUL(x,y));
		break;
	case'/':
		CLUE_TWO;
		IN_T(x,y);
		while(CHECK_ZERO(y))
		{  
			CLUE_ZERO;
			IN_T(x,y);
		}
		OUT_D(DIV(x,y));
		break;
	case'%':
		CLUE_TWO;
		IN_T(x,y);
		while(CHECK_ZERO(y))
		{  
			CLUE_ZERO;
			IN_T(x,y);
		}
		OUT_L(MOD(TO_LONG(x),TO_LONG(y)));
		break;
	case'2':
		CLUE_ONE;
		IN_O(x);
		OUT_D(SQUARE(x));
		break;
	case'3':
		CLUE_ONE;
		IN_O(x);
		OUT_D(CUBE(x));
		break;
	case's':
		CLUE_ONE;
		IN_O(x);
		while(!IS_PLUS(x))
		{
			CLUE_PLUS;
			IN_O(x);
		}
		OUT_D(SQRT(x));
		break;
	}
	
}


 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值