c语言预处理指令什么时候被运行,C语言--预处理指令小结

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;

}

你可能感兴趣的:(c语言预处理指令什么时候被运行)