程序分区模型(代码实例解析)

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;
}

注意:

  1. 这里不区分初始化和未初始化的数据区,是因为静态存储区内的变量若不显示初始化,则编译器会自动以默认的方式进行初始化,即静态存储区内不存在未初始化的变量。
  2. 全局静态存储区内的常量分为常变量和字符串常量,一经初始化,不可修改。静态存储内的常变量是全局变量,与局部常变量不同,区别在于局部常变量存放于栈,实际可间接通过指针或者引用进行修改,而全局常变量存放于静态常量区则不可以间接修改。
  3. 字符串常量存储在全局/静态存储区的常量区。

关于静态全局变量与非静态全局变量的讨论:

关于静态全局变量与非静态全局变量的讨论,内容过多,大家可以看下面一篇文章:
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 文件中的机器码数据 | 创建反汇编解析器实例对象 | 设置汇编解析器显示细节 )(代码片段

如何将 View 类中的代码片段移动到 OnAppearing() 方法?

C++内存分区模型

C++内存分区模型

片段事务中的实例化错误

实例详解C++程序的五大内存分区