目录
一、内存的分区
在32位平台 每一个进程 占4G空间(虚拟空间)
内存分区 | 性质 | 使用及存放 |
---|---|---|
堆区 | 可读写 | 使用malloc、calloc,realoc,free动态申请 |
栈区 | &可读写 | 局部变量、函数的形参、返回值>4B |
全局区/静态区 | &可读写 | 普通全局变量、静态全局变量、静态局部变量 |
文字常量 | 只读 | 数值常量、字符(串)常量、符号常量(数组名) |
代码区 | 只读 | 代码的二进制指令 |
二、变量
1、普通局部变量
定义::在{}里面定义的 普通变量
作用范围:所在的{}复合语句之间有效
生命周期:所在的{}复合语句之间有效
存储区域:栈区
注意事项:
1、普通局部变量 不初始化 内容 不确定
2、普通局部变量 同名 就近原则(尽量不同名)
2、普通全局变量
定义:在函数外定义的普通 变量
作用范围:所有源文件
生命周期:整个进程。
存储区域:全局区
注意事项:
1、全局变量不初始化 内容为0
2、全局变量 和 局部变量 同名 优先选择局部变量。
3、其他源文件使用全局变量或函数时必须对其进行extern声明。 extern 声明变量或函数来自外部,通过编译。
1.c
2.c
注意:两者都需编译,不能只编译一个文件
3、静态局部变量
定义:在{}加static定义的局部变量
作用范围:所在的{}复合语句之间有效
生命周期:整个进程有效,即变量不会消失
生命周期:整个进程有效
注意事项: 1、静态局部变量不初始化 内容为0
2、静态局部变量 整个进程都存在
4、静态全局变量
定义:在函数外 加static修饰定义的变量
作用范围:只能在当前源文件使用,不能在其他源文件使用。
生命周期:整个进程
存储区域:全局区
注意事项: 1、静态全局变量不初始化 内容为0
2、静态全局变量 只能在当前源文件使用
三、全局函数和静态函数
1、全局函数(函数默认 都为全局函数)
全局函数:在当前源文件 以及其他源文件 都可以使用
如果其他源文件使用需要 extern对全局函数 进行声明
2、静态函数(加static修饰的函数)
静态函数只能在当前源文件使用
形式:static 【返回值类型】 函数名(参数列表){......}
四、编译过程(了解)
为了在系统上运行hello.c.程序,每一条c语句都必须转化为低级的机器指令。然后将这些机器指令打包成可执行目标文件格式,并以二进制形式存储于磁盘中。预处理(Pre-processing)->编诵(Compiling)->汇编(Assembling)->链接(Linking)
编译的过程:预处理、编译、汇编、链接
以hello.c文件为例
预处理:宏替换、删除注释、头文件包含、条件编译 -E (不会报语法错误)
gcc ‐E hello.c ‐o hello.i
编译:将预处理后的文件 编译成 汇编文件/汇编指令 (报语法错误)
gcc ‐S hello.i –o hello.s
汇编:将汇编文件 生成 二进制文件
gcc ‐c hello.s ‐o hello.o
链接:将工程的二进制文件 +库函数+启动代码 生成可执行文件
gcc hello.o ‐o 【生成可执行文件名】
一步到位:
1 gcc main.c ‐o main
2 gcc main.c
五、头文件包含
#include<head.h> <>中建议包含系统头文件
<>从系统指定目录寻找head.h头文件
#include "head.h" ""中建议包含自定义头文件
“”先从当前目录寻找head.h头文件 若找不到,再到系统指定的目录下寻找
六、#define 宏
例: #define PI 3.14 (宏定义)
在预处理阶段使用3.14 替换所有出现PI的位置 (宏展开)
注:不要在宏后加分号,宏尽量大写和普通变量区分开
宏的作用范围:是从定义处开始 到 当前文件结束 都有效。
一般不能跨文件。跨文件需将宏定义放头文件。
#undef可以结束宏的作用域
宏 没有归属,即不被复合语句{}限制。
1、不带参数的宏
1 #define PI 3.14
2 #define STR "hello world"
3 #define N 100
2、 带参数的宏
以乘法为例:
#define MY_MUL(a, b) a*b
printf("%d\n", MY_MUL(10,20) );//10*20//printf("%d\n", 10*20 );
注:1、宏的参数不能有类型
#define MY_MUL(int a, int b) a*b //错误写法
2、宏不能保证参数的完整性
#define MY_MUL(a, b) a *b
printf("%d\n", MY_MUL(10, 20)); //10*20
printf("%d\n", MY_MUL(10 + 10, 20 + 20)); //10 + 10*20 + 20 == 230
可以使用()的形式 让带参数的宏 具备一定的完整性:#define MY_MUL(a, b) ((a) *(b))
printf("%d\n", MY_MUL2(10 + 10, 20 + 20));//((10 + 10) * (20 + 20))== 800
七、带参宏和带参函数的区别
带参宏被调用几次就会展开几次,执行代码时无函数调用过程,不需要压栈弹栈
故带参宏浪费了空间,节省了时间,带参函数浪费了时间,节省了空间。
八、条件编译/代码裁剪
适用于代码裁剪。
3种语句类型:
以字符大小写转换为例:
若定义宏:
判断是否成立:
九、防止头文件重复包含方式
1、条件编译方式(推荐)
#ifndef __05_A_H__
#define __05_A_H__
/* code */
int num=10; 5 6 #endif
2、windows方式
#pragma once
/* code */
10、动态库和静态库
库:将源文件生成 二进制文件 只需要链接 就可以生成可执行文件
静态库链接阶段将静态库的所有函数导入项目
优点:对库依赖小
缺点:生成的可执行文件大
linux下生成静态库:sudo gcc test.c(里有自定义头文件) -o main01(main01即为静态库)
动态库链接阶段仅建立动态库相关函数的关系,运行阶段才导入其二进制。
优点:生成的可执行文件小
缺点:对库依赖大
linux下生成动态库:sudo gcc test.c(里有自定义头文件) -o main02(main02即为动态库)
11、制作静态库
gcc ‐c fun.c ‐o fun.o
ar rc libtestlib.a fun.o
静态库libtestlib.a 是以lib开头 .a结尾 中间是库的名称testlib
静态库使用:
1、库和工程在同一目录
sudo gcc main.c(包含fun.c的自定义头文件fun.h) libtestlib.a
2、库和库的头文件在自定义目录下
sudo gcc main.c ‐I./lib ‐L./lib ‐ltestlib
‐I(大写的i)后面跟头文件的路径
‐L后面跟库的路径
‐l(小写的L)后面跟库的名称(lib和.a之间的名称)
3、库和库的头文件在系统目录
将库拷贝到/usr/lib下,将头文件拷贝到/usr/include下
然后执行sudo gcc main.c -ltestlib(给出库名)
12、制作动态库
gcc ‐shared fun.c ‐o libtestlib.so(红色部分为库名称)
动态态库使用:
编译链接:sudo gcc main.c -o main libtestlib.so
1、库和工程 在同一目录
将当前路径./添加到库的搜索目录可正常运行
export LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH($取该变量的值)(冒号分隔添加多条路径)
然后可以直接执行./main编译
2、库和库的头文件在自定义目录下
将当前路径./lib添加到库的搜索目录
export LD_LIBRARY_PATH=./lib:$LD_LIBRARY_PATH
3、库和库的头文件在系统目录
编译链接:sudo gcc main.c -o main -ltestlib
(若同名静态库和动态库testlib同时存在系统目录中,
默认编译选择动态库,只有加-static修饰才能链接静态。)
库在系统目录/usr/lib 头文件在/usr/include,可直接运行