预处理大致解析(参见《C语言深度解剖》更好得到学习)

一、预定义符号

1.C语言设置了一些预定义符号,可以直接使用,这些符号在预处理阶段会被处理!
  __ FILE __ //进行编译的源文件
  __ LINE __ //文件当前的行号
  __ DATE __ //文件被编译的日期
  __ TIME __ //文件编译的时间
  __ STDC __ //如果编译器遵循ANSI C,其值为1,否则未定义!

//测试 - - -

int main() {
	printf("%s\n", __FILE__);
	printf("%d\n", __LINE__);
	printf("%s\n", __DATE__);
	printf("%s\n", __TIME__);
	return 0;
}

1

二、#define

1.#define 定义常量
#define M 100
//注意后面不加(;),加了可能在替换是会出错!

#define forever for(;;)
int main() {
	//for循环的判断部分什么都不写,可能会造成死循环!
	forever;
	return 0;
}

2.#define 定义宏
//宏的定义是参数进行文本替换,只是在预处理阶段的替换,没有什么类型啥的说法!
//语法:#define name( parament-list ) stuff
//说明:左括号必须与name紧邻,如果隔开,解释成为stuff的一部分!
//#define 定义的宏括号问题!
#define SQUARE(X) XX
int main() {
printf(“%d\n”, SQUARE(5));
int a = 2;
printf(“%d\n”, SQUARE(a+1));
return 0;
}

1
//明显第二个有问题,宏定义的本质是等效替换,第二个替换就出问题了,成 //a+1a+1 = 2+2+1 = 5,所以这类定义一定要在替换的参数上加(),则可以这样定义
#define SQUEARE(X) ((X)*(X))
//这样,在任何情况下,没有问题!

//带副作用的宏问题!
//这个主要是++引起的问题!
//直接看列子

int main() {
	int a = 1;
	int b = 2;
	int max1 = MAX(a, b);
	//上面文本替换
	//((a)>(b)?(a):(b))
	printf("max1 = %d\n", max1); //2
	//++带来的副作用
	int max2 = MAX(a++, b++);
	//((a++)>(b++)?(a++):(b++))
	//a = 1,b = 2
	//a++,b++;-->  a = 2,b == 3,
	//因为b大
	//所以返回b++;(先使用,后++)
	//表达式 == 3:
	// a = 2,b = 4;
	printf("max2 = %d\n", max2); //3
	printf("a = %d\n", a);
	printf("b = %d\n", b);
	return 0;
}

int main() {
int a = 1;
int b = 2;
int max1 = MAX(a, b);
//上面文本替换
//((a)>(b)?(a):(b))
printf(“max1 = %d\n”, max1); //2
//++带来的副作用
int max2 = MAX(a++, b++);
//((a++)>(b++)?(a++):(b++))
//a = 1,b = 2
//a++,b++;–> a = 2,b == 3,
//因为b大
//所以返回b++;(先使用,后++)
//表达式 == 3:
// a = 2,b = 4;
printf(“max2 = %d\n”, max2); //3
printf(“a = %d\n”, a);
printf(“b = %d\n”, b);
return 0;
}

//上面副作用的代码已解释额!
---------------------------------

//宏替换规则
// 在程序中扩展#define定义符号和宏时,需要涉及⼏个步骤。
// * 在调⽤宏时,⾸先对参数进⾏检查,看看是否包含任何由#define定义的符号。如果是,它们⾸先被替换。
// * 替换⽂本随后被插⼊到程序中原来⽂本的位置。对于宏,参数名被他们的值所替换。
// * 最后,再次对结果⽂件进⾏扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。

 宏参数和#define定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。
 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。

----------------------------------------
//和宏函数的对比
//宏函数通常执行简单的运算
#define MAX(x,y) ((x)>(y)?(x):(y))
//写简单代码宏的优势
//1.⽤于调⽤函数和从函数返回的代码可能⽐实际执⾏这个⼩型计算⼯作所需要的时间更多。所以宏⽐函数在程序的规模和速度⽅⾯更胜⼀筹。
2. 更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使⽤。反之这个宏怎可以适⽤于整形、⻓整型、浮点型等可以⽤于 > 来⽐较的类型。宏是类型⽆关的。
//在这儿说一下,宏的最大优势就是无论什么,都可以文本替换

//和函数相比宏的劣势
//1.每次使⽤宏的时候,⼀份宏定义的代码将插⼊到程序中。除⾮宏⽐较短,否则可能⼤幅度增加程序的⻓度。Y//2.宏不能调试
//3.宏与类型无关,不够严谨
//4.宏处理不好一定会带来优先级问题!


//下面对malloc进行宏的处理
#define MALLOC(TYPE,NUM) (TYPE*) malloc(sizeof(TYPE)*NUM)

example: int * tmp = MALLOC(int,10);
1

