代码从编译到运行经历了什么?你真的清楚吗?

       在我们刚接触C语言时,会发现老师教的都是安装打开一个C语言的IDE,然后教如何创建工程、如何编译代码、如何运行代码,但是老师并没有讲编译的过程到底经历了那些工作。
       C语言诞生于美国的贝尔实验室,由D.M.Ritchie以B语言为基础发展而来,在它的主体设计完成后,Thompson和Ritchie用它完全重写了UNIX,且随着UNIX的发展,C语言也得到了不断的完善。GCC的初衷是为GNU操作系统专门编写的一款编译器。GNU系统是彻底的自由软件。此处,“自由”的含义是它尊重用户的自由 。
       GCC现已被大多数类Unix操作系统(如Linux、BSD、Mac OS X等)采纳为标准的编译器。所以通过gcc这个编译器来了解程序编译经历了,是很不错的!

GNU工具

  • 编译工具:把一个源程序编译为一个可执行程序
  • 调试工具:能对执行程序进行源码或者汇编级调试
  • 软件工程工具:用于协助多人开发或者大型软件项目的管理。如make、CVS、Subvision
  • 其他工具:用于把多个文件链接成可执行文件的连接器,或者用作格式转换的工具

GCC简介

  • 全称为GUN CC,GUN项目中符合ANSI C标准编译系统
  • 编译如C、C++、Object C、JAVA、Fortran、Pascal、Modula-3和Ada等多种语言
  • GCC是可以在多种硬件平台上编译出可执行程序的超级编译器,其执行效率与一般的编辑器相比平均效率高出20%~30%
  • 一个交叉平台编译器,适合在嵌入式平台领域的开发编译
  • gcc所支持后缀名解释
后缀名 解释程序类型
.c C原始程序
.C/.cc/.cxx C++原始程序
.m Objective-C原始程序
.i 已经过预处理的C原始程序
.ii 已经经过处理的C++原始程序
.s/.S 汇编语言原始程序
.h 预处理文件(头文件)
.o 目标文件
.a/.so 编译后的库文件

编译器的主要组件

  • 分析器:将源语言程序代码转换为汇编语言(C到汇编),所以分析器需要知道目标机器的汇编语言。
  • 汇编器:汇编器将汇编语言代码转换为CPU可以执行的字节码
  • 链接器:将汇编器生成的单独的目标文件组合成可执行的应用程序。连接器需要知道这种目标格式以便于工作。
  • 标准C库:核心的C语言库都有一个主要的C库来提供,如果在应用程序中用到了C库中的函数,这个库就会通过链接器和源代码连接来生成最终的可执行程序。

GCC的基本用法和选项

       GCC的最基本用法是:gcc [options] [filenames]

  • -c,只编译,不连接成为可执行文件,编译器只是由输入的.c等源代码文件生成.o的后缀的目标文件,通常用于编译不包含主程序的子程序文件。
  • -o output filename ,确定输出文件名称为output filename,同时这个名称不能和源文件同名。如果不给出这个选项,gcc就给出预设的可执行文件a.out
  • -g,产生符号调试工具(GUN的gdb)所必要的符号资讯,想要对源代码进行调试,我们必须加入这个选项
  • -O,对程序进行优化编译、连接,采用这个选项,整个源代码会在编译、连接过程中进行优化处理,这样产生的可执行文件的执行效率可以提高,但是,编译、连接的速度会相对慢一些。
  • -O2,比-O更好的优化编译、连接,当然整个编译、连接过程会很慢。
  • -I dirname,将dirname所指出的目录加入到程序头文件目录列表中,是在预编译过程中使用的参数
  • -L dirname,将dirname 所指出的目录加入到程序函数档案库文件的目录列表中,是在链接过程中使用的参数。

