程序分区模型(代码实例解析)
Posted 流楚丶格念
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了程序分区模型(代码实例解析)相关的知识,希望对你有一定的参考价值。
有没有那么一首歌
分区模型
栈区
由系统进行内存的管理。主要存放函数的参数以及局部变量。在函数完成执行,系统自行释放栈区内存,不需要用户管理。
代码示例:
char* func(){
char p[] = "hello world!"; //在栈区存储 乱码
printf("%s\\n", p);
return p;
}
void test(){
char* p = NULL;
p = func();
printf("%s\\n",p);
}
结果第一次可以输出,第二次输出乱码
堆区
由编程人员手动申请,手动释放,若不手动释放,程序结束后由系统回收,生命周期是整个程序运行期间。使用malloc或者new进行堆的申请。
代码示例:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<iostream>
using namespace std;
char* func() {
char* str = (char*)malloc(100);
strcpy(str, "hello world!");
printf("%s\\n", str);
return str;
}
void test01() {
char* p = NULL;
p = func();
printf("%s\\n", p);
}
void allocateSpace(char* p) {
p = (char*)malloc(100);
strcpy(p, "hello world!");
printf("%s\\n", p);
}
void test02() {
char* p = NULL;
allocateSpace(p);
printf("%s\\n", p);
}
int main()
{
test01();
test02();
}
堆分配内存API:
calloc
#include <stdlib.h>
void *calloc(size_t nmemb, size_t size);
- 功能:
在内存动态存储区中分配nmemb块长度为size字节的连续区域。calloc自动将分配的内存 置0。 - 参数:
nmemb
:所需内存单元数量
size
:每个内存单元的大小(单位:字节) - 返回值:
成功:分配空间的起始地址
失败:NULL
realloc
#include <stdlib.h>
void *realloc(void *ptr, size_t size);
- 功能:
重新分配用malloc或者calloc函数在堆中分配内存空间的大小。
realloc不会自动清理增加的内存,需要手动清理,如果指定的地址后面有连续的空间,那么就会在已有地址基础上增加内存,如果指定的地址后面没有空间,那么realloc会重新分配新的连续内存,把旧内存的值拷贝到新内存,同时释放旧内存。 - 参数:
ptr
:为之前用malloc或者calloc分配的内存地址,如果此参数等于NULL,那么和realloc与malloc功能一致
size
:为重新分配内存的大小, 单位:字节 - 返回值:
成功:新分配的堆内存地址
失败:NULL
示例代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void test01() {
int* p1 = (int *)calloc(10, sizeof(int));
if (p1 == NULL) {
return;
}
for (int i = 0; i < 10; i++) {
p1[i] = i + 1;
}
for (int i = 0; i < 10; i++) {
printf("%d ", p1[i]);
}
printf("\\n");
free(p1);
}
void test02() {
int* p1 = (int *)calloc(10, sizeof(int));
if (p1 == NULL) {
return;
}
for (int i = 0; i < 10; i++) {
p1[i] = i + 1;
}
int* p2 = (int *)realloc(p1, 15 * sizeof(int));
if (p2 == NULL) {
return;
}
printf("P1=%d\\n", p1);
printf("P2=%d\\n", p2);
//打印
for (int i = 0; i < 15; i++) {
printf("%d ", p2[i]);
}
printf("\\n");
//重新赋值
for (int i = 0; i < 15; i++) {
p2[i] = i + 1;
}
//再次打印
for (int i = 0; i < 15; i++) {
printf("%d ", p2[i]);
}
printf("\\n");
free(p2);
}
int main()
{
test01();
test02();
return 0;
}
全局/静态区
全局静态区内的变量在编译阶段已经分配好内存空间并初始化。
这块内存在程序运行期间一直存在,它主要存储全局变量、静态变量和常量。
例如:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int v1 = 10; //全局/静态区
const int v2 = 20; //常量,一旦初始化,不可修改
static int v3 = 20; //全局/静态区
char *p1; //全局/静态区,编译器默认初始化为NULL
void test() {
static int v4 = 20; //全局/静态区
}
int main()
{
test();
return 0;
}
注意:
- 这里不区分初始化和未初始化的数据区,是因为静态存储区内的变量若不显示初始化,则编译器会自动以默认的方式进行初始化,即静态存储区内不存在未初始化的变量。
- 全局静态存储区内的常量分为常变量和字符串常量,一经初始化,不可修改。静态存储内的常变量是全局变量,与局部常变量不同,区别在于局部常变量存放于栈,实际可间接通过指针或者引用进行修改,而全局常变量存放于静态常量区则不可以间接修改。
- 字符串常量存储在全局/静态存储区的常量区。
关于静态全局变量与非静态全局变量的讨论:
关于静态全局变量与非静态全局变量的讨论,内容过多,大家可以看下面一篇文章:
https://yangyongli.blog.csdn.net/article/details/120402589
静态全局变量与非静态全局变量的使用说明
1.若全局变量仅在单个C文件中访问,则可以将这个变量修改为静态全局变量,以降低模块间的耦合度;
2.若全局变量仅由单个函数访问,则可以将这个变量改为该函数的静态局部变量,以降低模块间的耦合度;
3.设计和使用访问动态全局变量、静态全局变量、静态局部变量的函数时,需要考虑重入问题,因为他们都放在静态数据存储区,全局可见;
4.如果我们需要一个可重入的函数,那么,我们一定要避免函数中使用static变量(这样的函数被称为:带“内部存储器”功能的的函数)
5.函数中必须要使用static变量情况:比如当某函数的返回值为指针类型时,则必须是static的局部变量的地址作为返回值,若为auto类型,则返回为错指针。
代码示例:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
char* func() {
static char arr[] = "hello world!"; //在静态区存储 可读可写
arr[2] = 'c';
const char* p = "hello world!"; //全局/静态区-字符串常量区
//p[2] = 'c'; //只读,不可修改
printf("%d\\n", arr);
printf("%d\\n", p);
printf("%s\\n", arr);
return arr;
}
void test() {
char* p = func();
printf("%s\\n", p);
}
int main()
{
test();
return 0;
}
总结
数据区包括:堆,栈,全局/静态存储区。
全局/静态存储区包括:常量区,全局区、静态区。
常量区包括:字符串常量区、常变量区。
代码区:存放程序编译后的二进制代码,不可寻址区。
可以说,C/C++内存分区其实只有两个,即代码区和数据区。
以上是关于程序分区模型(代码实例解析)的主要内容,如果未能解决你的问题,请参考以下文章
Android 逆向使用 Python 解析 ELF 文件 ( Capstone 反汇编 ELF 文件中的机器码数据 | 创建反汇编解析器实例对象 | 设置汇编解析器显示细节 )(代码片段