开发者社区> 流楚丶格念> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

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

简介: 程序分区模型(代码实例解析)
+关注继续查看

分区模型


栈区


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


代码示例:


char* func(){
    char p[] = "hello world!";  //在栈区存储 乱码
    printf("%s\n", p);
    return p;
}
void test(){
    char* p = NULL;
    p = func();  
    printf("%s\n",p); 
}


结果第一次可以输出,第二次输出乱码


image


堆区


由编程人员手动申请,手动释放,若不手动释放,程序结束后由系统回收,生命周期是整个程序运行期间。使用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();
}


image


image


堆分配内存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;
}


image


全局/静态区


全局静态区内的变量在编译阶段已经分配好内存空间并初始化。


这块内存在程序运行期间一直存在,它主要存储全局变量、静态变量和常量。


例如:


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


image


总结


数据区包括:堆,栈,全局/静态存储区。


全局/静态存储区包括:常量区,全局区、静态区。


常量区包括:字符串常量区、常变量区。


代码区:存放程序编译后的二进制代码,不可寻址区。


可以说,C/C++内存分区其实只有两个,即代码区和数据区。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
用Python绘制专业的K线图【含源代码】
使用Python绘制一幅专业的K线图,是量化投资和金融数据分析的必备功课。下面我将从K线图简介、数据获取、K线图绘制及成交量绘制等方面,结合源代码,一步步实现专业K线图的绘制。
299 0
gops —— Go 程序诊断分析工具
GitHub: https://github.com/google/gops 一个用于列出和诊断分析系统中正在运行的 Go 程序的命令行工具 安装 1 go get -u github.
1711 0
git分支管理模型推荐
前言 正如我了解到的,很多基于SVN的分支管理,类似如下的流程: 可能存在的问题: master合并成本比较高 特性分支有开发公共功能的需求, 需要及时合并 如下是一个比较成功的分支策略和发布管理,原文链接,另外,建议大家用sourceTree进行git的分支管理,因为上面的Git Flow就是如下图所示的管理流程。看图说明一切,然后使用一下so
1458 0
02-如何实例化模型
原文:02-如何实例化模型 在ThinkPHP中,可以无需进行任何模型定义。只有在需要封装单独的业务逻辑的时候,模型类才是必须被定义的,因此ThinkPHP在模型上有很多的灵活和方便性,让你无需因为表太多而烦恼。
843 0
Java线程同步:生产者-消费者 模型(代码示例)
public class ThreadSyn { public static void main(String[] args) { new ThreadSyn(); } public ThreadSyn...
696 0
+关注
流楚丶格念
csdn平台优质创作者,51cto TOP博主,360图书馆科技博主,燕山大学目前大三在读,日拱一卒,功不唐捐,加油!!!
1010
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载