两道题目

先来看两道与内存管理有关的题目

  1. 以下程序会出错吗?如果不会则输出什么?
#include <stdio.h>

char* func() {
	char* str = "Hello World";
	return str;
}
int main() {
	char* str = func();
	// 程序输出Hello World
	printf("%s\n", str);

	return 0;
}
  1. 以下程序会出错吗?如果不会则输出什么?
#include <stdio.h>

char* func() {
	char str[] = "Hello World";
	return str;
}
int main() {
	char* str = func();
	// 运行将会出现段错误
	printf("%s\n", str);

	return 0;
}

进程的内存分布

  1. 从高地址到低地址,一个进程由内核空间,命令行参数和环境变量、栈、文件映射区、堆、BSS段、数据段、代码段等组成。
    1. 命令行参数和环境变量:命令行参数即为以命令行启动程序运行时传递的参数
    2. 栈:存储函数参数值及局部变量
    3. 文件映射区
    4. 堆区:动态申请内存使用
    5. BSS段:存放未初始化的全局变量以及静态变量
    6. 数据段:存放已初始化的全局变量以及静态变量
    7. 代码段:存放只读的常量以及可执行的代码
    8. 只读数据段:存放只读变量(比如说const关键字修饰的变量)和字符串常量。

结论

  1. 函数内部返回指向字符串的指针时,其值为位于只读数据段的字符串常量地址。只读数据段和栈/堆数据段/代码段/bss段/代码段一样分布于Linux进程地址空间中
  2. 函数内返回数组名时,由于是数组,会使用栈空间为数组开辟内存,数组中的内容是只读数据段上的字符串的拷贝。数组名指向栈内存,当函数调用结束,函数外使用这块已经回收的内存,就会出现段错误。
  3. 测试如下:
void func() {
	char aaa[] = "Hello World";
	char* sss = "Hello World";
	char* www = "Hello World";
	// 0x7ffe427c1f7c,0x5575a85ce004,0x5575a85ce004
	cout << (void*)aaa << "," << (void*)sss <<  "," << (void*)www << endl;
	
}