上篇 C语言入门
$?是shell中一个特殊变量,表示上一条命令的退出状态。
增量式开发:写一些代码编译运行,没问题再写下面的。
在编译时要加上-g选项,生成的可执行文件才能用gdb进行源码级调试:
gcc -g main.c -o main -g选项的作用是在可执行文件中加入源代码信息,比如第几条机器指令对应第几行源代码。但并不将源代码嵌入,因此在使用gdb调试时仍要使得能找到源代码。
gdb提供类似shell的环境,常用命令:list、start、next、step(简写为s)、backtrace(简写为bt,查看函数调用的帧栈)、info(查看函数局部变量的值)、print、display(每次停下来都会显示指定变量值,取消跟踪用undisplay)、break(简写为b,可接代码行数,也可接函数名,还可以设置断点在满足某个条件时才激活)、delete breakpoints、disable breakpoints、enable (这三个命令均加断点号)、info breakpoints(查看设置了哪些断点)、continue(简写为c,连续运行直到断点)、watch设置观察点(watchpoints)、x(打印指定存储位置值,以字节而非变量为单位)、
使用断言,assert,assert.h。
深度优先搜索(通常使用栈),广度优先搜索(使用队列)。
下篇 C语言本质
通用寄存器(eax、ecx等),特殊寄存器(ebp、esp等,进位标志就保存在特殊寄存器中)。
链接器,Section Header Table,加载器,Program Header Table。
gcc -S选项生成汇编代码,-C选项生成目标文件,它们搭配-o使用可以对生成文件进行命名。
可以在main中直接调用exit函数终止进程而不返回到启动例程。
在链接时,.rodata段(const变量等保存在该段)和.text段合并到Text Segment中,在加载运行时操作系统把Text Segment的页面只读保护起来,防止意外改写。
虽然栈由高地址向低地址生长,但数组总是由低地址向高地址排列。
Bit-field也属于整型,可以用int或unsigned int声明,如 unsigned int a:1 ;指定a占一bit。
C内联汇编示例:(其中 用于将多条指令分隔开)
_asm_("movl $1, %eax
"
"movl $4, %ebx
"
"int $0x80");
gdb命令中指定某个.c文件中的某一行或某个函数,可以用“文件名:行号”或“文件名:函数名”。
静态库:
先编译成目标文件: gcc -c stack/stack.c stack/push.c stack/pop.c stack/is_empty.c
然后打包: ar rs libstack.a stack.o push.o pop.o is_empty.o
静态库.a作为后缀,表示Archive。ar命令类似tar,但把目标文件打包成静态库只能用ar,r选项表示将后面的目标文件列表添加到libstack.a,s选项表示为静态库创建索引,使用ranlib命令也可以为静态库创建索引。
然后把libstack.a和main.c编译链接在一起: gcc main.c -L. -lstack -Istack -o main
-L指定库文件路径,-L.表示在当前目录找,-I选项指定头文件路径。对库文件,首先在-L指定路径找动态库,没有再找静态库,没有再到默认搜索路径按同样步骤查找。
链接器从静态库取需要的目标文件来做链接,而不需要全部链接;使用静态库另一个好处是只需写一个库文件名,而不需要写一长串目标文件名。
共享库:
在编译组成共享库的目标文件时要加-fPIC选项,PIC表示生成位置无关代码(Position Independent Code)。
添加共享库路径方法:1、设置环境变量LD_LIBRARY_PATH;2、把共享库所在目录绝对路径写到配置文件/etc/ld.so.conf,然后运行ldconfig命令: sudo ldconfig -v ;3、把共享库复制到/usr/lib或/lib目录;4、在链接生成可执行文件时把共享库的路径写到文件中(不推荐)。
每个共享库有三个文件名:real name、soname和linker name。.dynamic段只记录共享库的soname。
堆空间可以向高地址增长,并且有很大增长余地;栈空间向低地址增长,栈空间可能用尽,且比堆空间更容易用尽。
虚拟内存管理机制作用:1、控制物理内存的访问权限;2、使每个进程有独立的地址空间;3、VA到PA的映射会给分配和释放内存带来方便;4、可使用交换设备(Swap device)从而在所需内存大于物理内存时仍能运行。