C语言为什么是高效的?
C语言是底层机器上的高级抽象,以至于让大部分低级语言没有意义。既可以用来编写高级算法,也可以用来处理底层硬件逻辑。
C语言是一种弱类型语言,所有调用函数的参数类型和返回值类型必须被明确定义;这样做的缺点就是调用者的自由度有限,缺点的同时也是优点,可以让人少犯错误,c语言尽力构建一个简洁的、通俗的类型系统,不需要考虑太多依赖关系,使开发变得简单。
C语言不隐藏消费空间和时间的任何细节,纯用C语言写的程序可以查看调用堆栈、变量、参数、当前线程,C会有野指针、不检查内存溢出等问题,但是也比较明显,可以提高开发效率,容易定位问题。
C语言编译过程
预编译-编译-链接
C预处理器
C预处理器在编译之前运行,主要执行内容有:
- 将所有#define删除,并展开宏替换内容:宏定义中参数应该加上(),以免产生因为纯字符替换产生的问题。宏末尾不加分号,宏名和参数的括号间不能有空格;宏替换在编译前运行,不分配内存;宏替换使源程序变长,增加编译时间,不增加运行时间。
- 处理所有的条件编译指令。条件编译:提供条件编译措施使同一个源程序根据编译条件产生不同的目标代码,用于便于调试和移植;文件包含可能会产生多重包含的问题,可以使用条件编译方式避免多重包含。
- 处理头文件包含,<>中的头文件将到系统路径中去查找头文件,””中包含的路径将去源文件路径下进行查找,将include所指向的文件插入到该行。
- 删除注释
- 添加行号和文件标识。
- 保留#pragma等编译器指令
C语言编译过程和链接过程
编译本质是将高级语言转化成机器语言的过程:包括词法分析、语法分析、语义分析、优化后生成相应的汇编代码。C语言->汇编->机器语言。
链接是将编译成的二进制与需要用到的库进行绑定,生成可执行文件。
Main函数的返回值是做什么的
C99标准、C++98以后标准规定main函数写法为 int main(void) / int main(int argc, char **argv), 标准中从未出现过 void main()形式。Main函数默认返回类型为int。
main 返回值表示其退出进程状态,返回0表示正常退出,非0表示异常退出,利用程序的返回值可以控制要不要执行下一个程序。
全局变量
全局变量不属于任何一个函数,生命周期贯穿整个进程。全局变量可声明多次,但只能定义一次。
一般情况下全局变量仅用于同一个文件中,比如在a.c中定义的全局变量,在b.c中如果直接使用,在编译时就会提示错误,如果要在b中使用a.c中定义的全局变量有两种方式:1. 在a.h中定义全局变量,b在编码时引用a.h,如果写错变量名会在编译时发生错误;2.在b中使用extern方式显式的声明全局变量,写错变量名在编译时会发生错误。
如果定义的全局变量不想被其他文件所使用,需要在全局变量声明时添加static关键词,将只会对当前文件可见。静态函数优点:可以让两个同名函数在在不用文件有不同作用;由于没有外部符号,可以减少编译消耗。
全局变量优点:相对于向每个函数传递参数而言,全局变量减少了函数调用前参数入栈和调用后出栈的开销,提高了效率。缺点:降低了程序的模块化程度,使程序变成有状态的。
C语言指针
指针是一个复合类型变量,认清指针需要知道指针本身的类型,指针所指的类型,指针所指的位置,指针本身所在的位置。
指针本身的类型,指针所指的类型,在定义指针时就进行了声明。
指针所指的位置,即指针的值,编译器通常不会对指针的值当作数值进行计算,而是当作一个地址,指针所指地址是那个内存地址的开始,长度为sizeof(指针所指类型)。
指针本身的位置本身没有太多实际意义,但指针的大小为sizeof(指针类型)。
指针的算术运算,通常与数组相关,加减代表指针所指位置的前移或后移。
定义数组时按照指针进行定义通常效率更高,占用存储空间更小,在操作时不需要传递整个数组只需要传递指针,减少内存分配消耗。
C语言字符串
字符串结尾添加’\0’,字符串长度为字符串原本长度+1,存储位置为连续字符数组,数组名即为指针,指针指向数组的起点。
C风格字符串使用库函数对字符串进行操作,引入头文件string.h。
strcpy(a, b) | 字符串复制,从b到a |
strcmp(a, b) | 字符串字典序比较,a<b 返回小于0 |
strcat(a, b) strncat(a, b, n) | 字符串拼接,将b接到a后面 |
strstr(a, b) | 子串查找,从a里面查找b |
strchr(a, b) | 字符查找,从a里面查找字符b |
strlen(a) | 串长度,查询a的串长度 |
strcpy(a, b) strncpy(a, b, n) | 串复制,从b复制到a |
strcoll(a, b) strncoll(a, b, n) | 字符串比较,根据LC_COLLATE进行比较,默认情况下和strcmp结果相同,但是设置语言或环境后会产生其他结果,例如汉字会根据拼音顺序进行比较 |
C语言内存操作
分配堆空间使用malloc,返回void* 类型的指针,分配失败时返回null,需要显式指出内存的大小,malloc后可以使用realloc对空间进行再次扩容。
calloc可分配空间并初始化,建议初始化为0,calloc初始化时根据实现不同可能会按位写入或者按字节写入或者多个字节写入。
获取的内存地址为虚拟内存地址,虚拟内存和物理内存通过MMU(Memory Management Unit)进行地址映射。映射内容不是字节而是地址页,页内偏移一致。(这个图和下面那个图来自很久之前看的一个博客,不好意思忘记来源了,侵权了我就删掉自己画个)
为加快速度会加入TLB(转换检测缓冲区,缓存最近从虚拟内存到物理内存映射)。
在MMU工作时如果发现在物理内存中没有对应的内存页,一般会触发page fault 缺页异常,系统将会到磁盘中取磁盘页装入内存,重新执行查询。
包含TLB的工作流程。