c语言——编译与链接

分析hello,world的分析——编译、链接的过程(cc编译指令,编译依赖平台的工具链)

文件:hello.c

#include<stdio.h>

#ifndef NUM                

#define NUM 100            //宏定义,就是NUM代表100

#endif

#ifdef NUM                 

#define HEHE  "hehe\n"

#endif

int main(){

         printf("hello,world!\n");

         printf(HEHE);

         return 0;

}

现在通过编译指令来分析一下这段最简单的代码:

编译一般分为下面的几个步骤:预处理——编译为汇编代码——汇编——连接(连接之前的操作一般被称为编译)

 

下面来逐步的分析这些过程,解析hello world的整个过程:

在开始之前,先来说明一个问题——我们最开始写的是c语言文件,最后生成的是连接得到的机器指令执行文件(以二进制存储在磁盘),因为电脑只能执行二进制机器指令。二进制机器指令十分复杂的,很难学会,所以发明了汇编语言,由汇编语言文件编译(一般叫做汇编)得到机器指令文件,汇编语言的学习不是那么复杂,但是学起来也很难,因此出现了c语言,由c语言编译生成汇编语言。

第一步、预处理(指令)——gcc -E hello.c -o test.i(-E代表进行预处理操作,-o左边为执行文件,右边为目的文件,o代表object)

查看结果指令(cat hello.i):

...(太多了,不写了)

extern voidfunlockfile (FILE *__stream) __attribute__ ((__nothrow__));

#936"/usr/include/stdio.h" 3 4

#2"hello.c" 2

int main(){

printf("hello,world 100!\n");

printf("hehe\n");

return 0;

}

从这里可以看出它的目的——把stdio.h头文件的内容移到这里代替#include<stdio.h>的位置;把下面的NUM与HEHE进行替换;(进行宏定义与头文件的替换)——此外,还会去除注释,添加行号和文件标识(这是为了在后面的编译时如果出现错误,可以及时的打印错误信息位置)

当然,这里#include<stdio.h>里面很多东西用不到,但是这一步只是替换,不会去分析是否用的到

预处理作用总结:

  1. 将头文件替换为它的具体内容
  2. 将#define的宏定义替换
  3. 去除注释
  4. 添加行号,文件标识————为了调试以及运行出错时报错

第二步、编译为汇编代码指令——cc -S hello.i -ohello.s        <将c语言编译为汇编语言代码>

查看结果指令(cat hello.s):

.file "hello.c"

.section .rodata

.LC0:

.string"hello,world!"

.LC1:

.string"hehe"

.text

.globlmain

.typemain,@function

main:

.LFB0:

.cfi_startproc

pushq %rbp

.cfi_def_cfa_offset16

movq %rsp,%rbp

.cfi_offset6, -16

.cfi_def_cfa_register6

movl $.LC0,%edi

call puts

movl $.LC1,%edi

call puts

movl $0,%eax

leave

.cfi_def_cfa7, 8

ret

.cfi_endproc

.LFE0:

.size main,.-main

.ident"GCC:(Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2"

.section.note.GNU-stack,"",@progbits

在这里暂时不去关注汇编语言的含义,但是可以看出,代码被缩减了,#include<stdio.h>里面只有用到的被汇编了,其他的都没有用。

第三步、汇编指令——cc -c hello.s -o hello.o   <将汇编代码编译为机器指令>

生成的文件内容这里就不贴上来了,但是使用cat查询会出现混乱,这是因为机器指令不是字符状态,而是二进制状态,而cat查看是以字符方式打开的,可以使用二进制(十六进制等)指令查看,不过只是一堆数据,根本看不懂~~~

第四步、连接指令——cc hello.o -o hello

连接的目的是得到可执行文件,通过与静态连接库(c语言连接库)与动态连接库(添加的头文件库与自定义头文件库)的连接,生成可执行文件。<链接库是机器指令的文件,因此在这一步实现链接——如printf操作的具体实现就是在这一步链接的>

注意:如果不添加#include<stdio.h>,仍然可以连接,这是因为静态连接库里面包含了printf这个东西,

 

到此得到可执行文件hello,当然可以直接使用指令(cc hello.c -o hello)完成全部操作

总结:

  1. 预处理——仅仅把#include与#define做替换,代码错了也会不管的
  2. 编译为汇编代码——由c语言到汇编,在变为汇编时会检查代码语法是否错了,没有定义需要的动态连接库也会报错
  3. 汇编——将汇编语言变为机器指令,只要上一步没错,这一步不会出错
  4. 连接——把所有.o文件与c基础连接库连接生成可执行文件

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值