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

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

分区模型


栈区


由系统进行内存的管理。主要存放函数的参数以及局部变量。在函数完成执行,系统自行释放栈区内存,不需要用户管理。


代码示例:


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. 这里不区分初始化和未初始化的数据区,是因为静态存储区内的变量若不显示初始化,则编译器会自动以默认的方式进行初始化,即静态存储区内不存在未初始化的变量。


  1. 全局静态存储区内的常量分为常变量和字符串常量,一经初始化,不可修改。静态存储内的常变量是全局变量,与局部常变量不同,区别在于局部常变量存放于栈,实际可间接通过指针或者引用进行修改,而全局常变量存放于静态常量区则不可以间接修改。


  1. 字符串常量存储在全局/静态存储区的常量区。


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


关于静态全局变量与非静态全局变量的讨论,内容过多,大家可以看下面一篇文章:


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++内存分区其实只有两个,即代码区和数据区。

相关文章
【51单片机】烧写教程:将代码下载到单片机中(图示&解析)
【51单片机】烧写教程:将代码下载到单片机中(图示&解析)
|
5天前
|
C++
【期末不挂科-C++考前速过系列P6】大二C++实验作业-模板(4道代码题)【解析,注释】
【期末不挂科-C++考前速过系列P6】大二C++实验作业-模板(4道代码题)【解析,注释】
【期末不挂科-C++考前速过系列P6】大二C++实验作业-模板(4道代码题)【解析,注释】
|
5天前
|
Serverless C++ 容器
【期末不挂科-C++考前速过系列P5】大二C++实验作业-多态性(3道代码题)【解析,注释】
【期末不挂科-C++考前速过系列P5】大二C++实验作业-多态性(3道代码题)【解析,注释】
|
5天前
|
C++ 芯片
【期末不挂科-C++考前速过系列P4】大二C++实验作业-继承和派生(3道代码题)【解析,注释】
【期末不挂科-C++考前速过系列P4】大二C++实验作业-继承和派生(3道代码题)【解析,注释】
|
5天前
|
编译器 C++
【期末不挂科-C++考前速过系列P3】大二C++第3次过程考核(20道选择题&12道判断题&2道代码题)【解析,注释】
【期末不挂科-C++考前速过系列P3】大二C++第3次过程考核(20道选择题&12道判断题&2道代码题)【解析,注释】
|
5天前
|
C++
【期末不挂科-C++考前速过系列P2】大二C++第2次过程考核(20道选择题&10道判断题&3道代码题)【解析,注释】
【期末不挂科-C++考前速过系列P2】大二C++第2次过程考核(20道选择题&10道判断题&3道代码题)【解析,注释】
|
6天前
|
存储 数据安全/隐私保护 C++
【期末不挂科-C++考前速过系列P1】大二C++第1次过程考核(3道简述题&7道代码题)【解析,注释】
【期末不挂科-C++考前速过系列P1】大二C++第1次过程考核(3道简述题&7道代码题)【解析,注释】
|
6天前
|
前端开发 JavaScript
浏览器通过构建DOM树来解析HTML代码
【4月更文挑战第30天】浏览器通过构建DOM树来解析HTML代码
20 1
|
7天前
|
编解码 前端开发 JavaScript
网页设计的艺术与技术:深入解析与代码实践
网页设计的艺术与技术:深入解析与代码实践
15 1
|
7天前
|
芯片
EDA设计:原理、实践与代码深度解析
EDA设计:原理、实践与代码深度解析
19 2

推荐镜像

更多