irpas技术客

C语言——底层思维_Dynasty阳_c语言的底层

大大的周 6419

底层思维,是指程序在硬件上如何编译存储运行的。

gcc编译 $ gcc main.c //编译C程序,默认生成a.out执行文件 //gcc -v main.c 显示详细的编译过程信息 //如果是C++文件(main.cpp),把gcc改成g++即可 $ ./a.out //运行程序 gdb调试 $ gcc -g main.c //编译生成带有调试信息的执行文件a.out $ gdb a.out //调试程序 $ l //list: 查看源码(默认10行) //list 8 查看8行附近的代码 //list main 查看函数名附近的码 $ b 5 //break: 在第5行设置断点 $ r //run: 运行到断点处(如无则运行直到结束) $ s //step: 单步执行(如果是函数则进入) $ n //next: 单步跳过(如果是函数则跳过) $ p a //print: 查看变量内容 // p &a 查看变量a的地址 // p &b+1 b变量的地址根据数据类型递增(int型 会加4,char型则加1) // p *(&b+1) // p a[1] // p *p1 查看p1指针里的内容 // p add(4,7) 调用函数add 并打印返回值 $ p/t b // t 二进制显示 变量的值 // x 按十六进制格式显示变量。 // d 按十进制格式显示变量。 // u 按十六进制格式显示无符号整型。 // o 按八进制格式显示变量。 // a 按十六进制格式显示变量。 // c 按字符格式显示变量。 // f 按浮点数格式显示变量。 $ x &a //显示某地址的内存值 //例如 float a = 0.45; 的内存值为 // 0x7fffffffdf8c: 00111110111001100110011001100110 上下键 //可查看历史命令 $ bt //查看堆栈信息 (如嵌套调用多个函数时) $ c //continue: 继续执行程序(直到下一个断点或者结束) $ q //quit: 退出gdb调试 $ dmesg //查看内核信息

如何编译生成执行文件? 生成执行文件的过程:main.c -> 预处理 -> 编译 -> 汇编 -> 连接 -> a.out

$ gcc main.c fun.c //编译多个文件,生成执行文件a.out $ ./a.out //运行程序 预处理 预处理执行的功能: (1)删除“#define”并展开所定义的宏 (2)处理所有条件预编译指令,如“#if”,“#ifdef”, “#endif”等 (3)插入头文件到“#include”处,可以递归方式进行处理 (4)删除所有的注释“//”和“/* */” (5)添加行号和文件名标识,以便编译时编译器产生调试用的行号信息 (6)保留所有#pragma编译指令(编译器需要用)

经过预编译处理后,得到的是预处理文件(如,hello.i) ,它还是一个可读的文本文件 ,但不包含任何宏定义,上面所说的第3条插入头文件到“include”处,可以理解为将头文件里面的内容进行展开

$ gcc -E main.c -o main.i //停在预处理阶段(生成 main.i文件) //发现宏被替换了,条件编译生效 编译 编译过程就是将预处理后得到的预处理文件(如 main.i)进行词法分析、语法分析、语义分析、优化后,生成汇编代码文件。用来进行编译处理的程序称为编译程序(编译器,Compiler) 经过编译后,得到的汇编代码文件(如 main.s)还是可读的文本文件,CPU无法理解和执行它,不要着急,接下来进行下一步汇编命令的执行过程 $ gcc -S main.c //停在编译阶段(生成汇编文件 main.s) 汇编 首先我们先了解下汇编代码文件(由汇编指令构成)称为汇编语言源程序,其实就是上面编译过程结束之后生成的.s文件,这个文件就是汇编代码文件,该文件是有一条条汇编指令构成,汇编的作用就是讲这一条条汇编指令转换成对应的机器码执行。 汇编结果是一个可重定位目标文件(如,main.o),其中包含的是不可读的二进制代码,必须用相应的工具软件来查看其内容

“汇编程序(汇编器)用来将汇编语言源程序转换为机器指令序列(机器语言程序) 汇编指令和机器指令一一对应,前者是后者的符号表示,它们都属于机器级指令,所构成的程序称为机器级代码,汇编的过程比较简单,只需要将相应的汇编指令翻译成对应的机器指令即可,没有什么复杂的变化。”

$ gcc -c main.c //停在汇编阶段(生成目标文件 main.o ) $ vim main.o //16进制显示 二进制文件 //在命令行模式下输入 :%!xxd 链接 预处理、编译和汇编三个阶段针对一个模块(一个*.c文件)进行处理,得到对应的一个可重定位目标文件(一个*.o文件),但是在程序的编写过程中,我们都是多个.c文件的,这样经过上面的预处理,编译汇编的过程之后我们得到的也是多个.o(可重定位目标文件),但是我们在最终执行的时候是只有一个可执行文件的,这个过程就是链接的目的了。 链接过程将多个可重定位目标文件合并以生成可执行目标文件,在最后一步连接的过程中,我们不只是要hello.o而且还需要printf.o(代码中含有printf函数),链接就是将这连个.o合并为一个生成可执行目标文件。 $ gcc -c fun.c $ gcc main.o fun.o //链接目标文件或库文件(.a .so),生成执行文件 a.out(.bin .exe) //独立的目标文件都是从0地址开始,需要链接来重新合并安排地址 //链接异常 会报 collect2: error: ld returned 1 exit status $ objdump -d a.out //查看反汇编代码 //可查看 objdump -d fun.o 等看链接过程是如何把代码嵌入式到main.o中

参考资料


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #c语言的底层