//下面介绍两个不常见的用法!
//#运算符
//#运算符将宏的⼀个参数转换为字符串字⾯量。它仅允许出现在带参数的宏的替换列表中。#运算符所执⾏的操作可以理解为”字符串化“
//也就是说在宏的替换参数前面加#,会把参数转换成字符串

//这儿宏定义所谓的  #参数  把参数替换后解析成一个字符串
#define PRINT(val,format) printf("the val of "   #val  " is "  format "\n",val)

int main() {
	int a = 1;
	int b = 2;
	PRINT(a, "%d");
	PRINT(b, "%d");
	return 0;
}

//这儿宏定义所谓的 #参数 把参数替换后解析成一个字符串
#define PRINT(val,format) printf("the val of " #val " is " format “\n”,val)
int main() {
int a = 1;
int b = 2;
PRINT(a, “%d”);
PRINT(b, “%d”);
return 0;
}

1

//##运算符问题
//##的作用是把它两边的符号合成一个符号
//可以是把宏的参数替换后,进行合成
//下面看列子
//写宏函数

//\续航符后面什么也不要(空格也不行)
//下面##把type和max连接起来
#define GENERIC_MAX(type)\
type type##_max(type x,type y)\
{\
	return ((x)>(y)?(x):(y));\
}

//声明函数
GENERIC_MAX(int)
GENERIC_MAX(float)

int main() {
	//直接使用
	int a = 1;
	int b = 2;
	int max = int_max(a, b);
	printf("max = %d\n", max);
	return 0;
}

//\续航符后面什么也不要(空格也不行)
//下面##把type和max连接起来
#define GENERIC_MAX(type)
type type##_max(type x,type y)
{
return ((x)>(y)?(x):(y));
}
//声明函数
GENERIC_MAX(int)
GENERIC_MAX(float)
int main() {
//直接使用
int a = 1;
int b = 2;
int max = int_max(a, b);
printf(“max = %d\n”, max);
return 0;
}

1

三、其他预处理指令

1、#undef NAME
//如果现存的⼀个名字需要被重新定义,那么它的旧名字首先要被移除。

2、许多C的编译器提供了⼀种能⼒,允许在命令⾏中定义符号。⽤于启动编译过程。
例如:当我们根据同⼀个源⽂件要编译出⼀个程序的不同版本的时候,这个特性有点⽤处。(假定某个程序中声明了⼀个某个⻓度的数组,如果机器内存有限,我们需要⼀个很⼩的数组,但是另外⼀个机器内存⼤些,我们需要⼀个数组能够⼤些。)

#include <stdio.h>
int main()
{
 int array [ARRAY_SIZE];
 int i = 0;
 for(i = 0; i< ARRAY_SIZE; i ++)
 {
 array[i] = i;
 }
 for(i = 0; i< ARRAY_SIZE; i ++)
 {
 printf("%d " ,array[i]);
 }
 printf("\n" );
 return 0;
}
//linux 环境演⽰: gcc -D ARRAY_SIZE=10 programe.c //可以指定参数的大小
//条件编译 ``
//条件编译
//
//#define __DEBUG__
//
//int main() {
//	int arr[10] = { 0 };
//	for (int i = 0; i < 10; i++) {
//		arr[i] = i;
//		//条件编译
//#ifdef __DEBUG__
//		printf("%d ", arr[i]);
//#endif
//	}
//	return 0;
//}


//int main() {
//	//#if(真/假)
//    //#end if 
//#if(0)
//	printf("haha\n");
//#endif
//	return 0;
//}

//#define M 3
//int main() {
//#if(M == 1)
//	printf("haha\n");
//#elif(M == 3)
//	printf("hehe\n");
//#else
//	printf("Hello World\n");
//#endif
//	return 0;
//}

//#define M 0
//
//int main() {
//#if defined(M)
//	printf("haha");
//#endif
//
//#if !defined(M)
//	printf("hehe");
//#endif
//	return 0;
//}

1

//头文件包含问题(重要)
//本地文件包含
//#include “filename”
//搜索路径:先在源文件路径下查找,如果找不到,直接到库函数的头文件所在路径查找,找不到,报错!
//Linux环境标准头文件路径
// /usr/include
//VS标准头文件的路径
//C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include
//VS2013默认路径

//库文件包含
//#include < filename.h >
//直接在库文件所在的标志路径查找,找不到报编译错误

这样是不是可以说,对于库⽂件也可以使⽤ “” 的形式包含?
答案是肯定的,可以,但是这样做查找的效率就低些,当然这样也不容易区分是库文件还是本地文件了。

//防止头文件重复包含问题
#include 包含的头文件,会在预编译期间直解替换成头文件的内容,如果头文件多次包含,就会多次替换,造成重复包含问题!
//怎么解决?那就是使用预编译指令条件编译
#ifndef __TEST_H__
#define __TEST_H__
//头⽂件的内容 
#endif //__TEST_H__
或直接使用#pragma once防止头文件重复包含
完结!!!
  • 8
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值