预处理
宏定义
C语言中所以的预处理命令都以字符 ‘ # ’开头。宏定义是预处理指令的一种,以#define开头。它提供了一种可以替换源文件中字符串的机制,预处理过程中,宏调用会被展开为对应的字符串,这个过程称为“宏展开”
定义宏对象
宏对象的宏名后不带参数。其定义方式:
#define 宏名 宏对象(要代替的字符串)
其中,宏名可以为关键字,如下:
#define int i
那程序中就不能用int 了 所以不用使用关键字作为宏名
#define N 10;
如果你使用宏作为容量 int array[N]; 那这样就错误了。
因为宏展开是 10; 及array[10;];
作为数组容量,这样有利于代码的修改。
#include <stdio.h>
#define N 10
int main(void)
{
int i = 0;
int array[N] = {1,2,3,4,5,6,7,8,9,10};
for(i = 0;i < N; ++i)
{
printf("%d\t",array[i]);
}
printf("\n");
return 0;
}
代替一些操作
#include <stdio.h>
#define D2 "%d,%d\n"
int main(void)
{
int i = 2;
int j = 9;
printf(D2,i,j);
return 0;
}
#include <stdio.h>
#define P printf
int main(void)
{
int x = 10;
P("x = %d\n",x);
return 0;
}
使用宏改C语言的编写风格。(无聊所以没有改彻底)
#include <stdio.h>
#define BEGIN {
#define END }
int main(void)
BEGIN
int i = 0;
int array[10];
for(i = 0;i < 10;++i)
BEGIN
scanf("%d",&array[i]);
END
for(i = 0;i < 10; ++i)
BEGIN
printf("%d\t",array[i]);
END
return 0;
END
其作用和应注意:
1.简化书写
2.改变风格(及输出格式)
3.不要使用宏对象来简化数据类型的书写
#include <stdio.h>
#define PCHAR char*
int main(void)
{
PCHAR pc,pc1;
printf("%d,%d\n",sizeof(pc),sizeof(pc1));
return 0;
}
输出结果是:4,1
为什么pc1是1?
把宏展开 char *pc,pc1;
那pc1只是普通的字符变量。
如果要简化数据类型,建议使用 typedef 如下
#include<stdio.h>
typedef char * PCHAR
int main(void)
{
PCHAR p1,p2;
printf(“%d,%d”,sizeof(p1),sizeof(p2));
return 0;
}
输出结果是 4,4
宏的作用域
宏跟变量一样有作用域。一般情况下其作用域为本文件中从被定义的语句开始,一直到该文件的结束。在其作用域外使用程序将报错,但是与全局变量类似,可以通过文件包含来扩大其作用域,因此,对于常用的宏定义,一般都写在头文件中,当需要的时候,直接包含头文件就可以了。
C语言中提供了一个预处理命令来结束宏定义的作用域。如下
#undef 宏名
该语句是取消宏定义,在该语句后就不能使用了该宏名了,除非重新定义。
#include <stdio.h>
int main(void)
{
int a = 0;
#define N a+=10
N;
printf("%d\n",a);
#undef N
//N;//该语句是错误的,因为#undef 取消了N
#define N a+=20;
N;
printf("%d\n",a);
return 0;
}
带参数的宏定义(宏函数)
定义宏函数
#define 宏名(参数列表) 宏函数体
其调用方式
宏名(参数列表);
注意参数列表要靠近宏名。如下
#define MAX(a,b) ((a) > (b)) ? (a) : (b)
如果这样 #define MAX (a,b) ((a) > (b)) ? (a) : (b)
那么MAX宏展开是 (a,b) ((a) > (b)) ? (a) : (b)
#include <stdio.h>
#define MAX(a,b) ( (a) > (b) ) ? (a) : (b)
int main(void)
{
int i = 0;
int j = 0;
scanf("%d%d",&i,&j);
printf("%d\n",MAX(i,j));
return 0;
}
MAX 替代成 ( (a) > (b) ) ? (a) : (b)
① 圆括号的好处
如果没有用圆括号会出现优先级的逻辑错误,所以我们要预防这类问题。如下:
#include <stdio.h>
#define H(a) a * a
int main(void)
{
int i = 2;
printf("%d\n",H(i+1));
return 0;
}
结果是5 为什么不是9
因为宏展开是i+1*i+1 == 2+2+1
#include <stdio.h>
#define H(a) (a) * (a)
int main(void)
{
int i = 2;
printf("%d\n",H(i+1));// (i+1)*(i+1)
return 0;
}
这样结果就没错误了,由于是逻辑错误,所以编译器不会报错的。
② 宏函数体过多时可以使用“\”将太长的宏函数体分行。
③ 控制宏函数体的长度
不要定义过长的宏函数,否则,当多次调用该宏函数会将该长函数体会多次替换到代码中导致代码量膨胀。
④ 宏函数可以使用#undef 来取消
宏函数与函数
可以看出宏函数和函数十分相似,几乎函数可以实现的功能,都可以用宏函数来实现。但是宏函数和函数的原理上有明显的不同
① 宏函数的展开只是简单字符串代替,而函数是参数传递,参数是有数据类型的,
宏函数的参数替换是不进行计算的,是直接替换的,而每次调用函数时,都要进行 形参 = 实参 的操作。
② 宏函数中运算是实参本身,而函数是形参进行运算。(因此,可以理解成宏函数没有参数,这只是代替。不过这样不好理解。)
③ 宏函数是在编译之前展开的,再进行编译,而函数是在编译之后,在程序运行时,才被调用。因此,宏函数占用的是编译期的时间,而函数是占用运行期的时间。
④ 宏函数是不占内存的,而函数要占用内存,因此不存在指向宏函数的指针。
⑤ 函数调用时,程序需要保持运行前的状态,函数返回时,要恢复原先的状态,而宏函数没有这些开销,因此,宏函数在执行效率上比函数高。
条件编译
一般情况下,C程序的代码都会被编译器编译的,当我们不需要某段代码进行编译。
就可以使用条件编译,条件编译通过条件判断的方法来判断是否编译指定代码。
#ifdef 命令
#ifdef 命令有三种形式
形式一
#ifdef 标识符
程序段1
#else
程序段2
#endif
如果程序定义了标识符,则编译程序段1,如果没有则编译程序段2
形式二
#ifdef 标识符
程序段1
#endif
如果程序程序定义了标识符,则编译程序段1
形式三
#ifdef 可以嵌套使用
#ifdef 标识符1
程序段1
#else
#ifdef 标识符2
程序段2
#else
#ifdef 标识符3
程序段3
#endif
#endif
#endif
演示#ifdef 的使用
#include <stdio.h>
#define N 10//注释该宏定义又会输出什么呢?
int main(void)
{
#ifdef N
printf("%d\n",N);
#else
#define N 8
#endif
printf("%d\n",N);
return 0;
}
结果:
10
10
注释#define N 10
结果:8
#ifndef 命令和#ifdef 命令相反,有两种形式。
形式1
#ifndef 标识符
程序段1
#else
程序段2
#endif
如果程序未定义标识符,则编译程序段1,如果程序定义了标识符,则编译程序段2
形式二
#ifndef 标识符
程序段1
#endif
如果程序未定义标志符,则编译程序段1
演示#ifndef的使用
#include <stdio.h>
#define A 1//注释该宏定义 将输出什么?
int main(void)
{
#ifndef A
#define A 88
#else
#define A 10
#endif
printf("%d\n",A);
return 0;
}
#if 命令
#if命令的功能与if语句的结构非常相似。
可以有以下三种方式
方式一
#if 表达式
程序段1
#else
程序段2
#endif
如果表达式为真,则编译程序段1,如果表达式为假,则编译程序段2
形式二
#if 表达式
程序段1
#enfif
如果表达式为真,则编译程序段1
这种方式可以用来注释代码,由于/* /* */ */ 注释不能嵌套注释。
形式三
#if 表达式1
程序段1
#elif 表达式2
程序段2
#elif 表达式3
程序段3
.......................
#else
程序段n
#endif
注意可以有多个#elif 但#else 只可以一个
演示#if的使用
#include <stdio.h>
#define N 0//把0 改 1 又会输出什么?
int main(void)
{
int i = 8;
#if N
printf("%d\n",i + 1);
#else
printf("%d\n",i + 2);
#endif
#if 0
/*这是注释*/
printf("这是注释\n");
#endif
return 0;
}
#include 命令
文件包含在每一个C程序都用到了,文件包含的预处理命令是 #include 调用命令如下:
#include <文件名>
#include “文件名”
尖括号和引号的区别
尖括号:从C语言库函数文件夹查找。
引号: 在当前文件夹查找,如果没有则从库函数文件夹查找。
注意事项
由于文件允许被多次包含和嵌套包含,因此,一个文件可能被多次重复包含,或循环包含。
fi1.h
/********fi1.h 的内容***********/
#ifndef _FI1_H
#define _FI1_H_
/*函数声明
声明变量*/
#endif
这样即使多次包含同一个文件,该文件也只会编译一次。