C/C++程序编译链接原理(1)

一段代码在编译链接后生成可执行文件,此时的可执行文件是存放在磁盘上的,在可执行文件执行时则会加载到内存上运行,而其也并非直接加载到内存,而是通过虚拟地址空间的映射而执行加载操作,在X86体系、32位linux环境下程序的虚拟地址空间大小为2^32字节(即4GB),我们基于这个情况来进行分析。

一、需要了解的小点

1.目前主流操作系统有windows和unix,windows大部分属于个人使用,而在进行软件开发时,由于windows并不是那么安全,所以一般公司开发使用的都是unix,而其中linux则被大量使用。操作系统的不同,使得其各个文件的扩展名也不同:
(1)windows的可执行文件的扩展名:.exe、.com、.sys。
(2)linux下的可执行文件则较多,因为linux系统上一切皆文件,在程序编译链接原理上我们只需要知道最终输出的文件为.out就可以了。
2.C/C++语言是一种本地编译语言,而像python、shell等语言则是一种解释型语言。本地编译型语言是必须经过编译器来生成可执行文件才可以执行的。
3.一段代码在编译链接后就会生成指令和数据,任何编程语言都如此,他们通过虚拟地址空间映射到内存上。
4.在C/C++中,每个函数运行时都会开辟一个栈帧,主函数也是。

二、进程的虚拟地址空间

前面我们说了可执行文件是通过进程的虚拟地址空间来加载到内存上,而虚拟地址空间的大小其实由操作系统的位数决定,32位操作系统其虚拟地址空间为2^32,64位的操作系统则会大很多。其中分为两个部分,3G的用户空间和1G的内核空间。如下图:
在这里插入图片描述
1.user space(用户空间)
(1)不可访问区:是程序运行中不可以访问的部分,不可读不可写,如果强行访问的话程序就会崩溃,平时我们涉及到的空指针就存在这个区域,如果强行对空指针进行解引用就会引发程序崩溃。
(2).text段:程序编译链接后生成的指令就存在这个区域,因为是指令嘛,所以就只能读不能写。
(3).rodata段:用于存储只能读不能写的数据,例如常量字符串等。
(4).data段:用于存储程序在编译链接后生成的且初始化不为0的数据。
(5).bss段:用于存储未初始化或者初始化为0的数据。
(6).heap:用于给malloc和new申请内存空间,由低地址向高地址增长。
(7)加载动态链接库的区域:windows下的动态链接库为.dll,而linux下的动态链接库则为.so。
(8).stack:用于给函数开辟栈帧,但空间有限,所以在递归程序中考虑到栈帧开辟的问题也不该无限递归,其使用时由高地址向低地址增长。
(9)存放命令行参数和环境变量的区域:用于存储执行程序传入的参数,程序搜索头文件的路径等。
2.kernal space(内核空间)
(1)ZONE_DMA:约16MB。
(2)ZONE_NAOMAL:约800MB,存放讷河空间的PCB块,内核线程,内核函数等。
(3)ZONE_HIGHMEN:用于高端地址映射。
3.注意:各个进程的用户空间是进程私有的,但其内核空间是共享的。

三、程序内一些量的存储位置

为了了解一些量的存储位置,有如下一段代码:

#include<iostream>

using namespace std;

static int data1 = 13;
static int data2 = 0;
static int dara3;

int data4 = 22;
int data5 = 0;
int data6;

int main()
{
    
    

	const char *string="hello world";
	
	int a = 12;
	int b = 0;
	int c;

	static int d = 18;
	static int e = 0;
	static int f;

	return 0;
}

在以上的代码中,全局变量都属于数据,在编译后均会产生符号,但由于初始化值的不同,所以他们存储的位置也不同;主函数内的三个局部变量不会产生符号,而是会产生一段指令;主函数内的三个由static修饰的变量也和全局的static变量相同;而常量字符串则保存在只读数据段。所以以上的量的存储区域如下:

#include<iostream>

using namespace std;

static int data1 = 13;                //数据---.data段
static int data2 = 0;                 //数据---.bss段
static int dara3;	                  //数据---.bss段

int data4 = 22;		                  //数据---.data段
int data5 = 0;		                  //数据---.bss段
int data6;			                  //数据---.bss段
int main()
{
    
    
	const char *string = "helloworld";//数据---.rodata段
	
	int a = 12;                       //指令---.text段
	int b = 0;                        //指令---.text段
	int c;                            //指令---.text段

	static int d = 18;                //数据---.data段
	static int e = 0;                 //数据---.bss段
	static int f;	                  //数据---.bss段

	return 0;
}

.text段的指令会在程序开始运行后在栈区开辟栈帧,再将a,b,c三个变量压入栈内,但是不能误以为这三个变量时存在栈上的,这点需要注意。关于这部分内容,推荐《深入理解计算机系统》的《链接》一章。

新手出道,请大佬多指教。

猜你喜欢

转载自blog.csdn.net/qq_45132647/article/details/105257950