c语言程序的执行过程(以输出hello,world为例)
1.c源文件的存储
任何数据在计算机中都以二进制的样式进行存储,即0和1两种存储表示,那么c源文件中的各种字符要想存储在计算机中作为可执行的指令,那么必须得以二进制0或1的形式存储在其中,因此,需要将c源文件的需要被以某种方式“翻译”成二进制存储进计算机。
数据类型表示数字的类型如int,double 等类型在内存中是以换算的二进制存的,而字符类型等在内存中是以ASCII存的,这个通过查ASCII得到相应的二进制,然后存放于内存。
比如:32767 当作int型 为整数,利用除2取余法得到相应的二进制数存于内存(本来应该存补码,但是正数的补码和原码相同)所占的内存空间跟其对应的数据类型有关,可能还与机器有关,
而32767 当作字符 即“32767”,这时应该分解‘3’,‘2’,‘7’,‘6’,‘7’,然后查对应的ASCII码 对应的值为:0011001 00110010 00110111 00110110 00110111 所以在存放的即为该二进制的组合,且占5个字节的内存
将指定的字符“翻译”成对应二进制文件需要“统一”的“密码本”,使得在任何一台计算上c源文件都以相同的二进制形式存储。而翻译这个c源文件的“密码本”就是ASCII码。
ASCII使用8位二进制数表示256个字符,这些字符包括32个大小写字符,10个数字,以及其他的字符,例如,一个输出“hello,world”的ASCII为
#include <stdio.h>
int main(void)
{
printf("hello,world\n");
return 0;
}
即符号#的ASCII码为35,i的ASCII为105......
而ASCII中35的二进制为0010 0011,i为0110 1001......
因此c源文件在计算机中存储为0010 0011 0110 1001 ......
2.c源文件的编译
Linux系统上的编译hello.c:
c源文件仅仅是以二进制的形式存储在计算机中,而我们要实现的是计算机输出“hello,world”,那么需要将这些二进制文件编译成计算机可以识别的指令,“告诉”计算机我们要输出“hello world”。
构成这些计算机可以识别的二进制指令称为机器语言,因此,c语言的编译就是编译成机器语言共计算机执行。
在Linux系统中,将c源文件编译成可执行的二进制指令文件是由gcc编译器完成的,gcc是gnu组织的开发的编程语言编译器。
输出“hello,world”的hello.c的编译流程:
编译一个hello.c的程序总共分为四个阶段:预处理,编译,汇编和链接
预处理阶段:预处理阶段是根据程序中字符#开头的命令并执行相关操作,在holle.c中#开头的第一行为#include <stdio.h>,预处理器就读取c库中的stdio.h的内容将其插入到hello.c文本中生成一个新的文件hello.i;
编译阶段:编译器ccl将hello.i翻译汇编程序储存在文件hello.s中,其中的main作为一个函数给出了机器语言的输出指令;
.file "hello.c"
.section .rodata
.LC0:
.string "hello,world"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $.LC0, %edi
movl $0, %eax
call printf
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609"
.section .note.GNU-stack,"",@progbits
汇编阶段:汇编器将hello.s中的汇编语言翻译成计算机可以识别的机器语言存为hello.o二进制文件;
链接阶段:在c源文件中我们引用了printf打印函数,而计算机要识别这个函数也要由二进制指令组成。而在头部文件中stdio.h作为c语言的标准库,其中已经定义了printf的接口,系统根据接口去读取已经写好的printf.o二进制指令文件,将其与hello.o文件合并为hello二进制文件,称为可执行目标文件或可执行文件。
3.hello文件的执行过程
Linux上执行hello打印输出:
打开shell从键盘输入./hello,shell会优先把输入的字符串当做内置命令,若不是则会将其当做可执行文件的名字处理。shell将计算机I/o接口连接的输入设备输入的字符串通过总线接口存入寄存器,再存入内存中。
从键盘中读取shell命令流程:
完成./hello输入后,当从键盘上输入回车后,shell得知命令输入完成,开始执行一系列指令从磁盘复制hell文件到内存中,流程如下:
当hello文件被复制到主存之后,处理器开始执行hello程序的main程序的指令。流程是将这些指令从主存复制到寄存器,再从寄存器复制到显示设备上,最终显示到屏幕上,流程如下图: