操作系统到底是什么?
操作系统其实就是类似于人类社会一样的软件系统,操作系统本身并不直接生产价值,他的主要任务是管理资源,并为应用程序提供服务,操作系统既是管理者,也是服务者。
操作系统的调用通道:API
应用程序和操作系统之间通过API接口进行互相调用,应用程序需要使用系统资源的时候,通过API向操作系统发出申请,操作系统响应申请,以帮助应用程序执行功能。
C库和API
某些时候,C库函数可以完成,系统API也可以完成,用哪个都可以,根据实际需求进行选择。
不同操作系统下的API
不同的操作系统,API是不同的,但是都能完成任务,只不过是完成任务调用的API不同,库函数在不同操作系统下也不相同,但是相似程度会高一些,所以在一个操作系统上编写的应用程序不能在另外一个操作系统上编译运行
main函数
main函数的返回值
main函数是特殊的,是C语言程序的入口,linux中一个新程序的执行本质上就是进程的创建和运行,进程是由其父进程fork出来的,所以main函数返回给了创建这个进程的父进程。父进程根据这个返回值判断子进程的任务执行结果成功还是失败了。
main函数的参数
main函数的参数,是由main函数所在程序的父进程传递过来的,main函数不传递参数也是可以的,之所以要传递参数是希望程序更加灵活,通过传参来控制程序运行,我们通过argc和argv给main函数传参,argc表示参数的个数,argv是字符串数组,用于存储多个字符串,每个字符串就是给main函数传的参数,父进程是通过exec函数族时,将参数传递给main的,但是参数传递要注意:
- 参数都是字符串类型的,不论什么数据类型传进去都会被转变为字符串
- 每个参数之间通过空格分开
- 程序内部要先检查argc查看有多少参数,再使用argv获取参数
void类型和NULL
void类型
C语言属于强类型语言,void表示不知道,还没确定的类型,在描述一段还没有具体使用的内存时,就需要使用void类型,例如malloc的返回值,void指向的内存是尚未确定类型的,因此我们后续可以将其转换为其他具体类型
NULL
NULL最常用的场景是用于判断一个指针是否是一个空指针,NULL不是C语言的关键字或者保留字,实质上是一个宏定义,是这么定义的#define NULL 0
,值是0x00000000,代表内存的0地址,在大部分操作系统中,地址为0的内存是不可以被随便访问的,一旦野指针指向这里可以保证野指针不会造成危害,此时解引用野指针就会触发段错误,可以帮助我们找到程序中的错误。
临时匿名变量
C语言是高级语言,而汇编语言则是低级语言,汇编语言和机器操作相对应,汇编语言是CPU机器码的助记符,使用汇编写程序必须拥有机器的思维,不同的CPU设计时指令集差异很大,因此汇编编程时差异很大。
高级语言对低级语言进行了封装,高级语言提供了更靠近人类思维的特性,使得人类可以按照自己的逻辑原理来编程,低级语言中需要好几部才能实现的运算,在高级语言中只需要一步即可完成
顺序结构
顺序结构反映了CPU的工作状态,以时间轴来顺序执行所有的代码语句直到停机,选择和循环语句之中,也是按照顺序结构来执行的。
编译器按照C文件中从前到后逐行编译的,所以编译器也是顺序执行的,这也表明了先定义后声明的重要性。
在链接时,也是按照链接脚本的指导下进行的,所以链接的顺序是按照链接脚本执行的。
程序调试的debug宏
单步调试
利用调试器进行单步调试,例如dbg以及一些IDE集成开发环境,适合于新手,比较直观,但是限制性大,速度慢
裸机调试
在裸机中使用LED或者蜂鸣器进行调试,因为很多裸机并不支持调试器
打印调试
这种方式是最基本的调试方式,应该是必须要掌握的,需要对程序很熟悉,有一定的编程水平,但是具有普遍性,几乎在所有情况下都适用。
log文件
系统运行时在特定时候会打印一些调试信息,以供后续查找问题,适合系统级或者大型程序的调试
调试信息不能太多也不能太少,数量要适当,debug版本通常就是包含了程序调试信息的版本,在程序测试中发布,这些调试信息可以帮助开发人员定位出现问题的位置,我们通过条件编译debug宏来控制生成DEBUG和RELEASE版本。
debug宏
如果要输出debug版本,就在条件编译语句之前加上#define DEBUG,要输出release版本就去掉即可,