《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机上,可以粗略地认为堆的大小就是所有可用内存的大小。

相关文章
|
2月前
|
存储 C语言
指针和动态内存分配
指针和动态内存分配
88 0
|
17天前
|
C++
析构造函数就是为了释放内存,就是在局部指针消失前释放内存,拷贝构造函数就是以构造函数为模块,在堆里面新开一块,同一个变量在堆里面的地址
本文讨论了C++中构造函数和析构函数的作用,特别是它们在管理动态内存分配和释放中的重要性,以及如何正确地实现拷贝构造函数以避免内存泄漏。
30 2
|
1月前
|
存储 人工智能 C语言
C语言程序设计核心详解 第八章 指针超详细讲解_指针变量_二维数组指针_指向字符串指针
本文详细讲解了C语言中的指针,包括指针变量的定义与引用、指向数组及字符串的指针变量等。首先介绍了指针变量的基本概念和定义格式,随后通过多个示例展示了如何使用指针变量来操作普通变量、数组和字符串。文章还深入探讨了指向函数的指针变量以及指针数组的概念,并解释了空指针的意义和使用场景。通过丰富的代码示例和图形化展示,帮助读者更好地理解和掌握C语言中的指针知识。
|
2月前
|
存储 安全 Go
Go 中的指针:了解内存引用
Go 中的指针:了解内存引用
|
3月前
|
运维
开发与运维数组问题之指针的加减法意义如何解决
开发与运维数组问题之指针的加减法意义如何解决
41 7
|
3月前
|
存储 C++ 运维
开发与运维数组问题之指针的定义语法如何解决
开发与运维数组问题之指针的定义语法如何解决
31 6
|
4月前
|
存储 设计模式 Java
JavaSE 面向对象程序设计初级 2024方法变量封装javabean结合内存图详解
JavaSE 面向对象程序设计初级 2024方法变量封装javabean结合内存图详解
37 7
|
4月前
|
Java
JavaSE 面向对象程序设计进阶 继承和方法重写 2024理论与内存详解
JavaSE 面向对象程序设计进阶 继承和方法重写 2024理论与内存详解
31 3
|
4月前
|
C语言
C语言学习记录——通讯录(静态内存)
C语言学习记录——通讯录(静态内存)
30 2
|
3月前
|
存储 安全 程序员