gcc的编译流程:
第一步:预处理---》将C源文件编译成C文件
头文件展开,注释去掉,宏替换掉,不做语法检查
gcc -E test.c -o test.i
第二步:编译--》将预处理之后C文件编译成汇编文件
gcc -S test.i -o test.s
第三步:汇编---》将汇编文件编译生成机器文件
gcc -c test.s -o test.o
第四步:链接---》将所有的机器文件共同参与链接,生成一个可执行文件
gcc *.o -o App
main函数传参
main带参数:
int main(int argc, const chat *argv[])
{
}
int argc:代表传递给main 函数的参数的个数,系统自己会计算传递过来的参数个数
const char *argv[]:代表存储传递给main函数的所有参数的地方
main函数无参:
int main() int main(void)
{ {
} }
二维数组的图解
gcc的编译流程:
第一步:预处理---》将C源文件编译成C文件
头文件展开,注释去掉,宏替换掉,不做语法检查
gcc -E test.c -o test.i
第二步:编译--》将预处理之后C文件编译成汇编文件
gcc -S test.i -o test.s
第三步:汇编---》将汇编文件编译生成机器文件
gcc -c test.s -o test.o
第四步:链接---》将所有的机器文件共同参与链接,生成一个可执行文件
gcc *.o -o App
main函数传参
main带参数:
int main(int argc, const chat *argv[])
{
}
int argc:代表传递给main 函数的参数的个数,系统自己会计算传递过来的参数个数
const char *argv[]:代表存储传递给main函数的所有参数的地方
main函数无参:
int main() int main(void)
{ {
} }
二维数组的图解
C高级
函数和指针的结合
函数:存在于内存中的一段二进制代码,函数名代表函数的入口地址,程序运行的时候,会为函数体寻找一片空间,又因为指针变量是用来存储变量地址,故,可以让指针存储函数的地址,将这样的指针称为“函数指针”
指针函数
概念:函数返回值类型是指针类型的函数
函数指针
概念:指向函数的指针(函数的本质是存在内存中的一段二进制代码,因此会占据一片空间,同时该片空间的首地址用函数名来代替)
void func(int *arr)
关键字typedef取别名的用法
typedef:给已存在的一个数据类型在其作用域内取一个别名
针对函数指针类型取别名的写法:返回值类型 (*新类型名)(形参列表);
函数指针数组的定义
函数指针数组:数组元素类型是函数指针类型的数组。
函数指针类型 数组名[元素个数]; //取完别名之后可以这样定义函数指针数组。
返回值类型 (*数组名[元素个数])(参数表列表);
回调函数
概念:把B函数的地址即(函数名或者&函数名)作为A函数的参数,传递给A函数,在A函数中通过使用这个参数来间接调B函数的过程就称为“回调函数”
动态内存管理
申请空间
void *malloc(size_t size);
功能:申请空间在堆区中
参数:所需申请空间占据的字节数
返回值:成功返回连续空间的首地址,失败返回NULL
void *calloc(size_t nmemb, size_t size);
功能:申请空间
参数:
参数1:所需申请的元素个数
参数2:元素的大小(字节数)
返回值:成功返回连续空间的首地址,失败返回NULL
释放空间
void free(void *ptr);
功能:释放空间
参数:所需释放空间的首地址
返回值:无返回值
扩容空间
void *realloc(void *ptr, size_t size);
功能:用来扩容空间
参数:
参数1:所需扩容空间的首地址(malloc的返回值)
参数2:扩容之后总的字节数(旧 + 新)
返回值:成功返回扩容之后空间的首地址,失败返回NULL
结构体
概念:是一种构造数据类型,目的是为了将一个事物的多重属性表示清楚(属性可能是不同的数据类型,也可以是相同的数据类型)。
struct 结构体名
{
数据类型1 成员1 ;
数据类型2 成员2;
。。。。
数据类型N 成员N;
};
结构体的字节对齐
在32OS:系统最多一次性开辟4个字节
怎样确定每一次开辟空间的大小,依赖于成员中占据字节空间最大的那个数据类型,将其作为基准,来进行每一次空间大小的开辟,但是成员有超过上限4字节的数据类型,此时仍按照4字节进行开辟空间。反之,则按照那个最大的类型占据的字节数进行开辟。
在64OS:系统最多一次性开辟8个字节
怎样确定每一次开辟空间的大小,依赖于成员中占据字节空间最大的那个数据类型,将其作为基准,来进行每一次空间大小的开辟,但是成员有超过上限8字节的数据类型,此时仍按照8字节进行开辟空间。反之,则按照那个最大的类型占据的字节数进行开辟。
不管是32OS还是64OS,在存储成员时,均需要保证存储的地址编号和该成员自身的数据类型大小成整数倍的关系,否则往后寻找下一个合适的地址空间进行存储。
共用体
概念:多个成员共用同一片空间,因此开辟内存空间时,会按照占据内存空间最大的数据类型进行开辟。
union 共用体名
{
数据类型1 成员1;
。。。。
数据类型N 成员N
};
共用体的使用方法和结构体一致,唯一区别是:结构体的成员空间是独立的,而共用体的成员共用同一片空间。
枚举
概念:跟宏比较类似,意味着也是一些常量值,只不过这些常量值是被放在一个集合里面。
enum 枚举名
{
枚举数值名1,
枚举数值名2,
。。。。
};
大小端(字节序)
概念:计算机中存储数据的时候到底将低字节或者高字节处的内容存储在低地址还是高地址的问题。
大端存储:低字节处的内容存储在高地址处,高字节处的内容存储在低地址处。
小端存储:低字节处的内容存储在低地址处,高字节处的内容存储在高地址处。
低字节:一串二进制数据的右边
低地址:小的地址编号
调试BUG的方式
方式1:printf打印 --->最直接
在自己感觉出错的代码段前后加上部分printf输出内容,然后编译运行来判断问题的出处。
方式2:gdb调试工具
流程:
- 生成带有调试信息环境的可执行文件
方式:gcc -g test.c -o App
- 进入该可执行文件
方式:gdb App
- 设置断点 -->让程序从哪里开始调试
方式:b 行标/函数名
- 运行
方式: r
- 单步运行
n: 不进入子函数内部,但不代表不执行子函数
s: 进入子函数内容,并开始执行子函数中每一行代码
- 找到错误所在的行
响应:看到程序打印出一行文本“Program recived Sigmentation fault....”,上一行就是引起错误的所在行。
- 结束调试
方式:按下q退出即可。
对于段错误的理解
在C语言阶段:出现段错误的原因:
- 数组越界
- 操作空指针或者野指针
- 修改常量区内容
递归函数
概念:重复调用其他函数或者调用自己,但是调用的时候一定要注意结束调用的条件。
头文件的书写(多文件编译)
创建一个后缀是.h文件eg: touch test.h
打开test.h ---> vim test.h
编写以下代码:
#ifndef _TEST_H_
#define _TEST_H_
...
- 宏定义
- 结构体定义
- 取别名
- 全局变量的声明
- 函数的声明
- 枚举类型的定义
#endif
Make
概念:工程管理工具
可以将很多.c文件+.h文件进行统一管理
嵌套Makefile:
自动变量
$< :依赖中的第一个依赖
$^ :所有依赖
$@:目标名
系统预定义变量
CC:编译器的名称,默认值是cc ,用户可以自己赋值为: eg CC :=gcc
CFLAGS:编译器的选项,无默认值,用户可以自己赋值:eg: CFLAGS:= -c -g -Wall
RM:代表删除,默认值为:rm -f,用户可以自己赋值为 eg: RM := -rf
环境变量
export :添加一些指定的环境变量到整个系统环境中去
eg:
export 变量1 变量2 变量3
LinuxOS
Linux的体系结构
内核:核心组件,负责管理操作系统的执行。
进程管理,内存管理,网络管理,文件系统管理,设备驱动管理
shell:本质是一个命令行解释器,起到中间桥梁的作用,负责接收用户输入的命令,并简化其翻译提交给内核,让内核做出回应。
文件系统:
数据在设备或者分区上组织文件的一种方法(数据结构)。
分为:
磁盘文件系统(U盘),虚拟文件系统(隐藏了跨文件系统类型数据传输的细节,调用者只需要使用统一的接口,即可完成不同文件系统类型的之间的数据交互。),网络文件系统(进程间网络通信时,数据的接收以及转发)。
Linux的软件包管理
软件包的命名方式图解:
如何设置一个固定的IP地址?
关心4个参数:IP地址(192.168.0.num) , 子网掩码(255.255.255.0) , 网关(192.168.0.1), DNS服务器(8.8.8.8 / 8.8.4.4)
Linux的网络基础
IP地址:
IP:Internet Protocol ---->网络协议,在通信中起到路由的功能
IP地址的形式:由点分十进制的数字组合而成,总共占4给字节(32位二进制数据):eg:192.168.0.89
IP地址的组成:网络地址(高位) + 主机地址(低位)
IP地址类型:因为网络地址和主机地址所占字节数的不同,可以划分为:A类 B类 C类 D类 E类
IP地址5种类型的划分,按照网络地址和主机地址所占的比例:
A类IP:网络地址占1个字节,主机地址占3个字节,并且规定网络地址的最高位是0
范围:0.0.0.0 ~ 127.255.255.255
注意:
0.0.0.0为任意地址,用户不会获得,网络地址会从1开始,主机地址也是从1开始分配给用户,127开头的IP地址都是本地环回地址,用户无网络时进程间通信,以255结尾的IP地址都是广播地址,用户也不会被分配到,因此,用户在A类IP范围中,可以获取的有效IP地址范围为:1.0.0.1 ~ 126.255.255.254
B类IP:网络地址占2个字节,主机地址占2个字节,并且规定网络地址的最高位是10
C类IP:网络地址占3个字节,主机地址占1个字节,并且规定网络地址的最高位是110
范围:192.0.0.1 ~ 223.255.255.254 (优化之后的有效IP地址范围)
D类IP:目前用于组播实现比较多
E类IP:目前处于保留,等待将来使用
Linux下的shell命令
常见的shell命令
软链接:
- 源文件和软链接是两块空间,并且软链接文件存储的是指向源文件的一条路径
- 源文件和软链接的影响是双向的。
- 创建软链接文件时,源文件建议写成绝对路径的方式,可以提高软链接文件的移植性。
- 当删除源文件时,软链接立马失效,当恢复(创建一个与之前源文件同名的新文件)源文件时,此时软链接文件会生效,但是此时指向的是最新的文件。
硬链接:
- 源文件和硬链接是同一片空间,可以理解为硬链接是源文件那片空间的一个别名而已。
- 源文件和硬链接的影响是双向的。
- 创建硬链接文件时,源文件以相对路径或者绝对路径都可以,不依赖路径而存在。
- 当删除源文件时,硬链接不受影响,可以理解为那片空间的名字少了一个,此时依旧可以通过硬链接来打开之前源文件的空间并进行操作。(可以当做备份或者误删来使用)。
chmod:对于文件的文权限进行更改
创建新文件的初始权限是:rw- rw- r-- ---》0664,如果需要对该文件的 权限进行升级或者降级时,就需要通过chmod来做。
-
假设:文件为:test.c --->初始权限:0664
方式1: 八进制法
chmod 0774 test.c / chmod 774 test.c
方式2:符号标记法
用户:u
用户组:g
其他用户:o
所有用户:a
chmod u+x test.c
chmod u-x test.c
chmod a+x test.c
chmod +x test.c (等价于a + x)
tar:来实现打包相关的命令
选项:
-c:打包
-x:解包
-v:可视化(可以看到打包,压缩,解包,解压的过程)
-z:可以调用压缩工具为gzip
-j:可以调用压缩工具为bzip2
-f:可以指明需要打包压缩的文件名rep :查找对象是文件中内容,在一个文件中去匹配指定的格式,匹配搜索指定的格式(文件中)
-
格式:
grep 选项 格式 路径
解释:
选项:
-c:匹配包含指定格式所在行的行数
-n:匹配包含指定格式所在行
-v:匹配除了包含指定格式所在行的其他行
-
find:查找,查找对象是文件
格式:
find 路径 选项 描述
解释:
-name:代表需要查找指定文件名
-
wc:统计文件的大小,单词数,以及文件的行数。
shell特殊字符
通配符
[范围]:匹配含有范围之内的一个长度的内容
[指定范围]:匹配含有指定范围之内的一个长度的内容
[^指定范围]:匹配除了指定范围之外的任何一个长度的内容
管道
格式:
command1 | command2 | ..... | commandN
理解:
将前一个命令的输出作为后一个命令的输入
命令置换
格式:
Command1 ` Command2`
理解:将Command2的输出作为Command1命令的参数
脚本的理解
脚本就是一门解释型的语言,本质是多条shell命令的有序集合(可能会加入一定量的控制结构语句)。
shell命令:理解为用户发送给内核一种信号(该信号是由shell来完成解释)
shell:命令行解释器(专门负责将用户输入的命令或者语句翻译给内核)
shell脚本:多条shell命令的有序集合(可能会引入一些控制结构语句)
添加新环境变量:
export 变量名=值 (可以一次性赋值多个,多个数值之间以冒号隔开)
修改环境变量值:
export 变量名=新值:${变量名} / export 变量名=${变量名} :新值
清除环境变量:
unset 变量名
提示语句的写法:
- 方式1:通过echo “提示语句”
- 方式2:使用read的同时去打印:read -p “提示语句” 变量名
通配符的转义
软转义:
双引号 和 不加双引号 ---》软转义(会保留shell特殊字符的含义)
eg:
在打印输出的时,遇到${变量名}时,会先执行${变量名},访问到数值之后再去输出。还有遇到命令的时候,也会先执行命令,将命令执行之后的结果再去输出。
总结:
双引号和不加双引号在多数情况下,两者没啥区别,但是又不完全等价。
测试命令test
测试命令---->代替判断语句
测试格式:
第一种: [ 对象1 测试条件 对象2 ]
第二种: test 对象1 测试条件 对象2
测试对象:
- 整形:测试对象假设为:num1 num2
等于: num1 -eq num2
不等于:num1 -ne num2
大于: num1 -gt num2
大于等于:num1 -ge num2
小于: num1 -lt num2
小于等于: num1 -le num2
等于:equal 不等于:not equal
大于:greater than 大于等于 :greater than equal
小于:less than 小于等于:less than equal
- 字符串:测试对象假设为:str1 str2
等于: str1 = str2 (空格必须有,否则被视为变量赋值)
不等于:str1 != str2
字符串长度为0: -z str1/str2
字符串长度不为0:-n str1/str2
- 文件属性:假设测试对象为:f1 f2
-f :测试对象是否存在且为普通文件
-d :测试对象是否存在且为目录文件
-L :测试对象是否存在且为软链接文件(符号链接文件)
-r:测试对象是否存在且具备可读权限
-w:测试对象是否存在且具备可写权限
-x:测试对象是否存在且具备可执行权限
-s:测试对象的大小
f1 -nt f2:测试f1是否比f2更新(文件最后一次保存的时间)
f1 -ot f2:测试f1是否比f2更旧(文件最后一次保存的时间)
结构性语句
- 顺序结构
- 选择结构
多路分支:
if:
形式参考如下:
if 测试语句
then
命令表
elif 测试语句
then
命令表
。。。
else
命令表
fi
case:
形式参考如下:
case 变量值 in
模式1)
命令表
;;
模式2)
命令表
;;
...
*)
命令表
esac
注意:
在脚本中:与使用-a表示,或使用-o表示,非使用!表示
3.循环结构
带参数列表
for 变量名 in 单词列表
do
命令表
done
seq:产生指定范围之内的所有整数。
用法:
seq 范围1 范围2
脚本中的while循环。
格式:
while 条件判断
do
命令表
done
死循环:
while [ true ]
do
命令表
done
脚本的函数
函数定义格式:
function_name()
{
命令表1
命令表2
。。。
命令表N
}