GCC的错误类型及对策

  • 第一类:C语法错误
           错误信息:文件source.c中第n行语法错误(systex error)。有些情况下,一个简单的语法错误,gcc会出一大堆错误,我们要保持头脑清醒,不要被吓到!
  • 第二类:头文件错误
           错误信息:找不到头文件head.h(Can not find include file head.h)。这类错误是源代码文件中的包含头文件有问题,可能的原因有头文件名错误、指定的头文件所在目录名错误等,也可能是错误的使用双引号和尖括号。
  • 第三类:档案库错误
           错误信息:链接程序找不到所需的函数库(ld:-lm:No such file or directory)。这类错误是与目标文件相连接的函数库有错误,可能的原因是函数库名错误、、指定的函数库所在目录名称错误等,检查的方法是使用find命令在可能的目录中寻找相应的函数库名,确定档案库及目录的名称并修改程序中及编译选项中的名称。
  • 第四类:未定义符号
           错误信息:有未定义的符号(Undefined symbo1)。这类错误是在连接过程中出现的,可能有两种原因:一是使用者自己定义的函数或者全局变量所在源代码文件,没有被编译连接,或者干脆还没有定义,这需要使用者根据实际情况修改源程序,给出全局变量或者函数的定义体;二是未定义的符号是一个标准的库函数,在源程序中使用了该库函数而连接过程中还没有给定相应的函数库的名称,或者是该档案库的目录名称有问题,这时需要使用档案库维护命令ar检查我们需要的库函数到底位于哪–个函数库中,确定之后,修改gcc连接选项中的-1和-L项。

GCC初体验

       test.c文件内容如下:

#include<stdio.h>
int main()
{
	int i,j;
	j=0;
	i=j+1;
	printf("hello,world\n");
	printf("the result is %d\n",i);
}

编译:$ gcc -o test test.c
执行:$ ./test
查看更详细的的信息:$ gcc -v -o test test.c

GCC编译过程

GCC的编译流程分为四个步骤:
       1、预处理(Pre-Processing)
       2、编译(Compiling)
       3、汇编 (Assembliang)
       4、链接(Linking)
Alt
新建一个hello.c的文件,文件内容如下:

#include<stdio.h>
#include<math.h>

#define N 20
#define _DEBUG_

int main(int argc,const char*argc[])
{
	double m=615,n;
	m+=N;
	n=sqrt(m);
#ifdef _DEBUG_
	printf("debug:m=%lf n=%lf\n",m,n);
#else
	printf("debug:m=%lf n=%lf\n",m,n);
	return 0;
}

hello的演变过程:
Alt

  • 生成预处理代码
$ gcc -E hello.c -o hello.i

通过命令:ls -l可以查看对比两个文件
在这里插入图片描述
        可以发现hello.i比hello.c增加了不少内容,主要是放在系统提供的include文件中的。这里需要注意的是预处理后生成的hello.i依旧是一个C程序,有语法错误也发现不了 !

  • 生成汇编代码
        检查语法错误,并生成汇编文件
$ gcc -S hello.c -o hello.s
  • 生成目标代码
        方法一,用gcc直接从C源代码中生成目标代码:
$ gcc -c hello.s - o hello.o

            方法二,用汇编器从汇编代码生成目标代码:

$ as hello.s -o hello.o
  • 生成可执行程序
        将目标文件链接库资源,生成可执行程序
$ gcc hello.s -o hello
./hello

调试器

  • 首先使用gcc对test.c进行编译,注意一定要加上选项‘-g’
  • gcc -g hello.c -o hello
  • gdb hello

gdb是调试运行错误的,对于语法错误不能进行调试。

GDB调试流程

作用 参数
查看文件 (gdb)l
设置断点 (gdb) b 6
查看断点情况 (gdb)info b
运行代码 (gdb)r
查看变量值 (gdb)p n
单步运行 (gdb)n/(gdb) s
恢复程序运行 (gdb)c
帮助 (gdb) help [command]

GDB调试

  • 运行被调试程序,设置所有的能影响该程序的参数和变量。
  • 保证被调试的程序在指定情况下停止运行。
  • 当被调用程序停止运行时,让开发工程师检查发生了什么。
  • 根据每次调试器的提示信息来做响应的改变,以便修正某个错误引起的问题。

       不积小流无以成江河,不积跬步无以至千里。而我想要成为万里羊,就必须坚持学习来获取更多知识,用知识来改变命运,用博客见证成长,用行动证明我在努力。
       如果我的博客对你有帮助、如果你喜欢我的博客内容,请“点赞” “评论” “收藏”一键三连哦!听说点赞的人运气不会太差,每一天都会元气满满呦!如果实在要白嫖的话,那祝你开心每一天,欢迎常来我博客看看。

在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章