C语言学习(5)第五章 预处理、动态库、静态库

本文详细介绍了C语言的预处理阶段,包括`include`、`define`宏定义,以及不带参和带参宏的使用。同时,探讨了选择性编译的ifdef、ifndef和if语句的用法。此外,还简单阐述了静态库和动态库的概念及它们之间的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

5.1 c语言编译过程

5.2 include

5.3 define

5.3.1 不带参宏

5.3.2 带参宏

5.3.3 带参宏和带参函数的区别

5.4选择性编译

5.5静态库(了解)

5.6动态库(了解)


5.1 c语言编译过程

1、预编译:将.c中的头文件展开、宏展开,生成.i文件

2、编译:.i文件生成.s汇编文件

3、汇编:.s汇编文件生成.o目标文件

4、链接:.o文件生成.exe文件

.c文件---->预处理产生.i文件---->编译产生.s文件---->汇编产生.o文件

---->链接产生. exe文件

5.2 include

#include<> 用尖括号包含头文件,编译器只在系统指定的路径下找头文件

#include"" 用双引号包含头文件,编译器先在当前目录下找头文件,找不到再去系统指定的路径下找。

注意:

1、#include可以包含.c文件,但不建议包含。include包含的文件在预编译时会展开,如果一个.c被包含多次会导致函数被重复定义;

2、预处理只是对include等预处理操作进行处理,并不会检查语法,即便有错误也不会报错,在编译环节才进行语法检查。

5.3 define

用define定义宏,宏是在预编译的时候进行替换,分为带参的宏和不带参的宏。

5.3.1 不带参宏

#define PI 3.14

建议用大写字母定义宏的名字,区别于函数名,3.14是要替换宏的内容,在预编译的时候如果代码中出现了PI,就用3.14去替换。

使用宏时,只要修改宏定义,其他地方在预编译的时候就会自动替换,方便快捷。

#include<stdio.h>

#define PI 6.66

int main()

{

double f;

printf("PI = %lf,", PI);

f = PI;

printf("f = %lf\n", f);

return 0;

}

在预编译时,PI都会被自动替换为6.66,函数体变为:

int main()

{

double f;

printf("PI = %lf,", 6.66);

f = PI;

printf("f = %lf\n", 6.66);

return 0;

}

运行的结果是:PI = 6.660000,f = 6.660000

改变宏定义,PI替换为新的值:

#include<stdio.h>

#define PI 7.0

int main()

{

double f;

printf("PI = %lf,", PI);

f = PI;

printf("f = %lf\n", f);

return 0;

}

运行的结果是:PI = 7.000000,f = 7.000000

注意:宏的作用范围是从定义的地方直到本文件的末尾,如果想要在某处终止宏,使用#undef PI终止。

常用的使用方法:

1、在.c文件中定义宏,那么在这个文件中就可以用宏;

2、在头文件中定义宏,某个.c文件需要用到这个宏,只需要包含头文件即可。

5.3.2 带参宏

#define S(a,b) a*b

带参宏的形参a,b是没有类型名的(函数的形参要带类型名)。

#include<stdio.h>

#define S(a,b) a*b  //S(a,b) = a*b,带参宏

int main()

{

int num1,num2;

num1 = S(2,4);

printf("num = %d,", num1);

num2 = S(7,9);

printf("num = %d", num2);

return 0;

}

预处理后,函数体变为:

int main()

{

int num1,num2;

num1 = 2*4;

printf("num = %d,", num1);

num2 = 7*9;

printf("num = %d", num2);

return 0;

}

可见,S(2,4)被替换为了2*4,a对应2,b对应4;S(7,9)被替换为了7*9,a对应7,b对应9。

运行的结果是:num = 8,num = 63。

注意:宏的替换只是简单的替换,不包括表达式的计算

int main()

{

int num;

num = S(2 + 4, 7);

return 0;

}

预处理后函数体变为:

int main()

{

int num;

num = 2 + 4 * 7;

return 0;

}

这就是宏的副作用,容易导致计算出错,在定义宏的时候添加括号(括号的优先级最高)可以解决。

#include<stdio.h>

#define S(a,b) (a)*(b)  //S(a,b) = (a)*(b)

int main()

{

int num;

num = S(4+2,7);

return 0;

}

这时替换的效果为:num = (4+2)*(7) ;

5.3.3 带参宏和带参函数的区别

带参宏被调用多少次都要被展开多少次,执行代码时没有函数调用的过程,不需要压栈弹栈。带参宏浪费了空间,但节省了时间。

带参函数定义一次,在调用时去代码段取指令,要压栈弹栈,有调用的过程。带参函数浪费了时间,但节省了空间。

带参函数的形参是有类型的,而带参宏的形参没有类型名。

5.4选择性编译

1、ifdef语句

#ifdef PI

代码段一

#else

代码段二

#endif

如果在当前.c文件中,在ifdef上面已经定义过宏PI,就编译代码段一,否则编译代码段二。

与if else的区别:if else 语句都会被编译,通过条件选择执行代码,而选择性编译只会编译一块代码,另一块相当于被注释了。

int main()

{

#ifdef PI

printf("代码段一\n");

#else

printf("代码段二\n");

#endif // PI

return 0;

}

预处理后的函数体:

int main()

{

printf("代码段二\n");

return 0;

}

只剩下了代码段二的部分,而代码段一的部分没有被编译

如果在前面定义了宏PI

#define PI

int main()

{

#ifdef PI

printf("代码段一\n");

#else

printf("代码段二\n");

#endif // PI

return 0;

}

预处理后的函数体:

int main()

{

printf("代码段一\n");

return 0;

}

只剩下了代码段一的部分,而代码段二的部分没有被编译

2、ifndef语句

#ifndef PI

代码段一

#else

代码段二

#endif

如果在当前.c文件中,在ifndef上面没有定义过宏PI,就编译代码段一,否则编译代码段二

int main()

{

#ifndef PI

printf("Hello World\n");

#else

printf("Hello China\n");

#endif // PI

return 0;

}

运行的结果:Hello World

前面没有定义过宏PI,所以执行代码段一,即打印Hello World

这种方法与第一种ifdef互补,常用于防止头文件被重复包含。

//fun.h

#ifndef __FUN_H__

#define __FUN_H__

extern int sum(int x, int y);

#endif // !PI


//main.c

#include<stdio.h>

#include"fun.h"

int main()

{


return 0;

}

ifndef实现了防止头文件被重复包含的作用:如果没有定义过宏__FUN_H__,那就定义这个宏,并声明函数;相反如果定义过这个宏(即函数被声明过了),就不做处理。

写多个.h文件时一定要注意重复包含的问题。

3、if语句

#if 表达式

程序段一

(#else

程序段二)

#endif

如果表达式为真,就预编译程序段一,否则预编译程序段二。代码段二可以不写,即如果表达式为假,都不参加预编译。

一般用于软件裁剪:

#define AAA 0

int main()

{

#if AAA

printf("程序一");

#else

printf("程序二");

#endif


return 0;

}

通过定义AAA的值,来决定哪个代码块预编译,相当于一个开关。

当AAA为1,执行程序一,当AAA为0,预编译程序二,或者直接不写#else部分,即都不预编译。

5.5静态库(了解)

1、动态编译

动态编译使用的是动态库进行编译,默认的使用动态编译方法

2、静态编译

使用静态库进行编译

3、区别

(1)使用的库文件格式不一样

(2)静态编译要把静态库文件打包编译到可执行程序中;而动态编译不会把动态库文件打包编译到可执行程序中,只是编译链接关系。

因此静态编译的程序文件会很大,但在运行的时候不依赖库文件,库已经被打包进了可执行程序中了。而动态编译由于只编译链接关系,没有打包库文件,在运行时需要依赖外部的库文件,好处是文件小。

制作一个静态库

在mylib.c文件中定义了两个函数max和min,在mylib.h文件中声明了这两个函数,为了防止重复头文件重复包含,使用#ifndef选择性编译

gcc -c mylib.c -o mylib.o

ar rc libmoplib.a mylib.o

静态库起名必须以lib开头,以.0结尾。

//mylib.c

int max(int x, int y)

{

return (x > y) ? x : y;

}

int min(int x, int y)

{

return(x < y) ? x : y;

}


//mtlib.h

#ifndef __MYLIB_H__

#define __MYLIB_H__

extern int max(int x, int y);

extern int min(int x, int y);

#endif // !__MYLIB_H__

5.6动态库(了解)

制作动态链接库

gcc -shared mylib.c -o libmoplib.a.so

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值