##4G的内存分布 从低到高:
代码段: 程序运行时加载程序指令到内存中形成4G虚拟内存镜像
存储代码指令 字符串字面值 代码段的内容不能修改 一旦修改必然核心已转储(段错误)
全局静态区-数据段
已初始化的全局变量和静态变量
全局静态区-bss段
未初始化的全局变量和静态变量 全部自动清0
堆(动态内存)
从小到大使用
程序员手动申请 手动释放
堆栈缓冲区(加载的动态库)
使堆栈数据不重叠
栈
从大到小使用
局部变量 形参 块变量 调用函数时的内存开销
程序自己维护 不需要程序员自己操心
自动分配 自动回收
命令行参数和环境列表
##字符串的长度:
strlen 不包括’\0’ 求字符串中字符的个数
sizeof 求内存大小 所以会包括’\0’
##常量指针和指针常量
const char p; 常量指针 指向常量的指针
const修饰p, *p只读 p可变
char const p;
const修饰p, *p只读 p可变
char * const p; 指针常量 指针是一个常量
const修饰p, *p可变 p只读
const char * const p;
*p和p都只读
编译过程
1. 预处理
gcc -E 源代码
不会自动生成文件
完成的工作: (1)去除注释内容
(2)进行预处理 执行预处理指令
#include 循环导入头文件的内容
#define 进行宏替换
gcc -E .c -o hello.i
2. 编译
gcc -S .c/预处理之后的文件
检查语法错误 自动生成汇编文件 汇编文件默认为.s
关键字写错 变量名没有声明
如果调用一个函数,该函数没有定义 不会报错
3. 汇编
gcc -c .s
把汇编文件汇编成机器指令 目标文件 .o
-c 只编译不链接 可以检查语法错误
4. 链接
gcc .o
把多个目标文件链接成可执行程序
链接main函数
把所有的函数调用进行链接(如果调用的某个函数不存在,则报错)
预处理指令
#开头
1. #include <文件>
把文件中内容导入到当前文件中
#include "" 也可以使用 #include <>
""和<>的区别:
<>: 是从系统所指定的路径下查找该头文件,如果该头文件不存在则报错
"": 是当前路径下查找该头文件,如果没有,则去系统指定的路径下查找,如果还是没有则报错
2. 宏定义
#define PI 3.14
#define YS 100LL*365*24*60*60*1000
注意了,LL 表示long long 类型
宏函数
#define SUB(x,y) x-y
#define SUBX(x,y) (x-y)
int a=10,b=5;
int res = SUB(a,b)/5; ==> a-b/5
res = SUBX(a,b)/5; ==> (a-b)/5
#define DIV(x,y) (x/y)
#define DIVX(x,y) ((x)/(y))
对于宏函数而言,在使用每个宏函数的参数时需要加(),然后对于整体表达式也需要加()
宏在预处理阶段进行"简单"替换
宏函数的语句如果要写在多行用 \
#define STR(a) #a 可以把标识符a变成一个字面值字符串
#define CON(a,b) a##b 把a和b合并成一个标识符
宏定义可以在编译代码时指定
gcc -D 宏=v .c
#undef 删除宏 取消宏定义
3.条件编译
条件编译语句在预处理阶段进行处理 只有一部分代码会编译
用分支选择语句实现相同的功能,但是分支选择语句编译之后文件要大,执行效率要低
#ifdef 宏
#ifndef 宏
#elsif 宏
#endif
头文件卫士: .h文件的格式
#ifndef XXXX
#define XXXX
c语句代码
#endif //XXXX
##机构体
###结构体指针变量:
struct 类型名 * 变量名;
结构体指针变量访问成员:
(结构体指针变量).成员名
结构体指针变量->成员名
运算符的优先级: . > *
###结构体的对齐补齐:
结构体成员对齐摆放: 从自身长度的整数倍的位置开始存放 超过4按4计算
结构体长度补齐: 结构体的长度会补齐为成员最大长度的整数倍 如果超过4按4计算
struct A{//4 ssc
short s;
char c;
};
struct B{//8 c—iiii
char c;
int i;
};
struct C{//8 ppppc***
int (p)[5];
char c;
};
struct D{//8 ppppcc*
char p;
char s[2];
};
struct E{//20 c—12c**
char c;
int *arr[3];
char x;
};
联合 共用体 union
###计算机的存储方式:
大端: 低地址存储高字节的数据 网络传输过程一律使用大端
小端: 低地址存储低字节的数据 一般来说大多数计算机都采用这种方式
0x12345678 低字节 0x78
###写一程序,判断当前所使用的机器是大端还是小端?
bool isLittel(){
union A a{
char c;
int i;
};
union A a;
a.i = 1;
if(a.c == 1){
return true;
}else{
return false;
}
}
##数组与指针:
int arr[5] = {1,2,3,4,5};
int p = &arr[0]; //&arr[0] == &(arr+0) == arr
arr+2 == &*(arr+2) == &arr[2]
arr == &arr[0]
&arr 与arr,&arr[0]都是同一个地址 但是类型不一样
arr,&arr[0] 都是指首元素的地址 +1 偏移一个元素的地址
&arr 是数组的地址 +1 偏移了整个数组的地址
&arr 其实是数组指针
int (*parr)[5] = &arr;
parr指向一个数组 数组长度为5
parr+1 偏移了一个一维数组的长度
parr[0] == *(parr+0) == *parr == &arr == arr
parr[0][0]
二维数组与指针:
int arr[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
&arr[0][0] 首元素地址
&arr[0][0] == &(arr[0]+0) == arr[0]
int p = arr[0];// &arr[0][0]
p = arr[2];//&(arr[2]+0) == &arr[2][0]
p = &arr[1][2];
int (*parr)[4] = &arr[0];// &arr[0] == &*(arr+0) == arr
parr = arr;
*parr == *arr == *&arr[0] == arr[0] == &*(arr[0]+0) == &arr[0][0]
**parr == arr[0][0]
int (*pta)[3][4] = &arr;
int arr[3][4][5];
int (*pa)[4][5] = arr;
int (*par)[3][4][5] = &arr;
##定义一个指针函数指针数组变量:
T* (*parr[n])(…)
T* (*parr[n])(...);
是数组 是指针数组 是指针函数指针数组
看定义识标识符是什么?
首先拿到标识符 parr 从标识符往后结合
如果是 [ 是数组
如果是 ( 是函数
如果是 ) 则往前结合
如果是 * 是指针
然后把上面结合的内容看作一个整体重复上面的动作
##插入排序
int compAsc(int x,int y){
return x-y;
}
int compDesc(int x,int y){
return y-x;
}
void sort(int arr[],size_t n){
int i,j;
for(i=1;i<n;i++){
int key = arr[i];
for(j=i-1;j>=0&&compDesc(arr[j],key)>0;–j){
arr[j+1] = arr[j];
}
arr[j+1] = key;
}
}