# 从编写源代码到程序在内存中运行的全过程解析

## 从编写源代码到程序在内存中运行的全过程解析

<https://blog.csdn.net/kang___xi/article/details/79571137>

c++这个文件，定义了很多数据，怎么存储运行的？

```cpp
int gdata1 = 10;
int gdata2 = 0;
int gdata3;

static int gdata4 = 11;
static int gdata5 = 0;
static int gdata6;

int main(void)
{
    int a = 12;
    int b = 0;
    int c;

    static int d = 13;
    static int e = 0;
    static int f;
    return 0;
}
```

### 1.什么是数据

* 数据

  数据指的是称序中定义的全局变量和静态变量。还有一种特殊的数据叫做常量。所以上面的的gdata1、gdata2、gdata3、gdata4、gdata5、gdata6、d、e和f均是数据。
* 数据存放在哪里

  数据存放的区域有三个地方：**.data段、.bss段和.rodata段**。

  对于初始化不为0的全局变量和静态变量存放在.data段，即gdata1、gdata4和d存放在.data段；

  对于未初始化或者初始化值为0的段存放在.bss段中，而且不占目标文件的空间，即gdata2、gdata3、gdata5、gdata6、e和f存放在.bss段。

  而对于字符串常量则存放在.rodata段中，而且对于字符串而言还有一个特殊的地方，就是它在内存中只存在一份。下面给个代码来测试：

```cpp
#include<stdio.h>
int main(void)
{
    const char *pStr1 = "hello,world";
    const char *pStr2 = "hello,world";
    printf("0x%x\n", pStr1);
    printf("0x%x\n", pStr2);
    return 0;
}
```

输出的地址肯定是一样的。因为常量字符串“hello,world”只存在一份。（其它语言python也是一样的）

### 2.什么是指令

​ 什么是指令？也就是什么是程序代码。很简单，程序中除了数据，剩下的就都是指令了。这里有一个容易混淆的地方，如下面的代码：

```cpp
#include<stdio.h>
int main()
{
    int a = 10;
    int b = 20;
    printf("a+b=%d\n", a + b);
    return 0;
}
```

### 3.什么是符号

​ 我们在编写程序完，进行链接时会碰到这样的错误："错误 LNK1169 找到一个或多个多重定义的符号 "，即符号重定义。那什么是符号，什么东西会产生符号，符号的作用域又是怎样的呢？

**在程序中，所有数据都会产生符号，而对于代码段只有函数名会产生符号**。而且**符号的作用域有global和local之分**，对于未用static修饰过的全局变量和函数产生的均是global符号，这样的变量和函数可以被其他文件所看见和引用；而使用static修饰过的变量和函数，它们的作用域仅局限于当前文件，不会被其他文件所看见，即其他文件中也无法引用local符号的变量和函数。

对于上面的 “找到一个或多个多重定义的符号” 错误原因有可能**是多个文件中定义同一个全局变量或函数，即函数名或全局变量名重了**。

### 4.虚拟地址空间布局

​ 对于32位操作系统，每个操作系统都有2^32字节的虚拟地址空间，即4G的虚拟地址空间。这4G的虚拟地址空间分为两个大部分：每个进程独立的3G的用户空间，和所有进程共享的1G的内核空间。具体分布如下图：

![img](https://img-blog.csdn.net/20180408174128923)

## 二、编译过程

### 1.编译

​ 整个编译分为四个步骤：首先编写**源文件main.c/main.cpp**；编写好代码以后进行**预编译成main.i**文件，预编译过程中去掉注释、进行宏替换、增加行号信息等；然后将**main.i文件经过语法分析、代码优化和汇总符号**等步骤后，编译**形成main.S的汇编文件**，里面存放的都是汇编代码；最后一个编译步骤是进行汇编，从**main.S变成二进制可重定位目标文件main.o**。

以上四个步骤对应的在linux下的命令为：

```
gcc -E main.c -o main.i  #预编译，生成main.i文件
gcc -S main.i            #编译，生成main.S文件
gcc -c main.S            #汇编，生成main.o文件
gcc main.o -o main       #链接，生成可执行文件
```

### 2.二进制可重定位目标文件的结构和布局

首先给出一个二进制可重定位目标文件(linux下&#x662F;*.o文件，windows中是*.obj文件)的总体布局，简单来说整个obj文件就是由**ELF header+各种段组成**：

![img](https://img-blog.csdn.net/20180408235958135)

二进制可重定位文件的头部，可以看到**ELF header占64个字节，里面存放着文件类型、支持的平台、程序入口点地址等信息**，如果你对每个字段的具体含义感兴趣，可以看《程序员自我修养》：


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://im-qianuxn.gitbook.io/pytorch/ji-suan-ji/c++/yun-xing-guo-cheng.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
