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

第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编程员欲哭无泪的罪魁祸首。另外,这也是唯一允许在声明之后改变数组长度的内存类型。

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

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

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

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

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

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

任何函数都在内存中占据一个空间,称为函数帧,用以保存与这个函数有关的信息,例如当函数执行完成之后返回到哪里,以及保存所有自动分配的变量的空间。
当一个函数(例如main)调用另一个函数时,第一个函数的函数帧中的活动就会暂停,并且一个新的函数被添加到这个堆栈帧中。当函数执行完成时,它的帧就从这个堆栈帧中弹出。在这个过程中,保存在这个函数帧中的所有变量都会消失。

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

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

时间: 2024-10-03 02:05:31

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

《C程序设计新思维》一导读

前言 C程序设计新思维 Is it really punk rock Like the party line? 它真的是朋克摇滚么, 就像政治路线? --选自Wilo的歌曲"Too Far Apart(遥远)" C就是Punk Rock C仅有为数不多的关键词,并且显得略微粗糙,但是它很棒[1]!你可以用C来做任何事情.它就像一把吉他上的C.G和D弦,你可以很快就掌握其基本原理,然后就得用你的余生来打磨和提高.不理解它的人害怕它的威力,并认为它粗糙得不够安全.实际上它在所有的编程语言排

《C程序设计新思维》一6.3 不使用malloc的指针

### 6.3 不使用malloc的指针当我们告诉计算机把A设置为B时,意思可能是下面两者之一: 把B的值复制给A.此时用A++来增加A的值,B的值不会改变.使A成为B的别名.于是A++也会增加B的值.在代码中,每次表示把A设置为B时,需要明确是为了创建一份拷贝还是创建一个别名.这绝不是C特有的问题. 对于C而言,我们总是创建一份拷贝.但是,如果我们复制了数据的地址,这个指针的一份拷贝就成为了这个数据的别名.这是一种精巧的实现别名的方式. 其他语言具有不同的习惯:LISP系的语言非常依赖于别名,

《C程序设计新思维》一6.2 持久性的状态变量

6.2 持久性的状态变量 本章主要讨论自动内存.手工内存和指针的交互,对静态变量的讨论较少,但是静态变量还是很有用处的,因此值得在此花点篇幅探讨一下它们的作用. 静态变量可以具有局部作用域.也就是说,我们可以让一个静态变量只存在于某个函数内部,但是当这个函数执行时,这个变量会保持它的值.因此,它可以作为内部计数器或可复用的临时空间.由于静态变量永远不会移动,因此指向一个静态变量的指针在函数完成执行之后仍然是有效的. 例6-1展示了一个传统的教科书例子:菲波那契数列.我们把前两个成员声明为0和1,

《C程序设计新思维》一1.4 使用Makefile

1.4 使用Makefile makefile提供一个解决所有以上这些麻烦的方案.它基本上可以看作一组有组织的变量和shell脚本.POSIX标准的make程序输入makefile作为指令和变量,然后自动化处理那些冗长繁琐的命令行.在这部分讲解之后,就没有什么必要去直接调用编译器了. 在"3.2 makefile还是shell脚本"中,会讲述关于makefile的更多细节:这里,先给出一个最小的.实用的,并且能够编译一个依赖于一个库的基本程序的makefile: 用法: 曾经的用法:将

《JavaScript设计与开发新思维》——第1章 (重新)介绍JavaScript 1.1 什么是JavaScript

第1部分 入门 第1章 (重新)介绍JavaScript 今天的JavaScript是一种被误解的编程语言,从JavaScript所能做到的,到它不能做到的,再到JavaScript不是什么(JavaScript不是Java),关于这种当今Web的核心技术有许多的混淆.不理解一种技术的精髓,就不可能有效地使用它,所以本书的第1章提供对现代JavaScript的一个概要介绍. 本章的大部分篇幅讨论JavaScript的概念,以及它迄今为止的变化历程.接下来,读者将看到有关JavaScript版本以

《C程序设计新思维》一1.6 以源文件利用库(即使你的系统管理员不想叫你这么做)

1.6 以源文件利用库(即使你的系统管理员不想叫你这么做) 你可能已经注意到了上一章节最后的关于为何你需要有root权限来安装POSIX系统到你的通常位置的警告.但是你在工作场所的共享机器里可能没有root权限,或者你被有特别的权限的某人控制. 那你就必须走入地下,制作一个属于自己的root目录. 第一步很简单,即创建这个目录: 由于我已经有了一个-/tech目录,用来保存我所有的技术文档.手册和源代码,所以我建立的是-/tech/root目录.名字其实无所谓,但是我还是喜欢用-/root作为本

《C程序设计新思维》一1.3 库的路径

1.3 库的路径 现在我们有了编译器,有POSIX的工具包,还有一个可以用来方便地安装几百个库的包管理器.现在我们着手解决用这些工具来编译我们程序的问题. 我们必须从编译器命令行开始,并很快会陷入一团混乱,但是我们还是可以用三个(有时候是三个半)相对简单的步骤结束. 1. 设置一个编译器配置变量表. 2. 设置一个要连接的库的变量表.所谓的半个步骤是指,有时你不得不设定一个唯一的变量,用来一边编译一边连接:或者有时不得不设定两个变量,分别用在编译时和运行时的连接. 3. 设置一个根据这些变量来协

《C程序设计新思维》一1.7 通过本地文档来编译C程序

1.7 通过本地文档来编译C程序 到此,你应该已经看出编译过程的套路了. 1. 设定一个表述编译器选项的变量. 2. 设定一个表述连接器选项的变量,包括为你用的所有库配置的-l选项. 3. 用make命令或者你的IDE的操作来把这些变量转换为完整的编译和连接命令. 本章的余下部分将把以上步骤做最后一次,并采取一种非常简短的设置:仅仅用shell.如果你思维敏捷,可以通过摘录一些语句段落到解析器上来学习脚本,你也将可以同样地把C代码贴在你的命令行上.**1.7.1 在命令行里包含头文件**gcc和

《C程序设计新思维》一1.2 在Windows下编译C

1.2 在Windows下编译C 在多数系统中,C享有一个中心的.贵宾礼遇的地位,以至于所有其他工具都处于从属的地位:但是在Windows机器中,C语言却被奇怪地忽略了. 所以我不得不花点时间讨论如何来准备好一台Windows机器以便用来写C程序.如果你现在不需要在Windows机器上编程,你可以直接跳到"1.3库的路径". 这并非是针对Microsoft的,请不要用这样的角度来理解这一节.我无意去推测Microsoft的动机和商业战略.不过,如果你想在Windows机器上用C来工作,