一、#define
#define实现的就是文本的代替表示,即#define x y ->用文本x代替表示y的内容,y的内容什么都可以,可以是常量、变量、表达式或者几段代码等等都可以
1.#define 定义常量
#define M 100
int main()
{
printf("%d\n",M);
return 0;
}
文本M代替表示的是常量值100,所以打印的值是100
2.#define 定义宏
当用文本代替表示的内容含有参数时,#define就形成了宏
- #define name(paramen-tlist) stuff
- #define 名称(参数,参数) 含参内容
左边这一块是文本,右边这一块是文本表示的内容
由于含有参数,代替时是左边含参的文本代替表示右边含参的内容,但其本质还是不变的,就是文本对内容的替换,只不过里面含有了参,替换时,要根据参数将参数代上去替换,得到此参数下的文本和此参数下其对应的代替内容,然后就可以知道此参数下的文本代替的值是什么了
★宏的特点:
其实说是参数,其实宏的参数是什么都能传,因为宏的传参本质上也是文本式替换操作,对传的内容都视为文本式的,对表达式文本不会算好再替换(因为视为纯文本),而是文本纯替换好后才开始对内容进行计算,所以根据它的特点,要注意一下几点:
2.1.参数传过去替换的过程中,是不加以任何计算的,参数原原本本传过去传完后才开始计算
定义一个宏,用来求平方
#define SQUARE(n) n*n
将含参表达式n*n这个内容替换为了SQUARE(n) 这个有参数结合的文本
int main()
{
int ret1,ret2 = 0;
ret1 = SQUARE(6);
printf("%d\n", ret1);//36
ret2 = SQUARE(5+1);
printf("%d\n", ret2);//11
return 0;
}
因为宏传过去的过程中是不加任何计算的,全部过去后才开始计算的,所以还原SQUARE(5+1)此文本代替的内容为5+1*5+1= 11,所以定义时(n)*(n)每一个参数都要加上括号,(5+1)*(5+1) = 36,这样就没问题了
还有一个例子:
定义一个宏,用来求一个数的2倍
#define DOUBLE(x) ((x)+(x))
int main()
{
int ret = 0;
ret = 10 * DOUBLE(5);
printf("%d\n", ret);//100
10*((5)+(5)) = 100
如果(x)+(x)外层不加括号:
-> 10*(5)+(5) = 55
return 0;
}
所以,内每一个x都要有括号括,外x构成的整体外部也要有括号括,总之,写宏时不要吝啬括号
2.2.宏的参数在宏体如果是出现多次的话,那么当传过来的参数是带有副作用的表达式时,参数都换成副作用表达式,副作用就不是预想的只产生一次的了,而是会产生多次
写一个宏,求2个数的较大值
#define MAX(X,Y) ((X)>(Y)?(X):(Y))
int main()
{
int a = 3;
int b = 5;
int m = MAX(a++, b++);
宏传参传过去里面全替换后:
-> int m = ((a++)>(b++)?(a++):(b++));
选择语句,选择执行(b++)
1.后置++下,表达式算完再++ ——> a = 3 < b = 5 ,表达式比较完后a++后为4;b++后为6,选择执行(b++)语句
2.执行(b++)语句,b++为后置++,先把值赋给m, m = b = 6 ,然后b++为7
printf("m = %d\n", m);//6
printf("a = %d\n", a);//4
printf("b = %d\n", b);//7
return 0;
}
2.3.宏都是替换式过去,文本式替换,什么都能替换,什么都能传
宏传类型如下:
#include<stdlib.h>
#define Malloc(n,type) (type*)malloc(n*sizeof(type))
int main()
{
int* p = (int*)malloc(10 * sizeof(int));
这里宏传的参是类型
int* ptr = Malloc(10, int);
——>int* ptr = (int*)malloc(10*sizeof(int));
}
2.4.宏的参数去找替换时它至少要按符号单位去替换的
举个例子:比如下图中要用参数传来的int替换函数名标识符type_max中的type,因为type与_max构成了标识符符号单位,int来替换时是做不到替换标识符部分的,因为它替换时的最小单位是符号,要换就要type_max这个标识符,而不能换里面的type标识符的一部分
所以,我们可以用 ## 操作符,它可以实现把符号切割,创建更小的合法的符号单位,即type##_max中,参数int来替换时,此时type是一个合法的符号单位,可以完成替换,即int##_max,替换好后,##又会将两端的符号单位合并,合并成一个符号单位—>int_max,此时就完成了参数在名字里部分的替换,形成了由参数部分构成的函数名,这样就可以实现声明多种不同函数类型的函数(它们函数名是不同的),实现批量制造函数了
定义一个声明函数的宏 声明函数的模具
#define GENERIC_MAX(type) \
type type##_max(type x, type y)\
{ \
return x>y?x:y; \
}
用宏替换完后是一个函数的声明 利用宏实现可以一句代码批量声明函数
GENERIC_MAX(int);
int int_max(int x, int y){ return x>y?x:y;}
——>即:
// int int_max(int x, int y)
//{
// return x > y ? x : y;\
//}
int main()
{
int r = int_max(3, 5);
printf("%d\n", r);//5
return 0;
}
二、条件编译
1)#if... #elif ... #else ... #endif
2)#if defined() ... #endif <=> #ifdef ... #endif
3)#if !defined() ... #endif <=>#ifndef ... #endif
判断是否被定义:
#define MAX 1
int main()
{
#ifdef用来判断符号是否被程序员定义过:
如果MAX被定义过为真
#ifdef MAX
printf("MAX已定义\n");
如果MAX未被定义
#else
printf("MAX未定义\n");
#endif
return 0;
}
1.每一个#if(包括#ifdef 、#ifndef)都要有 #endif 匹配的,都要有一一对应 (elif不属于#if)
2.#if到对应#endif的整块区域为一个条件编译判断区,不用{}括因为#endif会自动找#if匹配
3.#这些条件预处理的指令判断为假的部分,在源文件预处理中不会执行,在预处理后的预编译代码.i中也已经是直接删掉了那种,不会出现在后面的工程文件里,它最多只是在源文件中露了面的那种(跳过没去执行)
(补充:#undef 用于移除一个宏定义:)
#define MAX 100
int main()
{
printf("%d\n",MAX);100
#undef MAX
printf("%d\n",MAX);MAX未定义
}
三、头文件的包含
#include"add.h"与#include<add.h>:
-
#include"add.h"—>会先在源文件所在目录下查找,如果文件未找到,就会去标准库里查找
-
#include<add.h>—>直接去标准库里查找
为避免嵌套文件时头文件的重复包含引入,在每个头文件的开头写:
-
#ifndef __TEST_H__ #define __TSET_H__ 头文件的内容 #endif
或:
-
#pragma once 头文件内容