1.程序文件分析
ELF:Linux系统下,可执行程序文件。(可重定位文件)
(类似于Windows中的exe)
程序文件中,需要特别关注的文件段:
备注:
- file 指令,查看系统中某个文件类型。
file 文件名
- readelf指令,列举程序文件中文件段。
readelf -S 程序名
2.进程内存布局
浏览一下书本 《2.4 内存管理》章节
尝试找出以下问题的答案:
- 虚拟内存是如何分配的呢,由什么内容组成?
内核、栈、堆、数据段、代码段、不可访问区。
- 上面提到的特殊关注的文件段是什么东西?中文名是啥?
.init 系统初始化代码段
.text 用户代码段
.rodata 常量数据段(只读数据段)
.data 已初始化静态数据段
.bss 未初始化静态数据段
- 哪些段是静态不变的,哪些段是动态变化的?
不变:数据段、代码段
变化:
- 我们以前的代码,操作过哪些段落?
用户代码、常量区、栈。
备注:
- 局部变量和全局变量
函数内部定义的变量,称为局部变量,位于栈空间
函数外部定义的变量,称为全局变量,位于静态数据段
- 静态数据分为 .data和.bss
int g_a=100; // .data 已初始化,则不做任何处理 char g_b; // .bss 未初始化,这块内存会被自动清零
(栈空间中的局部变量,如果未初始化,则为乱码数据(随机值)。)
尽量不要滥用静态数据。
静态数据生命周期与进程相当,会一直占用内存。
多线程编程中,可能导致共享资源出现问题。
函数内部定义的局部变量,在函数结束后,会被自动释放。
static修饰符的用法:
修饰局部变量:将该局部变量置于静态数据段。
修饰全局变量、函数:将该变量或函数链接类型由外部链接类型修改为内部链接类型。
(如果全局变量、函数只在单个文件中使用,可以使用static修饰,避免出现重名冲突)
demo1_内存分布
#include<stdio.h> // 函数外部定义的变量,称为全局变量,位于静态数据段 int g_a=100; // .data 不做任何处理 char g_b; // .bss 这块内存会被自动清零 // 子函数存储在.text用户代码 unsigned char func_sum(char a, float b) // 形参位于栈空间 { // 168=65+3.5+100+0 return a+b+g_a+g_b; // 函数返回后,会将当前函数所有栈空间数据释放。(a, b) } // main主函数存储在.text用户代码 int main() { // 函数内部定义的变量,称为局部变量,位于栈空间 int n1=100; // n1位于栈空间,整型常量100位于常量区(只读区) float n2=3.5; // n2位于栈空间,浮点型常量3.5位于常量区 char n3='A'; // n3位于栈空间,字符常量'A'位于常量区 char *str = "hello"; // str位于栈空间,字符串常量"hello"位于常量区 // str[0] = 'a'; // 尝试对常量区写入会导致出现段错误 printf("%c\n", *str); // 对常量区读取是允许。 printf("%ld %ld\n", sizeof('A'), sizeof(100)); // sum位于栈空间,跳转到用户代码中的func_sum的入口地址 unsigned char sum = func_sum(n3, n2); printf("sum: %c(%d)\n", sum, sum); return 0; }
demo2_static
#include<stdio.h> // 如需使用其他文件的函数,需要添加外部声明 extern void func_2(void); // static:静态的 // int g_a = 200; // 未添加static,是外部链接类型 static int g_a = 200; // 添加static,修改为内部链接类型 void func_1(void) { // int a=100; // 局部变量 static int a=100; // static修饰的局部变量(只能被初始化1次) a++; printf("a(%p): %d\n", &a, a); } int main() { func_1(); func_1(); func_1(); func_1(); func_1(); func_2(); return 0; }
demo3_另一个文件
#include<stdio.h> // 如需要使用其他文件的变量,需添加外部声明 extern int g_a; void func_2(void) { printf("g_a: %d\n", g_a); }
demo4_堆空间使用
#include <stdio.h> #include <stdlib.h> // 申请堆空间 // #include <stdlib.h> // void *malloc(size_t size); // 申请 size 字节的堆空间 // void *calloc(size_t nmemb, size_t size); // 申请 nmemb*size 字节的堆空间 // void *realloc(void *ptr, size_t size); // 重新申请一块size字节堆空间 // 释放堆空间 // void free(void *ptr); // 但不需要使用到该块内存时,释放空间。 // 使用场景:需要使用的内存较大,需要自行决定内存释放时机。 int main() { // 1M字节 = 1024K字节 = 1024*1024 字节 // char arr1[7*1024*1024] = {0}; // 位于栈空间,可使用指令(ulimit -a)查看,最大是8M // 申请10M堆空间 // char *arr1 = malloc(10*1024*1024); char *arr1 = calloc(10, 1024*1024); // arr1[0] = 'A'; *(arr1+0) = 'A'; printf("%c\n", arr1[0]); // 释放堆空间(当程序结束时,所有内存也会被释放。) free(arr1); return 0; }