您的位置:首页 > 编程语言 > C语言/C++

C语言--预处理指令小结

2014-12-10 16:01 162 查看

C语言--预处理指令小结

预处理指令简介

1.C语言在对源程序进行编译之前,会先对一些特殊的预处理指令作解释(比如之前使用的#include文件包含指令),产生一个新的源程序(这个过程称为编译预处理),之后再进行通 常的编译

2.为了区分预处理指令和一般的C语句,所有预处理指令都以符号"#"开头,并且结尾不用分号

3.预处理指令可以出现在程序的任何位置,它的作用范围是从它出现的位置到文件尾。习惯上我们尽可能将预处理指令写在源程序开头,这种情况下,它的作用范围就是整个源 程序文件

4.C语言提供的预处理指令主要有:宏定义、文件包含、条件编译

作用:它的作用是在编译预处理时,将源程序中所有"宏名"替换成右边的"字符串",常用来定义常量。

一、宏定义 #define
预处理指令:宏定义一般分为两种:带参数 和 不带参数

不带参数的宏定义

#define 宏名 字符串

比如#define ABC 10

右边的字符串也可以省略,比如#define ABC

字符串一般大写 或以k开头, 小写一般表示变量名

作用域:在代码翻译成0 和 1 之前执行,从生成开始,直到最后或遇到#undef 字符串 时结束

位置不定:可以在任何位置

用双引号 ”” 括起来的宏定义名称不翻译

例如:

#include <stdio.h>

// 源程序中所有的宏名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 <stdio.h>
int main()
{
int c = sum(5+5, 3+4);//很明显,这里如果没有参数小括号会出错
printf("c = %d",c);
}

二、条件编译

概念:在很多情况下,我们希望程序的其中一部分代码只有在满足一定条件时才进行编译,否则不参与编译(只有参与编译的代码最终才能被执行),这就是条件编译

基本用法

#if 条件1
...code1...
#elif 条件2
...code2...
#else
...code3...
#endif
1> 如果条件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 <stdio.h>
// 因为都是宏定义,所以实在程序执行之前运行的,程序里的值是用不到的
// 所以必须在该宏定义执行之前进行判断,所以必须用宏定义
#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 <stdio.h>
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 <stdio.h>

// 定义一个结构体并起别名
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 <stdio.h>

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 <stdio.h>
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: