C语言深入讲解栈与堆和静态存储区的使用

 更新时间:2022年04月18日 11:14:33   作者:清风自在 流水潺潺  
对大多数C 语言初学者来说,堆栈却是一个很模糊的概念。堆栈是一种数据结构,一个在程序运行时用于存放的地方,相信这可能是很多初学者共同的认识,静态存储区即内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在

一、程序中的栈

  • 栈是现代计算机程序里最为重要的概念之一
  • 栈在程序中用于维护函数调用上下文
  • 函数中的参数和局部变量存储在栈上

 栈保存了一个函数调用所需的维护信息

  • 参数
  • 返回地址
  • 局部变量
  • 调用上下文

二、函数的调用过程

每次函数调用都对应着一个栈上的活动记录

  • 调用函数的活动记录位于栈的中部
  • 被调函数的活动记录位于栈的顶部

三、函数调用的栈变化

从main() 开始运行

 main() 调用 f()

 当从 f() 调用中返回 main()

四、函数调用栈上的数据

  • 函数调用时,对应的栈空间在函数返回前是专用的
  • 函数调用结束后,栈空间将被释放,数据不再有效

        下面看一个指向栈数据的指针: 

#include <stdio.h>
 
int* g()
{
    int a[10] = {0};
    return a;
}
 
void f()
{
    int i = 0;
    int b[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    int *pointer = g();
    
    for (i = 0; i < 10; i++)
    {
        b[i] = pointer[i];
    }
    for(i = 0; i < 10; i++)
    {
        printf("%d\n", b[i]);
    } 
}
 
int main()
{
    f();
    return 0;
}

         输出结果如下:

        如果把 

        for (i = 0; i < 10; i++)

       {

                b[i] = pointer[i];

       }

        注释了,直接打印 pointer[i] 里面的数据,如下:

#include <stdio.h>
 
int* g()
{
    int a[10] = {0};
    return a;
}
 
void f()
{
    int i = 0;
    int b[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    int *pointer = g();
    /*
    for (i = 0; i < 10; i++)
    {
        b[i] = pointer[i];
    }
    */
    for(i = 0; i < 10; i++)
    {
        printf("%d\n", pointer[i]);
    } 
}
 
int main()
{
    f();
    return 0;
}

        输出结果如下:

         为什么直接打印 pointer[i] 里面的值会是这样呢?因为 pointer 指向的空间是栈空间,栈空间在 g() 函数返回之后,活动记录就被释放了。被释放后调用 printf 函数,printf 函数需要在栈上面建立一个活动记录。这个活动记录就会有 printf 函数的参数信息和返回值等,所以 pointer 所指向的内存里面的数据由于 printf 函数的调用被改变了。因此,不能返回局部变量的地址,不能返回局部数组的数组名。

五、程序中的堆

  • 堆是程序中一块预留的内存空间,可由程序自由使用
  • 堆中被程序申请使用的内存在被主动释放前将一直有效

        为什么有了栈还需要堆?

        答:栈上的数据在函数返回后就会被释放掉,无法传递到函数外部,如:局部数组

C语言程序中通过库函数的调用获得堆空间

  • 头文件:malloc.h
  • malloc -- 以字节的方式动态申请堆空间
  • free -- 将堆空间归还给系统

系统对堆空间的管理方式

空闲链表法,位图法,对象池法等等

        以 int* p = (int*)malloc(sizeof(int)); 为例,要申请 4 个字节的大小,遍历之后发现跟 5 Bytes 这个节点最接近,找到一个可以用的单元之后,就将这个单元的地址返还给了 p 指针。以前也提过使用 malloc 申请内存空间时返回的内存空间可能比申请的实际内存空间要大一点点,原因就是在空闲链表管理堆空间这样的系统里面,它会找最近的那个,找到后的一般都大于等于所需要的内存空间,假如 5 Bytes 这个节点下所有的空闲内存单元都用完的话,就会找 12 Bytes 节点下的内存单元,这样malloc 返回的内存空间就有可能比自己实际申请的内存空间要大。

六、程序中的静态存储区

  • 静态存储区随着程序的运行而分配空间
  • 静态存储区的生命周期直到程序运行结束
  • 在程序的编译期静态存储区的大小就已经确定
  • 静态存储区主要用于保存全局变量和静态局部变量
  • 静态存储区的信息最终会保存到可执行程序中

        下面看一个静态存储区的验证代码:

#include <stdio.h>
 
int g_v = 1;
 
static int g_vs  = 2;
 
void f()
{
    static int g_vl = 3;
    
    printf("%p\n", &g_vl);
}
 
int main()
{
    printf("%p\n", &g_v);
    
    printf("%p\n", &g_vs);
    
    f();
    
    return 0;
}

        输出结果如下:

         可以看到这三个地址是顺序存放的,因为这三个变量都是存放在程序的静态存储区,静态存储区在程序里面有固定的起始地址。 

七、小结

栈,堆和静态存储区是程序中的三个基本数据区

  • 栈区主要用于函数调用的使用
  • 堆区主要是用于内存的动态申请和归还
  • 静态存储区用于保存全局变量和静态变量

到此这篇关于C语言深入讲解栈与堆和静态存储区的使用的文章就介绍到这了,更多相关C语言 栈与堆内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++实现简单插件机制原理解析

    C++实现简单插件机制原理解析

    这篇文章主要介绍了C++实现简单插件机制原理解析,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-02-02
  • C++内存模型与名称空间概念讲解

    C++内存模型与名称空间概念讲解

    这篇文章主要介绍了C++内存模型与名称空间,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2023-01-01
  • 浅谈C++ Explicit Constructors(显式构造函数)

    浅谈C++ Explicit Constructors(显式构造函数)

    下面小编就为大家带来一篇浅谈C++ Explicit Constructors(显式构造函数)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-12-12
  • 解读构造函数的调用规则、深拷贝与浅拷贝

    解读构造函数的调用规则、深拷贝与浅拷贝

    本文主要介绍了C++中的默认构造函数、拷贝构造函数以及深拷贝和浅拷贝的概念,并通过实际代码示例进行了详细讲解
    2024-11-11
  • 一文读懂c++11 Lambda表达式

    一文读懂c++11 Lambda表达式

    这篇文章主要介绍了c++11 Lambda表达式的相关资料,帮助大家更好的理解和学习C++,感兴趣的朋友可以了解下
    2020-08-08
  • C++ OpenCV实战之网孔检测的实现

    C++ OpenCV实战之网孔检测的实现

    这篇文章主要介绍了如何利用C++和OpenCV实现网孔检测,文中的示例代码讲解详细,对我们学习OpenCV有一定帮助,感兴趣的小伙伴可以了解一下
    2022-05-05
  • CentOS下Jsoncpp安装配置的方法

    CentOS下Jsoncpp安装配置的方法

    本文主要介绍了CentOS下Jsoncpp安装配置的方法,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-02-02
  • 详解C语言中的预处理命令

    详解C语言中的预处理命令

    初学C语言的时候,我们会在开头写下一句话,#include<stdio.h>,这就是预处理命令,下面我们通过这篇文章来了解一下,感兴趣的可以跟随小编一起学习一下
    2022-12-12
  • C语言中求字符串长度的函数的几种实现方法

    C语言中求字符串长度的函数的几种实现方法

    这篇文章主要介绍了C语言中求字符串长度的函数的几种实现方法,需要的朋友可以参考下
    2018-08-08
  • C++简单实现Dijkstra算法

    C++简单实现Dijkstra算法

    这篇文章主要为大家详细介绍了C++简单实现Dijkstra算法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-05-05

最新评论