C语言--预处理指令小结
预处理指令简介
1.C语言在对源程序进行编译之前,会先对一些特殊的预处理指令作解释(比如之前使用的#include文件包含指令),产生一个新的源程序(这个过程称为编译预处理),之后再进行通 常的编译
2.为了区分预处理指令和一般的C语句,所有预处理指令都以符号"#"开头,并且结尾不用分号
3.预处理指令可以出现在程序的任何位置,它的作用范围是从它出现的位置到文件尾。习惯上我们尽可能将预处理指令写在源程序开头,这种情况下,它的作用范围就是整个源 程序文件
4.C语言提供的预处理指令主要有:宏定义、文件包含、条件编译
作用:它的作用是在编译预处理时,将源程序中所有"宏名"替换成右边的"字符串",常用来定义常量。
一、宏定义 #define
预处理指令:宏定义一般分为两种:带参数 和 不带参数
不带参数的宏定义
#define 宏名 字符串
比如#define ABC 10
右边的字符串也可以省略,比如#define ABC
字符串一般大写 或以k开头, 小写一般表示变量名
作用域:在代码翻译成0 和 1 之前执行,从生成开始,直到最后或遇到#undef 字符串 时结束
位置不定:可以在任何位置
用双引号 ”” 括起来的宏定义名称不翻译
例如:
#include
// 源程序中所有的宏名PI在编译预处理的时候都会被3.14所代替
#define PI 3.14
// 根据圆的半径计radius算周长
float girth(float radius) {
return 2 * PI *radius;
}
int main ()
{
float g = girth(2);
printf("周长为:%f", g);
return 0;
}
带参数的宏定义
格式:#define 名称(参数,参数) 公式;((参数) 符号(参数))
带参数的宏在展开时,只作简单的字符和参数的替换,不进行任何计算操作。所以在定义宏时,一般用一个小括号括住字符串的参数。
每个参数都要加上小括号 例:#define sum(v1,v2) ((v1) *(v2))
与函数的区别
从整个使用过程可以发现,带参数的宏定义,在源程序中出现的形式与函数很像。但是两者是有本质区别的:
1>宏定义不涉及存储空间的分配、参数类型匹配、参数传递、返回值问题
2>函数调用在程序运行时执行,而宏替换只在编译预处理阶段进行。所以带参数的宏比函数具有更高的执行效
#define sum(a,b) ((a) *(b))
#include
int main()
{
int c = sum(5+5, 3+4);//很明显,这里如果没有参数小括号会出错
printf("c = %d",c);
}
二、条件编译
概念:在很多情况下,我们希望程序的其中一部分代码只有在满足一定条件时才进行编译,否则不参与编译(只有参与编译的代码最终才能被执行),这就是条件编译
基本用法
#if 条件1
...code1...
#elif 条件2
...code2...
#else
...code3...
#endif1> 如果条件1成立,那么编译器就会把#if 与 #elif之间的code1代码编译进去(注意:是编译进去,不是执行,很平时用的if-else是不一样的)
2> 如果条件1不成立、条件2成立,那么编译器就会把#elif 与 #else之间的code2代码编译进去
3> 如果条件1、2都不成立,那么编译器就会把#else 与 #endif之间的code3编译进去
4> 注意,条件编译结束后,要在最后面加一个#endif,不然后果很严重(自己思考一下后果)
5> #if 和 #elif后面的条件一般是判断宏定义而不是判断变量,因为条件编译是在编译之前做的判断,宏定义也是编译之前定义的,而变量是在运行时才产生的、才有使用的意义
下面是条件编译的总结:
1>#if(条件)
Code…
#elif(条件)
Code…
#else
Code…
#endif
2>#ifdef 宏名称 // 如果定义了这个宏则执行代码Code
Code… // 如果是#ifndef,其他不变,则是如果没有定义则执行代码
#endif
3>#ifdefined (宏名称) //如果定义了这个宏则执行代码/
Code… // 如果是#if !defined,其他不变,则是如果没有定义则执行代码
#endif
例1:
#include
// 因为都是宏定义,所以实在程序执行之前运行的,程序里的值是用不到的
// 所以必须在该宏定义执行之前进行判断,所以必须用宏定义
#define A5
intmain()
{
#if(A ==10)
printf("a = 10\n");
#elif(A== 5)
printf("a = 5\n");
#else
printf("a是其他值\n");
#endif
return 0;
}
例2:
#define A5
#include
int main( )
{
#ifdef A//如果定义了这个宏则执行代码
printf("执行了这行语句\n"); //如果是#ifndef,其他不变,则是如果没有定义则执行代码
#endif
}
三、文件包含
<>是用来表示系统文件
””是用来表示自定义文件
不允许互相包含(a.h包含b.h , b.h又包含a.h)
文件的合成 假设有3个文件 test.c lisi.c lisi.h test.c想用lisi.c文件里的函数
test.c文件
#include “lisi.h”//导入lisi的文件
intmain()
{
int c =sum(5,8);
printf(“5+8= %d\n”,c);
return 0;
}
lisi.c文件
int sum(int a , int b)
{
return a+b;
}
lisi.h文件// .h文件是放声明的文件//这加上条件编译,是防止多次导入带来的错误
#ifndef LISI_H // 判断宏定义是否存在,
#define LISI_H //如果不存在则定义一个宏,宏名称最好是文件名,不易记错出乱
int sum(int a , int b);
#endif
四、typedef 类型
给基本类型,指针,结构体,枚举 起个 新的别名
typedef int Myint; // 此后的 Myint 就相当于 int 类型 int a; Myint a;
也可定义指针类型 typedef char * String; 此后的String代表 (char *)
例如
typedef struct Student
{
int age;
}Mystu;
//或者
typedef struct Student
{
int age;
}Mystu;
int main()
{
struct Student stu2;
Mystu stu = {32};
return 0;
}
利用typedef简化定义指向函数的指针
int main()
{//原版
int sum(int a, int b)
{
return a+b;
}
int (*p)(int ,int ) = sum;
}
//利用typedef
typedef int (*Point)(int ,int);//定义一个指针Point指向函数,函数参数类型是(int ,int)
int main()
{
int sum(int a, int b)
{
return a+b;
}
Point p = sum;
}有时也要注意一下特殊情况
#define String2 char *
typedef char * String
int main()
{
//s1,s2都是char *指针
String s1,s2;
s1 = "jack";
s2 = "rose";
//s3是char *指针,s4是char
String2 s3,s4;
/*
char * s3,s4;
char *s3;
char s4;
*/
}
给指向结构体的指针起别名
#include
// 定义一个结构体并起别名
typedef struct {
float x;
float y;
} Point;
// 起别名
typedef Point *PP;
int main() {
// 定义结构体变量
Point point = {10, 20};
// 定义指针变量
PP p = &point;
// 利用指针变量访问结构体成员
printf("x=%f,y=%f", p->x, p->y);
return 0;
}
使用typedef给枚举类型起别名
// 定义枚举类型
enum Season {spring, summer, autumn, winter};
// 给枚举类型起别名
typedef enum Season Season;
也可以简化成这
typedef enum {spring, summer, autumn, winter} Season;
五、static 和 extern 对函数的影响
static
在定义函数时,在函数的最左边加上static可以把该函数声明为内部函数(又叫静态函数),这样该函数就只能在其定义所在的文件中使用。
如果在不同的文件中有同名的内部函数,则互不干扰。
* static也可以用来声明一个内部函数
extern
在定义函数时,如果在函数的最左边加上关键字extern,则表示此函数是外部函数,可供其他文件调用。C语言规定,如果在定义函数时省略extern,则隐含为外部函数。
* 在一个文件中要调用其他文件中的外部函数,则需要在当前文件中用extern声明该外部函数,然后就可以使用,这里的extern也可以省略。
总结
static:表示内部 而 extern 表示外部
外部函数:定义的函数能被本文件和其他文件访问,
1>默认的都是外部函数
2>整个项目都不能有同名的外部函数
内部函数:定义的函数只能被本文件访问,其他文件不能访问
1> 允许不同文件有同名内部函数
static 对函数的作用(static不可省略),调用时可以通过调用外部函数,
此外部函数调用其内部函数
1> 定义一个内部函数
2> 声明一个内部函数
例
#include
static void test();
int main()
{
test();
return 0;
}
static void test() {
printf("调用了test函数");
}
extern对函数的作用(extern可以省略)
1> 完整地定义一个外部函数 extern void 函数名(){}
2> 完整地声明一个外部函数 extern void 函数名();
六、static 和 extern 对变量的影响
外部变量:定义的变量能被本文件和其他文件访问
1> 默认情况下,所有的全局变量都是外部变量
2> 不同文件中的同名外部变量,都代表同一个变量
3> 声明一个外部变量:extern int a;(extern可省略)
内部变量:定义的变量只能被本文件访问,不能被其他文件访问
定义一个内部变量:static int a;
1> 不同文件中的同名变量互不影响
static 对局部变量的影响
1> 会延长局部变量的生命周期,直到程序结束才会销毁
2> 并没有改变局部变量的作用域
3> 当一个函数被调用很多次,而且函数值是不变的,则用static 定义
void test()
{
static double pi = 3.14;//它会保存,直到程序全部结束,
double zc = 2*pi*12;// 如果不加static则,它会重复建立,销毁pi 100次
}
Int main()
{
for(int I = 0;i < 100; i++ )
{ test(); }
}
七、递归
递归的2个条件
1> 函数自己调用自己
2> 必须有一个明确的返回值,用于终结调用
例:设计一个函数用来计算b的n次方
#include
Int pow(int b, int n);
int main()
{
int c = pow(3 , 4);
printf(“%d\n”,c);
return 0;
}
/*
pow(b , 0) == 1
pow(b , 1) == b == pow(b , 0) *b
pow(b , 2) == b *b == pow(b , 1) *b
pow(b , 3) == b*b*b == pow(b , 2) *b
1>n为0,结果肯定是1
2>n>0, pow(b , n) == pow(b , n-1) *b
*/
int pow(int b, int n)
{
if(n <= 0) return 1;
return pow(b, n-1)*b;
}