浅谈C语言中指针使用不当的危险性.doc
第 19 卷 Vol . 19 第 2 期 No . 2 洛阳师专学报 Journal of Luoyang Teachers College 2000 年 4 月 Apr. 2000 浅 谈 C 语言中指针使用不当的危险 性 康 牧 , 赵治 国 洛阳师专 , 河南洛阳 471022 摘 要 C 语言是一种中级语言 , 它的指针具有强大的功 能 , 它的功能是其它高级语言所 不 能比拟的 , 但是 , 如果对指针使用不当就会使 系统处于极端危险的状 态 , 有时会使系统丢 失 数 据 , 有时会使系 统 死 机 1 本文详细讨 论了产生这种情况的原 因 , 同时也给出了正确的 使 用方 法 . 关键词 C 语言 ; 指针 ; 悬挂访问 ; 中断向量表 ; 堆栈段 ; 代码段 ; 数据段 ; 附加段 中图分类号 TP312 文献标识码 A 文章编号 1007 - 2969 2000 02 - 0080 - 03 The Da nger s fo r Imp rop er U se of Pointer in C L a ngua ge KANG Mu ZHAO ZHi2guo Luoyang Teachers College , Luoyang 471022 ,China Abstract C language is a kind of intermediate language . Its pointer has so powerful a function that even some ad2 vanced languages do not have . However , if the pointer is not used properly , the system could be in great danger . Sometimes even data missing and a dead halt may happen to the system. In this article , a detailed analysis is made of the reasons why such problems occur and a direction of how to use it is provided. Key words C language ; pointer ; hanging use ; table of interrupt address ; stack segment ; code segment ; data seg2 ment ; extra segment 却 会 将错就 错 地执行下 去 . 所以在 使 用 C 语 言 编 程 , 特别 是使用指针时要特别小 心 , 否 则 , 可 能 会得到一 个 莫名其 妙 的结 果 , 有时甚至会 出 现系 统 死 机 , 不能正常结束的现 象 . 下面我们举两个用 C 语言编写的程序 , 说明 在 C 语言中 , 使用指针不当而得 到 莫 名其 妙 的 结 果和出现程序不能正常结束的现象 1 然后再分 析产生这种情况的原因 , 及应注意的事 项 . 0 引 言 C 语言 中 的 指 针 , 简 单 地 说 就 是 一 个 地 址 . 我 们定义一个整型指针 变 量 , 那么就可以让它 指 向一个整型数据变量 , 或 者说把一个整型数据 变 量 的地址赋给这个指针 变 量 . 同理可以定义实 型 指 针 , 字 符型指 针 , 结 构体指 针 , 数 组指 针 , 函 数指针 等 . C 语言 的 指 针 为 我 们 提 供 了 极 其 强 大 的 功 能 . 但是 , 由 于 C 语言对程序格式要求特别 宽 , 不 象 PASCAL 等语 言那样 要求严 格 , 这就不可 避 免 地带来了问 题 . 例如 PASCAL 语言中对数组 做 越界检查 , 也就是当数组 的下标超过数组下标 的 定 义范围 时 , 系统就会给出错误信 息 , 而不再 往 下执行 ; 而 C 语言中的 数 组却不做越界检 查 , 而 是 接着访问相邻的地址中的信 息 . 也就是 说 , 在 C 语言 中 , 即使使用不 当 , 也不 给出错误信 息 , 1 程 序 1 假设我们编一个程序 , 定义两个大小都是 10 的 整 型数 组 a 和 b. 数 组 a 先定 义 , 数 组 b 后 定 义 . 在数组 b 中先放入前 10 个裴波纳齐数 , 再用 一 个整型 指 针 p 指 向数 组 a , 通过指针给数 组 a 中 放入 15 个数 , 它们 分别是 从 - 1 到 - 15 , 然 后 再分别输出数组 a 和数组 b 的内 容 . 程序如下 include stdio . h main int 3 p ; int a 10 , b 10 , i ; b 0 0 ; b 1 1 ; for i 2 ;i 10 ;i b i b i - 1 b i - 2 ; p a ; for i 1 ;i 15 ;i 3 p - i ; p ; printf ” 数组 a 的内容为 for i 0 ;i 10 ;i printf p p , q p , p , q ; for i 1 ;i 200 ;i 3 q - - 3 p - - i ; printf 数据输入完毕 n ; printf good bye n ; 这 个程序运行时 , 最 后的两行提示信 息 输出 , 且不能正常结束 , 必须强行中断 . 3 原因分 析 3 . 1 程序 1 的错误原因 程序 1 出现错误原因是数组 a 和数 组 b 义的时候是紧紧相邻的 , 那么它们在物理 地 也是相 邻 的 . 在往数 组 b 中存放了裴波纳 齐 的前 10 个数以后 , 在通过指针 p 给数组 a 存 时 , 超出了数组 a 的范 围 , 而 非法地进入 了 b 的区域内 , 修改 了数 组 b 的内 容 , 所以 才 现上面那种情 况 . 而非法 地修改其它存储 区 数据是相当危险的 , 它将 会使系统丢失重 要 据 . 所以在使用指针访问 数组时一定要注 意 下 标的范围是从 0 开始 , 千万不要因为 粗 心 不必要的损失 . 3 . 2 程序 2 的错误原因 程序 2 出现错误的原因是当我们定义一 针变量时 , 除了最后定 义 的那个指针变量 所 的 存储单 元的地址一定是十六进制 的 00B5 通过实验所 知 , 前面定 义的指针变量所 指 n ; printf d t ,a i printf n ; printf 数组 b 的内容为 for i 0 ;i 10 ;i ; n ; printf d t ,b i ; 程序执行的结果如下 数组 a 的内容为 - 1 - 2 - 3 - 4 - 5 10 数组 b 的内容为 - 11 - 12 - 13 - 14 21 33 - 6 - 7 - 8 - 9 - 存 储单元的地址一般是随机 的 , 没有什么 规 操 作系统在定义指针变 量 时 , 总是尽可能 给 配一个空闲区域的地址 , 或者给它一个系 统 未用区域的地 址 . 当你给 随机单元中存放 较 信 息的时 候 , 影响其它信息的可能性不太 大 当你给随机单元中存放的信息较多或者当时 中 内存的空闲区域较少 的时 候 , 影响其它 信 可能性就很大 了 . - 15 5 8 13 而不是我们所期望的内容 0 1 1 2 3 5 8 13 21 33 程 序 2 假 设我们编一个程序 , 在程序内定义两 个 整 针 , 然后直接输出两 个指针所指向单元的 地 , 再直接向两指针各 自所指的地址单元相 邻 片连续存储区中存 放若干个整 数 , 最后输 出 提示信息 , 程序结 束 . 程序如下 include stdio . h main int 3 p , 3 q ; int i ; 这 种现象 的危险性与程 序 1 的危险性 相 就显得更大了 . 它属于 在 文 1 中提到 的 悬 问 问题 . 当一个指针变量没有指向一个 具 地址时 , 它的地址用户及 操作系统是很难 准 道的 , 而这个地址是操作系统随机给予 的 , 这 个地址不是通过合法 的方法得到 的 , 操 作 并 不对这个地址进行合 法存取保 护 , 这个 地 可 能在以后地址分配的 时候分配给其它变 量 通 过这种手段访问 , 就 是 悬挂访 问 . 而 这 机 地址有时可 能会指向 文 4 中所说的数 据 洛阳师专学报 2000 年 82 者 附加 段 , 而这种情况可能造成有用信息的 修 改 . 这对于一个大型的信 息管理系统来说是相 当 危 险的 ; 这 个 随 机 的 地 址 , 有 时 可 能 会 指 向 文 5 中所 说的中断向量 表 , 而中断向量表中存 放 的 是各个中断服务程序 的入口地 址 , 如果把它 们 的内容修改了 , 则可能会 引起中断调用不能正 常 进行 , 而中断调用在系统 的运行过程中是经常 发 生 的 . 例如 , 要访问某一台外 设 , 就需 要调用 中 断 , 而它的中断服务程序 的入口地址就保存在 中 断向量表 中 , 而此时中断 向量表中的内容已经 改 变 了 , 那么 , 该外设就不能被访问 了 . 有时这 个 随机地址可 能会指向 文 4 中所说的堆栈 段 , 而 堆栈段中所存放的信息 , 一般是子程序调用时 的 参数 , 也存放子程序调用返回时的断点地 址 , 在 子 程序返回 时需要从堆栈中取出它的断点地 址 , 而 此时堆栈中的内容已 经改变 了 , 那么子程序 就 不 能正常返 回 , 而使系统无法正常运 行 . 有时 这 个 随机 地址可能会 指向 文 4 中所说的代码 段 , 如 果在程序的运行过程中 , 代码段中的内容被 改 变了 , 那么就会使程序在运行的时候出问题 . 上输入 , 这就需要在程序 中定义一个空间足够 大 的 字符数 组 , 用来存放每次输入的汉字字符 串 . 对 于这种问题我们最好 不要偷懒省 事 , 宁可定 义 一 个大的字符数 组 , 而浪费一些空 间 , 也不要 犯 上 述错 误 . 也可以定义一个字符型指 针 , 事先 不 让 它指向一个具体的地址 , 在从键盘上输入汉 字 之前 , 先输入要输入汉字的个 数 , 然后以这个 数 值 乘以 2 所得到的 值 , 作为调 用 malloc 函数 或 calloc 函数 的参 数 , 申请一个这样大小 的空 间 , 让定义的那个字符指针指向这个存储空间的首地 址 , 就可以不浪费任何存储空 间 . 尽 管 C 语 言中的指针使用起来有这么大 的 危险 , 但 C 语言的指针为我们所提供 的优点是 远 远大于它的缺点的 , 在使用 C 语言的 指针的时 候 只要注意上面提到的问题即可扬其长而避其 短 . 本文所举例子均在 Turbo C 2 . 0 下调试通 过 . 参考文献 严蔚敏 , 吴伟 民 . 数据结 构 M . 北 京 清华大学 出 版社 , 1997. 谭浩强 , 等 . C 语言程序设计教程 M . 北 京 高等 教 育出版社 , 1994. 王爱民 . 实用 C 语言程序设计教 程 M . 成 都 成 都 科技大学出版社 , 1995. 沈美 明 , 温冬 婵 . IBM - PC 汇编语言程序设 计 M . 北京 清华大学出版社 , 1994. 美 Steven Armbrust , Ted Forgeron. 舒 志 勇 , 等 译 . DOS/ BIOS 使用 详 解 M . 北 京 电 子 工 业 出 版 社 , 1991. 1 2 4 建 议 3 假如需要把一个字符串的内容并入另一个字 符串中 , 应当注意把存放 结果字符串的字符数 组 的 空间定义的大一 些 , 以避免越界访 问 , 造成 不 必要的损 失 . 使用 C 语言编程 , 若每次处理的 汉字内容 不 同 、 个数也不同 时 , 希望每次处理的汉字从键 盘 4 5