C语言——底层思维

C语言——底层思维,第1张

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

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

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

$ gcc -c main.c  //停在汇编阶段(生成目标文件 main.o )
$ vim main.o  //16进制显示 二进制文件
              //在命令行模式下输入  :%!xxd
  1. 链接
    预处理、编译和汇编三个阶段针对一个模块(一个*.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中

参考资料

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/798191.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-06
下一篇 2022-05-06

发表评论

登录后才能评论

评论列表(0条)

保存