C语言、C++语言、数据结构和算法,这些重点关注算法逻辑,无需要关注平台。
UNIX系统、Linux系统,重点关注系统接口,需要关注代码的运行平台。
那么什么是环境呢,举个简单的例子:Java程序不能在c编译器中使用,因为他们的库函数和编译规则等不同,这就是环境不同,
了解这个之前先了解一下GNU编译器,它的构建过程是:
预处理: gcc -E code.c -o code.i -> code.i
编译: gcc -S code.i -> code.s
汇编: gcc -c code.s -> code.o
链接:gcc code.o ... -> a.out
而文件的区分是按后缀区分的
文件后缀:
.h 头文件
.h.gch 头文件的编译结果,会被优先使用,建议立即删除
.c 源文件
.i 预处理文件
.s 汇编文件
.o 目标文件
.a 静态库文件
.so 共享(动态)库文件
参数:
-E 预处理
-S 产生汇编文件
-c 只编译不链拉
-o 指定输出文件的名字
-Wall 尽可能多的产生警告
-Werror 把警告当错误处理
-x 指定源代码的语言
-g 生成调试信息
-pedantic 对不符全ANSI标准的语法产生警告(对gcc扩展的语法产生警告)。
编译器支持的预处理指令:
#include 导入头文件
#define 定义宏常量、宏函数
#undef 删除宏
## 连接两个标识符
# 把参数转换成字符串字面值
#if 判定
#ifdef/#ifndef 宏定义判定
#else 与#if/#ifdef/#ifndef配合使用
#elif 相当于else if
#endif 判定的结束标志
#error 产生错误
#warning 产生警告
#line 修改当前行号
#pragma GCC dependency “文件名” 监控某个文件不被私自修改
#pragma GCC poison <标识> 把某个标识符当病毒处理,监控某个标识符不被使用。
#pragma pack(1/2/4) 修改结构、联合的补齐、对齐字节数
库
库:也叫代码库,把一个些目标文件合并在一起方便使用,有静态库和共享库两种。
静态库:在链接时把库的二进制指令复制到调用模块中。
共享库:会和调用者一起加载到内存,当执行调用语句时会从程序的调用位置跳转到共享库中运行。
优缺点:
静态库的优点是运行速度快,但维护麻烦,当静态库中人内容更新后需要重新编译程序,使用静态库编译出的可执行文件会比共享库的要大。
共享库的优点是使用方便,共享库如果发生变化不需要重新编译程序,使用它编译出的可执行文件比使用静态库要小,运行速度要比使用静态库慢。
注意:静态库的扩展名是.a,共享库的扩展名是.so,共享库要有执行权限。
可以认为创建库的就是创建环境,比如你建立了一个tools.h的静态库,那么在程序里面你可以使用#include函数调用tools.h中的函数,而换成别人的,没有这个静态库的电脑就无法运行。
说完环境接下来是程序:
程序就是保存在磁盘上的可执行文件,进程就是被加加载到内存中正在执行的程序。
进程在内存空间中的分布就是进程映像,从低到高依次是:
代码段 text
可以执行的二进制指令、字面值常量、以及被const保护的原data区的变量,只读。
全局数据段 data
初始化的全局变量、静态变量。
静态数据段 bss
末初始化的全局变量、静态变量。
进程一旦被加载,该段内存就会被清理为0。
data和bss合称为全局区或静态区
堆区 heap
动态内存分配,从低地址向高地址扩展。
C语言没有管理堆内存的语句,只能通过标准库函数和系统函数对它进行管理。
栈区 stack
局变量、块变量、函数参数、返回值等都存储在该段内存。
由系统自动管理,由高到低扩展
命令行参数和环境变量表:指向命令行参数的指针和指向环境变量的指针。
而程序依托于内存:
内存管理的层次划分
STL 智能指针,可以自动分配/释放内存资源 调用C++
C++ new/delete运算符,构造/析构函数 调用标准C
标准C malloc/calloc/realloc/free 调用POSIX接口
POSIX brk/sbrk 调用Linux
Linux mmap/munmap 调用kernel
Kernel kmalloc/vmalloc 调用驱动
驱动 get_free_page
说到内存还有一个虚拟内存的概念:
虚拟内存
1、在32位系统下,每个进程都有0~4G虚拟内存。
2、这些虚拟内存不能直接使用,需要与物理内存建立映射关系后才能使用,否则就会出现段错误。
3、虚拟内存与物理内存的映射由操作系统动态维护。
4、用户永远无法直接使用物理内存,只能使用虚拟内存。
5、4G内存空间分为两部分
[0~3G] 用户空间 如某栈内存地址为0xbfe95000,约等于3G
[3G~4G] 内核空间
6、用户空间的代码是不能访问内核空间的代码和数据,但可以通过系统调用进入内核状态,间接的与系统内核交互。
7、每个进程都对应一个用户空间,进程一切换用户空间随之变化,每个进程都有一个独一无二的进程号。
值得注意的是运行内存和储存内存都是内存,只是运行内存读写速度很快但是都是缓存,当运行不够的情况下,有些系统可能会调用储存内存来应急
系统IO与标准IO:
1、当系统调用被执行时,需要从用户态切换到内核态,执行完毕后再从内核态切换到用户态,频繁的切换就会导致性能损失。
2、标准IO在内部维护一个缓冲区,只有在满足特定条件才会把缓冲区与内核同步,因此降低了系统调用的使用频率,减少用户太和内核态的来回切换次数,因此速度比系统IO要快。
3、如果想提高系统IO的速度,可以尝试维护一个更大的缓冲区,这样它会比标准IO更快。
4、系统IO中没文本文件读写的函数,可以配合缓冲区+ssanf/sprintf来实现。
注意:普通情况建议使用标准IO(比直接使用系统IO要快),如果对速度有很高的要求,可以使用系统IO+大缓冲区。
文件位置指针
1、每个打开的文件都有一个记录读写位置的变量,它可能是整数,但都习惯的称为位置指针。
2、文件的读写操作都是从位置指针所指向地操作的。
3、lseek可以设置文件的位置指针,与标准C不一样的是它的返回值是它调整后的位置指针,所以系统调用中没有与ftell对应的函数,因此lseek就包含fseek和ftell的功能。
off_t lseek(int fd, off_t offset, int whence);
功能:调整文件位置指针,用法与标准C的fseek基本一致
4、在超过文件末尾的位置写入数据就会形成文件黑洞,黑洞不会占用磁盘空间,但会计算成文件的大小,而且也会影响文件的读写。
这里可以说一下删除的机制,文件删除本质上是把指定位置的内存上的二进制码全部清0,然后移除文件指针,但是这样会极大的拖延速度,所以可以加个判断,1清0,0跳过,这样可以加快删除速度,但是这还不是最快的,最快的是直接移除指针,不改变值,下次写入内存的时候就直接覆盖,但是这样可能会使文件产生错误,所以不常用。