《C程序设计新思维》一第6章 玩转指针6.1 自动、静态和手工内存

简介:

本节书摘来自异步社区《C程序设计新思维》一书中的第6章,第6.1节,作者 【美】Ben Klemens,更多章节内容可以访问云栖社区“异步社区”公众号查看

第6章 玩转指针

C程序设计新思维
他就是那个

喜欢我们所有歌曲的人,

他喜欢一起哼唱,

他喜欢边开枪边唱,

但是他不知道这歌的意义。

——选自Nirvana的歌曲“In Bloom(风华正茂)”
就像一首描述音乐的歌曲、一部刻画好莱坞的电影,指针就是一种描述其他数据的数据。我们很容易被指针搞崩溃,像引用的引用、别名、内存管理和malloc之类的东西,很容易把我们搞得天旋地转。但是,这些纷繁复杂的痛苦可以分解为独立的片段。例如,我们可以使用指针作为别名,这样就不需要再关注malloc,20世纪90年代的教科书常教导我们需要熟练掌握这个函数。一方面,C语法中星号的用法常常令人困惑。另一方面,C的语法也向我们提供了工具,用于处理那些格外复杂的指针细节,例如函数指针。

6.1 自动、静态和手工内存

C提供了3种基本的内存管理模式,比大多数语言要多上2种,事实上我们真正需要关注的也只是其中一种。但是,在本书第12章中,我还要向读者介绍2种额外的内存模型。

自动

我们在第一次使用一个变量时对它进行声明,当离开自己的作用域之后变量就会被销毁。如果不使用static关键字,在函数内部所声明的所有变量都是自动变量。一般的编程语言只具有自动类型的数据。

静态

静态变量在程序的整个生命期内一直存在。数组的长度在一开始就是固定的,但它所包含的值却可以改变(因此它并不是完全静态的)。数据是在main函数启动之前被初始化的,因此所有的初始化值都必须是常量,并且不需要计算。在函数的外部所声明的(属于文件作用域)和函数内部用static关键字声明的变量都是静态变量。最后,如果忘了对一个静态变量进行初始化,它会默认初始化为全零(或NULL)。

手工

手工类型的变量涉及malloc和free函数,这也是许多段错误的根源。这种内存模型是让许多C编程员欲哭无泪的罪魁祸首。另外,这也是唯一允许在声明之后改变数组长度的内存类型。

下面这张表显示了三种内存模型的区别所在。在接下来的几章中,我们将详细讨论这些区别。


07c1a2f9c382d51c806028f308e4d414f1aaed68

                       

表中有些特性适用于变量,例如改变长度和方便的初始化。有些特性是内存系统的技术性结果,例如是否可以在初始化时设置值。因此,如果我们想要一个不同的特性,例如能够在运行时改变长度,就不得不关注malloc函数和指针所指向的堆。如果我们可以抹掉这一切重新开始,我们就不会把这三组特性与相关联的技术性烦恼捆绑在一起。但是,我们必须面对这一切。

这些就是当我们把数据存放到内存时所浮现的相关问题。它与变量本身不同,可以产生另一层次的乐趣:

1.如果我们用static关键字在函数外部或者函数内部声明一个struct、char、int、double或其他类型变量,它就是静态的。否则,它们就是动态的。

2. 如果我们声明了一个指针,它本身具有一种内存类型,很可能如规则1所述的属于自动或静态类型。但是,这个指针可以指向三种数据类型的任何一种,包括指向由malloc函数所分配数据的静态指针和指向静态数据的自动指针。所有的组合都是可以成立的。


824fee8c82f31e940dca6bf92f57a3c704cf8c50

自己动手:检查一些现有的代码,研究变量的分类:哪些数据位于静态内存、自动内存或手动内存,哪些变量是指向手工内存的指针、指向静态值的自动指针等。如果手头上没有现成的代码,可以用例6-6为素材完成这个练习。


fe0a49ac2b34665253a11e65a1eef2a4ad42621c

任何函数都在内存中占据一个空间,称为函数帧,用以保存与这个函数有关的信息,例如当函数执行完成之后返回到哪里,以及保存所有自动分配的变量的空间。

当一个函数(例如main)调用另一个函数时,第一个函数的函数帧中的活动就会暂停,并且一个新的函数被添加到这个堆栈帧中。当函数执行完成时,它的帧就从这个堆栈帧中弹出。在这个过程中,保存在这个函数帧中的所有变量都会消失。

遗憾的是,堆栈的长度限制要比一般内存小得多,大约是2~3M(本书写作时在Linux系统下大致如此)。这点空间对于保存莎士比亚的所有悲剧作品已经足够,因此不必担心分配一个包含10000个整数的数组会出现问题。但是,我们很可能会用到更大的数据集,因此需要使用malloc为它们在其它地方分配空间。

通过malloc所分配的内存并不位于堆栈中,而是在系统中称为堆的空间中。堆的大小可能有限制,也可能没有限制。在普通的PC机上,可以粗略地认为堆的大小就是所有可用内存的大小。

相关文章
|
26天前
|
存储 Java C++
C++ 引用和指针:内存地址、创建方法及应用解析
C++中的引用是现有变量的别名,创建时需用`&`运算符,如`string &meal = food;`。指针存储变量的内存地址,使用`*`创建,如`string* ptr = &food;`。引用必须初始化且不可为空,而指针可初始化为空。引用在函数参数传递和提高效率时有用,指针适用于动态内存分配和复杂数据结构操作。选择使用取决于具体需求。
39 9
|
1月前
|
存储 Go iOS开发
掌握Go语言:探索Go语言指针,解锁高效内存操作与动态数据结构的奥秘(19)
掌握Go语言:探索Go语言指针,解锁高效内存操作与动态数据结构的奥秘(19)
|
2月前
|
存储 C语言 C++
动态内存分配与指向它的指针变量
动态内存分配与指向它的指针变量
21 1
|
2月前
|
安全 程序员 C++
C++中的智能指针:从原始指针到现代内存管理
C++中的智能指针:从原始指针到现代内存管理
18 0
|
2月前
|
安全 C++ 容器
C++中的智能指针:自动内存管理的利器
C++中的智能指针:自动内存管理的利器
26 0
|
7天前
|
Java Go 区块链
【Go语言专栏】Go语言中的指针与内存管理
【4月更文挑战第30天】Go语言,由Google开发,是一种静态强类型、编译型、并发型语言,具有垃圾回收功能,常用于云计算、微服务、区块链等领域。本文聚焦Go中的指针和内存管理。指针表示变量内存地址,可用于直接访问和修改变量,如示例代码所示。指针运算有限制,仅支持相同类型变量和数组元素访问。内存管理由Go运行时的垃圾回收机制处理,自动回收无引用对象,简化管理但引入性能开销。可通过`runtime.GC()`手动触发垃圾回收。
|
2月前
|
存储 安全 编译器
C++智能指针:更简单、更高效的内存管理方法
C++智能指针:更简单、更高效的内存管理方法
22 0
|
2月前
|
C语言
指针与内存
指针与内存
17 0
|
2月前
|
存储 定位技术 C语言
C语言内存中的地图,方向盘,初始指针详解(1)
C语言内存中的地图,方向盘,初始指针详解(1)
|
3月前
|
C语言
C语言指针使用及动态分配内存
C语言指针使用及动态分配内存
39 0