C语言----程序环境-Linux

本文详细介绍了C语言程序从源代码到可执行文件的编译过程,包括预处理、编译、汇编和链接。在Linux环境下,通过实例展示了预处理如何替换头文件和宏,编译如何生成汇编代码,以及汇编如何转化为机器指令。此外,还阐述了程序的执行环境,重点解释了程序加载内存、调用main函数以及运行时堆栈的工作原理。

在C语言的代码编写完成到我们观察到程序执行效果的过程中,主要经历了两个不同的环境,分别是编译环境执行环境

目录

一、程序的翻译环境

        1. 编译

                (1)预处理

             (2)编译

               (3)汇编

        2.链接

二、程序的执行环境

         1.执行过程


一、程序的翻译环境

        翻译环境即源代码被转换为可执行的机器指令所处的环境。

        翻译环境中主要的执行过程的编译链接

        1. 编译

               编译的过程又可细分为预处理、编译、汇编

                (1)预处理

                        在此个过程中,每个源文件(.c)各自经过编译器的处理,生成各自的目标文件(.obj)

                        由于在VS编译器中此过程不易被观察到,在此使用Linux环境进行演示

                首先创建一个文件test.c,内容如下:

#include <stdio.h>

#define M 10
int Add(int x, int y)
{
        return x + y;
}

int main()
{
        int a = M;
        int b = 2;
        int c = Add(a, b);
        // This line is used to print the value of c
        printf("%d\n", c);
        return 0;
}

                然后使用 如下命令将test.c预编译,并将生成的内容保存到test.i中:

gcc test.c -E

                随后打开text.i,可观察到如下:

# 1 "test.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "test.c"
# 1 "/usr/include/stdio.h" 1 3 4
# 27 "/usr/include/stdio.h" 3 4
# 1 "/usr/include/features.h" 1 3 4
# 375 "/usr/include/features.h" 3 4
# 1 "/usr/include/sys/cdefs.h" 1 3 4
# 392 "/usr/include/sys/cdefs.h" 3 4
# 1 "/usr/include/bits/wordsize.h" 1 3 4
# 393 "/usr/include/sys/cdefs.h" 2 3 4
# 376 "/usr/include/features.h" 2 3 4
# 399 "/usr/include/features.h" 3 4
# 1 "/usr/include/gnu/stubs.h" 1 3 4
# 10 "/usr/include/gnu/stubs.h" 3 4
# 1 "/usr/include/gnu/stubs-64.h" 1 3 4
# 11 "/usr/include/gnu/stubs.h" 2 3 4
# 400 "/usr/include/features.h" 2 3 4
# 28 "/usr/include/stdio.h" 2 3 4





# 1 "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/include/stddef.h" 1 3 4
# 212 "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/include/stddef.h" 3 4
typedef long unsigned int size_t;
# 34 "/usr/include/stdio.h" 2 3 4

...

extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
# 943 "/usr/include/stdio.h" 3 4

# 2 "test.c" 2


int Add(int x, int y)
{
 return x + y;
}

int main()
{
 int a = 10;
 int b = 2;
 int c = Add(a, b);

 printf("%d\n", c);
 return 0;
}

                我们可以观察到,在经过预处理后,代码中发生了三点变化

                a. 完成了头文件的替换

                        (为了便于观察,将替换后的stdio.h的代码进行了省略)

                b. 完成了#define定义的符号的替换,和宏的替换(与函数的替换相似)

                c. 注释的内容被删除,不会参与编译

             (2)编译

                        编译的过程就是将预处理之后的C语言代码转换为汇编代码

                        使用 gcc test.i -S  将预处理之后的代码转换为汇编代码,如下:

 

                         汇编代码较为复杂,本文不做解读

               (3)汇编

                               即将汇编代码转换为机器指令(二进制指令)

                                使用 gcc test.s -c 生成目标文件

                                使用readelf工具下的 readelf test.o -s 查看内容

        2.链接

                在整个编译完成后,在项目中把生成的目标文件与链接库通过连接器生成可执行文件。

                以Linux为例,后缀为.out为可执行文件(windows中,为.exe)

                此处我们将test.c中的Add函数单独写入另一个源文件add.c中,并执行与test.c同样的编译方式生成add.o文件。完成后目录内的文件如下:

                 由于一个项目中只能有一个main函数(位于test.c中),所以只需要执行test.c即可将add.o也加入到链接中。使用gcc test.c 生成可执行文件

                

                可见生成了可执行文件a.out

二、程序的执行环境

        在程序编译完成后,我们需要执行生成的可执行文件来观察程序的运行结果

        使用./a.out来运行编译生成的可执行文件,如下:

                       

         1.执行过程

                 这里我们仅是观察到程序的运行结果,程序的执行过程主要有以下几点:        

                (1)程序必须载入内存中。在由操作系统的环境中时,有操作系统完成;独立环境中必须手动安排。

                (2)程序的执行开始后,接着调用main函数。

                (3)开始执行。程序使用一个运行时堆栈,存储所用到的变量和返回值。

                (4)终止程序。正常终止main函数,也可能是意外终止。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值