预处理、动态库、静态库
1.C语言编译过程
-
预处理
将.c中的头文件展开、宏展开
生成的文件是.i文件
-
编译
将预处理之后的.i 文件生成 .s 汇编文件
-
汇编
将 .s 汇编文件生成 .o 目标文件
-
链接
将 .o 文件链接成目标文件
Linux 下 GCC 编译器编译过程
gcc -E hello.c -o hello.i 1.预处理
gcc -S hello.i -o hello.s 2.编译
gcc -c hello.s -o hello.o 3. 汇编
gcc hello.o -o hello 4. 链接 (生成可执行文件)
2.include
#include<> 用**尖括号包含头文件,在系统指定的路径下找**头文件
#include"" 用**双引号包含头文件,先在当前目录下找头文件**,找不到再到系统指定的路径下找。
- 注意:include 经常用来包含头文件,可以包含 .c 文件,但是大家不要包含 .c
- 因为include 包含的文件会在**预编译被展开,如果一个.c 被包含多次,展开多次,会导致函数重定义。 所以不要包含.c** 文件
3.define
- 定义宏用define 去定义
- 宏是在预编译的时候进行替换
-
不带参宏
#define PI 3.14
在预编译的时候如果代码中出现了 IP 就用3.14 去替换。
宏的好处:只要修改宏定义,其他地方在预编译的时候就会重新替换。
注意:宏定义后边不要加分号。
#define PI 3.1415926 int main() { double f; printf("PI = %lf \n",PI); f = PI; return 0; }
宏定义的作用范围,从定义的地方到文本文件末尾.
如果想在中间终止宏的定义范围
#undef PI // 终止PI的作用
#define PI 3.1415926 // 定义宏 int main() { double f; printf("PI = %lf \n",PI); #undef PI // 终止宏 #define PI 3.14 // 重新定义宏 f = PI; return 0; }
-
带参宏
#define Name(a,b) a*b
注意带参宏的形参 a 和 b 没有类型名,
Name(2,4) 将来在预处理的时候替换成 实参代替字符串的形参, 其他字符保留, 2 * 4
#define Name(a,b) a*b // 带参宏 int main() { int num; num = Name(2,4); // num = 2*4 num = Name(5,7); // num = 5*7 num = Name(3+5,7); // num = 3+5*7 这个就是宏的副作用,跟函数不同,他是直接替换,函数是 8*7 return 0; }
#define Name(a,b) (a)*(b) // 带参宏,带上括号 int main() { int num; num = Name(3+5,7); // num = (3+5)*(7) 这样就可以解决 3+5*7 的问题了 return 0; }
-
带参宏和带参函数的区别
- 带参宏被调用多少次就会展开多少次,执行代码的时候没有函数调用的过程,不需要压栈弹栈。所以带参宏,就是浪费了空间,因为被展开多次,节省时间。
- 带参函数,代码只有一份,存在代码段,调用的时候去代码段取指令,调用的时候要,压栈弹栈。有个调用的过程。
- 所以所,带参**函数是浪费时间,节省了空间**。
带参函数的形参是有类型的,带参宏的形参没有类型名。
4.选择性编译
#ifdef HongName
// 如果上面定义过 HongName 这个宏就执行这段代码
#else
//如果没定义过 执行这段代码
#endif // 结束这个判断
-
如果在当前 .c ifdef 上边定义过HongName ,就执行上面的代码,否则编译第二个
-
注意和 if else 语句的区别,if else 语句都会被编译,通过条件选择执行代码,而选择性编译,只有一块代码被编译。
-
不参加编译的代码块会被注释掉
-
#define HongName int main() { #ifdef HongName printf("Hello World\n"); // 定义过这个宏,直接执行此代码 #else printf("Hello China!!\n"); //这个代码会被注释掉,编译后就没了 #endif return 0; }
#ifndef HongName
// 如果上面没有定义过 HongName 这个宏,就执行这段代码
#else
//如果定义过 执行这段代码
#endif // 结束这个判断
-
这种方法,经常用在防止头文件重复包含
-
为了方式头文件重复包含,咱们一般敲代码的时候
-
// fun.h 这个是头文件 .h文件 #ifndef __FUN_H__ #define __FUN_H__ extern int fun(int x,int y); #endif
3.if
#if 表达式 程序段一 #else 程序段二 #endif
-
如果表达式为真,编译第一段代码,否则编译第二段代码
-
选择性编译都是在预编译阶段干的事情
-
#define AAA 0 // 可以通过AAA的值来执行下边跑哪个代码, 类似于开关 int main() { #if AAA printf("执行第一段代码\n"); #else printf("执行第二段代码\n"); return 0; }
-
5.静态库
-
动态编译
动态编译使用的是动态库文件进行编译
gcc hello.c -o hello
默认的咱们使用的是动态编译方法
-
静态编译
静态编译使用的静态库文件进行编译
gcc -static hello.c -o hello
-
静态编译和动态编译的区别
-
使用的库文件的格式不一样
动态编译使用动态库,静态编译使用静态库
-
静态编译要把静态库文件打包编译到可执行程序中。(移植到其他电脑上不会报错,体积较大
-
动态编译**不会**把动态库文件打包编译到可执行程序中,它只是编译链接关系。(因为是链接关系,所以移动到别的电脑上如果没有动态库文件会报错) 存储容量小
-
// mylib.c
int max(int a,int b)
{
return (a > b) ? a : b;
}
int min(int a,int b)
{
return (a < b) ? a : b;
}
// mylib.h
#ifndef __MYLIB_H__
#define __MYLIB_H__
extern int max(int a,int b);
extern int min(int a,int b);
#endif
// mytext.c
#include <stdio.h>
#include "mylib.h"
int main()
{
int a=10,b=20,max_num,min_num;
max_num = max(a,b);
min_num = min(a,b);
printf("max_num=%d\n", max_num);
printf("min_num=%d\n", min_num);
return 0;
}
- 制作静态库:
gcc -c mylib.c -o mylib.o
ar rc libname.a mylib.o
- 注意: 静态库起名的时候必须以lib开头以 .a 结尾
- 编译方法
6.动态库
无了,,,,,,,,,,,,