两道题目
先来看两道与内存管理有关的题目
- 以下程序会出错吗?如果不会则输出什么?
#include <stdio.h>
char* func() {
char* str = "Hello World";
return str;
}
int main() {
char* str = func();
// 程序输出Hello World
printf("%s\n", str);
return 0;
}
- 以下程序会出错吗?如果不会则输出什么?
#include <stdio.h>
char* func() {
char str[] = "Hello World";
return str;
}
int main() {
char* str = func();
// 运行将会出现段错误
printf("%s\n", str);
return 0;
}
进程的内存分布
- 从高地址到低地址,一个进程由内核空间,命令行参数和环境变量、栈、文件映射区、堆、BSS段、数据段、代码段等组成。
- 命令行参数和环境变量:命令行参数即为以命令行启动程序运行时传递的参数
- 栈:存储函数参数值及局部变量
- 文件映射区
- 堆区:动态申请内存使用
- BSS段:存放未初始化的全局变量以及静态变量
- 数据段:存放已初始化的全局变量以及静态变量
- 代码段:存放只读的常量以及可执行的代码
- 只读数据段:存放只读变量(比如说const关键字修饰的变量)和字符串常量。
结论
- 函数内部返回指向字符串的指针时,其值为位于只读数据段的字符串常量地址。只读数据段和栈/堆数据段/代码段/bss段/代码段一样分布于Linux进程地址空间中
- 函数内返回数组名时,由于是数组,会使用栈空间为数组开辟内存,数组中的内容是只读数据段上的字符串的拷贝。数组名指向栈内存,当函数调用结束,函数外使用这块已经回收的内存,就会出现段错误。
- 测试如下:
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;
}