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

                              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;
}









  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿三先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值