<453A5C C435CB3F6B0E65C43D3EFD1D4C9EEB6C8BDE2C6CA5C43D3EFD1D4C9EEB6C8BDE2C6CA5FB1EAD7BC5C43D3EFD1D4C9EEB6C8BDE2C6CA5FB1EAD7BC5C43D3EFD1D4C9EEB6C8BDE2C6CA5FB1EAD7BC312E646F63>
|
|
|
- 排检 寿
- 9 years ago
- Views:
Transcription
1 C 语 言 深 度 解 剖 解 开 程 序 员 面 试 笔 试 的 秘 密 以 含 金 量 勇 敢 挑 战 国 内 外 同 类 书 籍 陈 正 冲 编 著 石 虎 审 阅
2 写 在 前 言 前 面 的 话 最 近 面 试 了 一 些 人, 包 括 应 届 本 科 硕 士 和 工 作 多 年 的 程 序 员, 在 问 到 C 语 言 相 关 的 问 题 的 时 候, 总 是 没 几 个 人 能 完 全 答 上 我 的 问 题 甚 至 一 些 工 作 多 年, 简 历 上 写 着 最 得 意 的 语 言 是 C 语 言, 对 C 有 很 深 的 研 究, 精 通 C 语 言 的 人 也 答 不 完 全 我 的 问 题, 甚 至 有 个 别 人 我 问 的 问 题 一 个 都 答 不 上 于 是 我 就 想 起 了 我 去 年 闲 的 使 用 写 的 这 本 小 册 子 这 本 小 册 子 已 经 在 我 电 脑 里 睡 了 一 年 大 觉 了 并 非 没 有 出 版 社 愿 意 出 版, 而 是 几 个 大 的 出 版 社 都 认 为 书 写 得 不 错, 但 太 薄, 利 润 太 低, 所 以 要 求 我 加 厚 到 300 页 以 上 我 拒 绝 加 厚, 并 为 此 和 几 个 出 版 社 僵 持 了 一 年 多 我 认 为 经 典 的 东 西 一 定 要 精 炼, 不 要 废 话 这 次 由 于 面 试 别 人, 所 以 终 于 记 起 了 我 还 写 过 这 么 一 本 小 册 子 想 了 想, 还 是 决 定 挂 到 网 上 免 费 让 大 家 看 得 了 并 为 此 专 门 为 本 书 开 了 个 博 客, 以 方 便 和 读 者 交 流 博 客 地 址 : 作 者 简 介 : 陈 正 冲 : 湖 南 沅 江 人, 毕 业 于 长 春 光 学 精 密 机 械 学 院 ( 长 春 理 工 大 学 ) 数 学 系 目 前 从 事 嵌 入 式 软 件 开 发 和 管 理 方 面 的 工 作 石 虎 : 湖 南 沅 江 人, 毕 业 于 吉 林 大 学 计 算 机 系 目 前 为 大 连 交 通 大 学 计 算 机 系 讲 师
3 前 言 我 遇 到 过 很 多 程 序 员 和 计 算 机 系 毕 业 的 学 生, 也 给 很 多 程 序 员 和 计 算 机 系 毕 业 的 学 生 讲 解 过 高 级 C 语 言 程 序 设 计 每 期 班 开 课 前, 我 总 会 问 学 生 : 你 感 觉 C 语 言 学 得 怎 么 样? 难 吗? 指 针 明 白 吗? 数 组 呢? 内 存 管 理 呢? 往 往 学 生 回 答 说 : 感 觉 还 可 以,C 语 言 不 难, 指 针 很 明 白, 数 组 很 简 单, 内 存 管 理 也 不 难 一 般 我 会 再 问 一 个 问 题 : 通 过 这 个 班 的 学 习, 你 想 达 到 什 么 程 度? 很 多 学 生 回 答 : 精 通 C 语 言 我 告 诉 他 们 : 我 很 无 奈, 也 很 无 语 因 为 我 完 全 在 和 一 群 业 余 者 或 者 是 C 语 言 爱 好 者 在 对 话 你 们 大 学 的 计 算 机 教 育 根 本 就 是 在 浪 费 你 们 的 时 间, 念 了 几 年 大 学, 连 C 语 言 的 门 都 没 摸 着 现 在 大 多 数 学 校 计 算 机 系 都 开 了 C C++ Java C# 等 等 语 言, 好 像 什 么 都 学 了, 但 是 什 么 都 不 会, 更 可 悲 的 是 有 些 大 学 居 然 取 消 了 C 语 言 课 程, 认 为 其 过 时 了 我 个 人 的 观 点 是 十 鸟 在 林, 不 如 一 鸟 在 手, 真 正 把 C 语 言 整 明 白 了 再 学 别 的 语 言 也 很 简 单, 如 果 C 语 言 都 没 整 明 白, 别 的 语 言 学 得 再 好 也 是 花 架 子, 因 为 你 并 不 了 解 底 层 是 怎 么 回 事 当 然 我 也 从 来 不 认 为 一 个 没 学 过 汇 编 的 人 能 真 正 掌 握 C 语 言 的 真 谛 我 个 人 一 直 认 为, 普 通 人 用 C 语 言 在 3 年 之 下, 一 般 来 说, 还 没 掌 握 C 语 言 ;5 年 之 下, 一 般 来 说 还 没 熟 悉 C 语 言 ;10 年 之 下, 谈 不 上 精 通 所 以, 我 告 诉 我 的 学 生 : 听 完 我 的 课, 远 达 不 到 精 通 的 目 标, 熟 悉 也 达 不 到, 掌 握 也 达 不 到 那 能 达 到 什 么 目 标?----- 领 你 们 进 入 C 语 言 的 大 门 入 门 之 后 的 造 化 如 何 在 于 你 们 自 己 不 过 我 可 以 告 诉 你 们 一 条 不 是 捷 径 的 捷 径 : 把 一 个 键 盘 的 F10 或 F11 按 坏, 当 然 不 能 是 垃 圾 键 盘 往 往 讲 到 这 里, 学 生 眼 里 总 是 透 露 着 疑 虑 C 语 言 有 这 么 难 吗? 我 的 回 答 是 : 不 难 但 你 就 是 用 不 明 白 学 生 说 : 以 前 大 学 老 师 讲 C 语 言, 我 学 得 很 好 老 师 讲 的 都 能 听 懂, 考 试 也 很 好 平 时 练 习 感 觉 自 己 还 不 错, 工 作 也 很 轻 松 找 到 了 我 告 诉 学 生 : 听 明 白, 看 明 白 不 代 表 你 懂 了, 你 懂 了 不 代 表 你 会 用 了, 你 会 用 了 不 代 表 你 能 用 明 白, 你 能 用 明 白 不 代 表 你 真 正 懂 了! 什 么 时 候 表 明 你 真 正 懂 了 呢? 你 站 在 我 这 来, 把 问 题 给 下 面 的 同 学 讲 明 白, 学 生 都 听 明 白 了, 说 明 你 真 正 懂 了 否 则, 你 就 没 真 正 懂, 这 是 检 验 懂 没 懂 的 唯 一 标 准 冰 山 大 家 都 没 见 过, 但 总 听 过 或 是 电 影 里 看 过 吧? 如 果 你 连 泰 坦 尼 克 都 没 看 过, 那 你 也 算 个 人 物 ( 开 个 玩 笑 ) 泰 坦 尼 克 里 的 冰 山 给 泰 坦 尼 克 造 成 了 巨 大 的 损 失 你 们 都 是 理 工 科 的, 应 该 明 白 冰 山 在 水 面 上 的 部 分 只 是 总 个 冰 山 的 1/8 我 现 在 就 告 诉 你 们,C 语 言 就 是 这 座 冰 山 你 们 现 在 仅 仅 是 摸 到 了 水 面 上 的 部 分, 甚 至 根 本 不 知 道 水 面 下 的 部 分 我 希 望 通 过 我 的 讲 解, 让 你 们 摸 到 水 面 下 的 部 分, 让 你 们 知 道 C 语 言 到 底 是 什 么 样 子 从 现 在 开 始, 除 非 在 特 殊 情 况 下, 不 允 许 用 printf 这 个 函 数 为 什 么 呢? 很 多 学 生 写 完 代 码, 直 接 用 printf 打 印 出 来, 发 现 结 果 不 对 然 后 就 举 手 问 我 : 老 师, 我 的 结 果 为 什 么 不 对 啊? 连 调 试 的 意 识 都 没 有! 大 多 数 学 生 根 本 就 不 会 调 试, 不 会 看 变 量 的 值, 内 存 的 值 只 知 道 printf 出 来 结 果 不 对, 却 不 知 道 为 什 么 不 对, 怎 么 解 决 这 种 情 况 还 算 好 的 往 往 很 多 时 候 printf 出 来 的 结 果 是 对 的, 然 后 呢, 学 生 也 理 所 当 然 的 认 为 程 序 没 有 问 题 是 这 样 吗? 往 往 不 是, 往 后 看, 你 能 看 到 例 子 的 永 远 给 我 记 住 一 点 : 结 果 对, 并 不 代 表 程 序 真 正 没 有 问 题 所 以, 以 后 尽 量 不 要 用 printf 函 数, 要 去 看 变 量 的 值, 内 存 的 值 当 然, 在 我 们 目 前 的 编 译 器 里, 变 量 的 值, 内 存 的 值 对 了 就 代 表 你 程 序 没 问 题 吗? 也 不 是, 往 后, 你 也 会 看 到 例 子 的 这 个 时 候 呢, 学 生 往 往 会 莫 名 其 妙 这 个 老 师 有 问 题 吧 大 学 里 我 们 老 师 都 教 我 们 怎 么 用 printf, 告 诉 我 们 要 经 常 用 printf 这 也 恰 恰 是 大 学 教 育 失 败 的 地 方 之 一 很 多 大 学 老 师 根 本 就 没 真 正 用 C 语 言 写 过 几 行 代 码, 更 别 说 教 学 生 调 试 代 码 了 不 调 试 代 码, 不 按 F10 或 F11, 水 平 永 远 也 无 法 提 上 来, 所 以, 要 想 学 好 一 门 编 程 语 言, 最 好 的 办 法 就 是 多 调 试 你 去 一 个 软 件 公 司 转 转, 去 看 人 家 的 键 盘, 如 果 发 现 键 盘 上 的 F10 或 F11 铮 亮 铮 亮, 毫 无 疑 问, 此 机 的 主 人 曾 经 或 现 在 是 开 发 人 员 ( 这 里 仅 指 写 代 码 的, 不 上 升 到 架 构 设 计 类 的 开 发 人 员 ),
4 否 则, 必 是 非 开 发 人 员 非 常 有 必 要 申 明, 本 人 并 非 什 么 学 者 或 是 专 家, 但 本 人 是 数 学 系 毕 业, 所 以 对 理 论 方 面 比 较 擅 长 讲 解 的 时 候 会 举 很 多 例 子 来 尽 量 使 学 生 明 白 这 个 知 识 点, 至 于 这 些 例 子 是 否 恰 当 则 是 见 仁 见 智 的 问 题 了 但 是 一 条, 长 期 的 数 学 训 练 使 得 本 人 思 维 比 较 严 谨, 讲 解 一 些 知 识 点 尤 其 是 一 些 概 念 性 原 理 性 的 东 西 时 会 抠 的 很 细 很 严, 这 一 点 相 信 读 者 会 体 会 得 到 的 本 书 是 我 平 时 讲 解 C 语 言 的 一 些 心 得 和 经 验, 其 中 有 很 多 我 个 人 的 见 解 或 看 法 经 过 多 期 培 训 班 的 实 践, 发 现 这 样 讲 解 得 比 较 透 彻, 学 生 听 得 明 白 很 多 学 生 听 完 课 后 告 诉 我 : 我 有 生 以 来 听 课 从 来 都 没 有 听 得 这 么 透 彻, 这 么 明 白 过 也 有 业 余 班 的 学 生 甚 至 辞 掉 本 职 工 作 来 听 我 的 课 的 当 然, 关 于 C 语 言 的 这 么 多 经 验 和 心 得 的 积 累 并 非 我 一 人 之 力 借 用 一 句 名 言 : 我 只 不 过 是 站 在 巨 人 的 肩 膀 上 而 已 给 学 生 做 培 训 的 时 候 我 参 考 得 比 较 多 的 书 有 :Kernighan & Ritchie 的 The C Programming Language ;Linden 的 Expert C Programming ; Andrew & Koening C Traps and Pitfalls ; Steve Maguire 的 Write Clean Code ;Steve McConnell 的 Code Complete. Second Edition ; 林 锐 的 高 质 量 C++/C 编 程 指 南 这 些 书 都 是 经 典 之 作, 但 却 都 有 着 各 自 的 缺 陷 读 者 往 往 需 要 同 时 阅 读 这 些 书 才 能 深 刻 的 掌 握 某 一 知 识 点 我 的 讲 课 的 试 图 时 候 融 各 家 之 长, 再 加 上 我 个 人 的 见 解 传 授 给 学 生 还 好, 学 生 反 映 还 可 以, 至 少 还 没 有 出 乱 子 这 些 书 饱 含 着 作 者 的 智 慧, 每 读 一 遍 都 有 不 同 的 收 获, 我 希 望 读 者 能 读 上 十 遍 另 外, 在 编 写 本 书 时 也 参 考 了 网 上 一 些 无 名 高 手 的 文 章, 这 些 高 手 的 文 章 见 解 深 刻, 使 我 受 益 匪 浅 这 里 要 感 谢 这 些 大 师 们, 如 果 不 是 他 们, 肯 怕 我 的 C 语 言 的 水 平 也 仅 仅 是 入 门 而 已 学 习 C 语 言, 这 几 本 书 如 果 真 正 啃 透 了, 水 平 不 会 差 到 哪 与 其 说 本 书 是 我 授 课 的 经 验 与 心 得, 不 如 说 本 书 是 我 对 这 些 大 师 们 智 慧 的 解 读 本 书 并 不 是 从 头 到 尾 讲 解 C 语 言 的 基 础 知 识, 所 以, 本 书 并 不 适 用 于 C 语 言 零 基 础 的 人 本 书 的 知 识 要 比 一 般 的 C 语 言 书 说 讲 的 深 的 多, 其 中 有 很 多 问 题 是 各 大 公 司 的 面 试 或 笔 试 题 所 以 本 书 的 读 者 应 该 是 中 国 广 大 的 计 算 机 系 的 学 生 和 初 级 程 序 员 如 果 本 书 上 面 的 问 题 能 真 正 明 白 80%, 作 为 一 个 应 届 毕 业 生, 肯 怕 没 有 一 家 大 公 司 会 拒 绝 你 当 然, 书 内 很 多 知 识 也 值 得 计 算 机 教 师 或 是 中 高 级 程 序 员 参 考 尤 其 书 内 的 一 些 例 子 或 比 方, 如 果 能 被 广 大 教 师 用 于 课 堂, 我 想 对 学 生 来 说 是 件 非 常 好 的 事 情 有 人 说 电 影 是 一 门 遗 憾 的 艺 术, 因 为 在 编 辑 完 成 之 后 总 能 或 多 或 少 的 发 现 一 些 本 来 可 以 做 得 更 好 的 缺 陷 讲 课 同 样 也 如 此, 每 次 讲 完 课 之 后 总 能 发 现 自 己 某 些 地 方 或 是 没 有 讲 到, 或 是 没 能 讲 透 彻 或 是 忘 了 举 一 个 轻 浅 的 例 子 等 等 整 理 本 书 的 过 程 也 是, 为 了 尽 量 精 炼, 总 是 犹 豫 一 些 东 西 的 去 留 限 于 作 者 水 平, 书 中 难 免 有 些 遗 漏 甚 至 错 误, 希 望 各 位 读 者 能 予 指 教 作 者 Mail:[email protected]. 陈 正 冲 2008 年 6 月 23 日
5 目 录 第 一 章 关 键 字 , 最 宽 恒 大 量 的 关 键 字 ----auto , 最 快 的 关 键 字 ---- register , 皇 帝 身 边 的 小 太 监 ---- 寄 存 器 , 使 用 register 修 饰 符 的 注 意 点 , 最 名 不 符 实 的 关 键 字 ----static , 修 饰 变 量 , 修 饰 函 数 , 基 本 数 据 类 型 ----short int long char float double , 数 据 类 型 与 模 子 , 变 量 的 命 名 规 则 , 最 冤 枉 的 关 键 字 ----sizeof , 常 年 被 人 误 认 为 函 数 ,sizeof(int)*p 表 示 什 么 意 思? ,signed unsigned 关 键 字 ,if else 组 合 ,bool 变 量 与 零 值 进 行 比 较 , float 变 量 与 零 值 进 行 比 较 , 指 针 变 量 与 零 值 进 行 比 较 ,else 到 底 与 哪 个 if 配 对 呢? ,if 语 句 后 面 的 分 号 , 使 用 if 语 句 的 其 他 注 意 事 项 ,switch case 组 合 , 不 要 拿 青 龙 偃 月 刀 去 削 苹 果 ,case 关 键 字 后 面 的 值 有 什 么 要 求 吗? ,case 语 句 的 排 列 顺 序 , 使 用 case 语 句 的 其 他 注 意 事 项 ,do while for 关 键 字 ,break 与 continue 的 区 别 , 循 环 语 句 的 注 意 点 ,goto 关 键 字 ,void 关 键 字 ,void a? ,return 关 键 字 ,const 关 键 字 也 许 该 被 替 换 为 readolny , 节 省 空 间, 避 免 不 必 要 的 内 存 分 配, 同 时 提 高 效 率 , 最 易 变 的 关 键 字 ----volatile , 最 会 带 帽 子 的 关 键 字 ----extern ,struct 关 键 字 , 空 结 构 体 多 大? , 柔 性 数 组 ,struct 与 class 的 区 别 ,union 关 键 字...40
6 1.15.1, 大 小 端 模 式 对 union 类 型 数 据 的 影 响 , 如 何 用 程 序 确 认 当 前 系 统 的 存 储 模 式? ,enum 关 键 字 , 枚 举 类 型 的 使 用 方 法 , 枚 举 与 #define 宏 的 区 别 , 伟 大 的 缝 纫 师 ----typedef 关 键 字 , 关 于 马 甲 的 笑 话 , 历 史 的 误 会 ---- 也 许 应 该 是 typerename ,typedef 与 #define 的 区 别 ,#define a int[10] 与 typedef int a[10]; 第 二 章 符 号 , 注 释 符 号 , 几 个 似 非 而 是 的 注 释 问 题 ,y = x/*p , 怎 样 才 能 写 出 出 色 的 注 释 , 安 息 吧, 路 德 维 希. 凡. 贝 多 芬 ,windows 大 师 们 用 注 释 讨 论 天 气 问 题 , 出 色 注 释 的 基 本 要 求 , 接 续 符 和 转 义 符 , 单 引 号 双 引 号 , 逻 辑 运 算 符 , 位 运 算 符 , 左 移 和 右 移 ,0x01<<2+3 的 值 为 多 少? , 花 括 号 ,++ -- 操 作 符 ,++i+++i+++i , 贪 心 法 ,2/(-2) 的 值 是 多 少? , 运 算 符 的 优 先 级 , 运 算 符 的 优 先 级 表 , 一 些 容 易 出 错 的 优 先 级 问 题 第 三 章 预 处 理 , 宏 定 义 , 数 值 宏 常 量 , 字 符 串 宏 常 量 , 用 define 宏 定 义 注 释 符 号? , 用 define 宏 定 义 表 达 式 , 宏 定 义 中 的 空 格 ,#undef , 条 件 编 译 , 文 件 包 含 ,#error 预 处 理 ,#line 预 处 理...67
7 3.6,#pragma 预 处 理 ,#pragma pack , 为 什 么 会 有 内 存 对 齐? , 如 何 避 免 内 存 对 齐 的 影 响 , # 运 算 符 ,## 预 算 符...72 第 四 章 指 针 和 数 组 , 指 针 , 指 针 的 内 存 布 局 , * 与 防 盗 门 的 钥 匙 ,int *p = NULL 和 *p = NULL 有 什 么 区 别? , 如 何 将 数 值 存 储 到 指 定 的 内 存 地 址 , 编 译 器 的 bug? , 如 何 达 到 手 中 无 剑 胸 中 也 无 剑 的 地 步 , 数 组 , 数 组 的 内 存 布 局 , 数 组 名 a 作 为 左 值 和 右 值 的 区 别 , 指 针 与 数 组 之 间 的 恩 恩 怨 怨 , 以 指 针 的 形 式 访 问 和 以 下 标 的 形 式 访 问 , 以 指 针 的 形 式 访 问 和 以 下 标 的 形 式 访 问 指 针 , 以 指 针 的 形 式 访 问 和 以 下 标 的 形 式 访 问 数 组 ,a 和 &a 的 区 别 , 指 针 和 数 组 的 定 义 与 声 明 , 定 义 为 数 组, 声 明 为 指 针 , 定 义 为 指 针, 声 明 为 数 组 , 指 针 和 数 组 的 对 比 , 指 针 数 组 和 数 组 指 针 , 指 针 数 组 和 数 组 指 针 的 内 存 布 局 , 再 论 a 和 &a 之 间 的 区 别 , 地 址 的 强 制 转 换 , 多 维 数 组 与 多 级 指 针 , 二 维 数 组 , 假 想 中 的 二 维 数 组 布 局 , 内 存 与 尺 子 的 对 比 ,&p[4][2] - &a[4][2] 的 值 为 多 少? , 二 级 指 针 , 二 级 指 针 的 内 存 布 局 , 数 组 参 数 与 指 针 参 数 , 一 维 数 组 参 数 , 能 否 向 函 数 传 递 一 个 数 组? , 无 法 向 函 数 传 递 一 个 数 组 , 一 级 指 针 参 数 , 能 否 把 指 针 变 量 本 身 传 递 给 一 个 函 数 , 无 法 把 指 针 变 量 本 身 传 递 给 一 个 函 数... 98
8 4.6.3, 二 维 数 组 参 数 与 二 维 指 针 参 数 , 函 数 指 针 , 函 数 指 针 的 定 义 , 函 数 指 针 的 使 用 , 函 数 指 针 使 用 的 例 子 ,*(int*)&p ---- 这 是 什 么? ,(*(void(*) ())0)() 这 是 什 么? , 函 数 指 针 数 组 , 函 数 指 针 数 组 的 指 针 第 五 章 内 存 管 理 , 什 么 是 野 指 针 , 栈 堆 和 静 态 区 , 常 见 的 内 存 错 误 及 对 策 , 指 针 没 有 指 向 一 块 合 法 的 内 存 , 结 构 体 成 员 指 针 未 初 始 化 , 没 有 为 结 构 体 指 针 分 配 足 够 的 内 存 , 函 数 的 入 口 校 验 , 为 指 针 分 配 的 内 存 太 小 , 内 存 分 配 成 功, 但 并 未 初 始 化 , 内 存 越 界 , 内 存 泄 漏 , 告 老 还 乡 求 良 田 , 如 何 使 用 malloc 函 数 , 用 malloc 函 数 申 请 0 字 节 内 存 , 内 存 释 放 , 内 存 释 放 之 后 , 内 存 已 经 被 释 放 了, 但 是 继 续 通 过 指 针 来 使 用 第 六 章 函 数 , 函 数 的 由 来 与 好 处 , 编 码 风 格 , 函 数 设 计 的 一 般 原 则 和 技 巧 , 函 数 递 归 , 一 个 简 单 但 易 出 错 的 递 归 例 子 , 不 使 用 任 何 变 量 编 写 strlen 函 数 第 七 章 文 件 结 构 , 文 件 内 容 的 一 般 规 则 , 文 件 名 命 名 的 规 则...130
9 第 一 章 关 键 字 每 次 讲 关 键 字 之 前, 我 总 是 问 学 生 :C 语 言 有 多 少 个 关 键 字?sizeof 怎 么 用? 它 是 函 数 吗? 有 些 学 生 不 知 道 C 语 言 有 多 少 个 关 键 字, 大 多 数 学 生 往 往 告 诉 我 sizeof 是 函 数, 因 为 它 后 面 跟 着 一 对 括 号 当 投 影 仪 把 这 32 个 关 键 字 投 到 幕 布 上 时, 很 多 学 生 表 情 惊 讶 有 些 关 键 字 从 来 没 见 过, 有 的 惊 讶 C 语 言 关 键 字 竟 有 32 个 之 多 更 有 甚 者, 说 大 学 老 师 告 诉 他 们 sizeof 是 函 数, 没 想 到 它 居 然 是 关 键 字! 由 此 可 想 而 知, 大 学 的 计 算 机 教 育 是 多 么 失 败! 表 (1.1)C 语 言 标 准 定 义 的 32 个 关 键 字 关 键 字 意 义 auto int double long char float short signed unsigned struct union enum static switch case default break register const volatile 声 明 自 动 变 量, 缺 省 时 编 译 器 一 般 默 认 为 auto 声 明 整 型 变 量 声 明 双 精 度 变 量 声 明 长 整 型 变 量 声 明 字 符 型 变 量 声 明 浮 点 型 变 量 声 明 短 整 型 变 量 声 明 有 符 号 类 型 变 量 声 明 无 符 号 类 型 变 量 声 明 结 构 体 变 量 声 明 联 合 数 据 类 型 声 明 枚 举 类 型 声 明 静 态 变 量 用 于 开 关 语 句 开 关 语 句 分 支 开 关 语 句 中 的 其 他 分 支 跳 出 当 前 循 环 声 明 寄 存 器 变 量 声 明 只 读 变 量 说 明 变 量 在 程 序 执 行 中 可 被 隐 含 地 改 变 typedef 用 以 给 数 据 类 型 取 别 名 ( 当 然 还 有 其 他 作 用 )
10 extern 声 明 变 量 是 在 其 他 文 件 正 声 明 ( 也 可 以 看 做 是 引 用 变 量 ) return 子 程 序 返 回 语 句 ( 可 以 带 参 数, 也 可 不 带 参 数 ) void continue do while if 声 明 函 数 无 返 回 值 或 无 参 数, 声 明 空 类 型 指 针 结 束 当 前 循 环, 开 始 下 一 轮 循 环 循 环 语 句 的 循 环 体 循 环 语 句 的 循 环 条 件 条 件 语 句 else 条 件 语 句 否 定 分 支 ( 与 if 连 用 ) for 一 种 循 环 语 句 ( 可 意 会 不 可 言 传 ) goto sizeof 无 条 件 跳 转 语 句 计 算 对 象 所 占 内 存 空 间 大 小 下 面 的 篇 幅 就 一 一 讲 解 这 些 关 键 字 但 在 讲 解 之 前 先 明 确 两 个 概 念 : 什 么 是 定 义? 什 么 是 声 明? 它 们 有 何 区 别? 举 个 例 子 : A)int i; B)extern int i;( 关 于 extern, 后 面 解 释 ) 哪 个 是 定 义? 哪 个 是 声 明? 或 者 都 是 定 义 或 者 都 是 声 明? 我 所 教 过 的 学 生 几 乎 没 有 一 人 能 回 答 上 这 个 问 题 这 个 十 分 重 要 的 概 念 在 大 学 里 从 来 没 有 被 提 起 过! 什 么 是 定 义 : 所 谓 的 定 义 就 是 ( 编 译 器 ) 创 建 一 个 对 象, 为 这 个 对 象 分 配 一 块 内 存 并 给 它 取 上 一 个 名 字, 这 个 名 字 就 是 我 们 经 常 所 说 的 变 量 名 或 对 象 名 但 注 意, 这 个 名 字 一 旦 和 这 块 内 存 匹 配 起 来 ( 可 以 想 象 是 这 个 名 字 嫁 给 了 这 块 空 间, 没 有 要 彩 礼 啊 ^_^), 它 们 就 同 生 共 死, 终 生 不 离 不 弃 并 且 这 块 内 存 的 位 置 也 不 能 被 改 变 一 个 变 量 或 对 象 在 一 定 的 区 域 内 ( 比 如 函 数 内, 全 局 等 ) 只 能 被 定 义 一 次, 如 果 定 义 多 次, 编 译 器 会 提 示 你 重 复 定 义 同 一 个 变 量 或 对 象 什 么 是 声 明 : 有 两 重 含 义, 如 下 : 第 一 重 含 义 : 告 诉 编 译 器, 这 个 名 字 已 经 匹 配 到 一 块 内 存 上 了 ( 伊 人 已 嫁, 吾 将 何 去 何 从? 何 以 解 忧, 唯 有 稀 粥 ), 下 面 的 代 码 用 到 变 量 或 对 象 是 在 别 的 地 方 定 义 的 声 明 可 以 出 现 多 次 第 二 重 含 义 : 告 诉 编 译 器, 我 这 个 名 字 我 先 预 定 了, 别 的 地 方 再 也 不 能 用 它 来 作 为 变 量 名 或 对 象 名 比 如 你 在 图 书 馆 自 习 室 的 某 个 座 位 上 放 了 一 本 书, 表 明 这 个 座 位 已 经 有 人 预 订, 别 人 再 也 不 允 许 使 用 这 个 座 位 其 实 这 个 时 候 你 本 人 并 没 有 坐 在 这 个 座 位 上 这 种 声 明 最 典 型 的 例 子 就 是 函 数 参 数 的 声 明, 例 如 : void fun(int i, char c); 好, 这 样 一 解 释, 我 们 可 以 很 清 楚 的 判 断 :A) 是 定 义 ;B) 是 声 明 那 他 们 的 区 别 也 很 清 晰 了 记 住, 定 义 声 明 最 重 要 的 区 别 : 定 义 创 建 了 对 象 并 为 这 个
11 对 象 分 配 了 内 存, 声 明 没 有 分 配 内 存 ( 一 个 抱 伊 人, 一 个 喝 稀 粥 ^_^) 1.1, 最 宽 恒 大 量 的 关 键 字 ----auto auto: 它 很 宽 恒 大 量 的, 你 就 当 它 不 存 在 吧 编 译 器 在 默 认 的 缺 省 情 况 下, 所 有 变 量 都 是 auto 的 1.2, 最 快 的 关 键 字 ---- register register: 这 个 关 键 字 请 求 编 译 器 尽 可 能 的 将 变 量 存 在 CPU 内 部 寄 存 器 中 而 不 是 通 过 内 存 寻 址 访 问 以 提 高 效 率 注 意 是 尽 可 能, 不 是 绝 对 你 想 想, 一 个 CPU 的 寄 存 器 也 就 那 么 几 个 或 几 十 个, 你 要 是 定 义 了 很 多 很 多 register 变 量, 它 累 死 也 可 能 不 能 全 部 把 这 些 变 量 放 入 寄 存 器 吧, 轮 也 可 能 轮 不 到 你 1.2.1, 皇 帝 身 边 的 小 太 监 ---- 寄 存 器 不 知 道 什 么 是 寄 存 器? 那 见 过 太 监 没 有? 没 有? 其 实 我 也 没 有 没 见 过 不 要 紧, 见 过 就 麻 烦 大 了 ^_^, 大 家 都 看 过 古 装 戏, 那 些 皇 帝 们 要 阅 读 奏 章 的 时 候, 大 臣 总 是 先 将 奏 章 交 给 皇 帝 旁 边 的 小 太 监, 小 太 监 呢 再 交 给 皇 帝 同 志 处 理 这 个 小 太 监 只 是 个 中 转 站, 并 无 别 的 功 能 好, 那 我 们 再 联 想 到 我 们 的 CPU CPU 不 就 是 我 们 的 皇 帝 同 志 么? 大 臣 就 相 当 于 我 们 的 内 存, 数 据 从 他 这 拿 出 来 那 小 太 监 就 是 我 们 的 寄 存 器 了 ( 这 里 先 不 考 虑 CPU 的 高 速 缓 存 区 ) 数 据 从 内 存 里 拿 出 来 先 放 到 寄 存 器, 然 后 CPU 再 从 寄 存 器 里 读 取 数 据 来 处 理, 处 理 完 后 同 样 把 数 据 通 过 寄 存 器 存 放 到 内 存 里,CPU 不 直 接 和 内 存 打 交 道 这 里 要 说 明 的 一 点 是 : 小 太 监 是 主 动 的 从 大 臣 手 里 接 过 奏 章, 然 后 主 动 的 交 给 皇 帝 同 志, 但 寄 存 器 没 这 么 自 觉, 它 从 不 主 动 干 什 么 事 一 个 皇 帝 可 能 有 好 些 小 太 监, 那 么 一 个 CPU 也 可 以 有 很 多 寄 存 器, 不 同 型 号 的 CPU 拥 有 寄 存 器 的 数 量 不 一 样 为 啥 要 这 么 麻 烦 啊? 速 度! 就 是 因 为 速 度 寄 存 器 其 实 就 是 一 块 一 块 小 的 存 储 空 间, 只 不 过 其 存 取 速 度 要 比 内 存 快 得 多 进 水 楼 台 先 得 月 嘛, 它 离 CPU 很 近,CPU 一 伸 手 就 拿 到 数 据 了, 比 在 那 么 大 的 一 块 内 存 里 去 寻 找 某 个 地 址 上 的 数 据 是 不 是 快 多 了? 那 有 人 问 既 然 它 速 度 那 么 快, 那 我 们 的 内 存 硬 盘 都 改 成 寄 存 器 得 了 呗 我 要 说 的 是 : 你 真 有 钱! 1.2.2, 使 用 register 修 饰 符 的 注 意 点 虽 然 寄 存 器 的 速 度 非 常 快, 但 是 使 用 register 修 饰 符 也 有 些 限 制 的 :register 变 量 必 须 是 能 被 CPU 寄 存 器 所 接 受 的 类 型 意 味 着 register 变 量 必 须 是 一 个 单 个 的 值, 并 且 其 长 度 应 小 于 或 等 于 整 型 的 长 度 而 且 register 变 量 可 能 不 存 放 在 内 存 中, 所 以 不 能 用 取 址 运 算 符 & 来 获 取 register 变 量 的 地 址
12 1.3, 最 名 不 符 实 的 关 键 字 ----static 不 要 误 以 为 关 键 字 static 很 安 静, 其 实 它 一 点 也 不 安 静 这 个 关 键 字 在 C 语 言 里 主 要 有 两 个 作 用,C++ 对 它 进 行 了 扩 展 1.3.1, 修 饰 变 量 第 一 个 作 用 : 修 饰 变 量 变 量 又 分 为 局 部 和 全 局 变 量, 但 它 们 都 存 在 内 存 的 静 态 区 静 态 全 局 变 量, 作 用 域 仅 限 于 变 量 被 定 义 的 文 件 中, 其 他 文 件 即 使 用 extern 声 明 也 没 法 使 用 他 准 确 地 说 作 用 域 是 从 定 义 之 处 开 始, 到 文 件 结 尾 处 结 束, 在 定 义 之 处 前 面 的 那 些 代 码 行 也 不 能 使 用 它 想 要 使 用 就 得 在 前 面 再 加 extern *** 恶 心 吧? 要 想 不 恶 心, 很 简 单, 直 接 在 文 件 顶 端 定 义 不 就 得 了 静 态 局 部 变 量, 在 函 数 体 里 面 定 义 的, 就 只 能 在 这 个 函 数 里 用 了, 同 一 个 文 档 中 的 其 他 函 数 也 用 不 了 由 于 被 static 修 饰 的 变 量 总 是 存 在 内 存 的 静 态 区, 所 以 即 使 这 个 函 数 运 行 结 束, 这 个 静 态 变 量 的 值 还 是 不 会 被 销 毁, 函 数 下 次 使 用 时 仍 然 能 用 到 这 个 值 static int j; void fun1(void) static int i = 0; i++; void fun2(void) j=0; j++; int main() for(k=0; k<10; k++) fun1(); fun2(); return 0;
13 i 和 j 的 值 分 别 是 什 么, 为 什 么? 1.3.2, 修 饰 函 数 第 二 个 作 用 : 修 饰 函 数 函 数 前 加 static 使 得 函 数 成 为 静 态 函 数 但 此 处 static 的 含 义 不 是 指 存 储 方 式, 而 是 指 对 函 数 的 作 用 域 仅 局 限 于 本 文 件 ( 所 以 又 称 内 部 函 数 ) 使 用 内 部 函 数 的 好 处 是 : 不 同 的 人 编 写 不 同 的 函 数 时, 不 用 担 心 自 己 定 义 的 函 数, 是 否 会 与 其 它 文 件 中 的 函 数 同 名 关 键 字 static 有 着 不 寻 常 的 历 史 起 初, 在 C 中 引 入 关 键 字 static 是 为 了 表 示 退 出 一 个 块 后 仍 然 存 在 的 局 部 变 量 随 后,static 在 C 中 有 了 第 二 种 含 义 : 用 来 表 示 不 能 被 其 它 文 件 访 问 的 全 局 变 量 和 函 数 为 了 避 免 引 入 新 的 关 键 字, 所 以 仍 使 用 static 关 键 字 来 表 示 这 第 二 种 含 义 当 然,C++ 里 对 static 赋 予 了 第 三 个 作 用, 这 里 先 不 讨 论, 有 兴 趣 的 可 以 找 相 关 资 料 研 究 1.4, 基 本 数 据 类 型 ----short int long char float double 短 整 型 short C 语 言 包 含 的 数 据 类 型 如 下 图 所 示 : 整 整 型 int 型 数 值 类 型 长 整 型 long 单 精 度 型 float 基 本 类 型 浮 点 型 字 符 类 型 char 双 精 度 型 double C 数 据 类 型 构 造 类 型 指 针 类 型 数 组 结 构 体 struct 共 用 体 union 枚 举 类 型 enum 空 类 型 void
14 1.4.1, 数 据 类 型 与 模 子 short int long char float double 这 六 个 关 键 字 代 表 C 语 言 里 的 六 种 基 本 数 据 类 型 怎 么 去 理 解 它 们 呢? 举 个 例 子 : 见 过 藕 煤 球 的 那 个 东 西 吧?( 没 见 过? 煤 球 总 见 过 吧 ) 那 个 东 西 叫 藕 煤 器, 拿 着 它 在 和 好 的 煤 堆 里 这 么 一 咔, 一 个 煤 球 出 来 了 半 径 12cm,12 个 孔 不 同 型 号 的 藕 煤 器 咔 出 来 的 煤 球 大 小 不 一 样, 孔 数 也 不 一 样 这 个 藕 煤 器 其 实 就 是 个 模 子 现 在 我 们 联 想 一 下,short int long char float double 这 六 个 东 东 是 不 是 很 像 不 同 类 型 的 藕 煤 器 啊? 拿 着 它 们 在 内 存 上 咔 咔 咔, 不 同 大 小 的 内 存 就 分 配 好 了, 当 然 别 忘 了 给 它 们 取 个 好 听 的 名 字 在 32 位 的 系 统 上 short 咔 出 来 的 内 存 大 小 是 2 个 byte;int 咔 出 来 的 内 存 大 小 是 4 个 byte;long 咔 出 来 的 内 存 大 小 是 4 个 byte;float 咔 出 来 的 内 存 大 小 是 4 个 byte; double 咔 出 来 的 内 存 大 小 是 8 个 byte;char 咔 出 来 的 内 存 大 小 是 1 个 byte ( 注 意 这 里 指 一 般 情 况, 可 能 不 同 的 平 台 还 会 有 所 不 同, 具 体 平 台 可 以 用 sizeof 关 键 字 测 试 一 下 ) 很 简 单 吧? 咔 咔 咔 很 爽 吧? 是 很 简 单, 也 确 实 很 爽, 但 问 题 就 是 你 咔 出 来 这 么 多 内 存 块, 你 总 不 能 给 他 取 名 字 叫 做 x1,x2,x3,x4,x5 或 者 长 江 1 号, 长 江 2 号 吧 它 们 长 得 这 么 像 ( 不 是 你 家 的 老 大, 老 二, 老 三 ), 过 一 阵 子 你 就 会 忘 了 到 底 哪 个 名 字 和 哪 个 内 存 块 匹 配 了 ( 到 底 谁 嫁 给 谁 了 啊?^_^) 所 以 呢, 给 他 们 取 一 个 好 的 名 字 绝 对 重 要 下 面 我 们 就 来 研 究 研 究 取 什 么 样 的 名 字 好 1.4.2, 变 量 的 命 名 规 则 一 般 规 则 : 规 则 1-1 命 名 应 当 直 观 且 可 以 拼 读, 可 望 文 知 意, 便 于 记 忆 和 阅 读 标 识 符 最 好 采 用 英 文 单 词 或 其 组 合, 不 允 许 使 用 拼 音 程 序 中 的 英 文 单 词 一 般 不 要 太 复 杂, 用 词 应 当 准 确 规 则 1-2 命 名 的 长 度 应 当 符 合 min-length && max-information 原 则 C 是 一 种 简 洁 的 语 言, 命 名 也 应 该 是 简 洁 的 例 如 变 量 名 MaxVal 就 比 MaxValueUntilOverflow 好 用 标 识 符 的 长 度 一 般 不 要 过 长, 较 长 的 单 词 可 通 过 去 掉 元 音 形 成 缩 写 另 外, 英 文 词 尽 量 不 缩 写, 特 别 是 非 常 用 专 业 名 词, 如 果 有 缩 写, 在 同 一 系 统 中 对 同 一 单 词 必 须 使 用 相 同 的 表 示 法, 并 且 注 明 其 意 思 规 则 1-3 当 标 识 符 由 多 个 词 组 成 时, 每 个 词 的 第 一 个 字 母 大 写, 其 余 全 部 小 写 比 如 : int CurrentVal; 这 样 的 名 字 看 起 来 比 较 清 晰, 远 比 一 长 串 字 符 好 得 多 规 则 1-4 尽 量 避 免 名 字 中 出 现 数 字 编 号, 如 Value1,Value2 等, 除 非 逻 辑 上 的 确 需 要 编 号 比 如 驱 动 开 发 时 为 管 脚 命 名, 非 编 号 名 字 反 而 不 好 初 学 者 总 是 喜 欢 用 带 编 号 的 变 量 名 或 函 数 名, 这 样 子 看 上 去 很 简 单 方 便, 但 其 实 是 一 颗 颗 定 时 炸 弹 这 个 习 惯 初 学 者 一 定 要 改 过 来
15 规 则 1-5 对 在 多 个 文 件 之 间 共 同 使 用 的 全 局 变 量 或 函 数 要 加 范 围 限 定 符 ( 建 议 使 用 模 块 名 ( 缩 写 ) 作 为 范 围 限 定 符 ) (GUI_,etc) 标 识 符 的 命 名 规 则 : 规 则 1-6 标 识 符 名 分 为 两 部 分 : 规 范 标 识 符 前 缀 ( 后 缀 )+ 含 义 标 识 非 全 局 变 量 可 以 不 用 使 用 范 围 限 定 符 前 缀 规 则 1-7 作 用 域 前 缀 命 名 规 则 No. 标 识 符 类 型 作 用 域 前 缀 1 GlobalVariable g 2 File Static Variable(native) n 3 Function Static Variable f 4 AutoVariable a 5 GlobalFunction g 6 Static Function n 规 则 1-8 数 据 类 型 前 缀 命 名 规 则 No. Prefix Suffix Data Type Example Remark 1 bt bit Bit btvariable; 2 b boolean boolean bvariable; 3 c char char cvariable; 4 i int int ivariable; 5 s short[int] short[int] svariable; 6 l long[int] long[int] lvariable; 7 u unsigned[int] unsigned[int] uivariable; 8 d double double dvariable; 9 f float float fvariable;
16 10 p pointer void *vpvariable; 指 针 前 缀 11 v void void vvariable; 13 st enum enum A stvariable; 14 st struct struct A stvariable; 15 st union union A stvariable; 16 fp function point void(* fpgetmodefunclist_a[])( void ) 17 _a array of char cvariable_a[table_max]; 当 自 定 义 typedef struct SM_EventOpt 结 构 数 据 类 型 时 使 18 _st _pst typedef enum/struct/u nion unsigned char unsigned int char 用 _st 后 缀 ; 当 自 定 义 结 构 数 据 类 型 为 指 SM_EventOpt_st,*SM_EventOpt_pst; 针 类 型 时 使 用 _pst 后 缀 ; 规 则 1-9 含 义 标 识 命 名 规 则, 变 量 命 名 使 用 名 词 性 词 组, 函 数 命 名 使 用 动 词 性 词 组 例 如 : No 变 量 名 目 标 词 动 词 ( 的 过 去 分 词 ) 状 语 目 的 地 含 义 1 DataGotFromSD Data Got From SD 从 SD 中 取 得 的 数 据 2 DataDeletedFromSD Data Deleted From SD 从 SD 中 删 除 的 数 据 变 量 含 义 标 识 符 构 成 : 目 标 词 + 动 词 ( 的 过 去 分 词 )+ [ 状 语 ]+[ 目 的 地 ]; N o 变 量 名 动 词 ( 一 般 现 时 ) 目 标 词 状 语 目 的 地 含 义 1 GetDataFromSD Get Data From SD 从 SD 中 取 得 数 据 2 DeleteDataFromSD Delete Data From SD 从 SD 中 删 除 数 据
17 函 数 含 义 标 识 符 构 成 : 动 词 ( 一 般 现 时 )+ 目 标 词 +[ 状 语 ]+[ 目 的 地 ]; 规 则 1-10 程 序 中 不 得 出 现 仅 靠 大 小 写 区 分 的 相 似 的 标 识 符 例 如 :int x, X; 变 量 x 与 X 容 易 混 淆 void foo(int x); 函 数 foo 与 FOO 容 易 混 淆 void FOO(float x); 这 里 还 有 一 个 要 特 别 注 意 的 就 是 1( 数 字 1) 和 l( 小 写 字 母 l) 之 间,0( 数 字 0) 和 o ( 小 写 字 母 o) 之 间 的 区 别 这 两 对 真 是 很 难 区 分 的, 我 曾 经 的 一 个 同 事 就 被 这 个 问 题 折 腾 了 一 次 规 则 1-11 一 个 函 数 名 禁 止 被 用 于 其 它 之 处 例 如 : #include "c_standards.h" void foo(int p_1) int x = p_1; void static_p(void) int foo = 1u; 规 则 1-12 所 有 宏 定 义 枚 举 常 数 只 读 变 量 全 用 大 写 字 母 命 名, 用 下 划 线 分 割 单 词 例 如 : const int MAX_LENGTH = 100; // 这 不 是 常 量, 而 是 一 个 只 读 变 量, 具 体 请 往 后 看 #define FILE_PATH /usr/tmp 规 则 1-13 考 虑 到 习 惯 性 问 题, 局 部 变 量 中 可 采 用 通 用 的 命 名 方 式, 仅 限 于 n i j 等 作 为 循 环 变 量 使 用 一 定 不 要 写 出 如 下 这 样 的 代 码 : int p; char i; int c; char * a;
18 一 般 来 说 习 惯 上 用 n,m,i,j,k 等 表 示 int 类 型 的 变 量 ;c,ch 等 表 示 字 符 类 型 变 量 ;a 等 表 示 数 组 ;p 等 表 示 指 针 当 然 这 仅 仅 是 一 般 习 惯, 除 了 i,j,k 等 可 以 用 来 表 示 循 环 变 量 外, 别 的 字 符 变 量 名 尽 量 不 要 使 用 规 则 1-14 定 义 变 量 的 同 时 千 万 千 万 别 忘 了 初 始 化 定 义 变 量 时 编 译 器 并 不 一 定 清 空 了 这 块 内 存, 它 的 值 可 能 是 无 效 的 数 据 这 个 问 题 在 内 存 管 理 那 章 有 非 常 详 细 的 讨 论, 请 参 看 规 则 1-15 不 同 类 型 数 据 之 间 的 运 算 要 注 意 精 度 扩 展 问 题, 一 般 低 精 度 数 据 将 向 高 精 度 数 据 扩 展 1.5, 最 冤 枉 的 关 键 字 ----sizeof 1.5.1, 常 年 被 人 误 认 为 函 数 sizeof 是 关 键 字 不 是 函 数, 其 实 就 算 不 知 道 它 是 否 为 32 个 关 键 字 之 一 时, 我 们 也 可 以 借 助 编 译 器 确 定 它 的 身 份 看 下 面 的 例 子 : int i=0; A),sizeof(int); B),sizeof(i); C),sizeof int; D),sizeof i; 毫 无 疑 问,32 位 系 统 下 A),B) 的 值 为 4 那 C) 的 呢?D) 的 呢? 在 32 位 系 统 下, 通 过 Visual C++6.0 或 任 意 一 编 译 器 调 试, 我 们 发 现 D) 的 结 果 也 为 4 咦?sizeof 后 面 的 括 号 呢? 没 有 括 号 居 然 也 行, 那 想 想, 函 数 名 后 面 没 有 括 号 行 吗? 由 此 轻 易 得 出 sizeof 绝 非 函 数 好, 再 看 C) 编 译 器 怎 么 怎 么 提 示 出 错 呢? 不 是 说 sizeof 是 个 关 键 字, 其 后 面 的 括 号 可 以 没 有 么? 那 你 想 想 sizeof int 表 示 什 么 啊?int 前 面 加 一 个 关 键 字? 类 型 扩 展? 明 显 不 正 确, 我 们 可 以 在 int 前 加 unsigned,const 等 关 键 字 但 不 能 加 sizeof 好, 记 住 :sizeof 在 计 算 变 量 所 占 空 间 大 小 时, 括 号 可 以 省 略, 而 计 算 类 型 ( 模 子 ) 大 小 时 不 能 省 略 一 般 情 况 下, 咱 也 别 偷 这 个 懒, 乖 乖 的 写 上 括 号, 继 续 装 作 一 个 函 数, 做 一 个 披 着 函 数 皮 的 关 键 字 做 我 的 关 键 字, 让 人 家 认 为 是 函 数 去 吧 1.5.2,sizeof(int)*p 表 示 什 么 意 思? sizeof(int)*p 表 示 什 么 意 思? 留 几 个 问 题 ( 讲 解 指 针 与 数 组 时 会 详 细 讲 解 ),32 位 系 统 下 : int *p = NULL; sizeof(p) 的 值 是 多 少? sizeof(*p) 呢?
19 int a[100]; sizeof (a) 的 值 是 多 少? sizeof(a[100]) 呢?// 请 尤 其 注 意 本 例 sizeof(&a) 呢? sizeof(&a[0]) 呢? int b[100]; void fun(int b[100]) sizeof(b);// sizeof (b) 的 值 是 多 少? 1.4,signed unsigned 关 键 字 我 们 知 道 计 算 机 底 层 只 认 识 0 1. 任 何 数 据 到 了 底 层 都 会 变 计 算 转 换 成 0 1. 那 负 数 怎 么 存 储 呢? 肯 定 这 个 - 号 是 无 法 存 入 内 存 的, 怎 么 办? 很 好 办, 做 个 标 记 把 基 本 数 据 类 型 的 最 高 位 腾 出 来, 用 来 存 符 号, 同 时 约 定 如 下 : 最 高 位 如 果 是 1, 表 明 这 个 数 是 负 数, 其 值 为 除 最 高 位 以 外 的 剩 余 位 的 值 添 上 这 个 - 号 ; 如 果 最 高 位 是 0, 表 明 这 个 数 是 正 数, 其 值 为 除 最 高 位 以 外 的 剩 余 位 的 值 这 样 的 话, 一 个 32 位 的 signed int 类 型 整 数 其 值 表 示 法 范 围 为 :- 2 ~ 2-1;8 位 的 7 7 char 类 型 数 其 值 表 示 的 范 围 为 - 2 ~ 2-1 一 个 32 位 的 unsigned int 类 型 整 数 其 值 表 示 法 32 8 范 围 为 :0~ 2-1;8 位 的 char 类 型 数 其 值 表 示 的 范 围 为 0~ 2-1 同 样 我 们 的 signed 关 键 字 也 很 宽 恒 大 量, 你 也 可 以 完 全 当 它 不 存 在, 编 译 器 缺 省 默 认 情 况 下 数 据 为 signed 类 型 的 上 面 的 解 释 很 容 易 理 解, 下 面 就 考 虑 一 下 这 个 问 题 : int main() char a[1000]; int i; for(i=0; i<1000; i++) a[i] = -1-i; printf("%d",strlen(a)); return 0;
20 此 题 看 上 去 真 的 很 简 单, 但 是 却 鲜 有 人 答 对 答 案 是 255 别 惊 讶, 我 们 先 分 析 分 析 for 循 环 内, 当 i 的 值 为 0 时,a[0] 的 值 为 -1 关 键 就 是 -1 在 内 存 里 面 如 何 存 储 我 们 知 道 在 计 算 机 系 统 中, 数 值 一 律 用 补 码 来 表 示 ( 存 储 ) 主 要 原 因 是 使 用 补 码, 可 以 将 符 号 位 和 其 它 位 统 一 处 理 ; 同 时, 减 法 也 可 按 加 法 来 处 理 另 外, 两 个 用 补 码 表 示 的 数 相 加 时, 如 果 最 高 位 ( 符 号 位 ) 有 进 位, 则 进 位 被 舍 弃 正 数 的 补 码 与 其 原 码 一 致 ; 负 数 的 补 码 : 符 号 位 为 1, 其 余 位 为 该 数 绝 对 值 的 原 码 按 位 取 反, 然 后 整 个 数 加 1 按 照 负 数 补 码 的 规 则, 可 以 知 道 -1 的 补 码 为 0xff,-2 的 补 码 为 0xfe 当 i 的 值 为 127 时,a[127] 的 值 为 -128, 而 -128 是 char 类 型 数 据 能 表 示 的 最 小 的 负 数 当 i 继 续 增 加,a[128] 的 值 肯 定 不 能 是 -129 因 为 这 时 候 发 生 了 溢 出,-129 需 要 9 位 才 能 存 储 下 来, 而 char 类 型 数 据 只 有 8 位, 所 以 最 高 位 被 丢 弃 剩 下 的 8 位 是 原 来 9 位 补 码 的 低 8 位 的 值, 即 0x7f 当 i 继 续 增 加 到 255 的 时 候,-256 的 补 码 的 低 8 位 为 0 然 后 当 i 增 加 到 256 时,-257 的 补 码 的 低 8 位 全 为 1, 即 低 八 位 的 补 码 为 0xff, 如 此 又 开 始 一 轮 新 的 循 环 按 照 上 面 的 分 析,a[0] 到 a[254] 里 面 的 值 都 不 为 0, 而 a[255] 的 值 为 0 strlen 函 数 是 计 算 字 符 串 长 度 的, 并 不 包 含 字 符 串 最 后 的 \0 而 判 断 一 个 字 符 串 是 否 结 束 的 标 志 就 是 看 是 否 遇 到 \0 如 果 遇 到 \0, 则 认 为 本 字 符 串 结 束 分 析 到 这 里,strlen(a) 的 值 为 255 应 该 完 全 能 理 解 了 这 个 问 题 的 关 键 就 是 要 明 白 char 类 型 默 认 情 况 下 是 有 符 号 的, 其 表 示 的 值 的 范 围 为 [-128,127], 超 出 这 个 范 围 的 值 会 产 生 溢 出 另 外 还 要 清 楚 的 就 是 负 数 的 补 码 怎 么 表 示 弄 明 白 了 这 两 点, 这 个 问 题 其 实 就 很 简 单 了 留 三 个 问 题 : 1), 按 照 我 们 上 面 的 解 释, 那 -0 和 +0 在 内 存 里 面 分 别 怎 么 存 储? 2),int i = -20; unsigned j = 10; i+j 的 值 为 多 少? 为 什 么? 3), 下 面 的 代 码 有 什 么 问 题? unsigned i ; for (i=9;i>=0;i--) printf("%u\n",i); 1.6,if else 组 合 if 语 句 很 简 单 吧 嗯, 的 确 很 简 单 那 我 们 就 简 单 的 看 下 面 几 个 简 单 的 问 题 : 1.6.1,bool 变 量 与 零 值 进 行 比 较 bool 变 量 与 零 值 进 行 比 较 的 if 语 句 怎 么 写?
21 bool btestflag = FALSE;// 想 想 为 什 么 一 般 初 始 化 为 FALSE 比 较 好? A), if(btestflag == 0); if(btestflag == 1); B), if(btestflag == TRUE); C), if(btestflag); if(btestflag == FLASE); if(!btestflag); 哪 一 组 或 是 那 些 组 正 确 呢? 我 们 来 分 析 分 析 : A) 写 法 :btestflag 是 什 么? 整 型 变 量? 如 果 要 不 是 这 个 名 字 遵 照 了 前 面 的 命 名 规 范, 肯 怕 很 容 易 让 人 误 会 成 整 型 变 量 所 以 这 种 写 法 不 好 B) 写 法 :FLASE 的 值 大 家 都 知 道, 在 编 译 器 里 被 定 义 为 0; 但 TRUE 的 值 呢? 都 是 1 吗? 很 不 幸, 不 都 是 1 Visual C++ 定 义 为 1, 而 它 的 同 胞 兄 弟 Visual Basic 就 把 TRUE 定 义 为 -1. 那 很 显 然, 这 种 写 法 也 不 好 大 家 都 知 道 if 语 句 是 靠 其 后 面 的 括 号 里 的 表 达 式 的 值 来 进 行 分 支 跳 转 的 表 达 式 如 果 为 真, 则 执 行 if 语 句 后 面 紧 跟 的 代 码 ; 否 则 不 执 行 那 显 然, 本 组 的 写 法 很 好, 既 不 会 引 起 误 会, 也 不 会 由 于 TRUE 或 FLASE 的 不 同 定 义 值 而 出 错 记 住 : 以 后 写 代 码 就 得 这 样 写 1.6.2, float 变 量 与 零 值 进 行 比 较 float 变 量 与 零 值 进 行 比 较 的 if 语 句 怎 么 写? float ftestval = 0.0; A), if(ftestval == 0.0); if(ftestval!= 0.0); B), if((ftestval >= -EPSINON) && (ftestval <= EPSINON)); //EPSINON 为 定 义 好 的 精 度 哪 一 组 或 是 那 些 组 正 确 呢? 我 们 来 分 析 分 析 : float 和 double 类 型 的 数 据 都 是 有 精 度 限 制 的, 这 样 直 接 拿 来 与 0.0 比, 能 正 确 吗? 明 显 不 能, 看 例 子 : 的 值 四 舍 五 入 精 确 到 小 数 点 后 10 位 为 : , 你 拿 它 减 去 然 后 再 四 舍 五 入 得 到 的 结 果 是 多 少? 你 能 说 前 后 两 个 值 一 样 吗? EPSINON 为 定 义 好 的 精 度, 如 果 一 个 数 落 在 [0.0-EPSINON,0.0+EPSINON] 这 个 闭 区 间 内, 我 们 认 为 在 某 个 精 度 内 它 的 值 与 零 值 相 等 ; 否 则 不 相 等 扩 展 一 下, 把 0.0 替 换 为 你 想 比 较 的 任 何 一 个 浮 点 数, 那 我 们 就 可 以 比 较 任 意 两 个 浮 点 数 的 大 小 了, 当 然 是 在 某 个 精 度 内 同 样 的 也 不 要 在 很 大 的 浮 点 数 和 很 小 的 浮 点 数 之 间 进 行 运 算, 比 如 : 这 样 计 算 后 的 结 果 可 能 会 让 你 大 吃 一 惊 1.6.3, 指 针 变 量 与 零 值 进 行 比 较 指 针 变 量 与 零 值 进 行 比 较 的 if 语 句 怎 么 写?
22 int*p=null;// 定 义 指 针 一 定 要 同 时 初 始 化, 指 针 与 数 组 那 章 会 详 细 讲 解 A), if(p == 0); if(p!= 0); B), if(p); if(!p); C), if(null == p); if(null!= p); 哪 一 组 或 是 那 些 组 正 确 呢? 我 们 来 分 析 分 析 : A) 写 法 :p 是 整 型 变 量? 容 易 引 起 误 会, 不 好 尽 管 NULL 的 值 和 0 一 样, 但 意 义 不 同 B) 写 法 :p 是 bool 型 变 量? 容 易 引 起 误 会, 不 好 C) 写 法 : 这 个 写 法 才 是 正 确 的, 但 样 子 比 较 古 怪 为 什 么 要 这 么 写 呢? 是 怕 漏 写 一 个 = 号 :if(p = NULL), 这 个 表 达 式 编 译 器 当 然 会 认 为 是 正 确 的, 但 却 不 是 你 要 表 达 的 意 思 所 以, 非 常 推 荐 这 种 写 法 1.6.4,else 到 底 与 哪 个 if 配 对 呢? else 常 常 与 if 语 句 配 对, 但 要 注 意 书 写 规 范, 看 下 面 例 子 : if(0==x) if(0==y) error(); else //program code 这 个 else 到 底 与 谁 匹 配 呢? 让 人 迷 糊, 尤 其 是 初 学 者 还 好,C 语 言 有 这 样 的 规 定 :else 始 终 与 同 一 括 号 内 最 近 的 未 匹 配 的 if 语 句 结 合 虽 然 老 手 可 以 区 分 出 来, 但 这 样 的 代 码 谁 都 会 头 疼 的, 任 何 时 候 都 别 偷 这 种 懒 关 于 程 序 中 的 分 界 符 和, 建 议 如 下 : 建 议 1-16 程 序 中 的 分 界 符 和 对 齐 风 格 如 下 : 注 意 下 表 中 代 码 的 缩 进 一 般 为 4 个 字 符, 但 不 要 使 用 Tab 键, 因 为 不 同 的 编 辑 器 Tab 键 定 义 的 空 格 数 量 不 一 样, 别 的 编 辑 器 打 开 Tab 键 缩 进 的 代 码 可 能 会 一 片 混 乱 提 倡 的 的 风 格 void Function(int x) //program code 不 提 倡 的 风 格 void Function(int x) //program code if (condition) //program code if (condition) //program code else //program code
23 else 或 : //program code if (condition) //program code else //program code 或 : if (width < height) dosomething(); for (initialization; condition; update) //program code for (initialization;condition; update) //program code while (condition) //program code while (condition) //program code do do //program code //program code while (condition); while (condition); 1.6.5,if 语 句 后 面 的 分 号 关 于 if-else 语 句 还 有 一 个 容 易 出 错 的 地 方 就 是 与 空 语 句 的 连 用 看 下 面 的 例 子 : if(null!= p) ; fun(); 这 里 的 fun() 函 数 并 不 是 在 NULL!= p 的 时 候 被 调 用, 而 是 任 何 时 候 都 会 被 调 用 问 题 就 出 在 if 语 句 后 面 的 分 号 上 在 C 语 言 中, 分 号 预 示 着 一 条 语 句 的 结 尾, 但 是 并 不 是 每 条 C 语 言 语 句 都 需 要 分 号 作 为 结 束 标 志 if 语 句 的 后 面 并 不 需 要 分 号, 但 如 果 你 不 小 心 写 了 个 分 号, 编 译 器 并 不 会 提 示 出 错 因 为 编 译 器 会 把 这 个 分 号 解 析 成 一 条 空 语 句 也 就 是 上 面 的 代 码 实 际 等 效 于 : if(null!= p)
24 ; fun(); 这 是 初 学 者 很 容 易 犯 的 错 误, 往 往 不 小 心 多 写 了 个 分 号, 导 致 结 果 与 预 想 的 相 差 很 远 所 以 建 议 在 真 正 需 要 用 空 语 句 时 写 成 这 样 : NULL; 而 不 是 单 用 一 个 分 号 这 就 好 比 汇 编 语 言 里 面 的 空 指 令, 比 如 ARM 指 令 中 的 NOP 指 令 这 样 做 可 以 明 显 的 区 分 真 正 必 须 的 空 语 句 和 不 小 心 多 写 的 分 号 1.6.6, 使 用 if 语 句 的 其 他 注 意 事 项 规 则 1-17 先 处 理 正 常 情 况, 再 处 理 异 常 情 况 在 编 写 代 码 是, 要 使 得 正 常 情 况 的 执 行 代 码 清 晰, 确 认 那 些 不 常 发 生 的 异 常 情 况 处 理 代 码 不 会 遮 掩 正 常 的 执 行 路 径 这 样 对 于 代 码 的 可 读 性 和 性 能 都 很 重 要 因 为,if 语 句 总 是 需 要 做 判 断, 而 正 常 情 况 一 般 比 异 常 情 况 发 生 的 概 率 更 大 ( 否 则 就 应 该 把 异 常 正 常 调 过 来 了 ), 如 果 把 执 行 概 率 更 大 的 代 码 放 到 后 面, 也 就 意 味 着 if 语 句 将 进 行 多 次 无 谓 的 比 较 另 外, 非 常 重 要 的 一 点 是, 把 正 常 情 况 的 处 理 放 在 if 后 面, 而 不 要 放 在 else 后 面 当 然 这 也 符 合 把 正 常 情 况 的 处 理 放 在 前 面 的 要 求 规 则 1-18 确 保 if 和 else 子 句 没 有 弄 反 这 一 点 初 学 者 也 容 易 弄 错, 往 往 把 本 应 该 放 在 if 语 句 后 面 的 代 码 和 本 应 该 放 在 else 语 句 后 面 的 代 码 弄 反 了 1.7,switch case 组 合 既 然 有 了 if else 组 合 为 什 么 还 需 要 switch case 组 合 呢? 1.7.1, 不 要 拿 青 龙 偃 月 刀 去 削 苹 果 那 你 既 然 有 了 菜 刀 为 什 么 还 需 要 水 果 刀 呢? 你 总 不 能 扛 着 云 长 的 青 龙 偃 月 刀 ( 又 名 冷 艳 锯 ) 去 削 苹 果 吧 如 果 你 真 能 做 到, 关 二 爷 也 会 佩 服 你 的 ^_^ if else 一 般 表 示 两 个 分 支 或 是 嵌 套 表 示 少 量 的 分 支, 但 如 果 分 支 很 多 的 话 还 是 用 switch case 组 合 吧 其 基 本 格 式 为 : switch(variable) case Value1: //program code
25 break; case Value2: //program code break; case Value3: //program code break; default: break; 很 简 单, 但 有 两 个 规 则 : 规 则 1-19 每 个 case 语 句 的 结 尾 绝 对 不 要 忘 了 加 break, 否 则 将 导 致 多 个 分 支 重 叠 ( 除 非 有 意 使 多 个 分 支 重 叠 ) 规 则 1-20 最 后 必 须 使 用 default 分 支 即 使 程 序 真 的 不 需 要 default 处 理, 也 应 该 保 留 语 句 : default : break; 这 样 做 并 非 画 蛇 添 足, 可 以 避 免 让 人 误 以 为 你 忘 了 default 处 理 1.7.2,case 关 键 字 后 面 的 值 有 什 么 要 求 吗? 好, 再 问 问 : 真 的 就 这 么 简 单 吗? 看 看 下 面 的 问 题 : Value1 的 值 为 0.1 行 吗?-0.1 呢?-1 呢? 呢? 1+2 呢?3/2 呢? A 呢? A 呢? 变 量 i( 假 设 i 已 经 被 初 始 化 ) 呢?NULL 呢? 等 等 这 些 情 形 希 望 你 亲 自 上 机 调 试 一 下, 看 看 到 底 哪 些 行, 哪 些 不 行 记 住 :case 后 面 只 能 是 整 型 或 字 符 型 的 常 量 或 常 量 表 达 式 ( 想 想 字 符 型 数 据 在 内 存 里 是 怎 么 存 的 ) 1.7.3,case 语 句 的 排 列 顺 序 似 乎 从 来 没 有 人 考 虑 过 这 个 问 题, 也 有 很 多 人 认 为 case 语 句 的 顺 序 无 所 谓 但 事 实 却 不 是 如 此 如 果 case 语 句 很 少, 你 也 许 可 以 忽 略 这 点, 但 是 如 果 case 语 句 非 常 多, 那 就 不 得 不 好 好 考 虑 这 个 问 题 了 比 如 你 写 的 是 某 个 驱 动 程 序, 也 许 会 经 常 遇 到 几 十 个 case 语 句 的 情 况 一 般 来 说, 我 们 可 以 遵 循 下 面 的 规 则 :
26 规 则 1-21 按 字 母 或 数 字 顺 序 排 列 各 条 case 语 句 如 果 所 有 的 case 语 句 没 有 明 显 的 重 要 性 差 别, 那 就 按 A-B-C 或 等 顺 序 排 列 case 语 句 这 样 做 的 话, 你 可 以 很 容 易 的 找 到 某 条 case 语 句 比 如 : switch(variable) case A: //program code break; case B: //program code break; case C: //program code break; default: break; 规 则 1-22 把 正 常 情 况 放 在 前 面, 而 把 异 常 情 况 放 在 后 面 如 果 有 多 个 正 常 情 况 和 异 常 情 况, 把 正 常 情 况 放 在 前 面, 并 做 好 注 释 ; 把 异 常 情 况 放 在 后 面, 同 样 要 做 注 释 比 如 : switch(variable) /////////////////////////////////////////////////////////////////////////////////// // 正 常 情 况 开 始 case A: //program code break; case B: //program code break; // 正 常 情 况 结 束 //////////////////////////////////////////////////////////////////////////////////////
27 // 异 常 情 况 开 始 case -1: //program code break; // 异 常 情 况 结 束 ////////////////////////////////////////////////////////////////////////////////////// default: break; 规 则 1-23 按 执 行 频 率 排 列 case 语 句 把 最 常 执 行 的 情 况 放 在 前 面, 而 把 最 不 常 执 行 的 情 况 放 在 后 面 最 常 执 行 的 代 码 可 能 也 是 调 试 的 时 候 要 单 步 执 行 的 最 多 的 代 码 如 果 放 在 后 面 的 话, 找 起 来 可 能 会 比 较 困 难, 而 放 在 前 面 的 话, 可 以 很 快 的 找 到 1.7.4, 使 用 case 语 句 的 其 他 注 意 事 项 规 则 1-24 简 化 每 种 情 况 对 应 的 操 作 使 得 与 每 种 情 况 相 关 的 代 码 尽 可 能 的 精 炼 case 语 句 后 面 的 代 码 越 精 炼,case 语 句 的 结 果 就 会 越 清 晰 你 想 想, 如 果 case 语 句 后 面 的 代 码 整 个 屏 幕 都 放 不 下, 这 样 的 代 码 谁 也 难 看 得 很 清 晰 吧 如 果 某 个 case 语 句 确 实 需 要 这 么 多 的 代 码 来 执 行 某 个 操 作, 那 可 以 把 这 些 操 作 写 成 一 个 或 几 个 子 程 序, 然 后 在 case 语 句 后 面 调 用 这 些 子 程 序 就 ok 了 一 般 来 说 case 语 句 后 面 的 代 码 尽 量 不 要 超 过 20 行 规 则 1-25 不 要 为 了 使 用 case 语 句 而 刻 意 制 造 一 个 变 量 case 语 句 应 该 用 于 处 理 简 单 的, 容 易 分 类 的 数 据 如 果 你 的 数 据 并 不 简 单, 那 可 能 使 用 ifelse if 的 组 合 更 好 一 些 为 了 使 用 case 而 刻 意 构 造 出 来 的 变 量 很 容 易 把 人 搞 糊 涂, 应 该 避 免 这 种 变 量 比 如 : char action = a[0]; switch (action) case c : fun1(); break; case d : break; default:
28 break; 这 里 控 制 case 语 句 的 变 量 是 action 而 action 的 值 是 取 字 符 数 组 a 的 一 个 字 符 但 是 这 种 方 式 可 能 带 来 一 些 隐 含 的 错 误 一 般 而 言, 当 你 为 了 使 用 case 语 句 而 刻 意 去 造 出 一 个 变 量 时, 真 正 的 数 据 可 能 不 会 按 照 你 所 希 望 的 方 式 映 射 到 case 语 句 里 在 这 个 例 子 中, 如 果 用 户 输 入 字 符 数 组 a 里 面 存 的 是 const 这 个 字 符 串, 那 么 case 语 句 会 匹 配 到 第 一 个 case 上, 并 调 用 fun1() 函 数 然 而 如 果 这 个 数 组 里 存 的 是 别 的 以 字 符 c 开 头 的 任 何 字 符 串 ( 比 如 : col, can ),case 分 支 同 样 会 匹 配 到 第 一 个 case 上 但 是 这 也 许 并 不 是 你 想 要 的 结 果, 这 个 隐 含 的 错 误 往 往 使 人 抓 狂 如 果 这 样 的 话 还 不 如 使 用 if-else if 组 合 比 如 : if(0 == strcmp( const,a)) fun1(); else if 规 则 1-26 把 default 子 句 只 用 于 检 查 真 正 的 默 认 情 况 有 时 候, 你 只 剩 下 了 最 后 一 种 情 况 需 要 处 理, 于 是 就 决 定 把 这 种 情 况 用 default 子 句 来 处 理 这 样 也 许 会 让 你 偷 懒 少 敲 几 个 字 符, 但 是 这 却 很 不 明 智 这 样 将 失 去 case 语 句 的 标 号 所 提 供 的 自 说 明 功 能, 而 且 也 丧 失 了 使 用 default 子 句 处 理 错 误 情 况 的 能 力 所 以, 奉 劝 你 不 要 偷 懒, 老 老 实 实 的 把 每 一 种 情 况 都 用 case 语 句 来 完 成, 而 把 真 正 的 默 认 情 况 的 处 理 交 给 default 子 句 1.8,do while for 关 键 字 C 语 言 中 循 环 语 句 有 三 种 :while 循 环 do-while 循 环 for 循 环 while 循 环 : 先 判 断 while 后 面 括 号 里 的 值, 如 果 为 真 则 执 行 其 后 面 的 代 码 ; 否 则 不 执 行 while(1) 表 示 死 循 环 死 循 环 有 没 有 用 呢? 看 下 面 例 子 : 比 如 你 开 发 一 个 系 统 要 日 夜 不 停 的 运 行, 但 是 只 有 操 作 员 输 入 某 个 特 定 的 字 符 # 才 可 以 停 下 来 while(1) if( # == GetInputChar()) break;
29 1.8.1,break 与 continue 的 区 别 break 关 键 字 很 重 要, 表 示 终 止 本 层 循 环 现 在 这 个 例 子 只 有 一 层 循 环, 当 代 码 执 行 到 break 时, 循 环 便 终 止 如 果 把 break 换 成 continue 会 是 什 么 样 子 呢?continue 表 示 终 止 本 次 ( 本 轮 ) 循 环 当 代 码 执 行 到 continue 时, 本 轮 循 环 终 止, 进 入 下 一 轮 循 环 while(1) 也 有 写 成 while(true) 或 者 while(1==1) 或 者 while((bool) 1) 等 形 式 的, 效 果 一 样 do-while 循 环 : 先 执 行 do 后 面 的 代 码, 然 后 再 判 断 while 后 面 括 号 里 的 值, 如 果 为 真, 循 环 开 始 ; 否 则, 循 环 不 开 始 其 用 法 与 while 循 环 没 有 区 别, 但 相 对 较 少 用 for 循 环 :for 循 环 可 以 很 容 易 的 控 制 循 环 次 数, 多 用 于 事 先 知 道 循 环 次 数 的 情 况 下 留 一 个 问 题 : 在 switch case 语 句 中 能 否 使 用 continue 关 键 字? 为 什 么? 1.8.2, 循 环 语 句 的 注 意 点 建 议 1-27 在 多 重 循 环 中, 如 果 有 可 能, 应 当 将 最 长 的 循 环 放 在 最 内 层, 最 短 的 循 环 放 在 最 外 层, 以 减 少 CPU 跨 切 循 环 层 的 次 数 例 如 : 长 循 环 在 最 内 层, 效 率 高 for (col=0; col<5; col++ ) for (row=0; row<100; row++) sum = sum + a[row][col]; 长 循 环 在 最 外 层, 效 率 低 for (row=0; row<100; row++) for ( col=0; col<5; col++ ) sum = sum + a[row][col]; 建 议 1-28 建 议 for 语 句 的 循 环 控 制 变 量 的 取 值 采 用 半 开 半 闭 区 间 写 法 半 开 半 闭 区 间 写 法 和 闭 区 间 写 法 虽 然 功 能 是 相 同, 但 相 比 之 下, 半 开 半 闭 区 间 写 法 写 法 更 加 直 观 半 开 半 闭 区 间 写 法 for(n=0;n<10;n++) 闭 区 间 写 法 for(n=0;n<=9;n++)
30 规 则 1-29 不 能 在 for 循 环 体 内 修 改 循 环 变 量, 防 止 循 环 失 控 for (n = 0; n < 10; n++) n=8;// 不 可, 很 可 能 违 背 了 你 的 原 意 规 则 1-30 循 环 要 尽 可 能 的 短, 要 使 代 码 清 晰, 一 目 了 然 如 果 你 写 的 一 个 循 环 的 代 码 超 过 一 显 示 屏, 那 会 让 读 代 码 的 人 发 狂 的 解 决 的 办 法 由 两 个 : 第 一, 重 新 设 计 这 个 循 环, 确 认 是 否 这 些 操 作 都 必 须 放 在 这 个 循 环 里 ; 第 二, 将 这 些 代 码 改 写 成 一 个 子 函 数, 循 环 中 只 调 用 这 个 子 函 数 即 可 一 般 来 说 循 环 内 的 代 码 不 要 超 过 20 行 规 则 1-31 把 循 环 嵌 套 控 制 在 3 层 以 内 国 外 有 研 究 数 据 表 明, 当 循 环 嵌 套 超 过 3 层, 程 序 员 对 循 环 的 理 解 能 力 会 极 大 的 降 低 如 果 你 的 循 环 嵌 套 超 过 3 层, 建 议 你 重 新 设 计 循 环 或 是 将 循 环 内 的 代 码 改 写 成 一 个 字 函 数 1.9,goto 关 键 字 一 般 来 说, 编 码 的 水 平 与 goto 语 句 使 用 的 次 数 成 反 比 有 的 人 主 张 慎 用 但 不 禁 用 goto 语 句, 但 我 主 张 禁 用 关 于 goto 语 句 的 更 多 讨 论 可 以 参 看 Steve McConnell 的 名 著 Code Complete. Second Edition 规 则 1-32 禁 用 goto 语 句 自 从 提 倡 结 构 化 设 计 以 来,goto 就 成 了 有 争 议 的 语 句 首 先, 由 于 goto 语 句 可 以 灵 活 跳 转, 如 果 不 加 限 制, 它 的 确 会 破 坏 结 构 化 设 计 风 格 ; 其 次,goto 语 句 经 常 带 来 错 误 或 隐 患 它 可 能 跳 过 了 变 量 的 初 始 化 重 要 的 计 算 等 语 句, 例 如 : struct student *p = NULL; goto state; p = (struct student *)malloc( ); // 被 goto 跳 过, 没 有 初 始 化
31 state: // 使 用 p 指 向 的 内 存 里 的 值 的 代 码 如 果 编 译 器 不 能 发 觉 此 类 错 误, 每 用 一 次 goto 语 句 都 可 能 留 下 隐 患 1.10,void 关 键 字 void 有 什 么 好 讲 的 呢? 如 果 你 认 为 没 有, 那 就 没 有 ; 但 如 果 你 认 为 有, 那 就 真 的 有 有 点 像 色 即 是 空, 空 即 是 色 ,void a? void 的 字 面 意 思 是 空 类 型,void * 则 为 空 类 型 指 针,void * 可 以 指 向 任 何 类 型 的 数 据 void 几 乎 只 有 注 释 和 限 制 程 序 的 作 用, 因 为 从 来 没 有 人 会 定 义 一 个 void 变 量, 看 看 下 面 的 例 子 : void a; Visual C++6.0 上, 这 行 语 句 编 译 时 会 出 错, 提 示 illegal use of type 'void' 不 过, 即 使 void a 的 编 译 不 会 出 错, 它 也 没 有 任 何 实 际 意 义 void 真 正 发 挥 的 作 用 在 于 : (1) 对 函 数 返 回 的 限 定 ; (2) 对 函 数 参 数 的 限 定 众 所 周 知, 如 果 指 针 p1 和 p2 的 类 型 相 同, 那 么 我 们 可 以 直 接 在 p1 和 p2 间 互 相 赋 值 ; 如 果 p1 和 p2 指 向 不 同 的 数 据 类 型, 则 必 须 使 用 强 制 类 型 转 换 运 算 符 把 赋 值 运 算 符 右 边 的 指 针 类 型 转 换 为 左 边 指 针 的 类 型 例 如 : float *p1; int *p2; p1 = p2; 其 中 p1 = p2 语 句 会 编 译 出 错, 提 示 '=' : cannot convert from 'int *' to 'float *', 必 须 改 为 : p1 = (float *)p2; 而 void * 则 不 同, 任 何 类 型 的 指 针 都 可 以 直 接 赋 值 给 它, 无 需 进 行 强 制 类 型 转 换 : void *p1; int *p2; p1 = p2; 但 这 并 不 意 味 着,void * 也 可 以 无 需 强 制 类 型 转 换 地 赋 给 其 它 类 型 的 指 针 因 为 空 类 型 可 以 包 容 有 类 型, 而 有 类 型 则 不 能 包 容 空 类 型 比 如, 我 们 可 以 说 男 人 和 女 人 都 是 人, 但 不 能 说 人 是 男 人 或 者 人 是 女 人 下 面 的 语 句 编 译 出 错 :
32 void *p1; int *p2; p2 = p1; 提 示 '=' : cannot convert from 'void *' to 'int *' ,void 修 饰 函 数 返 回 值 和 参 数 规 则 1-33 如 果 函 数 没 有 返 回 值, 那 么 应 声 明 为 void 类 型 在 C 语 言 中, 凡 不 加 返 回 值 类 型 限 定 的 函 数, 就 会 被 编 译 器 作 为 返 回 整 型 值 处 理 但 是 许 多 程 序 员 却 误 以 为 其 为 void 类 型 例 如 : add ( int a, int b ) return a + b; int main(int argc, char* argv[]) // 甚 至 很 多 人 以 为 main 函 数 无 返 回 值 // 或 是 为 void 型 的 printf("2+3=%d",add(2,3)); 程 序 运 行 的 结 果 为 输 出 : 2+3=5 这 说 明 不 加 返 回 值 说 明 的 函 数 的 确 为 int 函 数 因 此, 为 了 避 免 混 乱, 我 们 在 编 写 C 程 序 时, 对 于 任 何 函 数 都 必 须 一 个 不 漏 地 指 定 其 类 型 如 果 函 数 没 有 返 回 值, 一 定 要 声 明 为 void 类 型 这 既 是 程 序 良 好 可 读 性 的 需 要, 也 是 编 程 规 范 性 的 要 求 另 外, 加 上 void 类 型 声 明 后, 也 可 以 发 挥 代 码 的 自 注 释 作 用 所 谓 的 代 码 的 自 注 释 即 代 码 能 自 己 注 释 自 己 规 则 1-34 如 果 函 数 无 参 数, 那 么 应 声 明 其 参 数 为 void 在 C++ 语 言 中 声 明 一 个 这 样 的 函 数 : int function(void) return 1; 则 进 行 下 面 的 调 用 是 不 合 法 的 :function(2); 因 为 在 C++ 中, 函 数 参 数 为 void 的 意 思 是 这 个 函 数 不 接 受 任 何 参 数 但 是 在 Turbo C 2.0 中 编 译 : #include "stdio.h" fun() return 1; main() printf("%d",fun(2)); getchar();
33 编 译 正 确 且 输 出 1, 这 说 明, 在 C 语 言 中, 可 以 给 无 参 数 的 函 数 传 送 任 意 类 型 的 参 数, 但 是 在 C++ 编 译 器 中 编 译 同 样 的 代 码 则 会 出 错 在 C++ 中, 不 能 向 无 参 数 的 函 数 传 送 任 何 参 数, 出 错 提 示 'fun' : function does not take 1 parameters 所 以, 无 论 在 C 还 是 C++ 中, 若 函 数 不 接 受 任 何 参 数, 一 定 要 指 明 参 数 为 void ,void 指 针 规 则 1-35 千 万 小 心 又 小 心 使 用 void 指 针 类 型 按 照 ANSI(American National Standards Institute) 标 准, 不 能 对 void 指 针 进 行 算 法 操 作, 即 下 列 操 作 都 是 不 合 法 的 : void * pvoid; pvoid++; //ANSI: 错 误 pvoid += 1; //ANSI: 错 误 ANSI 标 准 之 所 以 这 样 认 定, 是 因 为 它 坚 持 : 进 行 算 法 操 作 的 指 针 必 须 是 确 定 知 道 其 指 向 数 据 类 型 大 小 的 也 就 是 说 必 须 知 道 内 存 目 的 地 址 的 确 切 值 例 如 : int *pint; pint++; //ANSI: 正 确 但 是 大 名 鼎 鼎 的 GNU(GNU's Not Unix 的 递 归 缩 写 ) 则 不 这 么 认 定, 它 指 定 void * 的 算 法 操 作 与 char * 一 致 因 此 下 列 语 句 在 GNU 编 译 器 中 皆 正 确 : pvoid++; //GNU: 正 确 pvoid += 1; //GNU: 正 确 在 实 际 的 程 序 设 计 中, 为 符 合 ANSI 标 准, 并 提 高 程 序 的 可 移 植 性, 我 们 可 以 这 样 编 写 实 现 同 样 功 能 的 代 码 : void * pvoid; (char *)pvoid++; //ANSI: 正 确 ;GNU: 正 确 (char *)pvoid += 1; //ANSI: 错 误 ;GNU: 正 确 GNU 和 ANSI 还 有 一 些 区 别, 总 体 而 言,GNU 较 ANSI 更 开 放, 提 供 了 对 更 多 语 法 的 支 持 但 是 我 们 在 真 实 设 计 时, 还 是 应 该 尽 可 能 地 符 合 ANSI 标 准 规 则 1-36 如 果 函 数 的 参 数 可 以 是 任 意 类 型 指 针, 那 么 应 声 明 其 参 数 为 void * 典 型 的 如 内 存 操 作 函 数 memcpy 和 memset 的 函 数 原 型 分 别 为 : void * memcpy(void *dest, const void *src, size_t len); void * memset ( void * buffer, int c, size_t num ); 这 样, 任 何 类 型 的 指 针 都 可 以 传 入 memcpy 和 memset 中, 这 也 真 实 地 体 现 了 内 存 操 作 函 数 的 意 义, 因 为 它 操 作 的 对 象 仅 仅 是 一 片 内 存, 而 不 论 这 片 内 存 是 什 么 类 型 如 果 memcpy 和 memset 的 参 数 类 型 不 是 void *, 而 是 char *, 那 才 叫 真 的 奇 怪 了! 这 样 的 memcpy 和 memset 明 显 不 是 一 个 纯 粹 的, 脱 离 低 级 趣 味 的 函 数! 下 面 的 代 码 执 行 正 确 : 例 子 :memset 接 受 任 意 类 型 指 针 int IntArray_a[100]; memset (IntArray_a, 0, 100*sizeof(int) ); // 将 IntArray_a 清 0 例 子 :memcpy 接 受 任 意 类 型 指 针 int destintarray_a[100], srcintarray_a[100];
34 // 将 srcintarray_a 拷 贝 给 destintarray_a memcpy (destintarray_a, srcintarray_a, 100*sizeof(int) ); 有 趣 的 是,memcpy 和 memset 函 数 返 回 的 也 是 void * 类 型, 标 准 库 函 数 的 编 写 者 都 不 是 一 般 人 ,void 不 能 代 表 一 个 真 实 的 变 量 规 则 1-37 void 不 能 代 表 一 个 真 实 的 变 量 因 为 定 义 变 量 时 必 须 分 配 内 存 空 间, 定 义 void 类 型 变 量, 编 译 器 到 底 分 配 多 大 的 内 存 呢 下 面 代 码 都 企 图 让 void 代 表 一 个 真 实 的 变 量, 因 此 都 是 错 误 的 代 码 : void a; // 错 误 function(void a); // 错 误 void 体 现 了 一 种 抽 象, 这 个 世 界 上 的 变 量 都 是 有 类 型 的, 譬 如 一 个 人 不 是 男 人 就 是 女 人 ( 人 妖 不 算 ) void 的 出 现 只 是 为 了 一 种 抽 象 的 需 要, 如 果 你 正 确 地 理 解 了 面 向 对 象 中 抽 象 基 类 的 概 念, 也 很 容 易 理 解 void 数 据 类 型 正 如 不 能 给 抽 象 基 类 定 义 一 个 实 例, 我 们 也 不 能 定 义 一 个 void( 让 我 们 类 比 的 称 void 为 抽 象 数 据 类 型 ) 变 量 void 简 单 吧? 到 底 是 色 还 是 空 呢? 1.10,return 关 键 字 return 用 来 终 止 一 个 函 数 并 返 回 其 后 面 跟 着 的 值 return (Val);// 此 括 号 可 以 省 略 但 一 般 不 省 略, 尤 其 在 返 回 一 个 表 达 式 的 值 时 return 可 以 返 回 些 什 么 东 西 呢? 看 下 面 例 子 : char * Func(void) char str[30]; return str; str 属 于 局 部 变 量, 位 于 栈 内 存 中, 在 Func 结 束 的 时 候 被 释 放, 所 以 返 回 str 将 导 致 错 误 规 则 1-38 return 语 句 不 可 返 回 指 向 栈 内 存 的 指 针, 因 为 该 内 存 在 函 数 体 结 束 时 被 自 动 销 毁 留 个 问 题 : return ; 这 个 语 句 有 问 题 吗? 如 果 没 有 问 题, 那 返 回 的 是 什 么?
35 1.11,const 关 键 字 也 许 该 被 替 换 为 readolny const 是 constant 的 缩 写, 是 恒 定 不 变 的 意 思, 也 翻 译 为 常 量 常 数 等 很 不 幸, 正 是 因 为 这 一 点, 很 多 人 都 认 为 被 const 修 饰 的 值 是 常 量 这 是 不 精 确 的, 精 确 的 说 应 该 是 只 读 的 变 量, 其 值 在 编 译 时 不 能 被 使 用, 因 为 编 译 器 在 编 译 时 不 知 道 其 存 储 的 内 容 或 许 当 初 这 个 关 键 字 应 该 被 替 换 为 readonly 那 么 这 个 关 键 字 有 什 么 用 处 和 意 义 呢? const 推 出 的 初 始 目 的, 正 是 为 了 取 代 预 编 译 指 令, 消 除 它 的 缺 点, 同 时 继 承 它 的 优 点 我 们 看 看 它 与 define 宏 的 区 别 ( 很 多 人 误 以 为 define 是 关 键 字, 在 这 里 我 提 醒 你 再 回 到 本 章 前 面 看 看 32 个 关 键 字 里 是 否 有 define) ,const 修 饰 的 只 读 变 量 定 义 const 只 读 变 量, 具 有 不 可 变 性 例 如 : const int Max=100; int Array[Max]; 这 里 请 在 Visual C++6.0 里 分 别 创 建.c 文 件 和.cpp 文 件 测 试 一 下 你 会 发 现 在.c 文 件 中, 编 译 器 会 提 示 出 错, 而 在.cpp 文 件 中 则 顺 利 运 行 为 什 么 呢? 我 们 知 道 定 义 一 个 数 组 必 须 指 定 其 元 素 的 个 数 这 也 从 侧 面 证 实 在 C 语 言 中,const 修 饰 的 Max 仍 然 是 变 量, 只 不 过 是 只 读 属 性 罢 了 ; 而 在 C++ 里, 扩 展 了 const 的 含 义, 这 里 就 不 讨 论 了 注 意 :const 修 饰 的 只 读 变 量 必 须 在 定 义 的 同 时 初 始 化, 想 想 为 什 么? 留 一 个 问 题 :case 语 句 后 面 是 否 可 以 是 const 修 饰 的 只 读 变 量 呢? 请 动 手 测 试 一 下 , 节 省 空 间, 避 免 不 必 要 的 内 存 分 配, 同 时 提 高 效 率 编 译 器 通 常 不 为 普 通 const 只 读 变 量 分 配 存 储 空 间, 而 是 将 它 们 保 存 在 符 号 表 中, 这 使 得 它 成 为 一 个 编 译 期 间 的 值, 没 有 了 存 储 与 读 内 存 的 操 作, 使 得 它 的 效 率 也 很 高 例 如 : #define M 3 // 宏 常 量 const int N=5; // 此 时 并 未 将 N 放 入 内 存 中... int i=n; // 此 时 为 N 分 配 内 存, 以 后 不 再 分 配! int I=M; // 预 编 译 期 间 进 行 宏 替 换, 分 配 内 存 int j=n; // 没 有 内 存 分 配 int J=M; // 再 进 行 宏 替 换, 又 一 次 分 配 内 存! const 定 义 的 只 读 变 量 从 汇 编 的 角 度 来 看, 只 是 给 出 了 对 应 的 内 存 地 址, 而 不 是 象 #define 一 样 给 出 的 是 立 即 数, 所 以,const 定 义 的 只 读 变 量 在 程 序 运 行 过 程 中 只 有 一 份 拷 贝 ( 因 为 它 是 全 局 的 只 读 变 量, 存 放 在 静 态 区 ), 而 #define 定 义 的 宏 常 量 在 内 存 中 有 若 干 个 拷 贝 #define 宏 是 在 预 编 译 阶 段 进 行 替 换, 而 const 修 饰 的 只 读 变 量 是 在 编 译 的 时 候 确 定 其 值 #define 宏 没 有 类 型, 而 const 修 饰 的 只 读 变 量 具 有 特 定 的 类 型
36 1.11.3, 修 饰 一 般 变 量 一 般 常 量 是 指 简 单 类 型 的 只 读 变 量 这 种 只 读 变 量 在 定 义 时, 修 饰 符 const 可 以 用 在 类 型 说 明 符 前, 也 可 以 用 在 类 型 说 明 符 后 例 如 : int const i=2; 或 const int i=2; , 修 饰 数 组 定 义 或 说 明 一 个 只 读 数 组 可 采 用 如 下 格 式 : int const a[5]=1, 2, 3, 4, 5; 或 const int a[5]=1, 2, 3, 4, 5; , 修 饰 指 针 const int *p; // p 可 变,p 指 向 的 对 象 不 可 变 int const *p; // p 可 变,p 指 向 的 对 象 不 可 变 int*constp; //p 不 可 变,p 指 向 的 对 象 可 变 const int *const p; // 指 针 p 和 p 指 向 的 对 象 都 不 可 变 在 平 时 的 授 课 中 发 现 学 生 很 难 记 住 这 几 种 情 况 这 里 给 出 一 个 记 忆 和 理 解 的 方 法 : 先 忽 略 类 型 名 ( 编 译 器 解 析 的 时 候 也 是 忽 略 类 型 名 ), 我 们 看 const 离 哪 个 近 近 水 楼 台 先 得 月, 离 谁 近 就 修 饰 谁 const int *p; //const 修 饰 *p,p 是 指 针,*p 是 指 针 指 向 的 对 象, 不 可 变 int const *p; //const 修 饰 *p,p 是 指 针,*p 是 指 针 指 向 的 对 象, 不 可 变 int *const p; //const 修 饰 p,p 不 可 变,p 指 向 的 对 象 可 变 const int *const p; // 前 一 个 const 修 饰 *p, 后 一 个 const 修 饰 p, 指 针 p 和 p 指 向 的 对 象 都 不 可 变 , 修 饰 函 数 的 参 数 const 修 饰 符 也 可 以 修 饰 函 数 的 参 数, 当 不 希 望 这 个 参 数 值 被 函 数 体 内 意 外 改 变 时 使 用 例 如 : void Fun(const int i); 告 诉 编 译 器 i 在 函 数 体 中 的 不 能 改 变, 从 而 防 止 了 使 用 者 的 一 些 无 意 的 或 错 误 的 修 改 , 修 饰 函 数 的 返 回 值 const 修 饰 符 也 可 以 修 饰 函 数 的 返 回 值, 返 回 值 不 可 被 改 变 例 如 : const int Fun (void); 在 另 一 连 接 文 件 中 引 用 const 只 读 变 量 : extern const int i; // 正 确 的 声 明 extern const int j=10; // 错 误! 只 读 变 量 的 值 不 能 改 变 注 意 这 里 是 声 明 不 是 定 义, 关 于 声 明 和 定 义 的 区 别, 请 看 本 章 开 始 处 讲 了 这 么 多 讲 完 了 吗? 远 没 有 在 C++ 里, 对 const 做 了 进 一 步 的 扩 展, 还 有 很 多 知 识 未 能
37 讲 完 有 兴 趣 的 话, 不 妨 查 找 相 关 资 料 研 究 研 究 1.12, 最 易 变 的 关 键 字 ----volatile volatile 是 易 变 的 不 稳 定 的 意 思 很 多 人 根 本 就 没 见 过 这 个 关 键 字, 不 知 道 它 的 存 在 也 有 很 多 程 序 员 知 道 它 的 存 在, 但 从 来 没 用 过 它 我 对 它 有 种 杨 家 有 女 初 长 成, 养 在 深 闺 人 未 识 的 感 觉 volatile 关 键 字 和 const 一 样 是 一 种 类 型 修 饰 符, 用 它 修 饰 的 变 量 表 示 可 以 被 某 些 编 译 器 未 知 的 因 素 更 改, 比 如 操 作 系 统 硬 件 或 者 其 它 线 程 等 遇 到 这 个 关 键 字 声 明 的 变 量, 编 译 器 对 访 问 该 变 量 的 代 码 就 不 再 进 行 优 化, 从 而 可 以 提 供 对 特 殊 地 址 的 稳 定 访 问 先 看 看 下 面 的 例 子 : int i=10; int j = i;//(1) 语 句 int k = i;//(2) 语 句 这 时 候 编 译 器 对 代 码 进 行 优 化, 因 为 在 (1) (2) 两 条 语 句 中,i 没 有 被 用 作 左 值 这 时 候 编 译 器 认 为 i 的 值 没 有 发 生 改 变, 所 以 在 (1) 语 句 时 从 内 存 中 取 出 i 的 值 赋 给 j 之 后, 这 个 值 并 没 有 被 丢 掉, 而 是 在 (2) 语 句 时 继 续 用 这 个 值 给 k 赋 值 编 译 器 不 会 生 成 出 汇 编 代 码 重 新 从 内 存 里 取 i 的 值, 这 样 提 高 了 效 率 但 要 注 意 :(1) (2) 语 句 之 间 i 没 有 被 用 作 左 值 才 行 再 看 另 一 个 例 子 : volatile int i=10; int j = i;//(3) 语 句 int k = i;//(4) 语 句 volatile 关 键 字 告 诉 编 译 器 i 是 随 时 可 能 发 生 变 化 的, 每 次 使 用 它 的 时 候 必 须 从 内 存 中 取 出 i 的 值, 因 而 编 译 器 生 成 的 汇 编 代 码 会 重 新 从 i 的 地 址 处 读 取 数 据 放 在 k 中 这 样 看 来, 如 果 i 是 一 个 寄 存 器 变 量 或 者 表 示 一 个 端 口 数 据 或 者 是 多 个 线 程 的 共 享 数 据, 就 容 易 出 错, 所 以 说 volatile 可 以 保 证 对 特 殊 地 址 的 稳 定 访 问 但 是 注 意 : 在 VC++6.0 中, 一 般 Debug 模 式 没 有 进 行 代 码 优 化, 所 以 这 个 关 键 字 的 作 用 有 可 能 看 不 出 来 你 可 以 同 时 生 成 Debug 版 和 Release 版 的 程 序 做 个 测 试 留 一 个 问 题 :const volatile int i=10; 这 行 代 码 有 没 有 问 题? 如 果 没 有, 那 i 到 底 是 什 么 属 性? 1.13, 最 会 带 帽 子 的 关 键 字 ----extern extern, 外 面 的 外 来 的 意 思 那 它 有 什 么 作 用 呢? 举 个 例 子 : 假 设 你 在 大 街 上 看 到
38 一 个 黑 皮 肤 绿 眼 睛 红 头 发 的 美 女 ( 外 星 人?) 或 者 帅 哥 你 的 第 一 反 应 就 是 这 人 不 是 国 产 的 extern 就 相 当 于 他 们 的 这 些 区 别 于 中 国 人 的 特 性 extern 可 以 置 于 变 量 或 者 函 数 前, 以 标 示 变 量 或 者 函 数 的 定 义 在 别 的 文 件 中, 下 面 的 代 码 用 到 的 这 些 变 量 或 函 数 是 外 来 的, 不 是 本 文 件 定 义 的, 提 示 编 译 器 遇 到 此 变 量 和 函 数 时 在 其 他 模 块 中 寻 找 其 定 义 就 好 比 在 本 文 件 中 给 这 些 外 来 的 变 量 或 函 数 带 了 顶 帽 子, 告 诉 本 文 件 中 所 有 代 码, 这 些 家 伙 不 是 土 著 那 你 想 想 extern 修 饰 的 变 量 或 函 数 是 定 义 还 是 声 明? 看 列 子 : A.c 文 件 中 定 义 : B.c 文 件 中 用 extern 修 饰 : int i = 10; extern int i;// 写 成 i=10; 行 吗? void fun(void) extern void fun(void);// 两 个 void 可 否 省 略? //code C.h 文 件 中 定 义 : D.c 文 件 中 用 extern 修 饰 : int j = 1; extern double j;// 这 样 行 吗? 为 什 么? int k = 2; j = 3.0;// 这 样 行 吗? 为 什 么? 至 于 extern C 的 用 法, 一 般 认 为 属 于 C++ 的 范 畴, 这 里 就 先 不 讨 论 当 然 关 于 extern 的 讨 论 还 远 没 有 结 束, 在 指 针 与 数 组 那 一 章, 你 还 会 和 它 亲 密 接 触 的 1.14,struct 关 键 字 struct 是 个 神 奇 的 关 键 字, 它 将 一 些 相 关 联 的 数 据 打 包 成 一 个 整 体, 方 便 使 用 在 网 络 协 议 通 信 控 制 嵌 入 式 系 统 驱 动 开 发 等 地 方, 我 们 经 常 要 传 送 的 不 是 简 单 的 字 节 流 (char 型 数 组 ), 而 是 多 种 数 据 组 合 起 来 的 一 个 整 体, 其 表 现 形 式 是 一 个 结 构 体 经 验 不 足 的 开 发 人 员 往 往 将 所 有 需 要 传 送 的 内 容 依 顺 序 保 存 在 char 型 数 组 中, 通 过 指 针 偏 移 的 方 法 传 送 网 络 报 文 等 信 息 这 样 做 编 程 复 杂, 易 出 错, 而 且 一 旦 控 制 方 式 及 通 信 协 议 有 所 变 化, 程 序 就 要 进 行 非 常 细 致 的 修 改, 非 常 容 易 出 错 这 个 时 候 只 需 要 一 个 结 构 体 就 能 搞 定 平 时 我 们 要 求 函 数 的 参 数 尽 量 不 多 于 4 个, 如 果 函 数 的 参 数 多 于 4 个 使 用 起 来 非 常 容 易 出 错 ( 包 括 每 个 参 数 的 意 义 和 顺 序 都 容 易 弄 错 ), 效 率 也 会 降 低 ( 与 具 体 CPU 有 关,ARM 芯 片 对 于 超 过 4 个 参 数 的 处 理 就 有 讲 究, 具 体 请 参 考 相 关 资 料 ) 这 个 时 候, 可 以 用 结 构 体 压 缩 参 数 个 数 , 空 结 构 体 多 大? 结 构 体 所 占 的 内 存 大 小 是 其 成 员 所 占 内 存 之 和 ( 关 于 结 构 体 的 内 存 对 齐, 请 参 考 预 处 理 那 章 ) 这 点 很 容 易 理 解, 但 是 下 面 的 这 种 情 况 呢?
39 struct student stu; sizeof(stu) 的 值 是 多 少 呢? 在 Visual C 上 测 试 一 下 很 遗 憾, 不 是 0, 而 是 1 为 什 么 呢? 你 想 想, 如 果 我 们 把 struct student 看 成 一 个 模 子 的 话, 你 能 造 出 一 个 没 有 任 何 容 积 的 模 子 吗? 显 然 不 行 编 译 器 也 是 如 此 认 为 编 译 器 认 为 任 何 一 种 数 据 类 型 都 有 其 大 小, 用 它 来 定 义 一 个 变 量 能 够 分 配 确 定 大 小 的 空 间 既 然 如 此, 编 译 器 就 理 所 当 然 的 认 为 任 何 一 个 结 构 体 都 是 有 大 小 的, 哪 怕 这 个 结 构 体 为 空 那 万 一 结 构 体 真 的 为 空, 它 的 大 小 为 什 么 值 比 较 合 适 呢? 假 设 结 构 体 内 只 有 一 个 char 型 的 数 据 成 员, 那 其 大 小 为 1byte( 这 里 先 不 考 虑 内 存 对 齐 的 情 况 ). 也 就 是 说 非 空 结 构 体 类 型 数 据 最 少 需 要 占 一 个 字 节 的 空 间, 而 空 结 构 体 类 型 数 据 总 不 能 比 最 小 的 非 空 结 构 体 类 型 数 据 所 占 的 空 间 大 吧 这 就 麻 烦 了, 空 结 构 体 的 大 小 既 不 能 为 0, 也 不 能 大 于 1, 怎 么 办? 定 义 为 0.5 个 byte? 但 是 内 存 地 址 的 最 小 单 位 是 1 个 byte,0.5 个 byte 怎 么 处 理? 解 决 这 个 问 题 的 最 好 办 法 就 是 折 中, 编 译 器 理 所 当 然 的 认 为 你 构 造 一 个 结 构 体 数 据 类 型 是 用 来 打 包 一 些 数 据 成 员 的, 而 最 小 的 数 据 成 员 需 要 1 个 byte, 编 译 器 为 每 个 结 构 体 类 型 数 据 至 少 预 留 1 个 byte 的 空 间 所 以, 空 结 构 体 的 大 小 就 定 位 1 个 byte , 柔 性 数 组 也 许 你 从 来 没 有 听 说 过 柔 性 数 组 (flexible array) 这 个 概 念, 但 是 它 确 实 是 存 在 的 C99 中, 结 构 中 的 最 后 一 个 元 素 允 许 是 未 知 大 小 的 数 组, 这 就 叫 做 柔 性 数 组 成 员, 但 结 构 中 的 柔 性 数 组 成 员 前 面 必 须 至 少 一 个 其 他 成 员 柔 性 数 组 成 员 允 许 结 构 中 包 含 一 个 大 小 可 变 的 数 组 sizeof 返 回 的 这 种 结 构 大 小 不 包 括 柔 性 数 组 的 内 存 包 含 柔 性 数 组 成 员 的 结 构 用 malloc () 函 数 进 行 内 存 的 动 态 分 配, 并 且 分 配 的 内 存 应 该 大 于 结 构 的 大 小, 以 适 应 柔 性 数 组 的 预 期 大 小 柔 性 数 组 到 底 如 何 使 用 呢? 看 下 面 例 子 : typedef struct st_type int i; int a[0]; type_a; 有 些 编 译 器 会 报 错 无 法 编 译 可 以 改 成 : typedef struct st_type int i; int a[]; type_a; 这 样 我 们 就 可 以 定 义 一 个 可 变 长 的 结 构 体, 用 sizeof(type_a) 得 到 的 只 有 4, 就 是 sizeof(i)=sizeof(int) 那 个 0 个 元 素 的 数 组 没 有 占 用 空 间, 而 后 我 们 可 以 进 行 变 长 操 作 了 通 过 如 下 表 达 式 给 结 构 体 分 配 内 存 : type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));
40 这 样 我 们 为 结 构 体 指 针 p 分 配 了 一 块 内 存 用 p->item[n] 就 能 简 单 地 访 问 可 变 长 元 素 但 是 这 时 候 我 们 再 用 sizeof(*p) 测 试 结 构 体 的 大 小, 发 现 仍 然 为 4 是 不 是 很 诡 异? 我 们 不 是 给 这 个 数 组 分 配 了 空 间 么? 别 急, 先 回 忆 一 下 我 们 前 面 讲 过 的 模 子 在 定 义 这 个 结 构 体 的 时 候, 模 子 的 大 小 就 已 经 确 定 不 包 含 柔 性 数 组 的 内 存 大 小 柔 性 数 组 只 是 编 外 人 员, 不 占 结 构 体 的 编 制 只 是 说 在 使 用 柔 性 数 组 时 需 要 把 它 当 作 结 构 体 的 一 个 成 员, 仅 此 而 已 再 说 白 点, 柔 性 数 组 其 实 与 结 构 体 没 什 么 关 系, 只 是 挂 羊 头 卖 狗 肉 而 已, 算 不 得 结 构 体 的 正 式 成 员 需 要 说 明 的 是 :C89 不 支 持 这 种 东 西,C99 把 它 作 为 一 种 特 例 加 入 了 标 准 但 是,C99 所 支 持 的 是 incomplete type, 而 不 是 zero array, 形 同 int item[0]; 这 种 形 式 是 非 法 的,C99 支 持 的 形 式 是 形 同 int item[]; 只 不 过 有 些 编 译 器 把 int item[0]; 作 为 非 标 准 扩 展 来 支 持, 而 且 在 C99 发 布 之 前 已 经 有 了 这 种 非 标 准 扩 展 了,C99 发 布 之 后, 有 些 编 译 器 把 两 者 合 而 为 一 了 当 然, 上 面 既 然 用 malloc 函 数 分 配 了 内 存, 肯 定 就 需 要 用 free 函 数 来 释 放 内 存 : free(p); 经 过 上 面 的 讲 解, 相 信 你 已 经 掌 握 了 这 个 看 起 来 似 乎 很 神 秘 的 东 西 不 过 实 在 要 是 没 掌 握 也 无 所 谓, 这 个 东 西 实 在 很 少 用 ,struct 与 class 的 区 别 在 C++ 里 struct 关 键 字 与 class 关 键 字 一 般 可 以 通 用, 只 有 一 个 很 小 的 区 别 struct 的 成 员 默 认 情 况 下 属 性 是 public 的, 而 class 成 员 却 是 private 的 很 多 人 觉 得 不 好 记, 其 实 很 容 易 你 平 时 用 结 构 体 时 用 public 修 饰 它 的 成 员 了 吗? 既 然 struct 关 键 字 与 class 关 键 字 可 以 通 用, 你 也 不 要 认 为 结 构 体 内 不 能 放 函 数 了 当 然, 关 于 结 构 体 的 讨 论 远 没 有 结 束, 在 指 针 与 数 组 那 一 章, 你 还 会 要 和 它 打 交 道 的 1.15,union 关 键 字 union 关 键 字 的 用 法 与 struct 的 用 法 非 常 类 似 union 维 护 足 够 的 空 间 来 置 放 多 个 数 据 成 员 中 的 一 种, 而 不 是 为 每 一 个 数 据 成 员 配 置 空 间, 在 union 中 所 有 的 数 据 成 员 共 用 一 个 空 间, 同 一 时 间 只 能 储 存 其 中 一 个 数 据 成 员, 所 有 的 数 据 成 员 具 有 相 同 的 起 始 地 址 例 子 如 下 : union StateMachine char character; int number; char *str; double exp; ; 一 个 union 只 配 置 一 个 足 够 大 的 空 间 以 来 容 纳 最 大 长 度 的 数 据 成 员, 以 上 例 而 言, 最 大 长 度 是 double 型 态, 所 以 StateMachine 的 空 间 大 小 就 是 double 数 据 类 型 的 大 小 在 C++ 里,union 的 成 员 默 认 属 性 页 为 public union 主 要 用 来 压 缩 空 间 如 果 一 些 数 据
41 不 可 能 在 同 一 时 间 同 时 被 用 到, 则 可 以 使 用 union , 大 小 端 模 式 对 union 类 型 数 据 的 影 响 下 面 再 看 一 个 例 子 : union int i; char a[2]; *p, u; p=&u; p->a[0] = 0x39; p->a[1] = 0x38; p.i 的 值 应 该 为 多 少 呢? 这 里 需 要 考 虑 存 储 模 式 : 大 端 模 式 和 小 端 模 式 大 端 模 式 (Big_endian): 字 数 据 的 高 字 节 存 储 在 低 地 址 中, 而 字 数 据 的 低 字 节 则 存 放 在 高 地 址 中 小 端 模 式 (Little_endian): 字 数 据 的 高 字 节 存 储 在 高 地 址 中, 而 字 数 据 的 低 字 节 则 存 放 在 低 地 址 中 union 型 数 据 所 占 的 空 间 等 于 其 最 大 的 成 员 所 占 的 空 间 对 union 型 的 成 员 的 存 取 都 是 相 对 于 该 联 合 体 基 地 址 的 偏 移 量 为 0 处 开 始, 也 就 是 联 合 体 的 访 问 不 论 对 哪 个 变 量 的 存 取 都 是 从 union 的 首 地 址 位 置 开 始 如 此 一 解 释, 上 面 的 问 题 是 否 已 经 有 了 答 案 呢? , 如 何 用 程 序 确 认 当 前 系 统 的 存 储 模 式? 上 述 问 题 似 乎 还 比 较 简 单, 那 来 个 有 技 术 含 量 的 : 请 写 一 个 C 函 数, 若 处 理 器 是 Big_endian 的, 则 返 回 0; 若 是 Little_endian 的, 则 返 回 1 先 分 析 一 下, 按 照 上 面 关 于 大 小 端 模 式 的 定 义, 假 设 int 类 型 变 量 i 被 初 始 化 为 1 以 大 端 模 式 存 储, 其 内 存 布 局 如 下 图 : 以 小 端 模 式 存 储, 其 内 存 布 局 如 下 图 :
42 变 量 i 占 4 个 字 节, 但 只 有 一 个 字 节 的 值 为 1, 另 外 三 个 字 节 的 值 都 为 0 如 果 取 出 低 地 址 上 的 值 为 0, 毫 无 疑 问, 这 是 大 端 模 式 ; 如 果 取 出 低 地 址 上 的 值 为 1, 毫 无 疑 问, 这 是 小 端 模 式 既 然 如 此, 我 们 完 全 可 以 利 用 union 类 型 数 据 的 特 点 : 所 有 成 员 的 起 始 地 址 一 致 到 现 在, 应 该 知 道 怎 么 写 了 吧? 参 考 答 案 如 下 : int checksystem( ) union check int i; char ch; c; c.i = 1; return (c.ch ==1); 现 在 你 可 以 用 这 个 函 数 来 测 试 你 当 前 系 统 的 存 储 模 式 了 当 然 你 也 可 以 不 用 函 数 而 直 接 去 查 看 内 存 来 确 定 当 前 系 统 的 存 储 模 式 如 下 图 : 图 中 0x01 的 值 存 在 低 地 址 上, 说 明 当 前 系 统 为 小 端 模 式 不 过 要 说 明 的 一 点 是, 某 些 系 统 可 能 同 时 支 持 这 两 种 存 储 模 式, 你 可 以 用 硬 件 跳 线 或 在 编 译 器 的 选 项 中 设 置 其 存 储 模 式 留 个 问 题 : 在 x86 系 统 下, 输 出 的 值 为 多 少? #include <stdio.h> int main() int a[5]=1,2,3,4,5; int *ptr1=(int *)(&a+1);
43 int *ptr2=(int *)((int)a+1); printf("%x,%x",ptr1[-1],*ptr2); return 0; 1.16,enum 关 键 字 很 多 初 学 者 对 枚 举 (enum) 感 到 迷 惑, 或 者 认 为 没 什 么 用, 其 实 枚 举 (enum) 是 个 很 有 用 的 数 据 类 型 , 枚 举 类 型 的 使 用 方 法 一 般 的 定 义 方 式 如 下 : enum enum_type_name ENUM_CONST_1, ENUM_CONST_2,... ENUM_CONST_n enum_variable_name; 注 意 :enum_type_name 是 自 定 义 的 一 种 数 据 数 据 类 型 名, 而 enum_variable_name 为 enum_type_name 类 型 的 一 个 变 量, 也 就 是 我 们 平 时 常 说 的 枚 举 变 量 实 际 上 enum_type_name 类 型 是 对 一 个 变 量 取 值 范 围 的 限 定, 而 花 括 号 内 是 它 的 取 值 范 围, 即 enum_type_name 类 型 的 变 量 enum_variable_name 只 能 取 值 为 花 括 号 内 的 任 何 一 个 值, 如 果 赋 给 该 类 型 变 量 的 值 不 在 列 表 中, 则 会 报 错 或 者 警 告 ENUM_CONST_1 ENUM_CONST_2... ENUM_CONST_n, 这 些 成 员 都 是 常 量, 也 就 是 我 们 平 时 所 说 的 枚 举 常 量 ( 常 量 一 般 用 大 写 ) enum 变 量 类 型 还 可 以 给 其 中 的 常 量 符 号 赋 值, 如 果 不 赋 值 则 会 从 被 赋 初 值 的 那 个 常 量 开 始 依 次 加 1, 如 果 都 没 有 赋 值, 它 们 的 值 从 0 开 始 依 次 递 增 1 如 分 别 用 一 个 常 数 表 示 不 同 颜 色 : enum Color GREEN = 1, RED, BLUE, GREEN_RED = 10, GREEN_BLUE ColorVal; 其 中 各 常 量 名 代 表 的 数 值 分 别 为 :
44 GREEN = 1 RED = 2 BLUE = 3 GREEN_RED = 10 GREEN_BLUE = , 枚 举 与 #define 宏 的 区 别 下 面 再 看 看 枚 举 与 #define 宏 的 区 别 : 1),#define 宏 常 量 是 在 预 编 译 阶 段 进 行 简 单 替 换 枚 举 常 量 则 是 在 编 译 的 时 候 确 定 其 值 2), 一 般 在 编 译 器 里, 可 以 调 试 枚 举 常 量, 但 是 不 能 调 试 宏 常 量 3), 枚 举 可 以 一 次 定 义 大 量 相 关 的 常 量, 而 #define 宏 一 次 只 能 定 义 一 个 留 两 个 问 题 : A), 枚 举 能 做 到 事,#define 宏 能 不 能 都 做 到? 如 果 能, 那 为 什 么 还 需 要 枚 举? B),sizeof(ColorVal) 的 值 为 多 少? 为 什 么? 1.17, 伟 大 的 缝 纫 师 ----typedef 关 键 字 , 关 于 马 甲 的 笑 话 有 这 样 一 个 笑 话 : 一 个 猎 人 在 河 边 抓 捕 一 条 蛇, 蛇 逃 进 了 水 里 过 一 会, 一 个 乌 龟 爬 到 岸 边 猎 人 一 把 抓 住 这 个 乌 龟, 大 声 的 说 道 : 小 样, 别 你 为 你 穿 了 个 马 甲 我 就 不 认 识 你 了! typedef 关 键 字 是 个 伟 大 的 缝 纫 师, 擅 长 做 马 甲, 任 何 东 西 穿 上 这 个 马 甲 就 立 马 变 样 它 可 以 把 狼 变 成 一 头 羊, 也 能 把 羊 变 成 一 头 狼 甚 至 还 可 以 把 长 着 翅 膀 的 鸟 人 变 成 天 使, 同 样 也 能 把 美 丽 的 天 使 变 成 鸟 人 所 以, 你 千 万 不 要 得 罪 它, 一 定 要 掌 握 它 的 脾 气, 不 然 哪 天 我 把 你 当 鸟 人, 你 可 别 怪 我 ^_^ , 历 史 的 误 会 ---- 也 许 应 该 是 typerename 很 多 人 认 为 typedef 是 定 义 新 的 数 据 类 型, 这 可 能 与 这 个 关 键 字 有 关 本 来 嘛,type 是 数 据 类 型 的 意 思 ;def(ine) 是 定 义 的 意 思, 合 起 来 就 是 定 义 数 据 类 型 啦 不 过 很 遗 憾, 这 种 理 解 是 不 正 确 的 也 许 这 个 关 键 字 该 被 替 换 为 typerename 或 是 别 的 词 typedef 的 真 正 意 思 是 给 一 个 已 经 存 在 的 数 据 类 型 ( 注 意 : 是 类 型 不 是 变 量 ) 取 一 个 别 名, 而 非 定 义 一 个 新 的 数 据 类 型 比 如 : 华 美 绝 伦 的 芍 药, 就 有 个 别 名 --- 将 离 中 国 古 代 男 女 交 往, 往 往 以 芍 药 相 赠, 表 达 惜 别 之 情, 送 芍 药 就 意 味 着 即 将 分 离 所 以 文 人 墨 客 就 给 芍 药 取 了 个 意 味 深 长 的 别 名 将 离 这 个 新 的 名 字 就 表 达 了 那 种 依 依 不 舍 的 惜 别 之 情
45 这 样 新 的 名 字 与 原 来 的 名 字 相 比, 就 更 能 表 达 出 想 要 表 达 的 意 思 在 实 际 项 目 中, 为 了 方 便, 可 能 很 多 数 据 类 型 ( 尤 其 是 结 构 体 之 类 的 自 定 义 数 据 类 型 ) 需 要 我 们 重 新 取 一 个 适 用 实 际 情 况 的 别 名 这 时 候 typedef 就 可 以 帮 助 我 们 例 如 : typedef struct student //code Stu_st,*Stu_pst;// 命 名 规 则 请 参 考 本 章 前 面 部 分 A),struct student stu1; 和 Stu_st stu1; 没 有 区 别 B),struct student *stu2; 和 Stu_pst stu2; 和 Stu_st *stu2; 没 有 区 别 这 个 地 方 很 多 初 学 者 迷 惑,B) 的 两 个 定 义 为 什 么 相 等 呢? 其 实 很 好 理 解 我 们 把 struct student /*code*/ 看 成 一 个 整 体,typedef 就 是 给 struct student /*code*/ 取 了 个 别 名 叫 Stu_st ; 同 时 给 struct student /*code*/ * 取 了 个 别 名 叫 Stu_pst 只 不 过 这 两 个 名 字 同 时 取 而 已, 好 比 你 给 你 家 小 狗 取 了 个 别 名 叫 大 黄, 同 时 你 妹 妹 给 小 狗 带 了 小 帽 子, 然 后 给 它 取 了 个 别 名 叫 小 可 爱 ^_^ 好, 下 面 再 把 typedef 与 const 放 在 一 起 看 看 : C),const Stu_pst stu3; D),Stu_pst const stu4; 大 多 数 初 学 者 认 为 C) 里 const 修 饰 的 是 stu3 指 向 的 对 象 ;D) 里 const 修 饰 的 是 stu4 这 个 指 针 很 遗 憾,C) 里 const 修 饰 的 并 不 是 stu3 指 向 的 对 象 那 const 这 时 候 到 底 修 饰 的 是 什 么 呢? 我 们 在 讲 解 const int i 的 时 候 说 过 const 放 在 类 型 名 int 前 后 都 行 ; 而 const int *p 与 int * const p 则 完 全 不 一 样 也 就 是 说, 我 们 看 const 修 饰 谁 都 时 候 完 全 可 以 将 数 据 类 型 名 视 而 不 见, 当 它 不 存 在 反 过 来 再 看 const Stu_pst stu3,stu_pst 是 struct student /*code*/ * 的 别 名, struct student /*code*/ * 是 一 个 整 体 对 于 编 译 器 来 说, 只 认 为 Stu_pst 是 一 个 类 型 名, 所 以 在 解 析 的 时 候 很 自 然 的 把 Stu_pst 这 个 数 据 类 型 名 忽 略 掉 现 在 知 道 const 到 底 修 饰 的 是 什 么 了 吧?^_^ ,typedef 与 #define 的 区 别 噢, 上 帝! 这 真 要 命! 别 急, 要 命 的 还 在 后 面 呢 看 如 下 例 子 : E), #define INT32 int unsigned INT32 i = 10; F),typedef int int32; unsigned int32 j = 10; 其 中 F) 编 译 出 错, 为 什 么 呢?E) 不 会 出 错, 这 很 好 理 解, 因 为 在 预 编 译 的 时 候 INT32 被 替 换 为 int, 而 unsigned int i = 10; 语 句 是 正 确 的 但 是, 很 可 惜, 用 typedef 取 的 别 名 不 支 持 这 种 类 型 扩 展 另 外, 想 想 typedef static int int32 行 不 行? 为 什 么?
46 下 面 再 看 一 个 与 #define 宏 有 关 的 例 子 : G),#define PCHAR char* PCHAR p3,p4; H),typedef char* pchar; pchar p1,p2; 两 组 代 码 编 译 都 没 有 问 题, 但 是, 这 里 的 p4 却 不 是 指 针, 仅 仅 是 一 个 char 类 型 的 字 符 这 种 错 误 很 容 易 被 忽 略, 所 以 用 #define 的 时 候 要 慎 之 又 慎 关 于 #define 当 然 还 有 很 多 话 题 需 要 讨 论, 请 看 预 处 理 那 一 章 当 然 关 于 typedef 的 讨 论 也 还 没 有 结 束, 在 指 针 与 数 组 那 一 章, 我 们 还 要 继 续 讨 论 ,#define a int[10] 与 typedef int a[10]; 留 两 个 问 题 : 1),#define a int[10] A),a[10] a[10]; B),a[10] a; C),int a[10]; D),int a; E),a b[10]; F),a b; G),a* b[10]; H),a* b; 2), typedef int a[10]; A),a[10] a[10]; B),a[10] a; C),int a[10]; D),int a; E),a b[10]; F),a b; G),a* b[10]; H),a* b; 3),#define a A),a[10] int*[10] a[10];
47 B),a[10] a; C),int a[10]; D),int a; E),a b[10]; F),a b; G),a* b[10]; H),a* b; 4), typedef int * a[10]; A),a[10] a[10]; B),a[10] a; C),int a[10]; D),int a; E),a b[10]; F),a b; G),a* b[10]; H),a* b; 5),#define *a int[10] A),a[10] a[10]; B),a[10] a; C),int a[10]; D),int a; E),a b[10]; F),a b; G),a* b[10]; H),a* b; 6), typedef int (* a)[10]; A),a[10] a[10]; B),a[10] a; C),int a[10]; D),int a; E),a b[10]; F),a b;
48 G),a* b[10]; H),a* b; 7),#define *a * int[10] A),a[10] a[10]; B),a[10] a; C),int a[10]; D),int a; E),a b[10]; F),a b; G),a* b[10]; H),a* b; 8), typedef int * (* a)[10]; A),a[10] a[10]; B),a[10] a; C),int a[10]; D),int a; E),a b[10]; F),a b; G),a* b[10]; H),a* b; 请 判 断 这 里 面 哪 些 定 义 正 确, 哪 些 定 义 不 正 确 另 外,int[10] 和 a[10] 到 底 该 怎 么 用?
49 第 二 章 符 号 符 号 有 什 么 好 说 的 呢? 确 实, 符 号 可 说 的 内 容 要 少 些, 但 总 还 是 有 些 可 以 唠 叨 地 方 有 一 次 上 课, 我 问 学 生 : / 这 个 符 号 在 C 语 言 里 都 用 在 哪 些 地 方? 没 有 一 个 人 能 答 完 整 这 说 明 C 语 言 的 基 础 掌 握 不 牢 靠, 如 果 真 正 掌 握 了 C 语 言, 你 就 能 很 轻 易 的 回 答 上 来 这 个 问 题 就 请 读 者 试 着 回 答 一 下 吧 本 章 不 会 像 关 键 字 一 样 一 个 一 个 深 入 讨 论, 只 是 将 容 易 出 错 的 地 方 讨 论 一 下 表 (2.1) 标 准 C 语 言 的 基 本 符 号 符 号 名 称 符 号 名 称, 逗 号 > 右 尖 括 号. 圆 点! 感 叹 号 ; 分 号 竖 线 : 冒 号 / 斜 杠? 问 号 \ 反 斜 杠 单 引 号 ~ 波 折 号 双 引 号 # 井 号 ( 左 圆 括 号 ) 右 圆 括 号 [ 左 方 括 号 ] 右 方 括 号 左 大 括 号 右 大 括 号 % 百 分 号 & and( 与 ) ^ xor( 异 或 ) * 乘 号 - 减 号 = 等 于 号 < 左 尖 括 号 + 加 号 C 语 言 的 基 本 符 号 就 有 20 多 个, 每 个 符 号 可 能 同 时 具 有 多 重 含 义, 而 且 这 些 符 号 之 间 相 互 组 合 又 使 得 C 语 言 中 的 符 号 变 得 更 加 复 杂 起 来 你 也 许 听 说 过 国 际 C 语 言 乱 码 大 赛 (IOCCC), 能 获 奖 的 人 毫 无 疑 问 是 世 界 顶 级 C 程 序 员 这 是 他 们 利 用 C 语 言 的 特 点 极 限 挖 掘 的 结 果 下 面 这 个 例 子 就 是 网 上 广 为 流 传 的 一 个 经 典 作 品 : #i nclude <stdio.h> main(t,_,a)char *a;return!0<t?t<3?main(-79,-13,a+main(-87,1-_, main(-86,0,a+1)+a)):1,t<_?main(t+1,_,a):3,main(-94,-27+t,a)&&t==2?_<13? main(2,_+1,"%s %d %d\n"):9:16:t<0?t<-72?main(_,t, "@n'+,#'/*w+/w#cdnr/+,r/*de+,/**+,/w%+,/w#q#n+,/#l+,/nn+,/+#n+,/#\ ;#q#n+,/+k#;*+,/'r :'d*'3,w+k w'k:'+e#';dq#'l \
50 q#'+d'k#!/+k#;q#'rekk#w'rekknl]'/#;#q#n'))#w'))nl]'/+#n';drw' i;# \ )nl]!/nn#'; r#w'r ncnl]'/#l,+'k rw' ik;[nl]'/w#q#n'wk nw' \ iwkkknl]!/w%'l##w#' i; :nl]'/*q#'ld;r'nlwb!/*de'c \ ;;nl'-rw]'/+,##'*#nc,',#nw]'/+kd'+e+;#'rdq#w! nr'/ ') +rl#'n' ')# \ '+##(!!/") :t<-50?_==*a?putchar(31[a]):main(-65,_,a+1):main((*a=='/')+t,_,a+1) :0<t?main(2,2,"%s"):*a=='/' main(0,main(-61,*a, "!ek;dc i@bk'(q)-[w]*%n+r3#l,:\nuwloca-o;m.vpbks,fxntdceghiry"),a+1); 还 没 发 狂? 看 来 你 抵 抗 力 够 强 的 这 是 IOCCC 1988 年 获 奖 作 品, 作 者 是 Ian Phillipps 毫 无 疑 问,Ian Phillipps 是 世 界 上 最 顶 级 的 C 语 言 程 序 员 之 一 你 可 以 数 数 这 里 面 用 了 多 少 个 符 号 当 然 这 里 我 并 不 会 讨 论 这 段 代 码, 也 并 不 是 鼓 励 你 也 去 写 这 样 的 代 码 ( 关 于 这 段 代 码 的 分 析, 你 可 以 上 网 查 询 ) 恰 恰 相 反, 我 要 告 诉 你 的 是 : 大 师 把 代 码 写 成 这 样 是 经 典, 你 把 代 码 写 成 这 样 是 垃 圾! 所 以 在 垃 圾 和 经 典 之 间, 你 需 要 做 一 个 抉 择 2.1, 注 释 符 号 2.1.1, 几 个 似 非 而 是 的 注 释 问 题 C 语 言 的 注 释 可 以 出 现 在 C 语 言 代 码 的 任 何 地 方 这 句 话 对 不 对? 这 是 我 当 学 生 时 我 老 师 问 的 一 个 问 题 我 当 时 回 答 是 不 对 好, 那 我 们 就 看 看 下 面 的 例 子 : A),int/*...*/i; B),char* s="abcdefgh //hijklmn"; C), //Is it a \ valid comment? D), in/* */t i; 我 们 知 道 C 语 言 里 可 以 有 两 种 注 释 方 式 :/* */ 和 // 那 上 面 3 条 注 释 对 不 对 呢? 建 议 你 亲 自 在 编 译 器 中 测 试 一 下 上 述 前 3 条 注 释 都 是 正 确 的, 最 后 一 条 不 正 确 A), 有 人 认 为 编 译 器 剔 除 掉 注 释 后 代 码 会 被 解 析 成 inti, 所 以 不 正 确 编 译 器 的 确 会 将 注 释 剔 除, 但 不 是 简 单 的 剔 除, 而 是 用 空 格 代 替 原 来 的 注 释 再 看 一 个 例 子 : /* 这 是 */#/* 一 条 */define/* 合 法 的 */ID/* 预 处 理 */replacement/* 指 */list/* 令 */ 你 可 以 用 编 译 器 试 试 B), 我 们 知 道 双 引 号 引 起 来 的 都 是 字 符 串 常 量, 那 双 斜 杠 也 不 例 外 C), 这 是 一 条 合 法 的 注 释, 因 为 \ 是 一 个 接 续 符 关 于 接 续 符, 下 面 还 有 更 多 讨 论 D), 前 面 说 过 注 释 会 被 空 格 替 换, 那 这 条 注 释 不 正 确 就 很 好 理 解 了 现 在 你 可 以 回 答 前 面 的 问 题 了 吧?
51 但 注 意 :/* */ 这 种 形 式 的 注 释 不 能 嵌 套, 如 : /* 这 是 /* 非 法 的 */*/ 因 为 /* 总 是 与 离 它 最 近 的 */ 匹 配 2.1.2,y = x/*p y=x/*p, 这 是 表 示 x 除 以 p 指 向 的 内 存 里 的 值, 把 结 果 赋 值 为 y? 我 们 可 以 在 编 译 器 上 测 试 一 下, 编 译 器 提 示 出 错 实 际 上, 编 译 器 把 /* 当 作 是 一 段 注 释 的 开 始, 把 /* 后 面 的 内 容 都 当 作 注 释 内 容, 直 到 出 现 */ 为 止 这 个 表 达 式 其 实 只 是 表 示 把 x 的 值 赋 给 y,/* 后 面 的 内 容 都 当 作 注 释 但 是, 由 于 没 有 找 到 */, 所 以 提 示 出 错 我 们 可 以 把 上 面 的 表 达 式 修 改 一 下 : y=x/ *p 或 者 y = x/(*p) 这 样 的 话, 表 达 式 的 意 思 就 是 x 除 以 p 指 向 的 内 存 里 的 值, 把 结 果 赋 值 为 y 了 也 就 是 说 只 要 斜 杠 (/) 和 星 号 (*) 之 间 没 有 空 格, 都 会 被 当 作 注 释 的 开 始 这 一 点 一 定 要 注 意 2.1.3, 怎 样 才 能 写 出 出 色 的 注 释 注 释 写 得 出 色 非 常 不 容 易, 但 是 写 得 糟 糕 却 是 人 人 可 为 之 糟 糕 的 注 释 只 会 帮 倒 忙 , 安 息 吧, 路 德 维 希. 凡. 贝 多 芬 在 Code Complete 这 本 书 中, 作 者 记 录 了 这 样 一 个 故 事 : 有 位 负 责 维 护 的 程 序 员 半 夜 被 叫 起 来, 去 修 复 一 个 出 了 问 题 的 程 序 但 是 程 序 的 原 作 者 已 经 离 职, 没 有 办 法 联 系 上 他 这 个 程 序 员 从 未 接 触 过 这 个 程 序 在 仔 细 检 查 所 有 的 说 明 后, 他 只 发 现 了 一 条 注 释, 如 下 : MOV AX 723h ;R.I. P.L.V. B. 这 个 维 护 程 序 员 通 宵 研 究 这 个 程 序, 还 是 对 注 释 百 思 不 得 其 解 虽 然 最 后 他 还 是 把 程 序 的 问 题 成 功 排 除 了, 但 这 个 神 秘 的 注 释 让 他 耿 耿 于 怀 说 明 一 点 : 汇 编 程 序 的 注 释 是 以 分 号 开 头 几 个 月 后, 这 名 程 序 员 在 一 个 会 议 上 遇 到 了 注 释 的 原 作 者 经 过 请 教 后, 才 明 白 这 条 注 释 的 意 思 : 安 息 吧, 路 德 维 希. 凡. 贝 多 芬 (Rest in peace, Ludwig Van Neethoven) 贝 多 芬 于 1827 年 逝 世, 而 1827 的 十 六 进 制 正 是 723 这 真 是 让 人 哭 笑 不 得! ,windows 大 师 们 用 注 释 讨 论 天 气 问 题 还 有 个 例 子 : 前 些 日 子 windows 的 源 代 码 曾 经 泄 漏 过 一 部 分 人 们 在 看 这 部 分 大 师 的
52 经 典 作 品 时, 却 发 现 很 多 与 代 码 毫 无 关 系 的 注 释! 有 的 注 释 在 讨 论 天 气, 有 的 在 讨 论 明 天 吃 什 么, 还 有 的 在 骂 公 司 和 老 板 这 些 注 释 虽 然 与 代 码 无 关, 但 总 比 上 面 那 个 让 贝 多 芬 安 息 的 注 释 要 强 些 的 至 少 不 会 让 你 抓 狂 不 过 这 种 事 情 只 有 大 师 们 才 可 以 做, 你 可 千 万 别 用 注 释 讨 论 天 气 , 出 色 注 释 的 基 本 要 求 规 则 2-1 注 释 应 当 准 确 易 懂, 防 止 有 二 义 性 错 误 的 注 释 不 但 无 益 反 而 有 害 规 则 2-2 边 写 代 码 边 注 释, 修 改 代 码 同 时 修 改 相 应 的 注 释, 以 保 证 注 释 与 代 码 的 一 致 性 不 再 有 用 的 注 释 要 及 时 删 除 规 则 2-3 注 释 是 对 代 码 的 提 示, 而 不 是 文 档 程 序 中 的 注 释 应 当 简 单 明 了, 注 释 太 多 了 会 让 人 眼 花 缭 乱 规 则 2-4 一 目 了 然 的 语 句 不 加 注 释 例 如 :i++; /* i 加 1*/ 多 余 的 注 释 规 则 2-5 对 于 全 局 数 据 ( 全 局 变 量 常 量 定 义 等 ) 必 须 要 加 注 释 规 则 2-6 注 释 采 用 英 文, 尽 量 避 免 在 注 释 中 使 用 缩 写, 特 别 是 不 常 用 缩 写 因 为 不 一 定 所 有 的 编 译 器 都 能 显 示 中 文, 别 人 打 开 你 的 代 码, 你 的 注 释 也 许 是 一 团 乱 码 还 有, 你 的 代 码 不 一 定 是 懂 中 文 的 人 阅 读 规 则 2-7 注 释 的 位 置 应 与 被 描 述 的 代 码 相 邻, 可 以 与 语 句 在 同 一 行, 也 可 以 在 上 行, 但 不 可 放 在 下 方 同 一 结 构 中 不 同 域 的 注 释 要 对 齐 规 则 2-8 当 代 码 比 较 长, 特 别 是 有 多 重 嵌 套 时, 应 当 在 一 些 段 落 的 结 束 处 加 注 释, 便 于 阅 读 规 则 2-9 注 释 的 缩 进 要 与 代 码 的 缩 进 一 致 规 则 2-10 注 释 代 码 段 时 应 注 重 为 何 做 (why), 而 不 是 怎 么 做 (how) 说 明 怎 么 做 的 注 释 一 般 停 留 在 编 程 语 言 的 层 次, 而 不 是 为 了 说 明 问 题 尽 力 阐 述 怎 么 做 的 注 释 一 般 没 有 告 诉 我 们 操 作 的 意 图, 而 指 明 怎 么 做 的 注 释 通 常 是 冗 余 的 规 则 2-11 数 值 的 单 位 一 定 要 注 释 注 释 应 该 说 明 某 数 值 的 单 位 到 底 是 什 么 意 思 比 如 : 关 于 长 度 的 必 须 说 明 单 位 是 毫 米, 米, 还 是 千 米 等 ; 关 于 时 间 的 必 须 说 明 单 位 是 时, 分, 秒, 还 是 毫 秒 等 规 则 2-12 对 变 量 的 范 围 给 出 注 释 规 则 2-13 对 一 系 列 的 数 字 编 号 给 出 注 释, 尤 其 在 编 写 底 层 驱 动 程 序 的 时 候 ( 比 如 管 脚 编 号 ) 规 则 2-13 对 于 函 数 的 入 口 出 口 数 据 给 出 注 释 关 于 函 数 的 注 释 在 函 数 那 章 有 更 详 细 的 讨 论
53 2.2, 接 续 符 和 转 义 符 C 语 言 里 以 反 斜 杠 (\) 表 示 断 行 编 译 器 会 将 反 斜 杠 剔 除 掉, 跟 在 反 斜 杠 后 面 的 字 符 自 动 接 续 到 前 一 行 但 是 注 意 : 反 斜 杠 之 后 不 能 有 空 格, 反 斜 杠 的 下 一 行 之 前 也 不 能 有 空 格 当 然 你 可 以 测 试 一 下 加 了 空 格 之 后 的 效 果 我 们 看 看 下 面 的 例 子 : // 这 是 一 条 合 法 的 \ 单 行 注 释 /\ / 这 是 一 条 合 法 的 单 行 注 释 #def\ ine MAC\ RO 这 是 一 条 合 法 的 \ 宏 定 义 cha\ r* s=" 这 是 一 个 合 法 的 \\ n 字 符 串 "; 反 斜 杠 除 了 可 以 被 用 作 接 续 符, 还 能 被 用 作 转 义 字 符 的 开 始 标 识 常 用 的 转 义 字 符 及 其 含 义 : 转 义 字 符 转 义 字 符 的 意 义 \n 回 车 换 行 \t 横 向 跳 到 下 一 制 表 位 置 \v 竖 向 跳 格 \b 退 格 \r 回 车 \f 走 纸 换 页 \\ 反 斜 扛 符 "\" \' 单 引 号 符 \a 鸣 铃 \ddd 1~3 位 八 进 制 数 所 代 表 的 字 符 \xhh 1~2 位 十 六 进 制 数 所 代 表 的 字 符 广 义 地 讲,C 语 言 字 符 集 中 的 任 何 一 个 字 符 均 可 用 转 义 字 符 来 表 示 表 中 的 \ddd 和 \xhh 正 是 为 此 而 提 出 的 ddd 和 hh 分 别 为 八 进 制 和 十 六 进 制 的 ASCII 代 码 如 \102 表 示 字 母 "B", \134 表 示 反 斜 线,\X0A 表 示 换 行 等
54 2.3, 单 引 号 双 引 号 我 们 知 道 双 引 号 引 起 来 的 都 是 字 符 串 常 量, 单 引 号 引 起 来 的 都 是 字 符 常 量 但 初 学 者 还 是 容 易 弄 错 这 两 点 比 如 : a 和 a 完 全 不 一 样, 在 内 存 里 前 者 占 1 个 byte, 后 者 占 2 个 byte 关 于 字 符 串 常 量 在 指 针 与 数 组 那 章 将 有 更 多 的 讨 论 这 两 个 列 子 还 好 理 解, 再 看 看 这 三 个 : 1, 1, 1 第 一 个 是 整 形 常 数,32 位 系 统 下 占 4 个 byte; 第 二 个 是 字 符 常 量, 占 1 个 byte; 第 三 个 是 字 符 串 常 量, 占 2 个 byte 三 者 表 示 的 意 义 完 全 不 一 样, 所 占 的 内 存 大 小 也 不 一 样, 初 学 者 往 往 弄 错 字 符 在 内 存 里 是 以 ASCAII 码 存 储 的, 所 以 字 符 常 量 可 以 与 整 形 常 量 或 变 量 进 行 运 算 如 : A , 逻 辑 运 算 符 和 && 是 我 们 经 常 用 到 的 逻 辑 运 算 符, 与 按 位 运 算 符 和 & 是 两 码 事 下 一 节 会 介 绍 按 位 运 算 符 虽 然 简 单, 但 毕 竟 容 易 犯 错 看 例 子 : int i=0; int j=0; if((++i>0) (++j>0)) // 打 印 出 i 和 j 的 值 结 果 :i=1;j=0 不 要 惊 讶 逻 辑 运 算 符 两 边 的 条 件 只 要 有 一 个 为 真, 其 结 果 就 为 真 ; 只 要 有 一 个 结 果 为 假, 其 结 果 就 为 假 if((++i>0) (++j>0)) 语 句 中, 先 计 算 (++i>0), 发 现 其 结 果 为 真, 后 面 的 (++j>0) 便 不 再 计 算 同 样 && 运 算 符 也 要 注 意 这 种 情 况 这 是 很 容 易 出 错 的 地 方, 希 望 读 者 注 意
55 2.5, 位 运 算 符 C 语 言 中 位 运 算 包 括 下 面 几 种 : & 按 位 与 按 位 或 ^ 按 位 异 或 ~ 取 反 << 左 移 >> 右 移 前 4 种 操 作 很 简 单, 一 般 不 会 出 错 但 要 注 意 按 位 运 算 符 和 & 与 逻 辑 运 算 符 和 && 完 全 是 两 码 事, 别 混 淆 了 其 中 按 位 异 或 操 作 可 以 实 现 不 用 第 三 个 临 时 变 量 交 换 两 个 变 量 的 值 : a^=b;b^=a;a^=b; 但 并 不 推 荐 这 么 做, 因 为 这 样 的 代 码 读 起 来 很 费 劲 2.5.1, 左 移 和 右 移 下 面 讨 论 一 下 左 移 和 右 移 : 左 移 运 算 符 << 是 双 目 运 算 符 其 功 能 把 << 左 边 的 运 算 数 的 各 二 进 位 全 部 左 移 若 干 位, 由 << 右 边 的 数 指 定 移 动 的 位 数, 高 位 丢 弃, 低 位 补 0 右 移 运 算 符 >> 是 双 目 运 算 符 其 功 能 是 把 >> 左 边 的 运 算 数 的 各 二 进 位 全 部 右 移 若 干 位, >> 右 边 的 数 指 定 移 动 的 位 数 但 注 意 : 对 于 有 符 号 数, 在 右 移 时, 符 号 位 将 随 同 移 动 当 为 正 数 时, 最 高 位 补 0; 而 为 负 数 时, 符 号 位 为 1, 最 高 位 是 补 0 或 是 补 1 取 决 于 编 译 系 统 的 规 定 Turbo C 和 很 多 系 统 规 定 为 补 ,0x01<<2+3 的 值 为 多 少? 再 看 看 下 面 的 例 子 : 0x01<<2+3; 结 果 为 7 吗? 测 试 一 下 结 果 为 32? 别 惊 讶,32 才 是 正 确 答 案 因 为 + 号 的 优 先 级 比 移 位 运 算 符 的 优 先 级 高 ( 关 于 运 算 符 的 优 先 级, 我 并 不 想 在 这 里 做 过 多 的 讨 论, 你 几 乎 可 以 在 任 何 一 本 C 语 言 书 上 找 到 ) 好, 在 32 位 系 统 下, 再 把 这 个 例 子 改 写 一 下 : 0x01<<2+30; 或 0x01<<2-3; 这 样 行 吗? 不 行 一 个 整 型 数 长 度 为 32 位, 左 移 32 位 发 生 了 什 么 事 情? 溢 出! 左 移 -1 位 呢? 反 过 来 移? 所 以, 左 移 和 右 移 的 位 数 是 有 讲 究 的 左 移 和 右 移 的 位 数 不 能 大 于 数 据 的 长 度, 不 能 小 于 0
56 2.6, 花 括 号 花 括 号 每 个 人 都 见 过, 很 简 单 吧 但 曾 经 有 一 个 学 生 问 过 我 如 下 问 题 : char a[10] = abcde ; 他 不 理 解 为 什 么 这 个 表 达 式 正 确 我 让 他 继 续 改 一 下 这 个 例 子 : char a[10] = abcde ; 问 他 这 样 行 不 行 那 读 者 以 为 呢? 为 什 么? 花 括 号 的 作 用 是 什 么 呢? 我 们 平 时 写 函 数,if while for switch 语 句 等 都 用 到 了 它, 但 有 时 又 省 略 掉 了 它 简 单 来 说 花 括 号 的 作 用 就 是 打 包 你 想 想 以 前 用 花 括 号 是 不 是 为 了 把 一 些 语 句 或 代 码 打 个 包 包 起 来, 使 之 形 成 一 个 整 体, 并 与 外 界 绝 缘 这 样 理 解 的 话, 上 面 的 问 题 就 不 是 问 题 了 2.7,++ -- 操 作 符 这 绝 对 是 一 对 让 人 头 疼 的 兄 弟 先 来 点 简 单 的 : int i = 3; (++i)+(++i)+(++i); 表 达 式 的 值 为 多 少?15 吗?16 吗?18 吗? 其 实 对 于 这 种 情 况,C 语 言 标 准 并 没 有 作 出 规 定 有 点 编 译 器 计 算 出 来 为 18, 因 为 i 经 过 3 次 自 加 后 变 为 6, 然 后 3 个 6 相 加 得 18; 而 有 的 编 译 器 计 算 出 来 为 16( 比 如 Visual C++6.0), 先 计 算 前 两 个 i 的 和, 这 时 候 i 自 加 两 次,2 个 i 的 和 为 10, 然 后 再 加 上 第 三 次 自 加 的 i 得 16 其 实 这 些 没 有 必 要 辩 论, 用 到 哪 个 编 译 器 写 句 代 码 测 试 就 行 了 但 不 会 计 算 出 15 的 结 果 来 的 作 为 前 缀, 我 们 知 道 是 先 自 加 或 自 减, 然 后 再 做 别 的 运 算 ; 但 是 作 为 后 缀 时, 到 底 什 么 时 候 自 加 自 减? 这 是 很 多 初 学 者 迷 糊 的 地 方 假 设 i=0, 看 例 子 : A),j =(i++,i++,i++); B),for(i=0;i<10;i++) //code C),k = (i++)+ (i++)+ (i++); 你 可 以 试 着 计 算 他 们 的 结 果 A) 例 子 为 逗 号 表 达 式,i 在 遇 到 每 个 逗 号 后, 认 为 本 计 算 单 位 已 经 结 束,i 这 时 候 自 加 关 于 逗 号 表 达 式 与 ++ 或 -- 的 连 用, 还 有 一 个 比 较 好 的 例 子 : int x; int i = 3;
57 x = (++i, i++, i+10); 问 x 的 值 为 多 少?i 的 值 为 多 少? 按 照 上 面 的 讲 解, 可 以 很 清 楚 的 知 道, 逗 号 表 达 式 中,i 在 遇 到 每 个 逗 号 后, 认 为 本 计 算 单 位 已 经 结 束,i 这 时 候 自 加 所 以, 本 例 子 计 算 完 后,i 的 值 为 5,x 的 值 为 15 B) 例 子 i 与 10 进 行 比 较 之 后, 认 为 本 计 算 单 位 已 经 结 束,i 这 时 候 自 加 C) 例 子 i 遇 到 分 号 才 认 为 本 计 算 单 位 已 经 结 束,i 这 时 候 自 加 也 就 是 说 后 缀 运 算 是 在 本 计 算 单 位 计 算 结 束 之 后 再 自 加 或 自 减 C 语 言 里 的 计 算 单 位 大 体 分 为 以 上 3 类 留 一 个 问 题 : for(i=0,printf( First=%d,i); i<10,printf( Second=%d,i); i++,printf( Third=%d,i)) printf( Fourth=%d,i); 打 印 出 什 么 结 果? 2.7.1,++i+++i+++i 上 面 的 例 子 很 简 单, 那 我 们 把 括 号 去 掉 看 看 : int i = 3; ++i+++i+++i; 天 啦! 这 到 底 是 什 么 东 西? 好, 我 们 先 看 看 这 个 :a+++b 和 下 面 哪 个 表 达 式 想 当 : A),a++ +b; B),a+ ++b; 2.7.2, 贪 心 法 C 语 言 有 这 样 一 个 规 则 : 每 一 个 符 号 应 该 包 含 尽 可 能 多 的 字 符 也 就 是 说, 编 译 器 将 程 序 分 解 成 符 号 的 方 法 是, 从 左 到 右 一 个 一 个 字 符 地 读 入, 如 果 该 字 符 可 能 组 成 一 个 符 号, 那 么 再 读 入 下 一 个 字 符, 判 断 已 经 读 入 的 两 个 字 符 组 成 的 字 符 串 是 否 可 能 是 一 个 符 号 的 组 成 部 分 ; 如 果 可 能, 继 续 读 入 下 一 个 字 符, 重 复 上 述 判 断, 直 到 读 入 的 字 符 组 成 的 字 符 串 已 不 再 可 能 组 成 一 个 有 意 义 的 符 号 这 个 处 理 的 策 略 被 称 为 贪 心 法 需 要 注 意 到 是, 除 了 字 符 串 与 字 符 常 量, 符 号 的 中 间 不 能 嵌 有 空 白 ( 空 格 制 表 符 换 行 符 等 ) 比 如 :== 是
58 单 个 符 号, 而 == 是 两 个 等 号 按 照 这 个 规 则 可 能 很 轻 松 的 判 断 a+++b 表 达 式 与 a++ +b 一 致 那 ++i+++i+++i; 会 被 解 析 成 什 么 样 子 呢? 希 望 读 者 好 好 研 究 研 究 另 外 还 可 以 考 虑 一 下 这 个 表 达 式 的 意 思 : a+++++b; 2.8,2/(-2) 的 值 是 多 少? 除 法 运 算 在 小 学 就 掌 握 了 的, 这 里 还 要 讨 论 什 么 呢? 别 急, 先 计 算 下 面 这 个 例 子 : 2/(-2) 的 值 为 多 少?2%(-2) 的 值 呢? 如 果 与 你 想 象 的 结 果 不 一 致, 不 要 惊 讶 我 们 先 看 看 下 面 这 些 规 则 : 假 定 我 们 让 a 除 以 b, 商 为 q, 余 数 为 r: q=a/b; r=a%b; 这 里 不 妨 先 假 定 b 大 于 0 我 们 希 望 a b q r 之 间 维 持 什 么 样 的 关 系 呢? 1, 最 重 要 的 一 点, 我 们 希 望 q*b + r == a, 因 为 这 是 定 义 余 数 的 关 系 2, 如 果 我 们 改 变 a 的 正 负 号, 我 们 希 望 q 的 符 号 也 随 之 改 变, 但 q 的 绝 对 值 不 会 变 3, 当 b>0 时, 我 们 希 望 保 证 r>=0 且 r<b 这 三 条 性 质 是 我 们 认 为 整 数 除 法 和 余 数 操 作 所 应 该 具 备 的 但 是, 很 不 幸, 它 们 不 可 能 同 时 成 立 先 考 虑 一 个 简 单 的 例 子 :3/2, 商 为 1, 余 数 也 为 1 此 时, 第 一 条 性 质 得 到 了 满 足 好, 把 例 子 稍 微 改 写 一 下 :(-3)/2 的 值 应 该 是 多 少 呢? 如 果 要 满 足 第 二 条 性 质, 答 案 应 该 是 -1 但 是, 如 果 是 这 样, 余 数 就 必 定 是 -1, 这 样 第 三 条 性 质 就 无 法 满 足 了 如 果 我 们 首 先 满 足 第 三 条 性 质, 即 余 数 是 1, 这 种 情 况 下 根 据 第 一 条 性 质, 商 应 该 为 -2, 那 么 第 二 条 性 质 又 无 法 满 足 了 上 面 的 矛 盾 似 乎 无 法 解 决 因 此,C 语 言 或 者 其 他 语 言 在 实 现 整 数 除 法 截 断 运 算 时, 必 须 放 弃 上 述 三 条 性 质 中 的 至 少 一 条 大 多 数 编 程 语 言 选 择 了 放 弃 第 三 条, 而 改 为 要 求 余 数 与 被 除 数 的 正 负 号 相 同 这 样 性 质 1 和 性 质 2 就 可 以 得 到 满 足 大 多 数 C 语 言 编 译 器 也 都 是 如 此 但 是,C 语 言 的 定 义 只 保 证 了 性 质 1, 以 及 当 a>=0 且 b>0 时, 保 证 r < b 以 及 r>=0 后 面 部 分 的 保 证 与 性 质 2 或 性 质 3 比 较 起 来, 限 制 性 要 弱 得 多 通 过 上 面 的 解 释, 你 是 否 能 准 确 算 出 2/(-2) 和 2%(-2) 的 值 呢? 2.9, 运 算 符 的 优 先 级 2.9.1, 运 算 符 的 优 先 级 表 C 语 言 的 符 号 众 多, 由 这 些 符 号 又 组 合 成 了 各 种 各 样 的 运 算 符 既 然 是 运 算 符 就 一 定 有 其 特 定 的 优 先 级, 下 表 就 是 C 语 言 运 算 符 的 优 先 级 表 : 优 先 级 运 算 符 名 称 或 含 义 使 用 形 式 结 合 方 向 说 明
59 [] 数 组 下 标 数 组 名 [ 常 量 表 达 式 ] ( 表 达 式 )/ 函 数 名 ( 形 () 圆 括 号 1 参 表 ) 左 到 右. 成 员 选 择 ( 对 象 ) 对 象. 成 员 名 -> 成 员 选 择 ( 指 针 ) 对 象 指 针 -> 成 员 名 - 负 号 运 算 符 - 表 达 式 单 目 运 算 符 ( 类 型 ) 强 制 类 型 转 换 ( 数 据 类 型 ) 表 达 式 ++ 自 增 运 算 符 ++ 变 量 名 / 变 量 名 ++ 单 目 运 算 符 -- 自 减 运 算 符 -- 变 量 名 / 变 量 名 -- 单 目 运 算 符 2 * 取 值 运 算 符 * 指 针 变 量 右 到 左 单 目 运 算 符 & 取 地 址 运 算 符 & 变 量 名 单 目 运 算 符! 逻 辑 非 运 算 符! 表 达 式 单 目 运 算 符 ~ 按 位 取 反 运 算 符 ~ 表 达 式 单 目 运 算 符 sizeof 长 度 运 算 符 sizeof( 表 达 式 ) / 除 表 达 式 / 表 达 式 双 目 运 算 符 * 乘 表 达 式 * 表 达 式 双 目 运 算 符 3 左 到 右 整 型 表 达 式 / 整 型 表 % 余 数 ( 取 模 ) 双 目 运 算 符 达 式 4 + 加 表 达 式 + 表 达 式 双 目 运 算 符 左 到 右 - 减 表 达 式 - 表 达 式 双 目 运 算 符 5 << 左 移 变 量 << 表 达 式 双 目 运 算 符 左 到 右 >> 右 移 变 量 >> 表 达 式 双 目 运 算 符 > 大 于 表 达 式 > 表 达 式 双 目 运 算 符 6 >= 大 于 等 于 表 达 式 >= 表 达 式 双 目 运 算 符 左 到 右 < 小 于 表 达 式 < 表 达 式 双 目 运 算 符 <= 小 于 等 于 表 达 式 <= 表 达 式 双 目 运 算 符 7 == 等 于 表 达 式 == 表 达 式 双 目 运 算 符 左 到 右!= 不 等 于 表 达 式!= 表 达 式 双 目 运 算 符 8 & 按 位 与 表 达 式 & 表 达 式 左 到 右 双 目 运 算 符 9 ^ 按 位 异 或 表 达 式 ^ 表 达 式 左 到 右 双 目 运 算 符 10 按 位 或 表 达 式 表 达 式 左 到 右 双 目 运 算 符 11 && 逻 辑 与 表 达 式 && 表 达 式 左 到 右 双 目 运 算 符 12 逻 辑 或 表 达 式 表 达 式 左 到 右 双 目 运 算 符 13?: 条 件 运 算 符 表 达 式 1? 表 达 式 2: 表 达 式 3 右 到 左 三 目 运 算 符 = 赋 值 运 算 符 变 量 = 表 达 式 /= 除 后 赋 值 变 量 /= 表 达 式 *= 乘 后 赋 值 变 量 *= 表 达 式 14 %= 取 模 后 赋 值 变 量 %= 表 达 式 += 加 后 赋 值 变 量 += 表 达 式 右 到 左 -= 减 后 赋 值 变 量 -= 表 达 式 <<= 左 移 后 赋 值 变 量 <<= 表 达 式 >>= 右 移 后 赋 值 变 量 >>= 表 达 式
60 &= 按 位 与 后 赋 值 变 量 &= 表 达 式 ^= 按 位 异 或 后 赋 值 变 量 ^= 表 达 式 = 按 位 或 后 赋 值 变 量 = 表 达 式 15, 逗 号 运 算 符 表 达 式, 表 达 式, 左 到 右 从 左 向 右 顺 序 运 算 注 : 同 一 优 先 级 的 运 算 符, 运 算 次 序 由 结 合 方 向 所 决 定 上 表 不 容 易 记 住 其 实 也 用 不 着 死 记, 用 得 多, 看 得 多 自 然 就 记 得 了 也 有 人 说 不 用 记 这 些 东 西, 只 要 记 住 乘 除 法 的 优 先 级 比 加 减 法 高 就 行 了, 别 的 地 方 一 律 加 上 括 号 这 在 你 自 己 写 代 码 的 时 候, 确 实 可 以, 但 如 果 是 你 去 阅 读 和 理 解 别 人 的 代 码 呢? 别 人 不 一 定 都 加 上 括 号 了 吧? 所 以, 记 住 这 个 表, 我 个 人 认 为 还 是 很 有 必 要 的 2.9.2, 一 些 容 易 出 错 的 优 先 级 问 题 上 表 中, 优 先 级 同 为 1 的 几 种 运 算 符 如 果 同 时 出 现, 那 怎 么 确 定 表 达 式 的 优 先 级 呢? 这 是 很 多 初 学 者 迷 糊 的 地 方 下 表 就 整 理 了 这 些 容 易 出 错 的 情 况 : 优 先 级 问 题 表 达 式 经 常 误 认 为 的 结 果 实 际 结 果. 的 优 先 级 高 于 * *p.f p 所 指 对 象 的 字 段 f 对 p 取 f 偏 移, 作 为 -> 操 作 符 用 于 消 除 这 (*p).f 指 针, 然 后 进 行 解 除 个 问 题 引 用 操 作 *(p.f) [] 高 于 * int *ap[] ap 是 个 指 向 int 数 组 ap 是 个 元 素 为 int 的 指 针 指 针 的 数 组 int (*ap)[] int *(ap[]) 函 数 () 高 于 * int *fp() fp 是 个 函 数 指 针, 所 fp 是 个 函 数, 返 回 指 函 数 返 回 int int * int (*fp)() int *(fp()) == 和!= 高 于 位 操 作 (val & mask!= 0) (val & mask)!= 0 val & (mask!= 0) == 和!= 高 于 赋 值 符 c = getchar()!= (c = getchar())!= c = (getchar()!= EOF EOF EOF) 算 术 运 算 符 高 于 位 移 msb<<4+lsb (msb<<4)+lsb msb<<(4+lsb) 运 算 符 逗 号 运 算 符 在 所 有 运 i=1,2 i=(1,2) (i=1),2 算 符 中 优 先 级 最 低 这 些 容 易 出 错 的 情 况, 希 望 读 者 好 好 在 编 译 器 上 调 试 调 试, 这 样 印 象 会 深 一 些 一 定 要 多 调 试, 光 靠 看 代 码, 水 平 是 很 难 提 上 来 的 调 试 代 码 才 是 最 长 水 平 的
61 第 三 章 预 处 理 往 往 我 说 今 天 上 课 的 内 容 是 预 处 理 时, 便 有 学 生 质 疑 : 预 处 理 不 就 是 include 和 define 么? 这 也 用 得 着 讲 啊? 是 的, 非 常 值 得 讨 论, 即 使 是 include 和 define 但 是 预 处 理 仅 限 于 此 吗? 远 远 不 止 先 看 几 个 个 常 识 性 问 题 : A), 预 处 理 是 C 语 言 的 一 部 分 吗? B), 包 含 # 号 的 都 是 预 处 理 吗? C), 预 处 理 指 令 后 面 都 不 需 要 加 ; 号 吗? 不 要 急 着 回 答, 先 看 看 ANSI 标 准 定 义 的 C 语 言 预 处 理 指 令 : 表 (3.1) 预 处 理 指 令 预 处 理 名 称 意 义 #define #undef #include #if #else #elif #endif #ifdef #ifndef #line 宏 定 义 撤 销 已 定 义 过 的 宏 名 使 编 译 程 序 将 另 一 源 文 件 嵌 入 到 带 有 #include 的 源 文 件 中 #if 的 一 般 含 义 是 如 果 #if 后 面 的 常 量 表 达 式 为 true, 则 编 译 它 与 #endif 之 间 的 代 码, 否 则 跳 过 这 些 代 码 命 令 #endif 标 识 一 个 #if 块 的 结 束 #else 命 令 的 功 能 有 点 象 C 语 言 中 的 else,#else 建 立 另 一 选 择 ( 在 #if 失 败 的 情 况 下 ) #elif 命 令 意 义 与 else if 相 同, 它 形 成 一 个 if else-if 阶 梯 状 语 句, 可 进 行 多 种 编 译 选 择 用 #ifdef 与 #ifndef 命 令 分 别 表 示 如 果 有 定 义 及 如 果 无 定 义, 是 条 件 编 译 的 另 一 种 方 法 改 变 当 前 行 数 和 文 件 名 称, 它 们 是 在 编 译 程 序 中 预 先 定 义 的 标 识 符 命 令 的 基 本 形 式 如 下 : #line number["filename"] #error #pragma 编 译 程 序 时, 只 要 遇 到 #error 就 会 生 成 一 个 编 译 错 误 提 示 消 息, 并 停 止 编 译 为 实 现 时 定 义 的 命 令, 它 允 许 向 编 译 程 序 传 送 各 种 指 令 例 如, 编 译 程 序 可 能 有 一 种 选 择, 它 支 持 对 程 序 执 行 的 跟 踪 可 用 #pragma 语 句 指 定 一 个 跟 踪 选 择 另 外 ANSI 标 准 C 还 定 义 了 如 下 几 个 宏 : _LINE_ 表 示 正 在 编 译 的 文 件 的 行 号 _FILE_ 表 示 正 在 编 译 的 文 件 的 名 字
62 _DATE_ 表 示 编 译 时 刻 的 日 期 字 符 串, 例 如 : "25 Dec 2007" _TIME_ 表 示 编 译 时 刻 的 时 间 字 符 串, 例 如 : "12:30:55" _STDC_ 判 断 该 文 件 是 不 是 定 义 成 标 准 C 程 序 如 果 编 译 器 不 是 标 准 的, 则 可 能 仅 支 持 以 上 宏 的 一 部 分, 或 根 本 不 支 持 当 然 编 译 器 也 有 可 能 还 提 供 其 它 预 定 义 的 宏 名 注 意 : 宏 名 的 书 写 由 标 识 符 与 两 边 各 二 条 下 划 线 构 成 相 信 很 多 初 学 者, 甚 至 一 些 有 经 验 的 程 序 员 都 没 有 完 全 掌 握 这 些 内 容, 下 面 就 一 一 详 细 讨 论 这 些 预 处 理 指 令 3.1, 宏 定 义 3.1.1, 数 值 宏 常 量 #define 宏 定 义 是 个 演 技 非 常 高 超 的 替 身 演 员, 但 也 会 经 常 耍 大 牌 的, 所 以 我 们 用 它 要 慎 之 又 慎 它 可 以 出 现 在 代 码 的 任 何 地 方, 从 本 行 宏 定 义 开 始, 以 后 的 代 码 就 就 都 认 识 这 个 宏 了 ; 也 可 以 把 任 何 东 西 定 义 成 宏 因 为 编 译 器 会 在 预 编 译 的 时 候 用 真 身 替 换 替 身, 而 在 我 们 的 代 码 里 面 却 又 用 常 常 用 替 身 来 帮 忙 看 例 子 : #define PI 在 此 后 的 代 码 中 你 尽 可 以 使 用 PI 来 代 替 , 而 且 你 最 好 就 这 么 做 不 然 的 话, 如 果 我 要 把 PI 的 精 度 再 提 高 一 些, 你 是 否 愿 意 一 个 一 个 的 去 修 改 这 串 数 呢? 你 能 保 证 不 漏 不 出 错? 而 使 用 PI 的 话, 我 们 却 只 需 要 修 改 一 次 这 种 情 况 还 不 是 最 要 命 的, 我 们 再 看 一 个 例 子 : #define ERROR_POWEROFF -1 如 果 你 在 代 码 里 不 用 ERROR_POWEROFF 这 个 宏 而 用 -1, 尤 其 在 函 数 返 回 错 误 代 码 的 时 候 ( 往 往 一 个 开 发 一 个 系 统 需 要 定 义 很 多 错 误 代 码 ) 肯 怕 上 帝 都 无 法 知 道 -1 表 示 的 是 什 么 意 思 吧 这 个 -1, 我 们 一 般 称 为 魔 鬼 数, 上 帝 遇 到 它 也 会 发 狂 的 所 以, 我 奉 劝 你 代 码 里 一 定 不 要 出 现 魔 鬼 数 第 一 章 我 们 详 细 讨 论 了 const 这 个 关 键 字, 我 们 知 道 const 修 饰 的 数 据 是 有 类 型 的, 而 define 宏 定 义 的 数 据 没 有 类 型 为 了 安 全, 我 建 议 你 以 后 在 定 义 一 些 宏 常 数 的 时 候 用 const 代 替, 编 译 器 会 给 const 修 饰 的 只 读 变 量 做 类 型 校 验, 减 少 错 误 的 可 能 但 一 定 要 注 意 const 修 饰 的 不 是 常 量 而 是 readonly 的 变 量,const 修 饰 的 只 读 变 量 不 能 用 来 作 为 定 义 数 组 的 维 数, 也 不 能 放 在 case 关 键 字 后 面 3.1.2, 字 符 串 宏 常 量 除 了 定 义 宏 常 数 之 外, 经 常 还 用 来 定 义 字 符 串, 尤 其 是 路 径 : A),#define ENG_PATH_1 E:\English\listen_to_this\listen_to_this_3 B),#define ENG_PATH_2 E:\English\listen_to_this\listen_to_this_3
63 噢, 到 底 哪 一 个 正 确 呢? 如 果 路 径 太 长, 一 行 写 下 来 比 较 别 扭 怎 么 办? 用 反 斜 杠 接 续 符 啊 : C), #define ENG_PATH_3 E:\English\listen_to_this\listen\ _to_this_3 还 没 发 现 问 题? 这 里 用 了 4 个 反 斜 杠, 到 底 哪 个 是 接 续 符? 回 去 看 看 接 续 符 反 斜 杠 反 斜 杠 作 为 接 续 符 时, 在 本 行 其 后 面 不 能 再 有 任 何 字 符, 空 格 都 不 行 所 以, 只 有 最 后 一 个 反 斜 杠 才 是 接 续 符 至 于 A) 和 B), 那 要 看 你 怎 么 用 了, 既 然 define 宏 只 是 简 单 的 替 换, 那 给 ENG_PATH_1 加 上 双 引 号 不 就 成 了 : ENG_PATH_1 但 是 请 注 意 : 有 的 系 统 里 规 定 路 径 的 要 用 双 反 斜 杠 \\, 比 如 : #define ENG_PATH_4 E:\\English\\listen_to_this\\listen_to_this_ , 用 define 宏 定 义 注 释 符 号? 上 面 对 define 的 使 用 都 很 简 单, 再 看 看 下 面 的 例 子 : #define BSC // #define BMC /* #define EMC */ D),BSC my single-line comment E),BMC my multi-line comment EMC D) 和 E) 都 错 误, 为 什 么 呢? 因 为 注 释 先 于 预 处 理 指 令 被 处 理, 当 这 两 行 被 展 开 成 // 或 /* */ 时, 注 释 已 处 理 完 毕, 此 时 再 出 现 // 或 /* */ 自 然 错 误. 因 此, 试 图 用 宏 开 始 或 结 束 一 段 注 释 是 不 行 的 3.1.4, 用 define 宏 定 义 表 达 式 这 些 都 好 理 解, 下 面 来 点 有 技 术 含 量 的 : 定 义 一 年 有 多 少 秒 : #define SEC_A_YEAR 60*60*24*365 这 个 定 义 没 错 吧? 很 遗 憾, 很 有 可 能 错 了, 至 少 不 可 靠 你 有 没 有 考 虑 在 16 位 系 统 下 把 这 样 一 个 数 赋 给 整 型 变 量 的 时 候 可 能 会 发 生 溢 出? 一 年 有 多 少 秒 也 不 可 能 是 负 数 吧 修 改 一 下 : #define SEC_A_YEAR (60*60*24*365)UL 又 出 现 一 个 问 题, 这 里 的 括 号 到 底 需 不 需 要 呢? 继 续 看 一 个 例 子 : 定 义 一 个 宏 函 数, 求 x 的 平 方 :
64 #define SQR (x) x * x 对 不 对? 试 试 : 假 设 x 的 值 为 10,SQR (x) 被 替 换 后 变 成 10*10 没 有 问 题 再 试 试 : 假 设 x 的 值 是 个 表 达 式 10+1,SQR (x) 被 替 换 后 变 成 10+1*10+1 问 题 来 了, 这 并 不 是 我 想 要 得 到 的 怎 么 办? 括 号 括 起 来 不 就 完 了? #define SQR (x) ((x)*(x)) 最 外 层 的 括 号 最 好 也 别 省 了, 看 例 子 : 求 两 个 数 的 和 : #define SUM (x) (x)+(x) 如 果 x 的 值 是 个 表 达 式 5*3, 而 代 码 又 写 成 这 样 :SUM (x)* SUM (x) 替 换 后 变 成 :(5*3)+ (5*3)*(5*3)+(5*3) 又 错 了! 所 以 最 外 层 的 括 号 最 好 也 别 省 了 我 说 过 define 是 个 演 技 高 超 的 替 身 演 员, 但 也 经 常 耍 大 牌 要 搞 定 它 其 实 很 简 单, 别 吝 啬 括 号 就 行 了 注 意 这 一 点 : 宏 函 数 被 调 用 时 是 以 实 参 代 换 形 参 而 不 是 值 传 送 留 四 个 问 题 : A), 上 述 宏 定 义 中 SUM SQR 是 宏 吗? B),#define EMPTY 这 样 定 义 行 吗? C), 打 印 上 述 宏 定 义 的 值 :printf( SUM (x) ); 结 果 是 什 么? D), #define M 100 是 宏 定 义 吗? 3.1.5, 宏 定 义 中 的 空 格 另 外 还 有 一 个 问 题 需 要 引 起 注 意, 看 下 面 例 子 : #define SUM (x) (x)+(x) 这 还 是 定 义 的 宏 函 数 SUM(x) 吗? 显 然 不 是 编 译 器 认 为 这 是 定 义 了 一 个 宏 :SUM, 其 代 表 的 是 (x) (x)+(x) 为 什 么 会 这 样 呢? 其 关 键 问 题 还 是 在 于 SUM 后 面 的 这 个 空 格 所 以 在 定 义 宏 的 时 候 一 定 要 注 意 什 么 时 候 该 用 空 格, 什 么 时 候 不 该 用 空 格 这 个 空 格 仅 仅 在 定 义 的 时 候 有 效, 在 使 用 这 个 宏 函 数 的 时 候, 空 格 会 被 编 译 器 忽 略 掉 也 就 是 说, 上 一 节 定 义 好 的 宏 函 数 SUM(x) 在 使 用 的 时 候 在 SUM 和 (x) 之 间 留 有 空 格 是 没 问 题 的 比 如 :SUM(3) 和 SUM (3) 的 意 思 是 一 样 的 3.1.6,#undef #undef 是 用 来 撤 销 宏 定 义 的, 用 法 如 下 : #define PI // code
65 #undef PI // 下 面 的 代 码 就 不 能 用 PI 了, 它 已 经 被 撤 销 了 宏 定 义 也 就 是 说 宏 的 生 命 周 期 从 #define 开 始 到 #undef 结 束 很 简 单, 但 是 请 思 考 一 下 这 个 问 题 : #define X 3 #define Y X*2 #undef X #define X 2 int z=y; z 的 值 为 多 少? 3.2, 条 件 编 译 条 件 编 译 的 功 能 使 得 我 们 可 以 按 不 同 的 条 件 去 编 译 不 同 的 程 序 部 分, 因 而 产 生 不 同 的 目 标 代 码 文 件 这 对 于 程 序 的 移 植 和 调 试 是 很 有 用 的 条 件 编 译 有 三 种 形 式, 下 面 分 别 介 绍 : 第 一 种 形 式 : #ifdef 标 识 符 程 序 段 1 #else 程 序 段 2 #endif 它 的 功 能 是, 如 果 标 识 符 已 被 #define 命 令 定 义 过 则 对 程 序 段 1 进 行 编 译 ; 否 则 对 程 序 段 2 进 行 编 译 如 果 没 有 程 序 段 2( 它 为 空 ), 本 格 式 中 的 #else 可 以 没 有, 即 可 以 写 为 : #ifdef 标 识 符 程 序 段 #endif 第 二 种 形 式 : #ifndef 标 识 符 程 序 段 1 #else 程 序 段 2 #endif 与 第 一 种 形 式 的 区 别 是 将 ifdef 改 为 ifndef 它 的 功 能 是, 如 果 标 识 符 未 被 #define 命 令 定 义 过 则 对 程 序 段 1 进 行 编 译, 否 则 对 程 序 段 2 进 行 编 译 这 与 第 一 种 形 式 的 功 能 正 相 反 第 三 种 形 式 : #if 常 量 表 达 式 程 序 段 1
66 #else 程 序 段 2 #endif 它 的 功 能 是, 如 常 量 表 达 式 的 值 为 真 ( 非 0), 则 对 程 序 段 1 进 行 编 译, 否 则 对 程 序 段 2 进 行 编 译 因 此 可 以 使 程 序 在 不 同 条 件 下, 完 成 不 同 的 功 能 至 于 #elif 命 令 意 义 与 else if 相 同, 它 形 成 一 个 if else-if 阶 梯 状 语 句, 可 进 行 多 种 编 译 选 择 3.3, 文 件 包 含 文 件 包 含 是 预 处 理 的 一 个 重 要 功 能, 它 可 用 来 把 多 个 源 文 件 连 接 成 一 个 源 文 件 进 行 编 译, 结 果 将 生 成 一 个 目 标 文 件 C 语 言 提 供 #include 命 令 来 实 现 文 件 包 含 的 操 作, 它 实 际 是 宏 替 换 的 延 伸, 有 两 种 格 式 : 格 式 1: #include <filename> 其 中,filename 为 要 包 含 的 文 件 名 称, 用 尖 括 号 括 起 来, 也 称 为 头 文 件, 表 示 预 处 理 到 系 统 规 定 的 路 径 中 去 获 得 这 个 文 件 ( 即 C 编 译 系 统 所 提 供 的 并 存 放 在 指 定 的 子 目 录 下 的 头 文 件 ) 找 到 文 件 后, 用 文 件 内 容 替 换 该 语 句 格 式 2: #include filename 其 中,filename 为 要 包 含 的 文 件 名 称 双 引 号 表 示 预 处 理 应 在 当 前 目 录 中 查 找 文 件 名 为 filename 的 文 件, 若 没 有 找 到, 则 按 系 统 指 定 的 路 径 信 息, 搜 索 其 他 目 录 找 到 文 件 后, 用 文 件 内 容 替 换 该 语 句 需 要 强 调 的 一 点 是 :#include 是 将 已 存 在 文 件 的 内 容 嵌 入 到 当 前 文 件 中 另 外 关 于 #include 的 路 径 也 有 点 要 说 明 :include 支 持 相 对 路 径, 格 式 如 trackant( 蚁 迹 寻 踪 ) 所 写 :. 代 表 当 前 目 录,.. 代 表 上 层 目 录 3.4,#error 预 处 理 #error 预 处 理 指 令 的 作 用 是, 编 译 程 序 时, 只 要 遇 到 #error 就 会 生 成 一 个 编 译 错 误 提 示 消 息, 并 停 止 编 译 其 语 法 格 式 为 : #error error-message 注 意, 宏 串 error-message 不 用 双 引 号 包 围 遇 到 #error 指 令 时, 错 误 信 息 被 显 示, 可 能 同 时 还 显 示 编 译 程 序 作 者 预 先 定 义 的 其 他 内 容 关 于 系 统 所 支 持 的 error-message 信 息, 请 查 找 相 关 资 料, 这 里 不 浪 费 篇 幅 来 做 讨 论
67 3.5,#line 预 处 理 #line 的 作 用 是 改 变 当 前 行 数 和 文 件 名 称, 它 们 是 在 编 译 程 序 中 预 先 定 义 的 标 识 符 命 令 的 基 本 形 式 如 下 : #line number["filename"] 其 中 [] 内 的 文 件 名 可 以 省 略 例 如 : #line 30 a.h 其 中, 文 件 名 a.h 可 以 省 略 不 写 这 条 指 令 可 以 改 变 当 前 的 行 号 和 文 件 名, 例 如 上 面 的 这 条 预 处 理 指 令 就 可 以 改 变 当 前 的 行 号 为 30, 文 件 名 是 a.h 初 看 起 来 似 乎 没 有 什 么 用, 不 过, 他 还 是 有 点 用 的, 那 就 是 用 在 编 译 器 的 编 写 中, 我 们 知 道 编 译 器 对 C 源 码 编 译 过 程 中 会 产 生 一 些 中 间 文 件, 通 过 这 条 指 令, 可 以 保 证 文 件 名 是 固 定 的, 不 会 被 这 些 中 间 文 件 代 替, 有 利 于 进 行 分 析 3.6,#pragma 预 处 理 在 所 有 的 预 处 理 指 令 中,#pragma 指 令 可 能 是 最 复 杂 的 了, 它 的 作 用 是 设 定 编 译 器 的 状 态 或 者 是 指 示 编 译 器 完 成 一 些 特 定 的 动 作 #pragma 指 令 对 每 个 编 译 器 给 出 了 一 个 方 法, 在 保 持 与 C 和 C++ 语 言 完 全 兼 容 的 情 况 下, 给 出 主 机 或 操 作 系 统 专 有 的 特 征 依 据 定 义, 编 译 指 示 是 机 器 或 操 作 系 统 专 有 的, 且 对 于 每 个 编 译 器 都 是 不 同 的 其 格 式 一 般 为 : #pragma para 其 中 para 为 参 数, 下 面 来 看 一 些 常 用 的 参 数 3.6.1,#pragma message message 参 数 :Message 参 数 是 我 最 喜 欢 的 一 个 参 数, 它 能 够 在 编 译 信 息 输 出 窗 口 中 输 出 相 应 的 信 息, 这 对 于 源 代 码 信 息 的 控 制 是 非 常 重 要 的 其 使 用 方 法 为 : #pragma message( 消 息 文 本 ) 当 编 译 器 遇 到 这 条 指 令 时 就 在 编 译 输 出 窗 口 中 将 消 息 文 本 打 印 出 来 当 我 们 在 程 序 中 定 义 了 许 多 宏 来 控 制 源 代 码 版 本 的 时 候, 我 们 自 己 有 可 能 都 会 忘 记 有 没 有 正 确 的 设 置 这 些 宏, 此 时 我 们 可 以 用 这 条 指 令 在 编 译 的 时 候 就 进 行 检 查 假 设 我 们 希 望 判 断 自 己 有 没 有 在 源 代 码 的 什 么 地 方 定 义 了 _X86 这 个 宏 可 以 用 下 面 的 方 法 #ifdef _X86 #Pragma message( _X86 macro activated! ) #endif 当 我 们 定 义 了 _X86 这 个 宏 以 后, 应 用 程 序 在 编 译 时 就 会 在 编 译 输 出 窗 口 里 显 示 _ X86 macro activated! 我 们 就 不 会 因 为 不 记 得 自 己 定 义 的 一 些 特 定 的 宏 而 抓 耳 挠 腮 了
68 3.6.2,#pragma code_seg 另 一 个 使 用 得 比 较 多 的 pragma 参 数 是 code_seg 格 式 如 : #pragma code_seg( ["section-name"[,"section-class"] ] ) 它 能 够 设 置 程 序 中 函 数 代 码 存 放 的 代 码 段, 当 我 们 开 发 驱 动 程 序 的 时 候 就 会 使 用 到 它 3.6.3,#pragma once #pragma once ( 比 较 常 用 ) 只 要 在 头 文 件 的 最 开 始 加 入 这 条 指 令 就 能 够 保 证 头 文 件 被 编 译 一 次, 这 条 指 令 实 际 上 在 Visual C++6.0 中 就 已 经 有 了, 但 是 考 虑 到 兼 容 性 并 没 有 太 多 的 使 用 它 3.6.4,#pragma hdrstop #pragma hdrstop 表 示 预 编 译 头 文 件 到 此 为 止, 后 面 的 头 文 件 不 进 行 预 编 译 BCB 可 以 预 编 译 头 文 件 以 加 快 链 接 的 速 度, 但 如 果 所 有 头 文 件 都 进 行 预 编 译 又 可 能 占 太 多 磁 盘 空 间, 所 以 使 用 这 个 选 项 排 除 一 些 头 文 件 有 时 单 元 之 间 有 依 赖 关 系, 比 如 单 元 A 依 赖 单 元 B, 所 以 单 元 B 要 先 于 单 元 A 编 译 你 可 以 用 #pragma startup 指 定 编 译 优 先 级, 如 果 使 用 了 #pragma package(smart_init),bcb 就 会 根 据 优 先 级 的 大 小 先 后 编 译 3.6.5,#pragma resource #pragma resource "*.dfm" 表 示 把 *.dfm 文 件 中 的 资 源 加 入 工 程 *.dfm 中 包 括 窗 体 外 观 的 定 义 3.6.6,#pragma warning #pragma warning( disable : ; once : 4385; error : 164 ) 等 价 于 : #pragma warning(disable: ) // 不 显 示 4507 和 34 号 警 告 信 息 #pragma warning(once:4385) // 4385 号 警 告 信 息 仅 报 告 一 次 #pragma warning(error:164) // 把 164 号 警 告 信 息 作 为 一 个 错 误 同 时 这 个 pragma warning 也 支 持 如 下 格 式 : #pragma warning( push [,n ] ) #pragma warning( pop ) 这 里 n 代 表 一 个 警 告 等 级 (1---4) #pragma warning( push ) 保 存 所 有 警 告 信 息 的 现 有 的 警 告 状 态 #pragma warning( push, n) 保 存 所 有 警 告 信 息 的 现 有 的 警 告 状 态, 并 且 把 全 局 警 告 等 级 设 定 为 n #pragma warning( pop ) 向 栈 中 弹 出 最 后 一 个 警 告 信 息, 在 入 栈 和 出 栈 之 间 所 作 的 一 切 改 动 取 消 例 如 : #pragma warning( push ) #pragma warning( disable : 4705 )
69 #pragma warning( disable : 4706 ) #pragma warning( disable : 4707 ) //... #pragma warning( pop ) 在 这 段 代 码 的 最 后, 重 新 保 存 所 有 的 警 告 信 息 ( 包 括 4705,4706 和 4707) 3.6.7,#pragma comment #pragma comment(...) 该 指 令 将 一 个 注 释 记 录 放 入 一 个 对 象 文 件 或 可 执 行 文 件 中 常 用 的 lib 关 键 字, 可 以 帮 我 们 连 入 一 个 库 文 件 比 如 : #pragma comment(lib, "user32.lib") 该 指 令 用 来 将 user32.lib 库 文 件 加 入 到 本 工 程 中 linker: 将 一 个 链 接 选 项 放 入 目 标 文 件 中, 你 可 以 使 用 这 个 指 令 来 代 替 由 命 令 行 传 入 的 或 者 在 开 发 环 境 中 设 置 的 链 接 选 项, 你 可 以 指 定 /include 选 项 来 强 制 包 含 某 个 对 象, 例 如 : #pragma comment(linker, "/include: mysymbol") 3.6.8,#pragma pack 这 里 重 点 讨 论 内 存 对 齐 的 问 题 和 #pragma pack() 的 使 用 方 法 什 么 是 内 存 对 齐? 先 看 下 面 的 结 构 : struct TestStruct1 char c1; short s; char c2; int i; ; 假 设 这 个 结 构 的 成 员 在 内 存 中 是 紧 凑 排 列 的, 假 设 c1 的 地 址 是 0, 那 么 s 的 地 址 就 应 该 是 1,c2 的 地 址 就 是 3,i 的 地 址 就 是 4 也 就 是 c1 地 址 为 , s 地 址 为 , c2 地 址 为 , i 地 址 为 可 是, 我 们 在 Visual C++6.0 中 写 一 个 简 单 的 程 序 : struct TestStruct1 a; printf("c1 %p, s %p, c2 %p, i %p\n", (unsigned int)(void*)&a.c1 - (unsigned int)(void*)&a, (unsigned int)(void*)&a.s - (unsigned int)(void*)&a, (unsigned int)(void*)&a.c2 - (unsigned int)(void*)&a, (unsigned int)(void*)&a.i - (unsigned int)(void*)&a); 运 行, 输 出 : c , s , c , i
70 为 什 么 会 这 样? 这 就 是 内 存 对 齐 而 导 致 的 问 题 , 为 什 么 会 有 内 存 对 齐? 字, 双 字, 和 四 字 在 自 然 边 界 上 不 需 要 在 内 存 中 对 齐 ( 对 字, 双 字, 和 四 字 来 说, 自 然 边 界 分 别 是 偶 数 地 址, 可 以 被 4 整 除 的 地 址, 和 可 以 被 8 整 除 的 地 址 ) 无 论 如 何, 为 了 提 高 程 序 的 性 能, 数 据 结 构 ( 尤 其 是 栈 ) 应 该 尽 可 能 地 在 自 然 边 界 上 对 齐 原 因 在 于, 为 了 访 问 未 对 齐 的 内 存, 处 理 器 需 要 作 两 次 内 存 访 问 ; 然 而, 对 齐 的 内 存 访 问 仅 需 要 一 次 访 问 一 个 字 或 双 字 操 作 数 跨 越 了 4 字 节 边 界, 或 者 一 个 四 字 操 作 数 跨 越 了 8 字 节 边 界, 被 认 为 是 未 对 齐 的, 从 而 需 要 两 次 总 线 周 期 来 访 问 内 存 一 个 字 起 始 地 址 是 奇 数 但 却 没 有 跨 越 字 边 界 被 认 为 是 对 齐 的, 能 够 在 一 个 总 线 周 期 中 被 访 问 某 些 操 作 双 四 字 的 指 令 需 要 内 存 操 作 数 在 自 然 边 界 上 对 齐 如 果 操 作 数 没 有 对 齐, 这 些 指 令 将 会 产 生 一 个 通 用 保 护 异 常 双 四 字 的 自 然 边 界 是 能 够 被 16 整 除 的 地 址 其 他 的 操 作 双 四 字 的 指 令 允 许 未 对 齐 的 访 问 ( 不 会 产 生 通 用 保 护 异 常 ), 然 而, 需 要 额 外 的 内 存 总 线 周 期 来 访 问 内 存 中 未 对 齐 的 数 据 缺 省 情 况 下, 编 译 器 默 认 将 结 构 栈 中 的 成 员 数 据 进 行 内 存 对 齐 因 此, 上 面 的 程 序 输 出 就 变 成 了 :c , s , c , i 编 译 器 将 未 对 齐 的 成 员 向 后 移, 将 每 一 个 都 成 员 对 齐 到 自 然 边 界 上, 从 而 也 导 致 了 整 个 结 构 的 尺 寸 变 大 尽 管 会 牺 牲 一 点 空 间 ( 成 员 之 间 有 部 分 内 存 空 闲 ), 但 提 高 了 性 能 也 正 是 这 个 原 因, 我 们 不 可 以 断 言 sizeof(teststruct1) 的 结 果 为 8 在 这 个 例 子 中,sizeof(TestStruct1) 的 结 果 为 , 如 何 避 免 内 存 对 齐 的 影 响 那 么, 能 不 能 既 达 到 提 高 性 能 的 目 的, 又 能 节 约 一 点 空 间 呢? 有 一 点 小 技 巧 可 以 使 用 比 如 我 们 可 以 将 上 面 的 结 构 改 成 : struct TestStruct2 char c1; char c2; short s; int i; ; 这 样 一 来, 每 个 成 员 都 对 齐 在 其 自 然 边 界 上, 从 而 避 免 了 编 译 器 自 动 对 齐 在 这 个 例 子 中,sizeof(TestStruct2) 的 值 为 8 这 个 技 巧 有 一 个 重 要 的 作 用, 尤 其 是 这 个 结 构 作 为 API 的 一 部 分 提 供 给 第 三 方 开 发 使 用 的 时 候 第 三 方 开 发 者 可 能 将 编 译 器 的 默 认 对 齐 选 项 改 变, 从 而 造 成 这 个 结 构 在 你 的 发 行 的 DLL 中 使 用 某 种 对 齐 方 式, 而 在 第 三 方 开 发 者 哪 里 却 使 用 另 外 一 种 对 齐 方 式 这 将 会 导 致 重 大 问 题 比 如,TestStruct1 结 构, 我 们 的 DLL 使 用 默 认 对 齐 选 项, 对 齐 为 c , s , c , i , 同 时 sizeof(teststruct1) 的 值 为 12 而 第 三 方 将 对 齐 选 项 关 闭, 导 致 c , s , c , i , 同 时 sizeof(teststruct1) 的 值 为 8 除 此 之 外 我 们 还 可 以 利 用 #pragma pack() 来 改 变 编 译 器 的 默 认 对 齐 方 式 ( 当 然 一 般 编 译 器
71 也 提 供 了 一 些 改 变 对 齐 方 式 的 选 项, 这 里 不 讨 论 ) 使 用 指 令 #pragma pack (n), 编 译 器 将 按 照 n 个 字 节 对 齐 使 用 指 令 #pragma pack (), 编 译 器 将 取 消 自 定 义 字 节 对 齐 方 式 在 #pragma pack (n) 和 #pragma pack () 之 间 的 代 码 按 n 个 字 节 对 齐 但 是, 成 员 对 齐 有 一 个 重 要 的 条 件, 即 每 个 成 员 按 自 己 的 方 式 对 齐. 也 就 是 说 虽 然 指 定 了 按 n 字 节 对 齐, 但 并 不 是 所 有 的 成 员 都 是 以 n 字 节 对 齐 其 对 齐 的 规 则 是, 每 个 成 员 按 其 类 型 的 对 齐 参 数 ( 通 常 是 这 个 类 型 的 大 小 ) 和 指 定 对 齐 参 数 ( 这 里 是 n 字 节 ) 中 较 小 的 一 个 对 齐, 即 : min( n, sizeof( item )) 并 且 结 构 的 长 度 必 须 为 所 用 过 的 所 有 对 齐 参 数 的 整 数 倍, 不 够 就 补 空 字 节 看 如 下 例 子 : #pragma pack(8) struct TestStruct4 char a; long b; ; struct TestStruct5 char c; TestStruct4 d; long long e; ; #pragma pack() 问 题 : A),sizeof(TestStruct5)=? B), TestStruct5 的 c 后 面 空 了 几 个 字 节 接 着 是 d? TestStruct4 中, 成 员 a 是 1 字 节 默 认 按 1 字 节 对 齐, 指 定 对 齐 参 数 为 8, 这 两 个 值 中 取 1,a 按 1 字 节 对 齐 ; 成 员 b 是 4 个 字 节, 默 认 是 按 4 字 节 对 齐, 这 时 就 按 4 字 节 对 齐, 所 以 sizeof(teststruct4) 应 该 为 8; TestStruct5 中,c 和 TestStruct4 中 的 a 一 样, 按 1 字 节 对 齐, 而 d 是 个 结 构, 它 是 8 个 字 节, 它 按 什 么 对 齐 呢? 对 于 结 构 来 说, 它 的 默 认 对 齐 方 式 就 是 它 的 所 有 成 员 使 用 的 对 齐 参 数 中 最 大 的 一 个, TestStruct4 的 就 是 4. 所 以, 成 员 d 就 是 按 4 字 节 对 齐. 成 员 e 是 8 个 字 节, 它 是 默 认 按 8 字 节 对 齐, 和 指 定 的 一 样, 所 以 它 对 到 8 字 节 的 边 界 上, 这 时, 已 经 使 用 了 12 个 字 节 了, 所 以 又 添 加 了 4 个 字 节 的 空, 从 第 16 个 字 节 开 始 放 置 成 员 e. 这 时, 长 度 为 24, 已 经 可 以 被 8( 成 员 e 按 8 字 节 对 齐 ) 整 除. 这 样, 一 共 使 用 了 24 个 字 节. 内 存 布 局 如 下 (* 表 示 空 闲 内 存,1 表 示 使 用 内 存 单 位 为 1byete): a b TestStruct4 的 内 存 布 局 :1***,1111, c TestStruct4.a TestStruct4.b d TestStruct5 的 内 存 布 局 : 1***, 1***, 1111, ****,
72 这 里 有 三 点 很 重 要 : 首 先, 每 个 成 员 分 别 按 自 己 的 方 式 对 齐, 并 能 最 小 化 长 度 其 次, 复 杂 类 型 ( 如 结 构 ) 的 默 认 对 齐 方 式 是 它 最 长 的 成 员 的 对 齐 方 式, 这 样 在 成 员 是 复 杂 类 型 时, 可 以 最 小 化 长 度 然 后, 对 齐 后 的 长 度 必 须 是 成 员 中 最 大 的 对 齐 参 数 的 整 数 倍, 这 样 在 处 理 数 组 时 可 以 保 证 每 一 项 都 边 界 对 齐 补 充 一 下, 对 于 数 组, 比 如 :char a[3]; 它 的 对 齐 方 式 和 分 别 写 3 个 char 是 一 样 的. 也 就 是 说 它 还 是 按 1 个 字 节 对 齐. 如 果 写 : typedef char Array3[3];Array3 这 种 类 型 的 对 齐 方 式 还 是 按 1 个 字 节 对 齐, 而 不 是 按 它 的 长 度 但 是 不 论 类 型 是 什 么, 对 齐 的 边 界 一 定 是 1,2,4,8,16,32,64... 中 的 一 个 另 外, 注 意 别 的 #pragma pack 的 其 他 用 法 : #pragma pack(push) #pragma pack(push,n) // 保 存 当 前 对 其 方 式 到 packing stack 等 效 于 #pragma pack(push) #pragma pack(n) //n=1,2,4,8,16 保 存 当 前 对 齐 方 式, 设 置 按 n 字 节 对 齐 #pragma pack(pop) //packing stack 出 栈, 并 将 对 其 方 式 设 置 为 出 栈 的 对 齐 方 3.7, # 运 算 符 # 也 是 预 处 理? 是 的, 你 可 以 这 么 认 为 那 怎 么 用 它 呢? 别 急, 先 看 下 面 例 子 : #define SQR(x) printf("the square of x is %d.\n", ((x)*(x))); 如 果 这 样 使 用 宏 : SQR(8); 则 输 出 为 : The square of x is 64. 注 意 到 没 有, 引 号 中 的 字 符 x 被 当 作 普 通 文 本 来 处 理, 而 不 是 被 当 作 一 个 可 以 被 替 换 的 语 言 符 号 假 如 你 确 实 希 望 在 字 符 串 中 包 含 宏 参 数, 那 我 们 就 可 以 使 用 #, 它 可 以 把 语 言 符 号 转 化 为 字 符 串 上 面 的 例 子 改 一 改 : #define SQR(x) printf("the square of "#x" is %d.\n", ((x)*(x))); 再 使 用 : SQR(8); 则 输 出 的 是 : The square of 8 is 64. 很 简 单 吧? 相 信 你 现 在 已 经 明 白 # 号 的 使 用 方 法 了 3.8,## 预 算 符 和 # 运 算 符 一 样,## 运 算 符 可 以 用 于 宏 函 数 的 替 换 部 分 这 个 运 算 符 把 两 个 语 言 符 号 组
73 合 成 单 个 语 言 符 号 看 例 子 : #define XNAME(n) x ## n 如 果 这 样 使 用 宏 : XNAME(8) 则 会 被 展 开 成 这 样 : x8 看 明 白 了 没?## 就 是 个 粘 合 剂, 将 前 后 两 部 分 粘 合 起 来
74 第 四 章 指 针 和 数 组 几 乎 每 次 讲 课 讲 到 指 针 和 数 组 时, 我 总 会 反 复 不 停 的 问 学 生 : 到 底 什 么 是 指 针? 什 么 是 数 组? 他 们 之 间 到 底 是 什 么 样 的 关 系 从 几 乎 没 人 能 回 答 明 白 到 几 乎 都 能 回 答 明 白, 需 要 经 历 一 段 惨 绝 人 寰 的 痛 指 针 是 C/C++ 的 精 华, 如 果 未 能 很 好 地 掌 握 指 针, 那 C/C++ 也 基 本 等 于 没 学 可 惜, 对 于 刚 毕 业 的 计 算 机 系 的 学 生, 几 乎 没 有 人 真 正 完 全 掌 握 了 指 针 和 数 组 以 及 内 存 管 理, 甚 至 有 的 学 生 告 诉 我 说 : 他 们 老 师 认 为 指 针 与 数 组 太 难, 工 作 又 少 用, 所 以 没 有 讲 解 对 于 这 样 的 学 校 与 老 师, 我 是 彻 底 的 无 语 我 没 有 资 格 去 谴 责 或 是 鄙 视 谁, 只 是 窃 以 为, 这 个 老 师 肯 怕 自 己 都 未 掌 握 指 针 大 学 里 很 多 老 师 并 未 真 正 写 过 多 少 代 码, 不 掌 握 指 针 的 老 师 肯 定 存 在, 这 样 的 老 师 教 出 来 的 学 生 如 何 能 找 到 工 作? 而 目 前 市 面 上 的 书 对 指 针 和 数 组 的 区 别 也 是 几 乎 避 而 不 谈, 这 就 更 加 加 深 了 学 生 掌 握 的 难 度 我 平 时 上 课 总 是 非 常 细 致 而 又 小 心 的 向 学 生 讲 解 这 些 知 识, 生 怕 一 不 小 心 就 讲 错 或 是 误 导 了 学 生 还 好, 至 少 到 目 前 为 止, 我 教 过 的 学 生 几 乎 都 能 掌 握 指 针 和 数 组 及 内 存 管 理 的 要 点, 当 然 要 到 能 运 用 自 如 的 程 度 还 远 远 不 够, 这 需 要 大 量 的 写 代 码 才 能 达 到 另 外 需 要 说 明 的 是, 讲 课 时 为 了 让 学 生 深 刻 的 掌 握 这 些 知 识, 我 举 了 很 多 各 式 各 样 的 例 子 来 帮 助 学 生 理 解 所 以, 我 也 希 望 读 者 朋 友 能 好 好 体 味 这 些 例 子 三 个 问 题 : A), 什 么 是 指 针? B), 什 么 是 数 组? C), 数 组 和 指 针 之 间 有 什 么 样 的 关 系? 4.1, 指 针 4.1.1, 指 针 的 内 存 布 局 先 看 下 面 的 例 子 : int *p; 大 家 都 知 道 这 里 定 义 了 一 个 指 针 p 但 是 p 到 底 是 什 么 东 西 呢? 还 记 得 第 一 章 里 说 过, 任 何 一 种 数 据 类 型 我 们 都 可 以 把 它 当 一 个 模 子 吗?p, 毫 无 疑 问, 是 某 个 模 子 咔 出 来 的 我 们 也 讨 论 过, 任 何 模 子 都 必 须 有 其 特 定 的 大 小, 这 样 才 能 用 来 咔 咔 咔 那 咔 出 p 的 这 个 模 子 到 底 是 什 么 样 子 呢? 它 占 多 大 的 空 间 呢? 现 在 用 sizeof 测 试 一 下 (32 位 系 统 ):sizeof (p) 的 值 为 4 嗯, 这 说 明 咔 出 p 的 这 个 模 子 大 小 为 4 个 byte 显 然, 这 个 模 子 不 是 int, 虽 然 它 大 小 也 为 4 既 然 不 是 int 那 就 一 定 是 int * 了 好, 那 现 在 我 们 可 以 这 么 理 解 这 个 定 义 : 一 个 int * 类 型 的 模 子 在 内 存 上 咔 出 了 4 个 字 节 的 空 间, 然 后 把 这 个 4 个 字 节 大 小 的
75 空 间 命 名 为 p, 同 时 限 定 这 4 个 字 节 的 空 间 里 面 只 能 存 储 某 个 内 存 地 址, 即 使 你 存 入 别 的 任 何 数 据, 都 将 被 当 作 地 址 处 理, 而 且 这 个 内 存 地 址 开 始 的 连 续 4 个 字 节 上 只 能 存 储 某 个 int 类 型 的 数 据 这 是 一 段 咬 文 嚼 字 的 说 明, 我 们 还 是 用 图 来 解 析 一 下 : p 0x0000FF00 int 0x0000FF00 10 p 4byte p p 4 0x0000FF00 4byte p 如 上 图 所 示, 我 们 把 p 称 为 指 针 变 量,p 里 存 储 的 内 存 地 址 处 的 内 存 称 为 p 所 指 向 的 内 存 指 针 变 量 p 里 存 储 的 任 何 数 据 都 将 被 当 作 地 址 来 处 理 我 们 可 以 简 单 的 这 么 理 解 : 一 个 基 本 的 数 据 类 型 ( 包 括 结 构 体 等 自 定 义 类 型 ) 加 上 * 号 就 构 成 了 一 个 指 针 类 型 的 模 子 这 个 模 子 的 大 小 是 一 定 的, 与 * 号 前 面 的 数 据 类 型 无 关 * 号 前 面 的 数 据 类 型 只 是 说 明 指 针 所 指 向 的 内 存 里 存 储 的 数 据 类 型 所 以, 在 32 位 系 统 下, 不 管 什 么 样 的 指 针 类 型, 其 大 小 都 为 4byte 可 以 测 试 一 下 sizeof(void *) 4.1.2, * 与 防 盗 门 的 钥 匙 这 里 这 个 * 号 怎 么 理 解 呢? 举 个 例 子 : 当 你 回 到 家 门 口 时, 你 想 进 屋 第 一 件 事 就 是 拿 出 钥 匙 来 开 锁 那 你 想 想 防 盗 门 的 锁 芯 是 不 是 很 像 这 个 * 号? 你 要 进 屋 必 须 要 用 钥 匙, 那 你 去 读 写 一 块 内 存 是 不 是 也 要 一 把 钥 匙 呢? 这 个 * 号 就 是 不 是 就 是 我 们 最 好 的 钥 匙? 使 用 指 针 的 时 候, 没 有 它, 你 是 不 可 能 读 写 某 块 内 存 的 4.1.3,int *p = NULL 和 *p = NULL 有 什 么 区 别? 很 多 初 学 者 都 无 法 分 清 这 两 者 之 间 的 区 别 我 们 先 看 下 面 的 代 码 : int *p = NULL; 这 时 候 我 们 可 以 通 过 编 译 器 查 看 p 的 值 为 0x 这 句 代 码 的 意 思 是 : 定 义 一 个 指 针 变 量 p, 其 指 向 的 内 存 里 面 保 存 的 是 int 类 型 的 数 据 ; 在 定 义 变 量 p 的 同 时 把 p 的 值 设 置 为 0x , 而 不 是 把 *p 的 值 设 置 为 0x 这 个 过 程 叫 做 初 始 化, 是 在 编 译 的 时 候 进 行 的
76 明 白 了 什 么 是 初 始 化 之 后, 再 看 下 面 的 代 码 : int *p; *p = NULL; 同 样, 我 们 可 以 在 编 译 器 上 调 试 这 两 行 代 码 第 一 行 代 码, 定 义 了 一 个 指 针 变 量 p, 其 指 向 的 内 存 里 面 保 存 的 是 int 类 型 的 数 据 ; 但 是 这 时 候 变 量 p 本 身 的 值 是 多 少 不 得 而 知, 也 就 是 说 现 在 变 量 p 保 存 的 有 可 能 是 一 个 非 法 的 地 址 第 二 行 代 码, 给 *p 赋 值 为 NULL, 即 给 p 指 向 的 内 存 赋 值 为 NULL; 但 是 由 于 p 指 向 的 内 存 可 能 是 非 法 的, 所 以 调 试 的 时 候 编 译 器 可 能 会 报 告 一 个 内 存 访 问 错 误 这 样 的 话, 我 们 可 以 把 上 面 的 代 码 改 写 改 写, 使 p 指 向 一 块 合 法 的 内 存 : int i = 10; int *p = &i; *p = NULL; 在 编 译 器 上 调 试 一 下, 我 们 发 现 p 指 向 的 内 存 由 原 来 的 10 变 为 0 了 ; 而 p 本 身 的 值, 即 内 存 地 址 并 没 有 改 变 经 过 上 面 的 分 析, 相 信 你 已 经 明 白 它 们 之 间 的 区 别 了 不 过 这 里 还 有 一 个 问 题 需 要 注 意, 也 就 是 这 个 NULL 初 学 者 往 往 在 这 里 犯 错 误 注 意 NULL 就 是 NULL, 它 被 宏 定 义 为 0: #define NULL 0 很 多 系 统 下 除 了 有 NULL 外, 还 有 NUL(Visual C 上 提 示 说 不 认 识 NUL) NUL 是 ASCII 码 表 的 第 一 个 字 符, 表 示 的 是 空 字 符, 其 ASCII 码 值 为 0 其 值 虽 然 都 为 0, 但 表 示 的 意 思 完 全 不 一 样 同 样,NULL 和 0 表 示 的 意 思 也 完 全 不 一 样 一 定 不 要 混 淆 另 外 还 有 初 学 者 在 使 用 NULL 的 时 候 误 写 成 null 或 Null 等 这 些 都 是 不 正 确 的,C 语 言 对 大 小 写 十 分 敏 感 啊 当 然, 也 确 实 有 系 统 也 定 义 了 null, 其 意 思 也 与 NULL 没 有 区 别, 但 是 你 千 万 不 用 使 用 null, 这 会 影 响 你 代 码 的 移 植 性 4.1.4, 如 何 将 数 值 存 储 到 指 定 的 内 存 地 址 假 设 现 在 需 要 往 内 存 0x12ff7c 地 址 上 存 入 一 个 整 型 数 0x100 我 们 怎 么 才 能 做 到 呢? 我 们 知 道 可 以 通 过 一 个 指 针 向 其 指 向 的 内 存 地 址 写 入 数 据, 那 么 这 里 的 内 存 地 址 0x12ff7c 其 本 质 不 就 是 一 个 指 针 嘛 所 以 我 们 可 以 用 下 面 的 方 法 : int *p = (int *)0x12ff7c; *p = 0x100; 需 要 注 意 的 是 将 地 址 0x12ff7c 赋 值 给 指 针 变 量 p 的 时 候 必 须 强 制 转 换 至 于 这 里 为 什 么 选 择 内 存 地 址 0x12ff7c, 而 不 选 择 别 的 地 址, 比 如 0xff00 等 这 仅 仅 是 为 了 方 便 在 Visual C 上 测 试 而 已 如 果 你 选 择 0xff00, 也 许 在 执 行 *p = 0x100; 这 条 语 句 的 时 候, 编 译 器 会 报 告 一 个 内 存 访 问 的 错 误, 因 为 地 址 0xff00 处 的 内 存 你 可 能 并 没 有 权 力 去 访 问 既 然 这 样, 我 们 怎 么 知 道 一 个 内 存 地 址 是 可 以 合 法 的 被 访 问 呢? 也 就 是 说 你 怎 么 知 道 地 址 0x12ff7c 处 的 内 存 是 可 以 被 访 问 的 呢? 其 实 这 很 简 单, 我 们 可 以 先 定 义 一 个 变 量 i, 比 如 : int i = 0; 变 量 i 所 处 的 内 存 肯 定 是 可 以 被 访 问 的 然 后 在 编 译 器 的 watch 窗 口 上 观 察 &i 的 值 不 就 知 道 其 内 存 地 址 了 么? 这 里 我 得 到 的 地 址 是 0x12ff7c, 仅 此 而 已 ( 不 同 的 编 译 器 可 能 每 次 给 变 量 i 分 配 的 内 存 地 址 不 一 样, 而 刚 好 Visual C 每 次 都 一 样 ) 你 完 全 可 以 给 任 意 一 个 可 以 被 合 法 访 问 的 地 址 赋 值 得 到 这 个 地 址 后 再 把 int i = 0; 这 句 代 码 删 除 一 切 罪 证
77 销 毁 得 一 干 二 净, 简 直 是 做 得 天 衣 无 缝 除 了 这 样 就 没 有 别 的 办 法 了 吗? 未 必 我 们 甚 至 可 以 直 接 这 么 写 代 码 : *(int *)0x12ff7c = 0x100; 这 行 代 码 其 实 和 上 面 的 两 行 代 码 没 有 本 质 的 区 别 先 将 地 址 0x12ff7c 强 制 转 换, 告 诉 编 译 器 这 个 地 址 上 将 存 储 一 个 int 类 型 的 数 据 ; 然 后 通 过 钥 匙 * 向 这 块 内 存 写 入 一 个 数 据 上 面 讨 论 了 这 么 多, 其 实 其 表 达 形 式 并 不 重 要, 重 要 的 是 这 种 思 维 方 式 也 就 是 说 我 们 完 全 有 办 法 给 指 定 的 某 个 内 存 地 址 写 入 数 据 的 4.1.5, 编 译 器 的 bug? 另 外 一 个 有 意 思 的 现 象, 在 Visual C 调 试 如 下 代 码 的 时 候 却 又 发 现 一 个 古 怪 的 问 题 : int *p = (int *)0x12ff7c; *p = NULL; p = NULL; 在 执 行 完 第 二 条 代 码 之 后, 发 现 p 的 值 变 为 0x 了 按 照 我 么 上 一 节 的 解 释, 应 该 p 的 值 不 变, 只 是 p 指 向 的 内 存 被 赋 值 为 0 难 道 我 们 讲 错 了 吗? 别 急, 再 试 试 如 下 代 码 : int i = 10; int *p = (int *)0x12ff7c; *p = NULL; p = NULL; 通 过 调 试, 发 现 这 样 子 的 话,p 的 值 没 有 变, 而 p 指 向 的 内 存 的 值 变 为 0 了 这 与 我 们 前 面 讲 解 的 完 全 一 致 当 然 这 里 的 i 的 地 址 刚 好 是 0x12ff7c, 但 这 并 不 能 改 变 *p = NULL; 这 行 代 码 的 功 能 为 了 再 次 测 试 这 个 问 题, 我 又 调 试 了 如 下 代 码 : int i = 10; int j = 100; int *p = (int *)0x12ff78; *p = NULL; p = NULL; 这 里 0x12ff78 刚 好 就 是 变 量 j 的 地 址 这 样 的 话 一 切 正 常, 但 是 如 果 把 int j = 100; 这 行 代 码 删 除 的 话, 又 出 现 上 述 的 问 题 了 测 试 到 这 里 我 还 是 不 甘 心, 编 译 器 怎 么 能 犯 这 种 低 级 错 误 呢? 于 是 又 接 着 进 行 了 如 下 测 试 : unsigned int i = 10; //unsigned int j = 100; unsigned int *p = (unsigned int *)0x12ff78; *p = NULL; p = NULL; 得 到 的 结 果 与 上 面 完 全 一 样 当 然, 我 还 是 没 有 死 心, 又 进 行 了 如 下 测 试 : char ch = 10; char *p = (char *)0x12ff7c; *p = NULL; p = NULL;
78 这 样 子 的 话, 完 全 正 常 但 当 我 删 除 掉 第 一 行 代 码 后 再 测 试, 这 里 的 p 的 值 并 未 变 成 0x , 而 是 变 成 了 0x0012ff00, 同 时 *p 的 值 变 成 了 0 这 又 是 怎 么 回 事 呢? 初 学 者 是 否 认 为 这 是 编 译 器 良 心 发 现, 把 *p 的 值 改 写 为 0 了 如 果 你 真 这 么 认 为, 那 就 大 错 特 错 了 这 里 的 *p 还 是 地 址 0x12ff7c 上 的 内 容 吗? 显 然 不 是, 而 是 地 址 0x0012ff00 上 的 内 容 至 于 0x12ff7c 为 什 么 变 成 0x0012ff00, 则 是 因 为 编 译 器 认 为 这 是 把 NULL 赋 值 给 char 类 型 的 内 存, 所 以 只 是 把 指 针 变 量 p 的 低 地 址 上 的 一 个 字 节 赋 值 为 0 至 于 为 什 么 是 低 地 址, 请 参 看 前 面 讲 解 过 大 小 端 模 式 相 关 内 容 测 试 到 这 里, 已 经 基 本 可 以 肯 定 这 是 Visual C 的 一 个 bug 所 以 平 时 一 定 不 要 迷 信 某 个 编 译 器, 要 相 信 自 己 的 判 断 当 然, 后 面 还 会 提 到 一 个 我 认 为 的 Visual C 的 一 个 bug 还 有, 这 个 小 小 的 例 子, 你 是 否 可 以 在 多 个 编 译 器 上 测 试 测 试 呢? 4.1.6, 如 何 达 到 手 中 无 剑 胸 中 也 无 剑 的 地 步 噢, 上 面 的 讨 论 一 不 小 心 就 这 么 多 了 这 里 我 为 什 么 要 把 这 个 小 小 的 问 题 放 到 这 里 长 篇 大 论 呢? 我 是 想 告 诉 读 者 : 研 究 问 题 一 定 要 肯 钻 研 千 万 不 要 小 看 某 一 个 简 单 的 事 情, 简 单 的 事 情 可 能 富 含 着 很 多 秘 密 经 过 这 样 一 番 深 究, 相 信 你 也 有 不 少 收 获 平 时 学 习 工 作 也 是 如 此, 不 要 小 瞧 任 何 一 件 简 单 的 事 情, 把 简 单 的 事 情 做 好 也 是 一 种 伟 大 劳 模 许 振 超 开 了 几 十 年 的 吊 车, 技 术 精 到 指 哪 打 哪 的 地 步 达 到 这 种 程 度 是 需 要 花 苦 功 夫 的, 几 十 年 如 一 日 天 天 重 复 这 件 看 似 很 简 单 的 事 情, 这 不 是 一 般 人 能 做 到 的 同 样 的, 在 天 龙 八 部 中, 萧 峰 血 战 聚 贤 庄 的 时 候, 一 套 平 平 凡 凡 的 太 祖 长 拳 打 得 虎 虎 生 威, 在 场 的 英 雄 无 不 佩 服 至 极, 这 也 是 其 苦 练 的 结 果 我 们 学 习 工 作 同 样 如 此, 要 肯 下 苦 功 夫 钻 研, 不 要 怕 钻 得 深, 只 怕 钻 得 不 深 其 实 这 也 就 是 为 什 么 同 一 个 班 的 学 生, 水 平 会 相 差 非 常 大 的 最 关 键 之 处 学 得 好 的, 往 往 是 那 些 舍 得 钻 研 的 学 生 我 平 时 上 课 教 学 生 的 绝 不 仅 仅 是 知 识 点, 更 多 的 时 候 我 在 教 他 们 学 习 和 解 决 问 题 的 方 法 有 时 候 这 个 过 程 远 比 结 论 要 重 要 的 多 后 面 的 内 容, 你 也 应 该 能 看 出 来, 我 非 常 注 重 过 程 的 分 析, 只 有 你 真 正 明 白 了 这 些 思 考 问 题 解 决 问 题 的 方 法 和 过 程, 你 才 能 真 正 立 于 不 败 之 地 所 有 的 问 题 对 你 来 说 都 是 一 个 样, 没 有 本 质 的 区 别 解 决 任 何 问 题 的 办 法 都 一 致, 那 就 是 把 没 见 过 的 不 会 的 问 题 想 法 设 法 转 换 成 你 见 过 的 你 会 的 问 题 ; 至 于 怎 么 去 转 换 那 就 要 靠 你 的 苦 学 苦 练 了 也 就 是 说 你 要 达 到 手 中 无 剑, 胸 中 也 无 剑 的 地 步 当 然 这 些 只 是 我 个 人 的 领 悟, 写 在 这 里 希 望 能 与 君 共 勉 4.2, 数 组 4.2.1, 数 组 的 内 存 布 局 先 看 下 面 的 例 子 : int a[5]; 所 有 人 都 明 白 这 里 定 义 了 一 个 数 组, 其 包 含 了 5 个 int 型 的 数 据 我 们 可 以 用 a[0],a[1] 等 来 访 问 数 组 里 面 的 每 一 个 元 素, 那 么 这 些 元 素 的 名 字 就 是 a[0],a[1] 吗? 看 下 面 的 示 意 图 :
79 5 int int 5 int a 20 byte a a[0],a[1] a 20 byte 如 上 图 所 示, 当 我 们 定 义 一 个 数 组 a 时, 编 译 器 根 据 指 定 的 元 素 个 数 和 元 素 的 类 型 分 配 确 定 大 小 ( 元 素 类 型 大 小 * 元 素 个 数 ) 的 一 块 内 存, 并 把 这 块 内 存 的 名 字 命 名 为 a 名 字 a 一 旦 与 这 块 内 存 匹 配 就 不 能 被 改 变 a[0],a[1] 等 为 a 的 元 素, 但 并 非 元 素 的 名 字 数 组 的 每 一 个 元 素 都 是 没 有 名 字 的 那 现 在 再 来 回 答 第 一 章 讲 解 sizeof 关 键 字 时 的 几 个 问 题 : sizeof(a) 的 值 为 sizeof(int)*5,32 位 系 统 下 为 20 sizeof(a[0]) 的 值 为 sizeof(int),32 位 系 统 下 为 4 sizeof(a[5]) 的 值 在 32 位 系 统 下 为 4 并 没 有 出 错, 为 什 么 呢? 我 们 讲 过 sizeof 是 关 键 字 不 是 函 数 函 数 求 值 是 在 运 行 的 时 候, 而 关 键 字 sizeof 求 值 是 在 编 译 的 时 候 虽 然 并 不 存 在 a[5] 这 个 元 素, 但 是 这 里 也 并 没 有 去 真 正 访 问 a[5], 而 是 仅 仅 根 据 数 组 元 素 的 类 型 来 确 定 其 值 所 以 这 里 使 用 a[5] 并 不 会 出 错 sizeof(&a[0]) 的 值 在 32 位 系 下 为 4, 这 很 好 理 解 取 元 素 a[0] 的 首 地 址 sizeof(&a) 的 值 在 32 位 系 统 下 也 为 4, 这 也 很 好 理 解 取 数 组 a 的 首 地 址 但 是 在 Visual C++6.0 上, 这 个 值 为 20, 我 认 为 是 错 误 的 4.2.2, 省 政 府 和 市 政 的 区 别 ----&a[0] 和 &a 的 区 别 这 里 &a[0] 和 &a 到 底 有 什 么 区 别 呢?a[0] 是 一 个 元 素,a 是 整 个 数 组, 虽 然 &a[0] 和 &a 的 值 一 样, 但 其 意 义 不 一 样 前 者 是 数 组 首 元 素 的 首 地 址, 而 后 者 是 数 组 的 首 地 址 举 个 例 子 : 湖 南 的 省 政 府 在 长 沙, 而 长 沙 的 市 政 府 也 在 长 沙 两 个 政 府 都 在 长 沙, 但 其 代 表 的 意 义 完 全 不 同 这 里 也 是 同 一 个 意 思 4.2.3, 数 组 名 a 作 为 左 值 和 右 值 的 区 别 简 单 而 言, 出 现 在 赋 值 符 = 右 边 的 就 是 右 值, 出 现 在 赋 值 符 = 左 边 的 就 是 左 值 比 如,x=y 左 值 : 在 这 个 上 下 文 环 境 中, 编 译 器 认 为 x 的 含 义 是 x 所 代 表 的 地 址 这 个 地 址 只 有 编 译 器 知 道, 在 编 译 的 时 候 确 定, 编 译 器 在 一 个 特 定 的 区 域 保 存 这 个 地 址, 我 们 完 全 不 必
80 考 虑 这 个 地 址 保 存 在 哪 里 右 值 : 在 这 个 上 下 文 环 境 中, 编 译 器 认 为 y 的 含 义 是 y 所 代 表 的 地 址 里 面 的 内 容 这 个 内 容 是 什 么, 只 有 到 运 行 时 才 知 道 C 语 言 引 入 一 个 术 语 可 修 改 的 左 值 意 思 就 是, 出 现 在 赋 值 符 左 边 的 符 号 所 代 表 的 地 址 上 的 内 容 一 定 是 可 以 被 修 改 的 换 句 话 说, 就 是 我 们 只 能 给 非 只 读 变 量 赋 值 既 然 已 经 明 白 左 值 和 右 值 的 区 别, 下 面 就 讨 论 一 下 数 组 作 为 左 值 和 右 值 的 情 况 : 当 a 作 为 右 值 的 时 候 代 表 的 是 什 么 意 思 呢? 很 多 书 认 为 是 数 组 的 首 地 址, 其 实 这 是 非 常 错 误 的 a 作 为 右 值 时 其 意 义 与 &a[0] 是 一 样, 代 表 的 是 数 组 首 元 素 的 首 地 址, 而 不 是 数 组 的 首 地 址 这 是 两 码 事 但 是 注 意, 这 仅 仅 是 代 表, 并 没 有 一 个 地 方 ( 这 只 是 简 单 的 这 么 认 为, 其 具 体 实 现 细 节 不 作 过 多 讨 论 ) 来 存 储 这 个 地 址, 也 就 是 说 编 译 器 并 没 有 为 数 组 a 分 配 一 块 内 存 来 存 其 地 址, 这 一 点 就 与 指 针 有 很 大 的 差 别 a 作 为 右 值, 我 们 清 楚 了 其 含 义, 那 作 为 左 值 呢? a 不 能 作 为 左 值! 这 个 错 误 几 乎 每 一 个 学 生 都 犯 过 编 译 器 会 认 为 数 组 名 作 为 左 值 代 表 的 意 思 是 a 的 首 元 素 的 首 地 址, 但 是 这 个 地 址 开 始 的 一 块 内 存 是 一 个 总 体, 我 们 只 能 访 问 数 组 的 某 个 元 素 而 无 法 把 数 组 当 一 个 总 体 进 行 访 问 所 以 我 们 可 以 把 a[i] 当 左 值, 而 无 法 把 a 当 左 值 其 实 我 们 完 全 可 以 把 a 当 一 个 普 通 的 变 量 来 看, 只 不 过 这 个 变 量 内 部 分 为 很 多 小 块, 我 们 只 能 通 过 分 别 访 问 这 些 小 块 来 达 到 访 问 整 个 变 量 a 的 目 的 4.3, 指 针 与 数 组 之 间 的 恩 恩 怨 怨 很 多 初 学 者 弄 不 清 指 针 和 数 组 到 底 有 什 么 样 的 关 系 我 现 在 就 告 诉 你 : 他 们 之 间 没 有 任 何 关 系! 只 是 他 们 经 常 穿 着 相 似 的 衣 服 来 逗 你 玩 罢 了 指 针 就 是 指 针, 指 针 变 量 在 32 位 系 统 下, 永 远 占 4 个 byte, 其 值 为 某 一 个 内 存 的 地 址 指 针 可 以 指 向 任 何 地 方, 但 是 不 是 任 何 地 方 你 都 能 通 过 这 个 指 针 变 量 访 问 到 数 组 就 是 数 组, 其 大 小 与 元 素 的 类 型 和 个 数 有 关 定 义 数 组 时 必 须 指 定 其 元 素 的 类 型 和 个 数 数 组 可 以 存 任 何 类 型 的 数 据, 但 不 能 存 函 数 既 然 它 们 之 间 没 有 任 何 关 系, 那 为 何 很 多 人 把 数 组 和 指 针 混 淆 呢? 甚 至 很 多 人 认 为 指 针 和 数 组 是 一 样 的 这 就 与 市 面 上 的 C 语 言 的 书 有 关, 几 乎 没 有 一 本 书 把 这 个 问 题 讲 透 彻, 讲 明 白 了 4.3.1, 以 指 针 的 形 式 访 问 和 以 下 标 的 形 式 访 问 下 面 我 们 就 详 细 讨 论 讨 论 它 们 之 间 似 是 而 非 的 一 些 特 点 例 如, 函 数 内 部 有 如 下 定 义 : A),char *p = abcdef ; B),char a[] = ;
81 , 以 指 针 的 形 式 访 问 和 以 下 标 的 形 式 访 问 指 针 例 子 A) 定 义 了 一 个 指 针 变 量 p,p 本 身 在 栈 上 占 4 个 byte,p 里 存 储 的 是 一 块 内 存 的 首 地 址 这 块 内 存 在 静 态 区, 其 空 间 大 小 为 7 个 byte, 这 块 内 存 也 没 有 名 字 对 这 块 内 存 的 访 问 完 全 是 匿 名 的 访 问 比 如 现 在 需 要 读 取 字 符 e, 我 们 有 两 种 方 式 : 1), 以 指 针 的 形 式 :*(p+4) 先 取 出 p 里 存 储 的 地 址 值, 假 设 为 0x0000FF00, 然 后 加 上 4 个 字 符 的 偏 移 量, 得 到 新 的 地 址 0x0000FF04 然 后 取 出 0x0000FF04 地 址 上 的 值 2), 以 下 标 的 形 式 :p[4] 编 译 器 总 是 把 以 下 标 的 形 式 的 操 作 解 析 为 以 指 针 的 形 式 的 操 作 p[4] 这 个 操 作 会 被 解 析 成 : 先 取 出 p 里 存 储 的 地 址 值, 然 后 加 上 中 括 号 中 4 个 元 素 的 偏 移 量, 计 算 出 新 的 地 址, 然 后 从 新 的 地 址 中 取 出 值 也 就 是 说 以 下 标 的 形 式 访 问 在 本 质 上 与 以 指 针 的 形 式 访 问 没 有 区 别, 只 是 写 法 上 不 同 罢 了 , 以 指 针 的 形 式 访 问 和 以 下 标 的 形 式 访 问 数 组 例 子 B) 定 义 了 一 个 数 组 a,a 拥 有 7 个 char 类 型 的 元 素, 其 空 间 大 小 为 7 数 组 a 本 身 在 栈 上 面 对 a 的 元 素 的 访 问 必 须 先 根 据 数 组 的 名 字 a 找 到 数 组 首 元 素 的 首 地 址, 然 后 根 据 偏 移 量 找 到 相 应 的 值 这 是 一 种 典 型 的 具 名 + 匿 名 访 问 比 如 现 在 需 要 读 取 字 符 5, 我 们 有 两 种 方 式 : 1), 以 指 针 的 形 式 :*(a+4) a 这 时 候 代 表 的 是 数 组 首 元 素 的 首 地 址, 假 设 为 0x0000FF00, 然 后 加 上 4 个 字 符 的 偏 移 量, 得 到 新 的 地 址 0x0000FF04 然 后 取 出 0x0000FF04 地 址 上 的 值 2), 以 下 标 的 形 式 :a[4] 编 译 器 总 是 把 以 下 标 的 形 式 的 操 作 解 析 为 以 指 针 的 形 式 的 操 作 a[4] 这 个 操 作 会 被 解 析 成 :a 作 为 数 组 首 元 素 的 首 地 址, 然 后 加 上 中 括 号 中 4 个 元 素 的 偏 移 量, 计 算 出 新 的 地 址, 然 后 从 新 的 地 址 中 取 出 值 由 上 面 的 分 析, 我 们 可 以 看 到, 指 针 和 数 组 根 本 就 是 两 个 完 全 不 一 样 的 东 西 只 是 它 们 都 可 以 以 指 针 形 式 或 以 下 标 形 式 进 行 访 问 一 个 是 完 全 的 匿 名 访 问, 一 个 是 典 型 的 具 名 + 匿 名 访 问 一 定 要 注 意 的 是 这 个 以 XXX 的 形 式 的 访 问 这 种 表 达 方 式 另 外 一 个 需 要 强 调 的 是 : 上 面 所 说 的 偏 移 量 4 代 表 的 是 4 个 元 素, 而 不 是 4 个 byte 只 不 过 这 里 刚 好 是 char 类 型 数 据 1 个 字 符 的 大 小 就 为 1 个 byte 记 住 这 个 偏 移 量 的 单 位 是 元 素 的 个 数 而 不 是 byte 数, 在 计 算 新 地 址 时 千 万 别 弄 错 了 4.3.2,a 和 &a 的 区 别 通 过 上 面 的 分 析, 相 信 你 已 经 明 白 数 组 和 指 针 的 访 问 方 式 了, 下 面 再 看 这 个 例 子 : main() int a[5]=1,2,3,4,5; int *ptr=(int *)(&a+1); printf("%d,%d",*(a+1),*(ptr-1));
82 打 印 出 来 的 值 为 多 少 呢? 这 里 主 要 是 考 查 关 于 指 针 加 减 操 作 的 理 解 对 指 针 进 行 加 1 操 作, 得 到 的 是 下 一 个 元 素 的 地 址, 而 不 是 原 有 地 址 值 直 接 加 1 所 以, 一 个 类 型 为 T 的 指 针 的 移 动, 以 sizeof(t) 为 移 动 单 位 因 此, 对 上 题 来 说,a 是 一 个 一 维 数 组, 数 组 中 有 5 个 元 素 ; ptr 是 一 个 int 型 的 指 针 &a + 1: 取 数 组 a 的 首 地 址, 该 地 址 的 值 加 上 sizeof(a) 的 值, 即 &a + 5*sizeof(int), 也 就 是 下 一 个 数 组 的 首 地 址, 显 然 当 前 指 针 已 经 越 过 了 数 组 的 界 限 (int *)(&a+1): 则 是 把 上 一 步 计 算 出 来 的 地 址, 强 制 转 换 为 int * 类 型, 赋 值 给 ptr *(a+1): a,&a 的 值 是 一 样 的, 但 意 思 不 一 样,a 是 数 组 首 元 素 的 首 地 址, 也 就 是 a[0] 的 首 地 址,&a 是 数 组 的 首 地 址,a+1 是 数 组 下 一 元 素 的 首 地 址, 即 a[1] 的 首 地 址,&a+1 是 下 一 个 数 组 的 首 地 址 所 以 输 出 2 *(ptr-1): 因 为 ptr 是 指 向 a[5], 并 且 ptr 是 int * 类 型, 所 以 *(ptr-1) 是 指 向 a[4], 输 出 5 这 些 分 析 我 相 信 大 家 都 能 理 解, 但 是 在 授 课 时, 学 生 向 我 提 出 了 如 下 问 题 : 在 Visual C++6.0 的 Watch 窗 口 中 &a+1 的 值 怎 么 会 是 (x0012ff6d(0x0012ff6c+1) 呢? 上 图 是 在 Visual C++6.0 调 试 本 函 数 时 的 截 图 a 在 这 里 代 表 是 的 数 组 首 元 素 的 地 址 即 a[0] 的 首 地 址, 其 值 为 0x0012ff6c &a 代 表 的 是 数 组 的 首 地 址, 其 值 为 0x0012ff6c a+1 的 值 是 0x0012ff6c+1*sizeof(int), 等 于 0x0012ff70 问 题 就 是 &a+1 的 值 怎 么 会 是 (x0012ff6d(0x0012ff6c+1) 呢? 按 照 我 们 上 面 的 分 析 应 该 为 0x0012ff6c+5*sizeof(int) 其 实 很 好 理 解 当 你 把 &a+1 放 到 Watch 窗 口 中 观 察 其 值 时, 表 达 式 &a+1 已 经 脱 离 其 上 下 文 环 境, 编 译 器 就 很 简 单 的 把 它 解 析 为 &a 的 值 然 后 加 上 1byte 而 a+1 的 解 析 就 正 确, 我 认 为 这 是 Visual C++6.0 的 一 个 bug 既 然 如 此, 我 们 怎 么 证 明 证 明 &a+1 的 值 确 实 为 0x0012ff6c+5*sizeof(int) 呢? 很 好 办, 用 printf 函 数 打 印 出 来 这 就 是 我 在 本 书 前 言 里 所 说 的, 有 的 时 候 我 们 确 实 需 要 printf 函 数 才 能 解 决 问 题 你 可 以 试 试 用 printf("%x",&a+1); 打 印 其 值, 看 是 否 为 0x0012ff6c+5*sizeof (int) 注 意 如 果 你 用 的 是 printf("%d",&a+1); 打 印, 那 你 必 须 在 十 进 制 和 十 六 进 制 之 间 换 算 一 下, 不 要 冤 枉 了 编 译 器 另 外 我 要 强 调 一 点 : 不 到 非 不 得 已, 尽 量 别 使 用 printf 函 数, 它 会 使 你 养 成 只 看 结 果 不 问 为 什 么 的 习 惯 比 如 这 个 列 子,*(a+1) 和 *(ptr-1) 的 值 完 全 可 以 通 过 Watch 窗 口 来 查 看 平 时 初 学 者 很 喜 欢 用 printf("%d,%d",*(a+1),*(ptr-1)); 这 类 的 表 达 式 来 直 接 打 印 出 值, 如 果 发 现 值 是 正 确 的 就 欢 天 喜 地 这 个 时 候 往 往 认 为 自 己 的 代 码 没 有 问 题, 根 本 就 不 去 查
83 看 其 变 量 的 值, 更 别 说 是 内 存 和 寄 存 器 的 值 了 更 有 甚 者,printf 函 数 打 印 出 来 的 值 不 正 确, 就 措 手 无 策, 举 手 问 老 师, 我 这 里 为 什 么 不 对 啊? 长 此 以 往 就 养 成 了 很 不 好 的 习 惯, 只 看 结 果, 不 重 调 试 这 就 是 为 什 么 同 样 的 几 年 经 验, 有 的 人 水 平 很 高, 而 有 的 人 水 平 却 很 低 其 根 本 原 因 就 在 于 此, 往 往 被 一 些 表 面 现 象 所 迷 惑 printf 函 数 打 印 出 来 的 值 是 对 的 就 能 说 明 你 的 代 码 一 定 没 问 题 吗? 我 看 未 必 曾 经 一 个 学 生, 我 让 其 实 现 直 接 插 入 排 序 算 法 很 快 他 把 函 数 写 完 了, 把 值 用 printf 函 数 打 印 出 来 给 我 看 我 看 其 代 码 却 发 现 他 使 用 的 算 法 本 质 上 其 实 是 冒 泡 排 序, 只 是 写 得 像 直 接 插 入 排 序 罢 了 等 等 这 种 情 况 数 都 数 不 过 来, 往 往 犯 了 错 误 还 以 为 自 己 是 对 的 所 以 我 平 时 上 课 之 前 往 往 会 强 调, 不 到 非 不 得 已, 不 允 许 使 用 printf 函 数, 而 要 自 己 去 查 看 变 量 和 内 存 的 值 学 生 的 这 种 不 好 的 习 惯 也 与 目 前 市 面 上 的 教 材 参 考 书 有 关, 这 些 书 甚 至 花 大 篇 幅 来 介 绍 scanf 和 printf 这 类 的 函 数, 却 几 乎 不 讲 解 调 试 技 术 甚 至 有 的 书 还 在 讲 TruboC 2.0 之 类 的 调 试 器! 如 此 教 材 教 出 来 的 学 生 质 量 可 想 而 知 4.3.3, 指 针 和 数 组 的 定 义 与 声 明 , 定 义 为 数 组, 声 明 为 指 针 文 件 1 中 定 义 如 下 : char a[100]; 文 件 2 中 声 明 如 下 ( 关 于 extern 的 用 法, 以 及 定 义 和 声 明 的 区 别, 请 复 习 第 一 章 ): extern char *a; 这 里, 文 件 1 中 定 义 了 数 组 a, 文 件 2 中 声 明 它 为 指 针 这 有 什 么 问 题 吗? 平 时 不 是 总 说 数 组 与 指 针 相 似, 甚 至 可 以 通 用 吗? 但 是, 很 不 幸, 这 是 错 误 的 通 过 上 面 的 分 析 我 们 也 能 明 白 一 些, 但 是 革 命 尚 未 成 功, 同 志 仍 需 努 力 你 或 许 还 记 得 我 上 面 说 过 的 话 : 数 组 就 是 数 组, 指 针 就 是 指 针, 它 们 是 完 全 不 同 的 两 码 事! 他 们 之 间 没 有 任 何 关 系, 只 是 经 常 穿 着 相 似 的 衣 服 来 迷 惑 你 罢 了 下 面 就 来 分 析 分 析 这 个 问 题 : 在 第 一 章 的 开 始, 我 就 强 调 了 定 义 和 声 明 之 间 的 区 别, 定 义 分 配 的 内 存, 而 声 明 没 有 定 义 只 能 出 现 一 次, 而 声 明 可 以 出 现 多 次 这 里 extern 告 诉 编 译 器 a 这 个 名 字 已 经 在 别 的 文 件 中 被 定 义 了, 下 面 的 代 码 使 用 的 名 字 a 是 别 的 文 件 定 义 的 再 回 顾 到 前 面 对 于 左 值 和 右 值 的 讨 论, 我 们 知 道 如 果 编 译 器 需 要 某 个 地 址 ( 可 能 还 需 要 加 上 偏 移 量 ) 来 执 行 某 种 操 作 的 话, 它 就 可 以 直 接 通 过 开 锁 动 作 ( 使 用 * 这 把 钥 匙 ) 来 读 或 者 写 这 个 地 址 上 的 内 存, 并 不 需 要 先 去 找 到 储 存 这 个 地 址 的 地 方 相 反, 对 于 指 针 而 言, 必 须 先 去 找 到 储 存 这 个 地 址 的 地 方, 取 出 这 个 地 址 值 然 后 对 这 个 地 址 进 行 开 锁 ( 使 用 * 这 把 钥 匙 ) 如 下 图 :
84 这 就 是 为 什 么 extern char a[] 与 extern char a[100] 等 价 的 原 因 因 为 这 只 是 声 明, 不 分 配 空 间, 所 以 编 译 器 无 需 知 道 这 个 数 组 有 多 少 个 元 素 这 两 个 声 明 都 告 诉 编 译 器 a 是 在 别 的 文 件 中 被 定 义 的 一 个 数 组,a 同 时 代 表 着 数 组 a 的 首 元 素 的 首 地 址, 也 就 是 这 块 内 存 的 起 始 地 址 数 组 内 地 任 何 元 素 的 的 地 址 都 只 需 要 知 道 这 个 地 址 就 可 以 计 算 出 来 但 是, 当 你 声 明 为 extern char *a 时, 编 译 器 理 所 当 然 的 认 为 a 是 一 个 指 针 变 量, 在 32 位 系 统 下, 占 4 个 byte 这 4 个 byte 里 保 存 了 一 个 地 址, 这 个 地 址 上 存 的 是 字 符 类 型 数 据 虽 然 在 文 件 1 中, 编 译 器 知 道 a 是 一 个 数 组, 但 是 在 文 件 2 中, 编 译 器 并 不 知 道 这 点 大 多 数 编 译 器 是 按 文 件 分 别 编 译 的, 编 译 器 只 按 照 本 文 件 中 声 明 的 类 型 来 处 理 所 以, 虽 然 a 实 际 大 小 为 100 个 byte, 但 是 在 文 件 2 中, 编 译 器 认 为 a 只 占 4 个 byte 我 们 说 过, 编 译 器 会 把 存 在 指 针 变 量 中 的 任 何 数 据 当 作 地 址 来 处 理 所 以, 如 果 需 要 访 问 这 些 字 符 类 型 数 据, 我 们 必 须 先 从 指 针 变 量 a 中 取 出 其 保 存 的 地 址 如 下 图 :
85 , 定 义 为 指 针, 声 明 为 数 组 显 然, 按 照 上 面 的 分 析, 我 们 把 文 件 1 中 定 义 的 数 组 在 文 件 2 中 声 明 为 指 针 会 发 生 错 误 同 样 的, 如 果 在 文 件 1 中 定 义 为 指 针, 而 在 文 件 中 声 明 为 数 组 也 会 发 生 错 误 : 文 件 1 char *p = abcdefg ; 文 件 2 extern char p[]; 在 文 件 1 中, 编 译 器 分 配 4 个 byte 空 间, 并 命 名 为 p 同 时 p 里 保 存 了 字 符 串 常 量 abcdefg 的 首 字 符 的 首 地 址 这 个 字 符 串 常 量 本 身 保 存 在 内 存 的 静 态 区, 其 内 容 不 可 更 改 在 文 件 2 中, 编 译 器 认 为 p 是 一 个 数 组, 其 大 小 为 4 个 byte, 数 组 内 保 存 的 是 char 类 型 的 数 据 在 文 件 2 中 使 用 p 的 过 程 如 下 图 : p 0x0000FF00 0x00 0x00 0xFF 0x00 p p 4 char char p[0] p[1] p[2] p[3] 0x00 0x00 0xFF 0x00 p[i] p 4.3.4, 指 针 和 数 组 的 对 比 通 过 上 面 的 分 析, 相 信 你 已 经 知 道 数 组 与 指 针 的 的 确 确 是 两 码 事 了 他 们 之 间 是 不 可 以 混 淆 的, 但 是 我 们 可 以 以 XXXX 的 形 式 访 问 数 组 的 元 素 或 指 针 指 向 的 内 容 以 后 一 定 要 确 认 你 的 代 码 在 一 个 地 方 定 义 为 指 针, 在 别 的 地 方 也 只 能 声 明 为 指 针 ; 在 一 个 的 地 方 定 义 为 数 组, 在 别 的 地 方 也 只 能 声 明 为 数 组 切 记 不 可 混 淆 下 面 再 用 一 个 表 来 总 结 一 下 指 针 和 数 组 的 特 性 : 指 针 保 存 数 据 的 地 址, 任 何 存 入 指 针 变 量 p 的 数 据 都 会 被 当 作 地 址 来 处 理 p 本 身 的 地 址 由 编 译 器 另 外 存 储, 存 储 在 哪 里, 我 们 并 不 知 数 组 保 存 数 据, 数 组 名 a 代 表 的 是 数 组 首 元 素 的 首 地 址 而 不 是 数 组 的 首 地 址 &a 才 是 整 个 数 组 的 首 地 址 a 本 身 的 地 址 由 编 译 器 另 外 存
86 道 储, 存 储 在 哪 里, 我 们 并 不 知 道 间 接 访 问 数 据, 首 先 取 得 指 针 变 量 p 的 内 容, 把 它 作 为 地 址, 然 后 从 这 个 地 址 提 取 数 据 或 向 这 个 地 址 写 入 数 据 指 针 可 以 以 指 针 的 形 式 访 问 *(p+i); 也 可 以 以 下 标 的 形 式 访 问 p[i] 但 其 本 质 都 是 先 取 p 的 内 容 然 后 加 上 i*sizeof( 类 型 ) 个 byte 作 为 数 据 的 真 正 地 址 通 常 用 于 动 态 数 据 结 构 相 关 的 函 数 为 malloc 和 free 直 接 访 问 数 据, 数 组 名 a 是 整 个 数 组 的 名 字, 数 组 内 每 个 元 素 并 没 有 名 字 只 能 通 过 具 名 + 匿 名 的 方 式 来 访 问 其 某 个 元 素, 不 能 把 数 组 当 一 个 整 体 来 进 行 读 写 操 作 数 组 可 以 以 指 针 的 形 式 访 问 *(a+i); 也 可 以 以 下 标 的 形 式 访 问 a[i] 但 其 本 质 都 是 a 所 代 表 的 数 组 首 元 素 的 首 地 址 加 上 i*sizeof( 类 型 ) 个 byte 作 为 数 据 的 真 正 地 址 通 常 用 于 存 储 固 定 数 目 且 数 据 类 型 相 同 的 元 素 隐 式 分 配 和 删 除 通 常 指 向 匿 名 数 据 ( 当 然 也 可 指 向 具 名 数 据 ) 自 身 即 为 数 组 名 4.4, 指 针 数 组 和 数 组 指 针 4.4.1, 指 针 数 组 和 数 组 指 针 的 内 存 布 局 初 学 者 总 是 分 不 出 指 针 数 组 与 数 组 指 针 的 区 别 其 实 很 好 理 解 : 指 针 数 组 : 首 先 它 是 一 个 数 组, 数 组 的 元 素 都 是 指 针, 数 组 占 多 少 个 字 节 由 数 组 本 身 决 定 它 是 储 存 指 针 的 数 组 的 简 称 数 组 指 针 : 首 先 它 是 一 个 指 针, 它 指 向 一 个 数 组 在 32 位 系 统 下 永 远 是 占 4 个 字 节, 至 于 它 指 向 的 数 组 占 多 少 字 节, 不 知 道 它 是 指 向 数 组 的 指 针 的 简 称 下 面 到 底 哪 个 是 数 组 指 针, 哪 个 是 指 针 数 组 呢 : A),int B),int *p1[10]; (*p2)[10]; 每 次 上 课 问 这 个 问 题, 总 有 弄 不 清 楚 的 这 里 需 要 明 白 一 个 符 号 之 间 的 优 先 级 问 题 [] 的 优 先 级 比 * 要 高 p1 先 与 [] 结 合, 构 成 一 个 数 组 的 定 义, 数 组 名 为 p1,int * 修 饰 的 是 数 组 的 内 容, 即 数 组 的 每 个 元 素 那 现 在 我 们 清 楚, 这 是 一 个 数 组, 其 包 含 10 个 指 向 int 类 型 数 据 的 指 针, 即 指 针 数 组 至 于 p2 就 更 好 理 解 了, 在 这 里 () 的 优 先 级 比 [] 高, * 号 和 p2 构 成 一 个 指 针 的 定 义, 指 针 变 量 名 为 p2,int 修 饰 的 是 数 组 的 内 容, 即 数 组 的 每 个 元 素 数 组 在 这 里 并 没 有 名 字, 是 个 匿 名 数 组 那 现 在 我 们 清 楚 p2 是 一 个 指 针, 它 指 向 一 个 包 含 10 个 int 类 型 数 据 的 数 组, 即 数 组 指 针 我 们 可 以 借 助 下 面 的 图 加 深 理 解 :
87 4.4.2,int (*)[10] p 也 许 应 该 这 么 定 义 数 组 指 针 这 里 有 个 有 意 思 的 话 题 值 得 探 讨 一 下 : 平 时 我 们 定 义 指 针 不 都 是 在 数 据 类 型 后 面 加 上 指 针 变 量 名 么? 这 个 指 针 p2 的 定 义 怎 么 不 是 按 照 这 个 语 法 来 定 义 的 呢? 也 许 我 们 应 该 这 样 来 定 义 p2: int (*)[10] p2; int (*)[10] 是 指 针 类 型,p2 是 指 针 变 量 这 样 看 起 来 的 确 不 错, 不 过 就 是 样 子 有 些 别 扭 其 实 数 组 指 针 的 原 型 确 实 就 是 这 样 子 的, 只 不 过 为 了 方 便 与 好 看 把 指 针 变 量 p2 前 移 了 而 已 你 私 下 完 全 可 以 这 么 理 解 这 点 虽 然 编 译 器 不 这 么 想 ^_^ 4.4.3, 再 论 a 和 &a 之 间 的 区 别 既 然 这 样, 那 问 题 就 来 了 前 面 我 们 讲 过 a 和 &a 之 间 的 区 别, 现 在 再 来 看 看 下 面 的 代 码 : int main() char a[5]='a','b','c','d'; char (*p3)[5] = &a; char (*p4)[5] = a; return 0;
88 上 面 对 p3 和 p4 的 使 用, 哪 个 正 确 呢?p3+1 的 值 会 是 什 么?p4+1 的 值 又 会 是 什 么? 毫 无 疑 问,p3 和 p4 都 是 数 组 指 针, 指 向 的 是 整 个 数 组 &a 是 整 个 数 组 的 首 地 址,a 是 数 组 首 元 素 的 首 地 址, 其 值 相 同 但 意 义 不 同 在 C 语 言 里, 赋 值 符 号 = 号 两 边 的 数 据 类 型 必 须 是 相 同 的, 如 果 不 同 需 要 显 示 或 隐 式 的 类 型 转 换 p3 这 个 定 义 的 = 号 两 边 的 数 据 类 型 完 全 一 致, 而 p4 这 个 定 义 的 = 号 两 边 的 数 据 类 型 就 不 一 致 了 左 边 的 类 型 是 指 向 整 个 数 组 的 指 针, 右 边 的 数 据 类 型 是 指 向 单 个 字 符 的 指 针 在 Visual C++6.0 上 给 出 如 下 警 告 :warning C4047: 'initializing' : 'char (*)[5]' differs in levels of indirection from 'char *' 还 好, 这 里 虽 然 给 出 了 警 告, 但 由 于 &a 和 a 的 值 一 样, 而 变 量 作 为 右 值 时 编 译 器 只 是 取 变 量 的 值, 所 以 运 行 并 没 有 什 么 问 题 不 过 我 仍 然 警 告 你 别 这 么 用 既 然 现 在 清 楚 了 p3 和 p4 都 是 指 向 整 个 数 组 的, 那 p3+1 和 p4+1 的 值 就 很 好 理 解 了 但 是 如 果 修 改 一 下 代 码, 会 有 什 么 问 题?p3+1 和 p4+1 的 值 又 是 多 少 呢? int main() char a[5]='a','b','c','d'; char (*p3)[3] = &a; char (*p4)[3] = a; return 0; 甚 至 还 可 以 把 代 码 再 修 改 : int main() char a[5]='a','b','c','d'; char (*p3)[10] = &a; char (*p4)[10] = a; return 0; 这 个 时 候 又 会 有 什 么 样 的 问 题?p3+1 和 p4+1 的 值 又 是 多 少? 上 述 几 个 问 题, 希 望 读 者 能 仔 细 考 虑 考 虑 4.4.4, 地 址 的 强 制 转 换 先 看 下 面 这 个 例 子 : struct Test int Num; char *pcname;
89 short sdate; char cha[2]; short sba[4]; *p; 假 设 p 的 值 为 0x 如 下 表 表 达 式 的 值 分 别 为 多 少? p + 0x1 = 0x? (unsigned long)p + 0x1 = 0x? (unsigned int*)p + 0x1 = 0x? 我 相 信 会 有 很 多 人 一 开 始 没 看 明 白 这 个 问 题 是 什 么 意 思 其 实 我 们 再 仔 细 看 看, 这 个 知 识 点 似 曾 相 识 一 个 指 针 变 量 与 一 个 整 数 相 加 减, 到 底 该 怎 么 解 析 呢? 还 记 得 前 面 我 们 的 表 达 式 a+1 与 &a+1 之 间 的 区 别 吗? 其 实 这 里 也 一 样 指 针 变 量 与 一 个 整 数 相 加 减 并 不 是 用 指 针 变 量 里 的 地 址 直 接 加 减 这 个 整 数 这 个 整 数 的 单 位 不 是 byte 而 是 元 素 的 个 数 所 以 : p+0x1 的 值 为 0x sizof(Test)*0x1 至 于 此 结 构 体 的 大 小 为 20byte, 前 面 的 章 节 已 经 详 细 讲 解 过 所 以 p+0x1 的 值 为 :0x (unsigned long)p + 0x1 的 值 呢? 这 里 涉 及 到 强 制 转 换, 将 指 针 变 量 p 保 存 的 值 强 制 转 换 成 无 符 号 的 长 整 型 数 任 何 数 值 一 旦 被 强 制 转 换, 其 类 型 就 改 变 了 所 以 这 个 表 达 式 其 实 就 是 一 个 无 符 号 的 长 整 型 数 加 上 另 一 个 整 数 所 以 其 值 为 :0x (unsigned int*)p + 0x1 的 值 呢? 这 里 的 p 被 强 制 转 换 成 一 个 指 向 无 符 号 整 型 的 指 针 所 以 其 值 为 :0x sizof(unsigned int)*0x1, 等 于 0x 上 面 这 个 问 题 似 乎 还 没 啥 技 术 含 量, 下 面 就 来 个 有 技 术 含 量 的 : 在 x86 系 统 下, 其 值 为 多 少? int main() int a[4]=1,2,3,4; int *ptr1=(int *)(&a+1); int *ptr2=(int *)((int)a+1); printf("%x,%x",ptr1[-1],*ptr2); return 0; 这 是 我 讲 课 时 一 个 学 生 问 我 的 题, 他 在 网 上 看 到 的, 据 说 难 倒 了 n 个 人 我 看 题 之 后 告 诉 他, 这 些 人 肯 定 不 懂 汇 编, 一 个 懂 汇 编 的 人, 这 种 题 实 在 是 小 case 下 面 就 来 分 析 分 析 这 个 问 题 : 根 据 上 面 的 讲 解,&a+1 与 a+1 的 区 别 已 经 清 楚 ptr1: 将 &a+1 的 值 强 制 转 换 成 int* 类 型, 赋 值 给 int* 类 型 的 变 量 ptr,ptr1 肯 定 指 到 数 组 a 的 下 一 个 int 类 型 数 据 了 ptr1[-1] 被 解 析 成 *(ptr1-1), 即 ptr1 往 后 退 4 个 byte 所 以 其 值 为 0x4 ptr2: 按 照 上 面 的 讲 解,(int)a+1 的 值 是 元 素 a[0] 的 第 二 个 字 节 的 地 址 然 后 把 这 个 地 址 强 制 转 换 成 int* 类 型 的 值 赋 给 ptr2, 也 就 是 说 *ptr2 的 值 应 该 为 元 素 a[0] 的 第 二 个 字 节 开 始 的 连 续 4 个 byte 的 内 容 其 内 存 布 局 如 下 图 :
90 好, 问 题 就 来 了, 这 连 续 4 个 byte 里 到 底 存 了 什 么 东 西 呢? 也 就 是 说 元 素 a[0],a[1] 里 面 的 值 到 底 怎 么 存 储 的 这 就 涉 及 到 系 统 的 大 小 端 模 式 了, 如 果 懂 汇 编 的 话, 这 根 本 就 不 是 问 题 既 然 不 知 道 当 前 系 统 是 什 么 模 式, 那 就 得 想 办 法 测 试 大 小 端 模 式 与 测 试 的 方 法 在 第 一 章 讲 解 union 关 键 字 时 已 经 详 细 讨 论 过 了, 请 翻 到 彼 处 参 看, 这 里 就 不 再 详 述 我 们 可 以 用 下 面 这 个 函 数 来 测 试 当 前 系 统 的 模 式 int checksystem( ) union check int i; char ch; c; c.i = 1; return (c.ch ==1); 如 果 当 前 系 统 为 大 端 模 式 这 个 函 数 返 回 0; 如 果 为 小 端 模 式, 函 数 返 回 1 也 就 是 说 如 果 此 函 数 的 返 回 值 为 1 的 话,*ptr2 的 值 为 0x 如 果 此 函 数 的 返 回 值 为 0 的 话,*ptr2 的 值 为 0x , 多 维 数 组 与 多 级 指 针 多 维 数 组 与 多 级 指 针 也 是 初 学 者 感 觉 迷 糊 的 一 个 地 方 超 过 二 维 的 数 组 和 超 过 二 级 的 指 针 其 实 并 不 多 用 如 果 能 弄 明 白 二 维 数 组 与 二 级 指 针, 那 二 维 以 上 的 也 不 是 什 么 问 题 了 所 以 本 节 重 点 讨 论 二 维 数 组 与 二 级 指 针
91 4.5.1, 二 维 数 组 , 假 想 中 的 二 维 数 组 布 局 我 们 前 面 讨 论 过, 数 组 里 面 可 以 存 任 何 数 据, 除 了 函 数 下 面 就 详 细 讨 论 讨 论 数 组 里 面 存 数 组 的 情 况 Excel 表, 我 相 信 大 家 都 见 过 我 们 平 时 就 可 以 把 二 维 数 组 假 想 成 一 个 excel 表, 比 如 : char a[3][4]; , 内 存 与 尺 子 的 对 比 实 际 上 内 存 不 是 表 状 的, 而 是 线 性 的 见 过 尺 子 吧? 尺 子 和 我 们 的 内 存 非 常 相 似 一 般 尺 子 上 最 小 刻 度 为 毫 米, 而 内 存 的 最 小 单 位 为 1 个 byte 平 时 我 们 说 32 毫 米, 是 指 以 零 开 始 偏 移 32 毫 米 ; 平 时 我 们 说 内 存 地 址 为 0x0000FF00 也 是 指 从 内 存 零 地 址 开 始 偏 移 0x0000FF00 个 byte 既 然 内 存 是 线 性 的, 那 二 维 数 组 在 内 存 里 面 肯 定 也 是 线 性 存 储 的 实 际 上 其 内 存 布 局 如 下 图 : 以 数 组 下 标 的 方 式 来 访 问 其 中 的 某 个 元 素 :a[i][j] 编 译 器 总 是 将 二 维 数 组 看 成 是 一 个 一 维 数 组, 而 一 维 数 组 的 每 一 个 元 素 又 都 是 一 个 数 组 a[3] 这 个 一 维 数 组 的 三 个 元 素 分 别 为 : a[0],a[1],a[2] 每 个 元 素 的 大 小 为 sizeof(a[0]), 即 sizof(char)*4 由 此 可 以 计 算 出 a[0],a[1],a[2] 三 个 元 素 的 首 地 址 分 别 为 &a[0],& a[0]+ 1*sizof(char)*4,& a[0]+ 2*sizof(char)*4 亦 即 a[i] 的 首 地 址 为 & a[0]+ i*sizof(char)*4 这 时 候 再 考 虑 a[i] 里 面 的 内 容 就 本 例 而 言,a[i] 内 有 4 个 char 类 型 的 元 素, 其 每 个 元 素 的 首 地 址 分 别 为 &a[i],&a[i]+1*sizof(char), &a[i]+2*sizof(char),&a[i]+3*sizof(char), 即 a[i][j] 的 首 地 址 为 &a[i]+j*sizof(char) 再 把 &a[i]
92 的 值 用 a 表 示, 得 到 a[i][j] 元 素 的 首 地 址 为 :a+ i*sizof(char)*4+ j*sizof(char) 同 样, 可 以 换 算 成 以 指 针 的 形 式 表 示 :*(*(a+i)+j) 经 过 上 面 的 讲 解, 相 信 你 已 经 掌 握 了 二 维 数 组 在 内 存 里 面 的 布 局 了 下 面 就 看 一 个 题 : #include <stdio.h> int main(int argc,char * argv[]) int a [3][2]=(0,1),(2,3),(4,5); int *p; p=a [0]; printf("%d",p[0]); 问 打 印 出 来 的 结 果 是 多 少? 很 多 人 都 觉 得 这 太 简 单 了, 很 快 就 能 把 答 案 告 诉 我 :0 不 过 很 可 惜, 错 了 答 案 应 该 是 1 如 果 你 也 认 为 是 0, 那 你 实 在 应 该 好 好 看 看 这 个 题 花 括 号 里 面 嵌 套 的 是 小 括 号, 而 不 是 花 括 号! 这 里 是 花 括 号 里 面 嵌 套 了 逗 号 表 达 式! 其 实 这 个 赋 值 就 相 当 于 int a [3][2]= 1, 3, 5; 所 以, 在 初 始 化 二 维 数 组 的 时 候 一 定 要 注 意, 别 不 小 心 把 应 该 用 的 花 括 号 写 成 小 括 号 了 ,&p[4][2] - &a[4][2] 的 值 为 多 少? 上 面 的 问 题 似 乎 还 比 较 好 理 解, 下 面 再 看 一 个 例 子 : int a[5][5]; int (*p)[4]; p=a; 问 &p[4][2] - &a[4][2] 的 值 为 多 少? 这 个 问 题 似 乎 非 常 简 单, 但 是 几 乎 没 有 人 答 对 了 我 们 可 以 先 写 代 码 测 试 一 下 其 值, 然 后 分 析 一 下 到 底 是 为 什 么 在 Visual C++6.0 里, 测 试 代 码 如 下 : int main() int a[5][5]; int (*p)[4]; p=a; printf("a_ptr=%#p,p_ptr=%#p\n",&a[4][2],&p[4][2]); printf("%p,%d\n",&p[4][2] - &a[4][2],&p[4][2] - &a[4][2]); return 0; 经 过 测 试, 可 知 &p[4][2] - &a[4][2] 的 值 为 -4 这 到 底 是 为 什 么 呢? 下 面 我 们 就 来 分 析 一 下 :
93 前 面 我 们 讲 过, 当 数 组 名 a 作 为 右 值 时, 代 表 的 是 数 组 首 元 素 的 首 地 址 这 里 的 a 为 二 维 数 组, 我 们 把 数 组 a 看 作 是 包 含 5 个 int 类 型 元 素 的 一 维 数 组, 里 面 再 存 储 了 一 个 一 维 数 组 如 此, 则 a 在 这 里 代 表 的 是 a[0] 的 首 地 址 a+1 表 示 的 是 一 维 数 组 a 的 第 二 个 元 素 a[4] 表 示 的 是 一 维 数 组 a 的 第 5 个 元 素, 而 这 个 元 素 里 又 存 了 一 个 一 维 数 组 所 以 &a[4][2] 表 示 的 是 &a[0][0]+4*5*sizeof(int) + 2*sizeof(int) 根 据 定 义,p 是 指 向 一 个 包 含 4 个 元 素 的 数 组 的 指 针 也 就 是 说 p+1 表 示 的 是 指 针 p 向 后 移 动 了 一 个 包 含 4 个 int 类 型 元 素 的 数 组 这 里 1 的 单 位 是 p 所 指 向 的 空 间, 即 4*sizeof(int) 所 以,p[4] 相 对 于 p[0] 来 说 是 向 后 移 动 了 4 个 包 含 4 个 int 类 型 元 素 的 数 组, 即 &p[4] 表 示 的 是 &p[0]+4*4*sizeof(int) 由 于 p 被 初 始 化 为 &a[0], 那 么 &p[4][2] 表 示 的 是 &a[0][0]+4*4*sizeof(int)+2* sizeof(int) 再 由 上 面 的 讲 述,&p[4][2] 和 &a[4][2] 的 值 相 差 4 个 int 类 型 的 元 素 现 在, 上 面 测 试 出 来 的 结 果 也 可 以 理 解 了 吧? 其 实 我 们 最 简 单 的 办 法 就 是 画 内 存 布 局 图 : 这 里 最 重 要 的 一 点 就 是 明 白 数 组 指 针 p 所 指 向 的 内 存 到 底 是 什 么 解 决 这 类 问 题 的 最 好 办 法 就 是 画 内 存 布 局 图 4.5.2, 二 级 指 针 , 二 级 指 针 的 内 存 布 局 二 级 指 针 是 经 常 用 到 的, 尤 其 与 二 维 数 组 在 一 起 的 时 候 更 是 令 人 迷 糊 例 如 : char **p; 定 义 了 一 个 二 级 指 针 变 量 p p 是 一 个 指 针 变 量, 毫 无 疑 问 在 32 位 系 统 下 占 4 个 byte 它 与 一 级 指 针 不 同 的 是, 一 级 指 针 保 存 的 是 数 据 的 地 址, 二 级 指 针 保 存 的 是 一 级 指 针 的 地 址 下 图 帮 助 理 解 :
94 我 们 试 着 给 变 量 p 初 始 化 : A),p = NULL; B),char *p2; p = &p2; 任 何 指 针 变 量 都 可 以 被 初 始 化 为 NULL( 注 意 是 NULL, 不 是 NUL, 更 不 是 null), 二 级 指 针 也 不 例 外 也 就 是 说 把 指 针 指 向 数 组 的 零 地 址 联 想 到 前 面 我 们 把 尺 子 比 作 内 存, 如 果 把 内 存 初 始 化 为 NULL, 就 相 当 于 把 指 针 指 向 尺 子 上 0 毫 米 处, 这 时 候 指 针 没 有 任 何 内 存 可 用 当 我 们 真 正 需 要 使 用 p 的 时 候, 就 必 须 把 一 个 一 级 指 针 的 地 址 保 存 到 p 中, 所 以 B) 的 赋 值 方 式 也 是 正 确 的 给 p 赋 值 没 有 问 题, 但 怎 么 使 用 p 呢? 这 就 需 要 我 们 前 面 多 次 提 到 的 钥 匙 ( * ) 第 一 步 : 根 据 p 这 个 变 量, 取 出 它 里 面 存 的 地 址 第 二 步 : 找 到 这 个 地 址 所 在 的 内 存 第 三 步 : 用 钥 匙 打 开 这 块 内 存, 取 出 它 里 面 的 地 址,*p 的 值 第 四 步 : 找 到 第 二 次 取 出 的 这 个 地 址 第 五 步 : 用 钥 匙 打 开 这 块 内 存, 取 出 它 里 面 的 内 容, 这 就 是 我 们 真 正 的 数 据,**p 的 值 我 们 在 这 里 用 了 两 次 钥 匙 ( * ) 才 最 终 取 出 了 真 正 的 数 据 也 就 是 说 要 取 出 二 级 指 针 所 真 正 指 向 的 数 据, 需 要 使 用 两 次 两 次 钥 匙 ( * ) 至 于 超 过 二 维 的 数 组 和 超 过 二 维 的 指 针 一 般 使 用 比 较 少, 而 且 按 照 上 面 的 分 析 方 法 同 样 也 可 以 很 轻 松 的 分 析 明 白, 这 里 就 不 再 详 细 讨 论 读 者 有 兴 趣 的 话, 可 以 研 究 研 究 4.6, 数 组 参 数 与 指 针 参 数 我 们 都 知 道 参 数 分 为 形 参 和 实 参 形 参 是 指 声 明 或 定 义 函 数 时 的 参 数, 而 实 参 是 在 调 用 函 数 时 主 调 函 数 传 递 过 来 的 实 际 值 4.6.1, 一 维 数 组 参 数 , 能 否 向 函 数 传 递 一 个 数 组? 看 例 子 : void fun(char a[10]) char c = a[3];
95 int main() char b[10] = abcdefg ; fun(b[10]); return 0; 先 看 上 面 的 调 用,fun(b[10]); 将 b[10] 这 个 数 组 传 递 到 fun 函 数 但 这 样 正 确 吗?b[10] 是 代 表 一 个 数 组 吗? 显 然 不 是, 我 们 知 道 b[0] 代 表 是 数 组 的 一 个 元 素, 那 b[10] 又 何 尝 不 是 呢? 只 不 过 这 里 数 组 越 界 了, 这 个 b[10] 并 不 存 在 但 在 编 译 阶 段, 编 译 器 并 不 会 真 正 计 算 b[10] 的 地 址 并 取 值, 所 以 在 编 译 的 时 候 编 译 器 并 不 认 为 这 样 有 错 误 虽 然 没 有 错 误, 但 是 编 译 器 仍 然 给 出 了 两 个 警 告 : warning C4047: 'function' : 'char *' differs in levels of indirection from 'char ' warning C4024: 'fun' : different types for formal and actual parameter 1 这 是 什 么 意 思 呢? 这 两 个 警 告 告 诉 我 们, 函 数 参 数 需 要 的 是 一 个 char* 类 型 的 参 数, 而 实 际 参 数 为 char 类 型, 不 匹 配 虽 然 编 译 器 没 有 给 出 错 误, 但 是 这 样 运 行 肯 定 会 有 问 题 如 图 : 这 是 一 个 内 存 异 常, 我 们 分 析 分 析 其 原 因 其 实 这 里 至 少 有 两 个 严 重 的 错 误 第 一 :b[10] 并 不 存 在, 在 编 译 的 时 候 由 于 没 有 去 实 际 地 址 取 值, 所 以 没 有 出 错, 但 是 在 运 行 时, 将 计 算 b[10] 的 实 际 地 址, 并 且 取 值 这 时 候 发 生 越 界 错 误 第 二 : 编 译 器 的 警 告 已 经 告 诉 我 们 编 译 器 需 要 的 是 一 个 char* 类 型 的 参 数, 而 传 递 过 去 的 是 一 个 char 类 型 的 参 数, 这 时 候 fun 函 数 会 将 传 入 的 char 类 型 的 数 据 当 地 址 处 理, 同 样 会 发 生 错 误 ( 这 点 前 面 已 经 详 细 讲 解 ) 第 一 个 错 误 很 好 理 解, 那 么 第 二 个 错 误 怎 么 理 解 呢?fun 函 数 明 明 传 递 的 是 一 个 数 组 啊, 编 译 器 怎 么 会 说 是 char * 类 型 呢? 别 急, 我 们 先 把 函 数 的 调 用 方 式 改 变 一 下 : fun(b); b 是 一 个 数 组, 现 在 将 数 组 b 作 为 实 际 参 数 传 递 这 下 该 没 有 问 题 了 吧? 调 试 运 行, 一 切 正 常, 没 有 问 题, 收 工! 很 轻 易 是 吧? 但 是 你 确 认 你 真 正 明 白 了 这 是 怎 么 回 事? 数 组 b
96 真 的 传 递 到 了 函 数 内 部? , 无 法 向 函 数 传 递 一 个 数 组 我 们 完 全 可 以 验 证 一 下 : void fun(char a[10]) int i = sizeof(a); char c = a[3]; 如 果 数 组 b 真 正 传 递 到 函 数 内 部, 那 i 的 值 应 该 为 10 但 是 我 们 测 试 后 发 现 i 的 值 竟 然 为 4! 为 什 么 会 这 样 呢? 难 道 数 组 b 真 的 没 有 传 递 到 函 数 内 部? 是 的, 确 实 没 有 传 递 过 去, 这 是 因 为 这 样 一 条 规 则 : C 语 言 中, 当 一 维 数 组 作 为 函 数 参 数 的 时 候, 编 译 器 总 是 把 它 解 析 成 一 个 指 向 其 首 元 素 首 地 址 的 指 针 这 么 做 是 有 原 因 的 在 C 语 言 中, 所 有 非 数 组 形 式 的 数 据 实 参 均 以 传 值 形 式 ( 对 实 参 做 一 份 拷 贝 并 传 递 给 被 调 用 的 函 数, 函 数 不 能 修 改 作 为 实 参 的 实 际 变 量 的 值, 而 只 能 修 改 传 递 给 它 的 那 份 拷 贝 ) 调 用 然 而, 如 果 要 拷 贝 整 个 数 组, 无 论 在 空 间 上 还 是 在 时 间 上, 其 开 销 都 是 非 常 大 的 更 重 要 的 是, 在 绝 大 部 分 情 况 下, 你 其 实 并 不 需 要 整 个 数 组 的 拷 贝, 你 只 想 告 诉 函 数 在 那 一 刻 对 哪 个 特 定 的 数 组 感 兴 趣 这 样 的 话, 为 了 节 省 时 间 和 空 间, 提 高 程 序 运 行 的 效 率, 于 是 就 有 了 上 述 的 规 则 同 样 的, 函 数 的 返 回 值 也 不 能 是 一 个 数 组, 而 只 能 是 指 针 这 里 要 明 确 的 一 个 概 念 就 是 : 函 数 本 身 是 没 有 类 型 的, 只 有 函 数 的 返 回 值 才 有 类 型 很 多 书 都 把 这 点 弄 错 了, 甚 至 出 现 XXX 类 型 的 函 数 这 种 说 法 简 直 是 荒 唐 至 极! 经 过 上 面 的 解 释, 相 信 你 已 经 理 解 上 述 的 规 定 以 及 它 的 来 由 上 面 编 译 器 给 出 的 提 示, 说 函 数 的 参 数 是 一 个 char* 类 型 的 指 针, 这 点 相 信 也 可 以 理 解 既 然 如 此, 我 们 完 全 可 以 把 fun 函 数 改 写 成 下 面 的 样 子 : void fun(char *p) char c = p[3];// 或 者 是 char c = *(p+3); 同 样, 你 还 可 以 试 试 这 样 子 : void fun(char a[10]) char c = a[3];
97 int main() char b[100] = abcdefg ; fun(b); return 0; 运 行 完 全 没 有 问 题 实 际 传 递 的 数 组 大 小 与 函 数 形 参 指 定 的 数 组 大 小 没 有 关 系 既 然 如 此, 那 我 们 也 可 以 改 写 成 下 面 的 样 子 : void fun(char a[ ]) char c = a[3]; 改 写 成 这 样 或 许 比 较 好, 至 少 不 会 让 人 误 会 成 只 能 传 递 一 个 10 个 元 素 的 数 组 4.6.2, 一 级 指 针 参 数 , 能 否 把 指 针 变 量 本 身 传 递 给 一 个 函 数 我 们 把 上 一 节 讨 论 的 列 子 再 改 写 一 下 : void fun(char *p) char c = p[3];// 或 者 是 char c = *(p+3); int main() char *p2 = abcdefg ; fun(p2); return 0; 这 个 函 数 调 用, 真 的 把 p2 本 身 传 递 到 了 fun 函 数 内 部 吗? 我 们 知 道 p2 是 main 函 数 内 的 一 个 局 部 变 量, 它 只 在 main 函 数 内 部 有 效 ( 这 里 需 要 澄 清 一 个 问 题 :main 函 数 内 的 变 量 不 是 全 局 变 量, 而 是 局 部 变 量, 只 不 过 它 的 生 命 周 期 和
98 全 局 变 量 一 样 长 而 已 全 局 变 量 一 定 是 定 义 在 函 数 外 部 的 初 学 者 往 往 弄 错 这 点 ) 既 然 它 是 局 部 变 量,fun 函 数 肯 定 无 法 使 用 p2 的 真 身 那 函 数 调 用 怎 么 办? 好 办 : 对 实 参 做 一 份 拷 贝 并 传 递 给 被 调 用 的 函 数 即 对 p2 做 一 份 拷 贝, 假 设 其 拷 贝 名 为 _p2 那 传 递 到 函 数 内 部 的 就 是 _p2 而 并 非 p2 本 身 , 无 法 把 指 针 变 量 本 身 传 递 给 一 个 函 数 这 很 像 孙 悟 空 拔 下 一 根 猴 毛 变 成 自 己 的 样 子 去 忽 悠 小 妖 怪 所 以 fun 函 数 实 际 运 行 时, 用 到 的 都 是 _p2 这 个 变 量 而 非 p2 本 身 如 此, 我 们 看 下 面 的 例 子 : void GetMemory(char * p, int num) p = (char *)malloc(num*sizeof(char)); int main() char *str = NULL; GetMemory(str,10); strcpy(str, hello ); free(str);//free 并 没 有 起 作 用, 内 存 泄 漏 return 0; 在 运 行 strcpy(str, hello ) 语 句 的 时 候 发 生 错 误 这 时 候 观 察 str 的 值, 发 现 仍 然 为 NULL 也 就 是 说 str 本 身 并 没 有 改 变, 我 们 malloc 的 内 存 的 地 址 并 没 有 赋 给 str, 而 是 赋 给 了 _str 而 这 个 _str 是 编 译 器 自 动 分 配 和 回 收 的, 我 们 根 本 就 无 法 使 用 所 以 想 这 样 获 取 一 块 内 存 是 不 行 的 那 怎 么 办? 两 个 办 法 : 第 一 : 用 return char * GetMemory(char * p, int num) p = (char *)malloc(num*sizeof(char)); return p; int main()
99 char *str = NULL; str = GetMemory(str,10); strcpy(str, hello ); free(str); return 0; 这 个 方 法 简 单, 容 易 理 解 第 二 : 用 二 级 指 针 void GetMemory(char ** p, int num) *p = (char *)malloc(num*sizeof(char)); return p; int main() char *str = NULL; GetMemory(&str,10); strcpy(str, hello ); free(str); return 0; 注 意, 这 里 的 参 数 是 &str 而 非 str 这 样 的 话 传 递 过 去 的 是 str 的 地 址, 是 一 个 值 在 函 数 内 部, 用 钥 匙 ( * ) 来 开 锁 :*(&str), 其 值 就 是 str 所 以 malloc 分 配 的 内 存 地 址 是 真 正 赋 值 给 了 str 本 身 另 外 关 于 malloc 和 free 的 具 体 用 法, 内 存 管 理 那 章 有 详 细 讨 论 4.6.3, 二 维 数 组 参 数 与 二 维 指 针 参 数 前 面 详 细 分 析 了 二 维 数 组 与 二 维 指 针, 那 它 们 作 为 参 数 时 与 不 作 为 参 数 时 又 有 什 么 区 别 呢? 看 例 子 : void fun(char a[3][4]);
100 我 们 按 照 上 面 的 分 析, 完 全 可 以 把 a[3][4] 理 解 为 一 个 一 维 数 组 a[3], 其 每 个 元 素 都 是 一 个 含 有 4 个 char 类 型 数 据 的 数 组 上 面 的 规 则, C 语 言 中, 当 一 维 数 组 作 为 函 数 参 数 的 时 候, 编 译 器 总 是 把 它 解 析 成 一 个 指 向 其 首 元 素 首 地 址 的 指 针 在 这 里 同 样 适 用, 也 就 是 说 我 们 可 以 把 这 个 函 数 声 明 改 写 为 : void fun(char (*p)[4]); 这 里 的 括 号 绝 对 不 能 省 略, 这 样 才 能 保 证 编 译 器 把 p 解 析 为 一 个 指 向 包 含 4 个 char 类 型 数 据 元 素 的 数 组, 即 一 维 数 组 a[3] 的 元 素 同 样, 作 为 参 数 时, 一 维 数 组 [] 号 内 的 数 字 完 全 可 以 省 略 : void fun(char a[ ][4]); 不 过 第 二 维 的 维 数 却 不 可 省 略, 想 想 为 什 么 不 可 以 省 略? 注 意 : 如 果 把 上 面 提 到 的 声 明 void fun(char (char *p[4]) 可 以 改 写 成 : (*p)[4]) 中 的 括 号 去 掉 之 后, 声 明 void f un void fun(char **p); 这 是 因 为 参 数 *p[4], 对 于 p 来 说, 它 是 一 个 包 含 4 个 指 针 的 一 维 数 组, 同 样 把 这 个 一 维 数 组 也 改 写 为 指 针 的 形 式, 那 就 得 到 上 面 的 写 法 上 面 讨 论 了 这 么 多, 那 我 们 把 二 维 数 组 参 数 和 二 维 指 针 参 数 的 等 效 关 系 整 理 一 下 : 数 组 参 数 等 效 的 指 针 参 数 数 组 的 数 组 :char a[3][4] 数 组 的 指 针 :char (*p)[10] 指 针 数 组 : char *a[5] 指 针 的 指 针 :char **p 这 里 需 要 注 意 的 是 :C 语 言 中, 当 一 维 数 组 作 为 函 数 参 数 的 时 候, 编 译 器 总 是 把 它 解 析 成 一 个 指 向 其 首 元 素 首 地 址 的 指 针 这 条 规 则 并 不 是 递 归 的, 也 就 是 说 只 有 一 维 数 组 才 是 如 此, 当 数 组 超 过 一 维 时, 将 第 一 维 改 写 为 指 向 数 组 首 元 素 首 地 址 的 指 针 之 后, 后 面 的 维 再 也 不 可 改 写 比 如 :a[3][4][5] 作 为 参 数 时 可 以 被 改 写 为 (*p)[4][5] 至 于 超 过 二 维 的 数 组 和 超 过 二 级 的 指 针, 由 于 本 身 很 少 使 用, 而 且 按 照 上 面 的 分 析 方 法 也 能 很 好 的 理 解, 这 里 就 不 再 详 细 讨 论 有 兴 趣 的 可 以 好 好 研 究 研 究 4.7, 函 数 指 针 4.7.1, 函 数 指 针 的 定 义 顾 名 思 义, 函 数 指 针 就 是 函 数 的 指 针 它 是 一 个 指 针, 指 向 一 个 函 数 看 例 子 : A),char * (*fun1)(char * p1,char * p2); B),char * *fun2(char * p1,char * p2); C),char * fun3(char * p1,char * p2);
101 看 看 上 面 三 个 表 达 式 分 别 是 什 么 意 思? C): 这 很 容 易,fun3 是 函 数 名,p1,p2 是 参 数, 其 类 型 为 char * 型, 函 数 的 返 回 值 为 char * 类 型 B): 也 很 简 单, 与 C) 表 达 式 相 比, 唯 一 不 同 的 就 是 函 数 的 返 回 值 类 型 为 char**, 是 个 二 级 指 针 A):fun1 是 函 数 名 吗? 回 忆 一 下 前 面 讲 解 数 组 指 针 时 的 情 形 我 们 说 数 组 指 针 这 么 定 义 或 许 更 清 晰 : int (*)[10] p; 再 看 看 A) 表 达 式 与 这 里 何 其 相 似! 明 白 了 吧 这 里 fun1 不 是 什 么 函 数 名, 而 是 一 个 指 针 变 量, 它 指 向 一 个 函 数 这 个 函 数 有 两 个 指 针 类 型 的 参 数, 函 数 的 返 回 值 也 是 一 个 指 针 同 样, 我 们 把 这 个 表 达 式 改 写 一 下 :char * (*)(char * p1,char * p2) fun1; 这 样 子 是 不 是 好 看 一 些 呢? 只 可 惜 编 译 器 不 这 么 想 ^_^ 4.7.2, 函 数 指 针 的 使 用 , 函 数 指 针 使 用 的 例 子 上 面 我 们 定 义 了 一 个 函 数 指 针, 但 如 何 来 使 用 它 呢? 先 看 如 下 例 子 : #include <stdio.h> #include <string.h> char * fun(char * p1,char * p2) int i = 0; i = strcmp(p1,p2); if (0 == i) return p1; else return p2; int main() char * (*pf)(char * p1,char * p2); pf = &fun; (*pf) ("aa","bb");
102 return 0; 我 们 使 用 指 针 的 时 候, 需 要 通 过 钥 匙 ( * ) 来 取 其 指 向 的 内 存 里 面 的 值, 函 数 指 针 使 用 也 如 此 通 过 用 (*pf) 取 出 存 在 这 个 地 址 上 的 函 数, 然 后 调 用 它 这 里 需 要 注 意 到 是, 在 Visual C++6.0 里, 给 函 数 指 针 赋 值 时, 可 以 用 &fun 或 直 接 用 函 数 名 fun 这 是 因 为 函 数 名 被 编 译 之 后 其 实 就 是 一 个 地 址, 所 以 这 里 两 种 用 法 没 有 本 质 的 差 别 这 个 例 子 很 简 单, 就 不 再 详 细 讨 论 了 ,*(int*)&p ---- 这 是 什 么? 也 许 上 面 的 例 子 过 于 简 单, 我 们 看 看 下 面 的 例 子 : void Function() printf("call Function!\n"); int main() void (*p)(); *(int*)&p=(int)function; (*p) (); return 0; 这 是 在 干 什 么?*(int*)&p=(int)Function; 表 示 什 么 意 思? 别 急, 先 看 这 行 代 码 : void (*p)(); 这 行 代 码 定 义 了 一 个 指 针 变 量 p,p 指 向 一 个 函 数, 这 个 函 数 的 参 数 和 返 回 值 都 是 void &p 是 求 指 针 变 量 p 本 身 的 地 址, 这 是 一 个 32 位 的 二 进 制 常 数 (32 位 系 统 ) (int*)&p 表 示 将 地 址 强 制 转 换 成 指 向 int 类 型 数 据 的 指 针 (int)function 表 示 将 函 数 的 入 口 地 址 强 制 转 换 成 int 类 型 的 数 据 分 析 到 这 里, 相 信 你 已 经 明 白 *(int*)&p=(int)function; 表 示 将 函 数 的 入 口 地 址 赋 值 给 指 针 变 量 p 那 么 (*p) (); 就 是 表 示 对 函 数 的 调 用 讲 解 到 这 里, 相 信 你 已 经 明 白 了 其 实 函 数 指 针 与 普 通 指 针 没 什 么 差 别, 只 是 指 向 的 内 容 不 同 而 已 使 用 函 数 指 针 的 好 处 在 于, 可 以 将 实 现 同 一 功 能 的 多 个 模 块 统 一 起 来 标 识, 这 样 一 来 更 容 易 后 期 的 维 护, 系 统 结 构 更 加 清 晰 或 者 归 纳 为 : 便 于 分 层 设 计 利 于 系 统 抽 象 降 低 耦 合 度 以 及 使 接 口 与 实 现 分 开 4.7.3,(*(void(*) ())0)() 这 是 什 么? 是 不 是 感 觉 上 面 的 例 子 太 简 单, 不 够 刺 激? 好, 那 就 来 点 刺 激 的, 看 下 面 这 个 例 子 : (*(void(*) ())0)();
103 这 是 C Traps and Pitfalls 这 本 经 典 的 书 中 的 一 个 例 子 没 有 发 狂 吧? 下 面 我 们 就 来 分 析 分 析 : 第 一 步 :void(*) (), 可 以 明 白 这 是 一 个 函 数 指 针 类 型 这 个 函 数 没 有 参 数, 没 有 返 回 值 第 二 步 :(void(*) ())0, 这 是 将 0 强 制 转 换 为 函 数 指 针 类 型,0 是 一 个 地 址, 也 就 是 说 一 个 函 数 存 在 首 地 址 为 0 的 一 段 区 域 内 第 三 步 :(*(void(*) ())0), 这 是 取 0 地 址 开 始 的 一 段 内 存 里 面 的 内 容, 其 内 容 就 是 保 存 在 首 地 址 为 0 的 一 段 区 域 内 的 函 数 第 四 步 :(*(void(*) ())0)(), 这 是 函 数 调 用 好 像 还 是 很 简 单 是 吧, 上 面 的 例 子 再 改 写 改 写 : (*(char**(*) (char **,char **))0) ( char **,char **); 如 果 没 有 上 面 的 分 析, 肯 怕 不 容 易 把 这 个 表 达 式 看 明 白 吧 不 过 现 在 应 该 是 很 简 单 的 一 件 事 了 读 者 以 为 呢? 4.7.4, 函 数 指 针 数 组 现 在 我 们 清 楚 表 达 式 char * (*pf)(char * p) 定 义 的 是 一 个 函 数 指 针 pf 既 然 pf 是 一 个 指 针, 那 就 可 以 储 存 在 一 个 数 组 里 把 上 式 修 改 一 下 : char * (*pf[3])(char * p); 这 是 定 义 一 个 函 数 指 针 数 组 它 是 一 个 数 组, 数 组 名 为 pf, 数 组 内 存 储 了 3 个 指 向 函 数 的 指 针 这 些 指 针 指 向 一 些 返 回 值 类 型 为 指 向 字 符 的 指 针 参 数 为 一 个 指 向 字 符 的 指 针 的 函 数 这 念 起 来 似 乎 有 点 拗 口 不 过 不 要 紧, 关 键 是 你 明 白 这 是 一 个 指 针 数 组, 是 数 组 函 数 指 针 数 组 怎 么 使 用 呢? 这 里 也 给 出 一 个 非 常 简 单 的 例 子, 只 要 真 正 掌 握 了 使 用 方 法, 再 复 杂 的 问 题 都 可 以 应 对 如 下 : #include <stdio.h> #include <string.h> char * fun1(char * p) printf("%s\n",p); return p; char * fun2(char * p) printf("%s\n",p); return p;
104 char * fun3(char * p) printf("%s\n",p); return p; int main() char * (*pf[3])(char * p); pf[0] = fun1; // 可 以 直 接 用 函 数 名 pf[1] = &fun2; // 可 以 用 函 数 名 加 上 取 地 址 符 pf[2] = &fun3; pf[0]("fun1"); pf[0]("fun2"); pf[0]("fun3"); return 0; 4.7.5, 函 数 指 针 数 组 的 指 针 看 着 这 个 标 题 没 发 狂 吧? 函 数 指 针 就 够 一 般 初 学 者 折 腾 了, 函 数 指 针 数 组 就 更 加 麻 烦, 现 在 的 函 数 指 针 数 组 指 针 就 更 难 理 解 了 其 实, 没 这 么 复 杂 前 面 详 细 讨 论 过 数 组 指 针 的 问 题, 这 里 的 函 数 指 针 数 组 指 针 不 就 是 一 个 指 针 嘛 只 不 过 这 个 指 针 指 向 一 个 数 组, 这 个 数 组 里 面 存 的 都 是 指 向 函 数 的 指 针 仅 此 而 已 下 面 就 定 义 一 个 简 单 的 函 数 指 针 数 组 指 针 : char * (*(*pf)[3])(char * p); 注 意, 这 里 的 pf 和 上 一 节 的 pf 就 完 全 是 两 码 事 了 上 一 节 的 pf 并 非 指 针, 而 是 一 个 数 组 名 ; 这 里 的 pf 确 实 是 实 实 在 在 的 指 针 这 个 指 针 指 向 一 个 包 含 了 3 个 元 素 的 数 组 ; 这 个 数 字 里 面 存 的 是 指 向 函 数 的 指 针 ; 这 些 指 针 指 向 一 些 返 回 值 类 型 为 指 向 字 符 的 指 针 参 数 为 一 个 指 向 字 符 的 指 针 的 函 数 这 比 上 一 节 的 函 数 指 针 数 组 更 拗 口 其 实 你 不 用 管 这 么 多, 明 白 这 是 一 个 指 针 就 ok 了 其 用 法 与 前 面 讲 的 数 组 指 针 没 有 差 别 下 面 列 一 个 简 单 的 例 子 :
105 #include <stdio.h> #include <string.h> char * fun1(char * p) printf("%s\n",p); return p; char * fun2(char * p) printf("%s\n",p); return p; char * fun3(char * p) printf("%s\n",p); return p; int main() char * (*a[3])(char * p); char * (*(*pf)[3])(char * p); pf = &a; a[0] = fun1; a[1] = &fun2; a[2] = &fun3; pf[0][0]("fun1"); pf[0][1]("fun2");
106 pf[0][2]("fun3"); return 0;
107 第 五 章 内 存 管 理 欢 迎 您 进 入 这 片 雷 区 我 欣 赏 能 活 着 走 出 这 片 雷 区 的 高 手, 但 更 欣 赏 粉 身 碎 骨 浑 不 怕, 不 留 地 雷 在 人 间 的 勇 者 请 您 不 要 把 这 当 作 一 个 扫 雷 游 戏, 因 为 没 有 人 能 以 游 戏 的 心 态 取 胜 曾 经 很 短 暂 的 使 用 过 一 段 时 间 的 C# 头 三 天 特 别 不 习 惯, 因 为 没 有 指 针! 后 来 用 起 来 越 来 越 顺 手, 还 是 因 为 没 有 指 针! 几 天 的 时 间 很 轻 易 的 写 了 1 万 多 行 C# 代 码, 感 觉 比 用 C 或 C++ 简 单 多 了 因 为 你 根 本 就 不 用 去 考 虑 底 层 的 内 存 管 理, 也 不 用 考 虑 内 存 泄 漏 的 问 题, 更 加 不 怕 野 指 针 ( 有 的 书 叫 悬 垂 指 针 ) 所 有 这 一 切, 系 统 都 给 你 做 了, 所 以 可 以 很 轻 松 的 拿 来 就 用 但 是 C 或 C++, 这 一 切 都 必 须 你 自 己 来 处 理, 即 使 经 验 丰 富 的 老 手 也 免 不 了 犯 错 我 曾 经 做 过 一 个 项 目, 软 件 提 交 给 客 户 很 久 之 后, 客 户 发 现 一 个 很 严 重 的 bug 这 个 bug 很 少 出 现, 但 是 一 旦 出 现 就 是 致 命 的, 系 统 无 法 启 动! 这 个 问 题 交 给 我 来 解 决 由 于 要 再 现 这 个 bug 十 分 困 难, 按 照 客 户 给 定 的 操 作 步 骤 根 本 无 法 再 现 经 过 大 概 2 周 时 间 天 天 和 客 户 越 洋 视 频 之 后, 终 于 找 到 了 bug 的 原 因 野 指 针! 所 以 关 于 内 存 管 理, 尤 其 是 野 指 针 的 问 题, 千 万 千 万 不 要 掉 以 轻 心, 否 则, 你 会 很 惨 的 5.1, 什 么 是 野 指 针 那 到 底 什 么 是 野 指 针 呢? 怎 么 去 理 解 这 个 野 呢? 我 们 先 看 别 的 两 个 关 于 野 的 词 : 野 孩 子 : 没 人 要, 没 人 管 的 孩 子 ; 行 为 动 作 不 守 规 矩, 调 皮 捣 蛋 的 孩 子 野 狗 : 没 有 主 人 的 狗, 没 有 链 子 锁 着 的 狗, 喜 欢 四 处 咬 人 对 付 野 孩 子 的 最 好 办 法 是 给 他 定 一 套 规 矩, 好 好 管 教 一 旦 发 现 没 有 按 规 矩 办 事 就 好 好 收 拾 他 对 付 野 狗 最 好 的 办 法 就 是 拿 条 狗 链 锁 着 它, 不 让 它 四 处 乱 跑 对 付 也 指 针 肯 怕 比 对 付 野 孩 子 或 野 狗 更 困 难 我 们 需 要 把 对 付 野 孩 子 和 野 狗 的 办 法 都 用 上 既 需 要 规 矩, 也 需 要 链 子 前 面 我 们 把 内 存 比 作 尺 子, 很 轻 松 的 理 解 了 内 存 尺 子 上 的 0 毫 米 处 就 是 内 存 的 0 地 址 处, 也 就 是 NULL 地 址 处 这 条 栓 野 指 针 的 链 子 就 是 这 个 NULL 定 义 指 针 变 量 的 同 时 最 好 初 始 化 为 NULL, 用 完 指 针 之 后 也 将 指 针 变 量 的 值 设 置 为 NULL 也 就 是 说 除 了 在 使 用 时, 别 的 时 间 都 把 指 针 栓 到 0 地 址 处 这 样 它 就 老 实 了 5.2, 栈 堆 和 静 态 区 对 于 程 序 员, 一 般 来 说, 我 们 可 以 简 单 的 理 解 为 内 存 分 为 三 个 部 分 : 静 态 区, 栈, 堆 很 多 书 没 有 把 把 堆 和 栈 解 释 清 楚, 导 致 初 学 者 总 是 分 不 清 楚 其 实 堆 栈 就 是 栈, 而 不 是 堆 堆 的 英 文 是 heap; 栈 的 英 文 是 stack, 也 翻 译 为 堆 栈 堆 和 栈 都 有 自 己 的 特 性, 这 里 先 不 做
108 讨 论 再 打 个 比 方 : 一 层 教 学 楼, 可 能 有 外 语 教 室, 允 许 外 语 系 学 生 和 老 师 进 入 ; 还 可 能 有 数 学 教 师, 允 许 数 学 系 学 生 和 老 师 进 入 ; 还 可 能 有 校 长 办 公 室, 允 许 校 长 进 入 同 样, 内 存 也 是 这 样, 内 存 的 三 个 部 分, 不 是 所 有 的 东 西 都 能 存 进 去 的 静 态 区 : 保 存 自 动 全 局 变 量 和 static 变 量 ( 包 括 static 全 局 和 局 部 变 量 ) 静 态 区 的 内 容 在 总 个 程 序 的 生 命 周 期 内 都 存 在, 由 编 译 器 在 编 译 的 时 候 分 配 栈 : 保 存 局 部 变 量 栈 上 的 内 容 只 在 函 数 的 范 围 内 存 在, 当 函 数 运 行 结 束, 这 些 内 容 也 会 自 动 被 销 毁 其 特 点 是 效 率 高, 但 空 间 大 小 有 限 堆 : 由 malloc 系 列 函 数 或 new 操 作 符 分 配 的 内 存 其 生 命 周 期 由 free 或 delete 决 定 在 没 有 释 放 之 前 一 直 存 在, 直 到 程 序 结 束 其 特 点 是 使 用 灵 活, 空 间 比 较 大, 但 容 易 出 错 5.3, 常 见 的 内 存 错 误 及 对 策 5.3.1, 指 针 没 有 指 向 一 块 合 法 的 内 存 定 义 了 指 针 变 量, 但 是 没 有 为 指 针 分 配 内 存, 即 指 针 没 有 指 向 一 块 合 法 的 内 存 浅 显 的 例 子 就 不 举 了, 这 里 举 几 个 比 较 隐 蔽 的 例 子 , 结 构 体 成 员 指 针 未 初 始 化 struct student char *name; int score; stu,*pstu; int main() strcpy(stu.name,"jimy"); stu.score = 99; return 0; 很 多 初 学 者 犯 了 这 个 错 误 还 不 知 道 是 怎 么 回 事 这 里 定 义 了 结 构 体 变 量 stu, 但 是 他 没 想 到 这 个 结 构 体 内 部 char *name 这 成 员 在 定 义 结 构 体 变 量 stu 时, 只 是 给 name 这 个 指 针 变 量 本 身 分 配 了 4 个 字 节 name 指 针 并 没 有 指 向 一 个 合 法 的 地 址, 这 时 候 其 内 部 存 的 只 是 一 些 乱 码 所 以 在 调 用 strcpy 函 数 时, 会 将 字 符 串 "Jimy" 往 乱 码 所 指 的 内 存 上 拷 贝, 而 这 块 内
109 存 name 指 针 根 本 就 无 权 访 问, 导 致 出 错 解 决 的 办 法 是 为 name 指 针 malloc 一 块 空 间 同 样, 也 有 人 犯 如 下 错 误 : int main() pstu = (struct student*)malloc(sizeof(struct student)); strcpy(pstu->name,"jimy"); pstu->score = 99; free(pstu); return 0; 为 指 针 变 量 pstu 分 配 了 内 存, 但 是 同 样 没 有 给 name 指 针 分 配 内 存 错 误 与 上 面 第 一 种 情 况 一 样, 解 决 的 办 法 也 一 样 这 里 用 了 一 个 malloc 给 人 一 种 错 觉, 以 为 也 给 name 指 针 分 配 了 内 存 , 没 有 为 结 构 体 指 针 分 配 足 够 的 内 存 int main() pstu = (struct student*)malloc(sizeof(struct student*)); strcpy(pstu->name,"jimy"); pstu->score = 99; free(pstu); return 0; 为 pstu 分 配 内 存 的 时 候, 分 配 的 内 存 大 小 不 合 适 这 里 把 sizeof(struct student) 误 写 为 sizeof(struct student*) 当 然 name 指 针 同 样 没 有 被 分 配 内 存 解 决 办 法 同 上 , 函 数 的 入 口 校 验 不 管 什 么 时 候, 我 们 使 用 指 针 之 前 一 定 要 确 保 指 针 是 有 效 的 一 般 在 函 数 入 口 处 使 用 assert(null!= p) 对 参 数 进 行 校 验 在 非 参 数 的 地 方 使 用 if(null!= p) 来 校 验 但 这 都 有 一 个 要 求, 即 p 在 定 义 的 同 时 被 初 始 化 为 NULL 了 比 如 上 面 的 例 子, 即 使 用 if(null!= p) 校 验 也 起 不 了 作 用, 因 为 name 指 针 并 没 有 被 初 始 化 为 NULL, 其 内 部 是 一 个 非 NULL 的 乱 码
110 assert 是 一 个 宏, 而 不 是 函 数, 包 含 在 assert.h 头 文 件 中 如 果 其 后 面 括 号 里 的 值 为 假, 则 程 序 终 止 运 行, 并 提 示 出 错 ; 如 果 后 面 括 号 里 的 值 为 真, 则 继 续 运 行 后 面 的 代 码 这 个 宏 只 在 Debug 版 本 上 起 作 用, 而 在 Release 版 本 被 编 译 器 完 全 优 化 掉, 这 样 就 不 会 影 响 代 码 的 性 能 有 人 也 许 会 问, 既 然 在 Release 版 本 被 编 译 器 完 全 优 化 掉, 那 Release 版 本 是 不 是 就 完 全 没 有 这 个 参 数 入 口 校 验 了 呢? 这 样 的 话 那 不 就 跟 不 使 用 它 效 果 一 样 吗? 是 的, 使 用 assert 宏 的 地 方 在 Release 版 本 里 面 确 实 没 有 了 这 些 校 验 但 是 我 们 要 知 道, assert 宏 只 是 帮 助 我 们 调 试 代 码 用 的, 它 的 一 切 作 用 就 是 让 我 们 尽 可 能 的 在 调 试 函 数 的 时 候 把 错 误 排 除 掉, 而 不 是 等 到 Release 之 后 它 本 身 并 没 有 除 错 功 能 再 有 一 点 就 是, 参 数 出 现 错 误 并 非 本 函 数 有 问 题, 而 是 调 用 者 传 过 来 的 实 参 有 问 题 assert 宏 可 以 帮 助 我 们 定 位 错 误, 而 不 是 排 除 错 误 5.3.2, 为 指 针 分 配 的 内 存 太 小 为 指 针 分 配 了 内 存, 但 是 内 存 大 小 不 够, 导 致 出 现 越 界 错 误 char *p1 = abcdefg ; char *p2 = (char *)malloc(sizeof(char)*strlen(p1)); strcpy(p2,p1); p1 是 字 符 串 常 量, 其 长 度 为 7 个 字 符, 但 其 所 占 内 存 大 小 为 8 个 byte 初 学 者 往 往 忘 了 字 符 串 常 量 的 结 束 标 志 \0 这 样 的 话 将 导 致 p1 字 符 串 中 最 后 一 个 空 字 符 \0 没 有 被 拷 贝 到 p2 中 解 决 的 办 法 是 加 上 这 个 字 符 串 结 束 标 志 符 : char *p2 = (char *)malloc(sizeof(char)*strlen(p1)+1*sizeof(char)); 这 里 需 要 注 意 的 是, 只 有 字 符 串 常 量 才 有 结 束 标 志 符 比 如 下 面 这 种 写 法 就 没 有 结 束 标 志 符 了 : char a[7] = a, b, c, d, e, f, g ; 另 外, 不 要 因 为 char 类 型 大 小 为 1 个 byte 就 省 略 sizof(char) 这 种 写 法 这 样 只 会 使 你 的 代 码 可 移 植 性 下 降 5.3.3, 内 存 分 配 成 功, 但 并 未 初 始 化 犯 这 个 错 误 往 往 是 由 于 没 有 初 始 化 的 概 念 或 者 是 以 为 内 存 分 配 好 之 后 其 值 自 然 为 0 未 初 始 化 指 针 变 量 也 许 看 起 来 不 那 么 严 重, 但 是 它 确 确 实 实 是 个 非 常 严 重 的 问 题, 而 且 往 往 出 现 这 种 错 误 很 难 找 到 原 因 曾 经 有 一 个 学 生 在 写 一 个 windows 程 序 时, 想 调 用 字 库 的 某 个 字 体 而 调 用 这 个 字 库 需 要 填 充 一 个 结 构 体 他 很 自 然 的 定 义 了 一 个 结 构 体 变 量, 然 后 把 他 想 要 的 字 库 代 码 赋 值 给 了 相 关 的 变 量 但 是, 问 题 就 来 了, 不 管 怎 么 调 试, 他 所 需 要 的 这 种 字 体 效 果 总 是 不 出 来 我 在 检 查 了 他 的 代 码 之 后, 没 有 发 现 什 么 问 题, 于 是 单 步 调 试 在 观 察 这 个 结 构 体 变
111 量 的 内 存 时, 发 现 有 几 个 成 员 的 值 为 乱 码 就 是 其 中 某 一 个 乱 码 惹 得 祸! 因 为 系 统 会 按 照 这 个 结 构 体 中 的 某 些 特 定 成 员 的 值 去 字 库 中 寻 找 匹 配 的 字 体, 当 这 些 值 与 字 库 中 某 种 字 体 的 某 些 项 匹 配 时, 就 调 用 这 种 字 体 但 是 很 不 幸, 正 是 因 为 这 几 个 乱 码, 导 致 没 有 找 到 相 匹 配 的 字 体! 因 为 系 统 并 无 法 区 分 什 么 数 据 是 乱 码, 什 么 数 据 是 有 效 的 数 据 只 要 有 数 据, 系 统 就 理 所 当 然 的 认 为 它 是 有 效 的 也 许 这 种 严 重 的 问 题 并 不 多 见, 但 是 也 绝 不 能 掉 以 轻 心 所 以 在 定 义 一 个 变 量 时, 第 一 件 事 就 是 初 始 化 你 可 以 把 它 初 始 化 为 一 个 有 效 的 值, 比 如 : int i = 10; char *p = (char *)malloc(sizeof(char)); 但 是 往 往 这 个 时 候 我 们 还 不 确 定 这 个 变 量 的 初 值, 这 样 的 话 可 以 初 始 化 为 0 或 NULL int i = 0; char *p = NULL; 如 果 定 义 的 是 数 组 的 话, 可 以 这 样 初 始 化 : int a[10] = 0; 或 者 用 memset 函 数 来 初 始 化 为 0: memset(a,0,sizeof(a)); memset 函 数 有 三 个 参 数, 第 一 个 是 要 被 设 置 的 内 存 起 始 地 址 ; 第 二 个 参 数 是 要 被 设 置 的 值 ; 第 三 个 参 数 是 要 被 设 置 的 内 存 大 小, 单 位 为 byte 这 里 并 不 想 过 多 的 讨 论 memset 函 数 的 用 法, 如 果 想 了 解 更 多, 请 参 考 相 关 资 料 至 于 指 针 变 量 如 果 未 被 初 始 化, 会 导 致 if 语 句 或 assert 宏 校 验 失 败 这 一 点, 上 面 已 有 分 析 5.3.4, 内 存 越 界 内 存 分 配 成 功, 且 已 经 初 始 化, 但 是 操 作 越 过 了 内 存 的 边 界 这 种 错 误 经 常 是 由 于 操 作 数 组 或 指 针 时 出 现 多 1 或 少 1 比 如 : int a[10] = 0; for (i=0; i<=10; i++) a[i] = i; 所 以,for 循 环 的 循 环 变 量 一 定 要 使 用 半 开 半 闭 的 区 间, 而 且 如 果 不 是 特 殊 情 况, 循 环 变 量 尽 量 从 0 开 始
112 5.3.5, 内 存 泄 漏 内 存 泄 漏 几 乎 是 很 难 避 免 的, 不 管 是 老 手 还 是 新 手, 都 存 在 这 个 问 题 甚 至 包 括 windows,linux 这 类 软 件, 都 或 多 或 少 有 内 存 泄 漏 也 许 对 于 一 般 的 应 用 软 件 来 说, 这 个 问 题 似 乎 不 是 那 么 突 出, 重 启 一 下 也 不 会 造 成 太 大 损 失 但 是 如 果 你 开 发 的 是 嵌 入 式 系 统 软 件 呢? 比 如 汽 车 制 动 系 统, 心 脏 起 搏 器 等 对 安 全 要 求 非 常 高 的 系 统 你 总 不 能 让 心 脏 起 搏 器 重 启 吧, 人 家 阎 王 老 爷 是 非 常 好 客 的 会 产 生 泄 漏 的 内 存 就 是 堆 上 的 内 存 ( 这 里 不 讨 论 资 源 或 句 柄 等 泄 漏 情 况 ), 也 就 是 说 由 malloc 系 列 函 数 或 new 操 作 符 分 配 的 内 存 如 果 用 完 之 后 没 有 及 时 free 或 delete, 这 块 内 存 就 无 法 释 放, 直 到 整 个 程 序 终 止 , 告 老 还 乡 求 良 田 怎 么 去 理 解 这 个 内 存 分 配 和 释 放 过 程 呢? 先 看 下 面 这 段 对 话 : 万 岁 爷 : 爱 卿, 你 为 朕 立 下 了 汗 马 功 劳, 想 要 何 赏 赐 啊? 某 功 臣 : 万 岁, 黄 金 白 银, 臣 视 之 如 粪 土 臣 年 岁 已 老, 欲 告 老 还 乡 臣 乞 良 田 千 亩 以 荫 后 世, 别 无 他 求 万 岁 爷 : 爱 卿, 你 劳 苦 功 高, 却 仅 要 如 此 小 赏, 朕 今 天 就 如 你 所 愿 户 部 刘 侍 郎, 查 看 湖 广 一 带 是 否 还 有 千 亩 上 等 良 田 未 曾 封 赏 刘 侍 郎 : 长 沙 尚 有 五 万 余 亩 上 等 良 田 未 曾 封 赏 万 岁 爷 : 在 长 沙 拨 良 田 千 亩 封 赏 爱 卿 爱 卿, 良 田 千 亩, 你 欲 何 用 啊? 某 功 臣 : 谢 万 岁 长 沙 一 带, 适 合 种 水 稻, 臣 想 用 来 种 水 稻 种 水 稻 需 要 把 田 分 为 一 亩 一 块, 方 便 耕 种 , 如 何 使 用 malloc 函 数 不 要 莫 名 其 妙, 其 实 上 面 这 段 小 小 的 对 话, 就 是 malloc 的 使 用 过 程 malloc 是 一 个 函 数, 专 门 用 来 从 堆 上 分 配 内 存 使 用 malloc 函 数 需 要 几 个 要 求 : 内 存 分 配 给 谁? 这 里 是 把 良 田 分 配 给 某 功 臣 分 配 多 大 内 存? 这 里 是 分 配 一 千 亩 是 否 还 有 足 够 内 存 分 配? 这 里 是 还 有 足 够 良 田 分 配 内 存 的 将 用 来 存 储 什 么 格 式 的 数 据, 即 内 存 用 来 做 什 么? 这 里 是 用 来 种 水 稻, 需 要 把 田 分 成 一 亩 一 块 分 配 好 的 内 存 在 哪 里? 这 里 是 在 长 沙 如 果 这 五 点 都 确 定, 那 内 存 就 能 分 配 下 面 先 看 malloc 函 数 的 原 型 : (void *)malloc(int size)
113 malloc 函 数 的 返 回 值 是 一 个 void 类 型 的 指 针, 参 数 为 int 类 型 数 据, 即 申 请 分 配 的 内 存 大 小, 单 位 是 byte 内 存 分 配 成 功 之 后,malloc 函 数 返 回 这 块 内 存 的 首 地 址 你 需 要 一 个 指 针 来 接 收 这 个 地 址 但 是 由 于 函 数 的 返 回 值 是 void * 类 型 的, 所 以 必 须 强 制 转 换 成 你 所 接 收 的 类 型 也 就 是 说, 这 块 内 存 将 要 用 来 存 储 什 么 类 型 的 数 据 比 如 : char *p = (char *)malloc(100); 在 堆 上 分 配 了 100 个 字 节 内 存, 返 回 这 块 内 存 的 首 地 址, 把 地 址 强 制 转 换 成 char * 类 型 后 赋 给 char * 类 型 的 指 针 变 量 p 同 时 告 诉 我 们 这 块 内 存 将 用 来 存 储 char 类 型 的 数 据 也 就 是 说 你 只 能 通 过 指 针 变 量 p 来 操 作 这 块 内 存 这 块 内 存 本 身 并 没 有 名 字, 对 它 的 访 问 是 匿 名 访 问 上 面 就 是 使 用 malloc 函 数 成 功 分 配 一 块 内 存 的 过 程 但 是, 每 次 你 都 能 分 配 成 功 吗? 不 一 定 上 面 的 对 话, 皇 帝 让 户 部 侍 郎 查 询 是 否 还 有 足 够 的 良 田 未 被 分 配 出 去 使 用 malloc 函 数 同 样 要 注 意 这 点 : 如 果 所 申 请 的 内 存 块 大 于 目 前 堆 上 剩 余 内 存 块 ( 整 块 ), 则 内 存 分 配 会 失 败, 函 数 返 回 NULL 注 意 这 里 说 的 堆 上 剩 余 内 存 块 不 是 所 有 剩 余 内 存 块 之 和, 因 为 malloc 函 数 申 请 的 是 连 续 的 一 块 内 存 既 然 malloc 函 数 申 请 内 存 有 不 成 功 的 可 能, 那 我 们 在 使 用 指 向 这 块 内 存 的 指 针 时, 必 须 用 if(null!= p) 语 句 来 验 证 内 存 确 实 分 配 成 功 了 , 用 malloc 函 数 申 请 0 字 节 内 存 另 外 还 有 一 个 问 题 : 用 malloc 函 数 申 请 0 字 节 内 存 会 返 回 NULL 指 针 吗? 可 以 测 试 一 下, 也 可 以 去 查 找 关 于 malloc 函 数 的 说 明 文 档 申 请 0 字 节 内 存, 函 数 并 不 返 回 NULL, 而 是 返 回 一 个 正 常 的 内 存 地 址 但 是 你 却 无 法 使 用 这 块 大 小 为 0 的 内 存 这 好 尺 子 上 的 某 个 刻 度, 刻 度 本 身 并 没 有 长 度, 只 有 某 两 个 刻 度 一 起 才 能 量 出 长 度 对 于 这 一 点 一 定 要 小 心, 因 为 这 时 候 if(null!= p) 语 句 校 验 将 不 起 作 用 , 内 存 释 放 既 然 有 分 配, 那 就 必 须 有 释 放 不 然 的 话, 有 限 的 内 存 总 会 用 光, 而 没 有 释 放 的 内 存 却 在 空 闲 与 malloc 对 应 的 就 是 free 函 数 了 free 函 数 只 有 一 个 参 数, 就 是 所 要 释 放 的 内 存 块 的 首 地 址 比 如 上 例 : free(p); free 函 数 看 上 去 挺 狠 的, 但 它 到 底 作 了 什 么 呢? 其 实 它 就 做 了 一 件 事 : 斩 断 指 针 变 量 与 这 块 内 存 的 关 系 比 如 上 面 的 例 子, 我 们 可 以 说 malloc 函 数 分 配 的 内 存 块 是 属 于 p 的, 因 为 我 们 对 这 块 内 存 的 访 问 都 需 要 通 过 p 来 进 行 free 函 数 就 是 把 这 块 内 存 和 p 之 间 的 所 有 关 系 斩 断 从 此 p 和 那 块 内 存 之 间 再 无 瓜 葛 至 于 指 针 变 量 p 本 身 保 存 的 地 址 并 没 有 改 变, 但 是 它 对 这 个 地 址 处 的 那 块 内 存 却 已 经 没 有 所 有 权 了 那 块 被 释 放 的 内 存 里 面 保 存 的 值 也 没 有 改 变, 只 是 再 也 没 有 办 法 使 用 了 这 就 是 free 函 数 的 功 能 按 照 上 面 的 分 析, 如 果 对 p 连 续 两 次 以 上 使 用 free 函 数, 肯 定 会 发 生 错 误 因 为 第 一 使 用 free 函 数 时,p 所 属 的 内 存 已 经 被 释 放, 第 二 次 使 用 时 已 经 无 内 存 可 释 放 了 关 于 这 点, 我 上 课 时 让 学 生 记 住 的 是 : 一 定 要 一 夫 一 妻 制, 不 然 肯 定 出 错
114 malloc 两 次 只 free 一 次 会 内 存 泄 漏 ;malloc 一 次 free 两 次 肯 定 会 出 错 也 就 是 说, 在 程 序 中 malloc 的 使 用 次 数 一 定 要 和 free 相 等, 否 则 必 有 错 误 这 种 错 误 主 要 发 生 在 循 环 使 用 malloc 函 数 时, 往 往 把 malloc 和 free 次 数 弄 错 了 这 里 留 个 练 习 : 写 两 个 函 数, 一 个 生 成 链 表, 一 个 释 放 链 表 两 个 函 数 的 参 数 都 只 使 用 一 个 表 头 指 针 , 内 存 释 放 之 后 既 然 使 用 free 函 数 之 后 指 针 变 量 p 本 身 保 存 的 地 址 并 没 有 改 变, 那 我 们 就 需 要 重 新 把 p 的 值 变 为 NULL: p = NULL; 这 个 NULL 就 是 我 们 前 面 所 说 的 栓 野 狗 的 链 子 如 果 你 不 栓 起 来 迟 早 会 出 问 题 的 比 如 : 在 free(p) 之 后, 你 用 if(null!= p) 这 样 的 校 验 语 句 还 能 起 作 用 吗? 例 如 : char *p = (char *) malloc(100); strcpy(p, hello ); free(p); /* p 所 指 的 内 存 被 释 放, 但 是 p 所 指 的 地 址 仍 然 不 变 */ if (NULL!= p) /* 没 有 起 到 防 错 作 用 */ strcpy(p, world ); /* 出 错 */ 释 放 完 块 内 存 之 后, 没 有 把 指 针 置 NULL, 这 个 指 针 就 成 为 了 野 指 针, 也 有 书 叫 悬 垂 指 针 这 是 很 危 险 的, 而 且 也 是 经 常 出 错 的 地 方 所 以 一 定 要 记 住 一 条 :free 完 之 后, 一 定 要 给 指 针 置 NULL 同 时 留 一 个 问 题 : 对 NULL 指 针 连 续 free 多 次 会 出 错 吗? 为 什 么? 如 果 让 你 来 设 计 free 函 数, 你 会 怎 么 处 理 这 个 问 题? 5.3.6, 内 存 已 经 被 释 放 了, 但 是 继 续 通 过 指 针 来 使 用 这 里 一 般 有 三 种 情 况 : 第 一 种 : 就 是 上 面 所 说 的,free(p) 之 后, 继 续 通 过 p 指 针 来 访 问 内 存 解 决 的 办 法 就 是 给 p 置 NULL 第 二 种 : 函 数 返 回 栈 内 存 这 是 初 学 者 最 容 易 犯 的 错 误 比 如 在 函 数 内 部 定 义 了 一 个 数 组, 却 用 return 语 句 返 回 指 向 该 数 组 的 指 针 解 决 的 办 法 就 是 弄 明 白 栈 上 变 量 的 生 命 周 期
115 第 三 种 : 内 存 使 用 太 复 杂, 弄 不 清 到 底 哪 块 内 存 被 释 放, 哪 块 没 有 被 释 放 解 决 的 办 法 是 重 新 设 计 程 序, 改 善 对 象 之 间 的 调 用 关 系 上 面 详 细 讨 论 了 常 见 的 六 种 错 误 及 解 决 对 策, 希 望 读 者 仔 细 研 读, 尽 量 使 自 己 对 每 种 错 误 发 生 的 原 因 及 预 防 手 段 烂 熟 于 胸 一 定 要 多 练, 多 调 试 代 码, 同 时 多 总 结 经 验
116 第 六 章 函 数 什 么 是 函 数? 为 什 么 需 要 函 数? 这 两 个 看 似 很 简 单 的 问 题, 你 能 回 答 清 楚 吗? 6.1, 函 数 的 由 来 与 好 处 其 实 在 汇 编 语 言 阶 段, 函 数 这 个 概 念 还 是 比 较 模 糊 的 汇 编 语 言 的 代 码 往 往 就 是 从 入 口 开 始 一 条 一 条 执 行, 直 到 遇 到 跳 转 指 令 ( 比 如 ARM 指 令 B BL BX BLX 之 类 ) 然 后 才 跳 转 到 目 的 指 令 处 执 行 这 个 时 候 所 有 的 代 码 仅 仅 是 按 其 将 要 执 行 的 顺 序 排 列 而 已 后 来 人 们 发 现 这 样 写 代 码 非 常 费 劲, 容 易 出 错, 也 不 方 便 于 是 想 出 一 个 办 法, 把 一 些 功 能 相 对 来 说 能 成 为 一 个 整 体 的 代 码 放 到 一 起 打 包, 通 过 一 些 数 据 接 口 和 外 界 通 信 这 就 是 函 数 的 由 来 那 函 数 能 给 我 们 带 来 什 么 好 处 呢? 简 单 来 说 可 以 概 括 成 以 下 几 点 : 1 降 低 复 杂 性 : 使 用 函 数 的 最 首 要 原 因 是 为 了 降 低 程 序 的 复 杂 性, 可 以 使 用 函 数 来 隐 含 信 息, 从 而 使 你 不 必 再 考 虑 这 些 信 息 2 避 免 重 复 代 码 段 : 如 果 在 两 个 不 同 函 数 中 的 代 码 很 相 似, 这 往 往 意 味 着 分 解 工 作 有 误 这 时, 应 该 把 两 个 函 数 中 重 复 的 代 码 都 取 出 来, 把 公 共 代 码 放 入 一 个 新 的 通 用 函 数 中, 然 后 再 让 这 两 个 函 数 调 用 新 的 通 用 函 数 通 过 使 公 共 代 码 只 出 现 一 次, 可 以 节 约 许 多 空 间 因 为 只 要 在 一 个 地 方 改 动 代 码 就 可 以 了 这 时 代 码 也 更 可 靠 了 3 限 制 改 动 带 来 的 影 响 : 由 于 在 独 立 区 域 进 行 改 动, 因 此, 由 此 带 来 的 影 响 也 只 限 于 一 个 或 最 多 几 个 区 域 中 4 隐 含 顺 序 : 如 果 程 序 通 常 先 从 用 户 那 里 读 取 数 据, 然 后 再 从 一 个 文 件 中 读 取 辅 助 数 据, 在 设 计 系 统 时 编 写 一 个 函 数, 隐 含 哪 一 个 首 先 执 行 的 信 息 5 改 进 性 能 : 把 代 码 段 放 入 函 数 也 使 得 用 更 快 的 算 法 或 执 行 更 快 的 语 言 ( 如 汇 编 ) 来 改 进 这 段 代 码 的 工 作 变 得 容 易 些 6 进 行 集 中 控 制 : 专 门 化 的 函 数 去 读 取 和 改 变 内 部 数 据 内 容, 也 是 一 种 集 中 的 控 制 形 式 7 隐 含 数 据 结 构 : 可 以 把 数 据 结 构 的 实 现 细 节 隐 含 起 来 8 隐 含 指 针 操 作 : 指 针 操 作 可 读 性 很 差, 而 且 很 容 易 引 发 错 误 通 过 把 它 们 独 立 在 函 数 中, 可 以 把 注 意 力 集 中 到 操 作 意 图 而 不 是 集 中 到 的 指 针 操 作 本 身 9 隐 含 全 局 变 量 : 参 数 传 递 C 语 言 中, 函 数 其 实 就 是 一 些 语 句 的 的 集 合, 而 语 句 又 是 由 关 键 字 和 符 号 等 元 素 组 成, 如 果 我 们 把 关 键 字 符 号 等 基 本 元 素 弄 明 白 了, 函 数 不 就 没 有 问 题 了 么? 我 看 未 必 真 正 要 编 写 出 高 质 量 的 函 数 来, 是 非 常 不 容 易 的 前 辈 们 经 过 大 量 的 探 讨 和 研 究 总 结 出 来 一 下 一 些 通 用 的 规 则 和 建 议 : 6.2, 编 码 风 格 很 多 人 不 重 视 这 点, 认 为 无 所 谓, 甚 至 国 内 的 绝 大 多 数 教 材 也 不 讨 论 这 个 话 题, 导 致 学
117 生 入 公 司 后 仍 要 进 行 编 码 风 格 的 教 育 我 接 触 过 很 多 学 生, 发 现 他 们 由 于 平 时 缺 乏 这 种 意 识, 养 成 了 不 好 的 习 惯, 导 致 很 难 改 正 过 来 代 码 没 有 注 释, 变 量 函 数 等 命 名 混 乱, 过 两 天 自 己 都 看 不 懂 自 己 的 代 码 下 面 是 一 些 我 见 过 的 比 较 好 的 做 法, 希 望 读 者 能 有 所 收 获 规 则 6-1 每 一 个 函 数 都 必 须 有 注 释, 即 使 函 数 短 到 可 能 只 有 几 行 头 部 说 明 需 要 包 含 包 含 的 内 容 和 次 序 如 下 : /************************************************************************ * Function Name : nucfindthread * Create Date : 2000/01/07 * Author/Corporation : your name/your company name * * Description : Find a proper thread in thread array. * Ifit sanewthensearchanempty. * * Param : ThreadNo: someparam description * ThreadStatus: someparam description * * Return Code : Return Code description,eg: ERROR_Fail: not find a thread ERROR_SUCCEED: found * * Global Variable : DISP_wuiSegmentAppID * File Static Variable : naucthreadno * Function Static Variable : None * * * Revision History * No. Date Revised by Item Description * V /01/07 your name ************************************************************************/ static unsigned char nucfindthread(unsigned char ThreadNo,unsigned char ThreadStatus) 规 则 6-2 每 个 函 数 定 义 结 束 之 后 以 及 每 个 文 件 结 束 之 后 都 要 加 一 个 或 若 干 个 空 行 例 如 : /************************************************************************ * * Function1 Description * ************************************************************************/ void Function1( ) //Blank Line /************************************************************************ * * Function2 Description * ************************************************************************/ void Function2( ) //Blank Line /************************************************************************ * * Function3 Description
118 * ************************************************************************/ void Function3( ) //Blank Line 规 则 6-3 在 一 个 函 数 体 内, 变 量 定 义 与 函 数 语 句 之 间 要 加 空 行 例 如 : /************************************************************************ * * Function Description * ************************************************************************/ void Function1() int n; //Blank Line statement1. 规 则 6-4 逻 揖 上 密 切 相 关 的 语 句 之 间 不 加 空 行, 其 它 地 方 应 加 空 行 分 隔 例 如 : //Blank Line while (condition) statement1; //Blank Line if (condition) statement2; else statement3; //Blank Line statement4 规 则 6-5 复 杂 的 函 数 中, 在 分 支 语 句, 循 环 语 句 结 束 之 后 需 要 适 当 的 注 释, 方 便 区 分 各 分 支 或 循 环 体 while (condition) statement1; if (condition) for(condition) Statement2; //end for(condition) else
119 statement3; // end if (condition) statement4 //end while (condition) 规 则 6-6 修 改 别 人 代 码 的 时 候 不 要 轻 易 删 除 别 人 的 代 码, 应 该 用 适 当 的 注 释 方 式, 例 如 : while (condition) statement1; ////////////////////////////////////// //your name, 2008/01/07 delete //if (condition) // // for(condition) // // Statement2; // // //else // // statement3; // //////////////////////////////////////// /////////////////////////////////////// // your name, 2000/01/07 add new code /////////////////////////////////////// statement4 规 则 6-7 用 缩 行 显 示 程 序 结 构, 使 排 版 整 齐, 缩 进 量 统 一 使 用 4 个 字 符 ( 不 使 用 TAB 缩 进 ) 每 个 编 辑 器 的 TAB 键 定 义 的 空 格 数 不 一 致, 可 能 导 致 在 别 的 编 辑 器 打 开 你 的 代 码 乱 成 一 团 糟 规 则 6-8 在 函 数 体 的 开 始 结 构 / 联 合 的 定 义 枚 举 的 定 义 以 及 循 环 判 断 等 语 句 中 的 代 码 都 要 采 用 缩 行 规 则 6-9 同 层 次 的 代 码 在 同 层 次 的 缩 进 层 上 例 如 : 提 倡 的 的 风 格 不 提 倡 的 风 格 void Function(int x) void Function(int x) //program code //program code struct tagmystruct struct tagmystruct
120 int a; int b; int c; ; if (condition) //program code else //program code int a; int b; int c; ; if (condition) //program code else //program code 规 则 6-10 代 码 行 最 大 长 度 宜 控 制 在 80 个 字 符 以 内, 较 长 的 语 句 表 达 式 等 要 分 成 多 行 书 写 规 则 6-11 长 表 达 式 要 在 低 优 先 级 操 作 符 处 划 分 新 行, 操 作 符 放 在 新 行 之 首 ( 以 便 突 出 操 作 符 ) 拆 分 出 的 新 行 要 进 行 适 当 的 缩 进, 使 排 版 整 齐, 语 句 可 读 例 如 : if ((very_longer_variable1 >= very_longer_variable12) && (very_longer_variable3 <= very_longer_variable14) && (very_longer_variable5 <= very_longer_variable16)) dosomething(); for (very_longer_initialization; very_longer_condition; very_longer_update) dosomething(); 规 则 6-12 如 果 函 数 中 的 参 数 较 长, 则 要 进 行 适 当 的 划 分 例 如 : void function(float very_longer_var1, float very_longer_var2, float very_longer_var3) 规 则 6-13 用 正 确 的 反 义 词 组 命 名 具 有 互 斥 意 义 的 变 量 或 相 反 动 作 的 函 数 等 例 如 : int aiminvalue; int aimaxvalue; int niset_value( ); int niget_value( ); 规 则 6-14 如 果 代 码 行 中 的 运 算 符 比 较 多, 用 括 号 确 定 表 达 式 的 操 作 顺 序, 避 免 使 用 默 认 的 优 先 级
121 例 如 : leap_year = ((year % 4 == 0) && (year % 100!= 0)) (year % 400 == 0); 规 则 6-15 不 要 编 写 太 复 杂 的 复 合 表 达 式 例 如 : i=a>=b&&c<d&&c+f<=g+h; 复 合 表 达 式 过 于 复 杂 规 则 6-16 不 要 有 多 用 途 的 复 合 表 达 式 例 如 : d = (a = b + c) + r; 该 表 达 式 既 求 a 值 又 求 d 值 应 该 拆 分 为 两 个 独 立 的 语 句 : a=b+c; d=a+r; 建 议 6-17 尽 量 避 免 含 有 否 定 运 算 的 条 件 表 达 式 例 如 : 如 : if (!(num >= 10)) 应 改 为 : if (num < 10) 规 则 6-18 参 数 的 书 写 要 完 整, 不 要 贪 图 省 事 只 写 参 数 的 类 型 而 省 略 参 数 名 字 如 果 函 数 没 有 参 数, 则 用 void 填 充 例 如 : 提 倡 的 风 格 不 提 倡 的 风 格 void set_value(int width, int height); void set_value (int, int); float get_value(void); float get_value (); 6.2, 函 数 设 计 的 一 般 原 则 和 技 巧 规 则 6-19 原 则 上 尽 量 少 使 用 全 局 变 量, 因 为 全 局 变 量 的 生 命 周 期 太 长, 容 易 出 错, 也 会 长 时 间 占 用 空 间. 各 个 源 文 件 负 责 本 身 文 件 的 全 局 变 量, 同 时 提 供 一 对 对 外 函 数, 方 便 其 它 函 数 使 用 该 函 数 来 访 问 变 量 比 如 :niset_valuename( );niget_valuename( ); 不 要 直 接 读 写 全 局 变 量, 尤 其 是 在 多 线 程 编 程 时, 必 须 使 用 这 种 方 式, 并 且 对 读 写 操 作 加 锁 如 规 则 6-20 参 数 命 名 要 恰 当, 顺 序 要 合 理 例 如 编 写 字 符 串 拷 贝 函 数 str_copy, 它 有 两 个 参 数 如 果 把 参 数 名 字 起 为 str1 和 str2, 例 void str_copy (char *str1, char *str2); 那 么 我 们 很 难 搞 清 楚 究 竟 是 把 str1 拷 贝 到 str2 中, 还 是 刚 好 倒 过 来 可 以 把 参 数 名 字 起 得 更 有 意 义, 如 叫 strsource 和 strdestination 这 样 从 名 字 上 就 可 以 看 出 应 该 把 strsource 拷 贝 到 strdestination 还 有 一 个 问 题, 这 两 个 参 数 那 一 个 该 在 前 那 一 个 该 在 后? 参 数 的 顺 序 要 遵 循 程 序 员 的 习 惯 一 般 地, 应 将 目 的 参 数 放 在 前 面, 源 参 数 放 在 后 面
122 如 果 将 函 数 声 明 为 : void str_copy (char *strsource, char *strdestination); 别 人 在 使 用 时 可 能 会 不 假 思 索 地 写 成 如 下 形 式 : char str[20]; str_copy (str, Hello World ); 参 数 顺 序 颠 倒 规 则 6-21 如 果 参 数 是 指 针, 且 仅 作 输 入 参 数 用, 则 应 在 类 型 前 加 const, 以 防 止 该 指 针 在 函 数 体 内 被 意 外 修 改 例 如 : void str_copy (char *strdestination,const char *strsource); 规 则 6-22 不 要 省 略 返 回 值 的 类 型, 如 果 函 数 没 有 返 回 值, 那 么 应 声 明 为 void 类 型 如 果 没 有 返 回 值, 编 译 器 则 默 认 为 函 数 的 返 回 值 是 int 类 型 的 规 则 6-23 在 函 数 体 的 入 口 处, 对 参 数 的 有 效 性 进 行 检 查 尤 其 是 指 针 参 数, 尽 量 使 用 assert 宏 做 入 口 校 验, 而 不 使 用 if 语 句 校 验 ( 关 于 此 问 题 讨 论, 详 见 指 针 与 数 组 那 章 ) 规 则 6-24 return 语 句 不 可 返 回 指 向 栈 内 存 的 指 针, 因 为 该 内 存 在 函 数 体 结 束 时 被 自 动 销 毁 例 如 : char * Func(void) char str[30]; return str; str 属 于 局 部 变 量, 位 于 栈 内 存 中, 在 Func 结 束 的 时 候 被 释 放, 所 以 返 回 str 将 导 致 错 误 规 则 6-25 函 数 的 功 能 要 单 一, 不 要 设 计 多 用 途 的 函 数 微 软 的 Win32 API 就 是 违 反 本 规 则 的 典 型, 其 函 数 往 往 因 为 参 数 不 一 样 而 功 能 不 一, 导 致 很 多 初 学 者 迷 惑 规 则 6-26 函 数 体 的 规 模 要 小, 尽 量 控 制 在 80 行 代 码 之 内 建 议 6-27 相 同 的 输 入 应 当 产 生 相 同 的 输 出 尽 量 避 免 函 数 带 有 记 忆 功 能 带 有 记 忆 功 能 的 函 数, 其 行 为 可 能 是 不 可 预 测 的, 因 为 它 的 行 为 可 能 取 决 于 某 种 记 忆 状 态 这 样 的 函 数 既 不 易 理 解 又 不 利 于 测 试 和 维 护 在 C 语 言 中, 函 数 的 static 局 部 变 量 是 函 数 的 记 忆 存 储 器 建 议 尽 量 少 用 static 局 部 变 量, 除 非 必 需 建 议 6-28 避 免 函 数 有 太 多 的 参 数, 参 数 个 数 尽 量 控 制 在 4 个 或 4 个 以 内 如 果 参 数 太 多, 在 使 用 时 容 易 将 参 数 类 型 或 顺 序 搞 错 微 软 的 Win32 API 就 是 违 反 本 规 则 的 典 型, 其 函 数 的 参 数 往 往 七 八 个 甚 至 十 余 个 比 如 一 个 CreateWindow 函 数 的 参 数 就 达 11 个 之 多 建 议 6-29 尽 量 不 要 使 用 类 型 和 数 目 不 确 定 的 参 数 C 标 准 库 函 数 printf 是 采 用 不 确 定 参 数 的 典 型 代 表, 其 原 型 为 : int printf(const chat *format[, argument] ); 这 种 风 格 的 函 数 在 编 译 时 丧 失 了 严 格 的 类 型 安 全 检 查 建 议 6-30 有 时 候 函 数 不 需 要 返 回 值, 但 为 了 增 加 灵 活 性 如 支 持 链 式 表 达, 可 以 附 加 返 回 值 例 如 字 符 串 拷 贝 函 数 strcpy 的 原 型 :
123 char *strcpy(char *strdest,const char *strsrc); strcpy 函 数 将 strsrc 拷 贝 至 输 出 参 数 strdest 中, 同 时 函 数 的 返 回 值 又 是 strdest 这 样 做 并 非 多 此 一 举, 可 以 获 得 如 下 灵 活 性 : char str[20]; int length = strlen(strcpy(str, Hello World ) ); 建 议 6-31 不 仅 要 检 查 输 入 参 数 的 有 效 性, 还 要 检 查 通 过 其 它 途 径 进 入 函 数 体 内 的 变 量 的 有 效 性, 例 如 全 局 变 量 文 件 句 柄 等 规 则 6-32 函 数 名 与 返 回 值 类 型 在 语 义 上 不 可 冲 突 违 反 这 条 规 则 的 典 型 代 表 就 是 C 语 言 标 准 库 函 数 getchar 几 乎 没 有 一 部 名 著 没 有 提 到 getchar 函 数, 因 为 它 实 在 太 经 典, 太 容 易 让 人 犯 错 误 了 所 以, 每 一 个 有 经 验 的 作 者 都 会 拿 这 个 例 子 来 警 示 他 的 读 者, 我 这 里 也 是 如 此 : char c; c=getchar(); if(eof == c) 按 照 getchar 名 字 的 意 思, 应 该 将 变 量 c 定 义 为 char 类 型 但 是 很 不 幸,getchar 函 数 的 返 回 值 却 是 int 类 型, 其 原 型 为 : int getchar(void); 由 于 c 是 char 类 型 的, 取 值 范 围 是 [-128,127], 如 果 宏 EOF 的 值 在 char 的 取 值 范 围 之 外, EOF 的 值 将 无 法 全 部 保 存 到 c 内, 会 发 生 截 断, 将 EOF 值 的 低 8 位 保 存 到 c 里 这 样 if 语 句 有 可 能 总 是 失 败 这 种 潜 在 的 危 险, 如 果 不 是 犯 过 一 次 错, 肯 怕 很 难 发 现 6.4, 函 数 递 归 6.4.1, 一 个 简 单 但 易 出 错 的 递 归 例 子 几 乎 每 一 本 C 语 言 基 础 的 书 都 讲 到 了 函 数 递 归 的 问 题, 但 是 初 学 者 仍 然 容 易 在 这 个 地 方 犯 错 误 先 看 看 下 面 的 例 子 : void fun(int i) if (i>0) fun(i/2); printf("%d\n",i); int main()
124 fun(10); return 0; 问 : 输 出 结 果 是 什 么? 这 是 我 上 课 时, 一 个 学 生 问 我 的 问 题 他 不 明 白 为 什 么 输 出 的 结 果 会 是 这 样 : 他 认 为 应 该 输 出 0 因 为 当 i 小 于 或 等 于 0 时 递 归 调 用 结 束, 然 后 执 行 printf 函 数 打 印 i 的 值 这 就 是 典 型 的 没 明 白 什 么 是 递 归 其 实 很 简 单,printf("%d\n",i); 语 句 是 fun 函 数 的 一 部 分, 肯 定 执 行 一 次 fun 函 数, 就 要 打 印 一 行 怎 么 可 能 只 打 印 一 次 呢? 关 键 就 是 不 明 白 怎 么 展 开 递 归 函 数 展 开 过 程 如 下 : void fun(int i) if (i>0) //fun(i/2); if(i/2>0) if(i/4>0) printf("%d\n",i/4); printf("%d\n",i/2); printf("%d\n",i); 这 样 一 展 开, 是 不 是 清 晰 多 了? 其 实 递 归 本 身 并 没 有 什 么 难 处, 关 键 是 其 展 开 过 程 别 弄 错 了 6.4.2, 不 使 用 任 何 变 量 编 写 strlen 函 数 看 到 这 里, 也 许 有 人 会 说,strlen 函 数 这 么 简 单, 有 什 么 好 讨 论 的 是 的, 我 相 信 你 能 熟 练 应 用 这 个 函 数, 也 相 信 你 能 轻 易 的 写 出 这 个 函 数 但 是 如 果 我 把 要 求 提 高 一 些 呢 : 不 允 许 调 用 库 函 数, 也 不 允 许 使 用 任 何 全 局 或 局 部 变 量 编 写 int my_strlen (char *strdest);
125 似 乎 问 题 就 没 有 那 么 简 单 了 吧? 这 个 问 题 曾 经 在 网 络 上 讨 论 的 比 较 热 烈, 我 几 乎 是 全 程 观 战, 差 点 也 忍 不 住 手 痒 了 不 过 因 为 我 的 解 决 办 法 在 我 看 到 帖 子 时 已 经 有 人 提 出 了, 所 以 作 罢 解 决 这 个 问 题 的 办 法 由 好 几 种, 比 如 嵌 套 有 编 语 言 因 为 嵌 套 汇 编 一 般 只 在 嵌 入 式 底 层 开 发 中 用 到, 所 以 本 书 就 不 打 算 讨 论 C 语 言 嵌 套 汇 编 的 知 识 了 有 兴 趣 的 读 者, 可 以 查 找 相 关 资 料 也 许 有 的 读 者 想 到 了 用 递 归 函 数 来 解 决 这 个 问 题 是 的, 你 应 该 想 得 到, 因 为 我 把 这 个 问 题 放 在 讲 解 函 数 递 归 的 时 候 讨 论 既 然 已 经 有 了 思 路, 这 个 问 题 就 很 简 单 了 代 码 如 下 : int my_strlen( const char* strdest ) assert(null!= strdest); if ('\0' == *strdest) return 0; else return (1 + my_strlen(++strdest)); 第 一 步 : 用 assert 宏 做 入 口 校 验 第 二 步 : 确 定 参 数 传 递 过 来 的 地 址 上 的 内 存 存 储 的 是 否 为 '\0' 如 果 是, 表 明 这 是 一 个 空 字 符 串, 或 者 是 字 符 串 的 结 束 标 志 第 三 步 : 如 果 参 数 传 递 过 来 的 地 址 上 的 内 存 不 为 '\0', 则 说 明 这 个 地 址 上 的 内 存 上 存 储 的 是 一 个 字 符 既 然 这 个 地 址 上 存 储 了 一 个 字 符, 那 就 计 数 为 1, 然 后 将 地 址 加 1 个 char 类 型 元 素 的 大 小, 然 后 再 调 用 函 数 本 身 如 此 循 环, 当 地 址 加 到 字 符 串 的 结 束 标 志 符 '\0' 时, 递 归 停 止 当 然, 同 样 是 利 用 递 归, 还 有 人 写 出 了 更 加 简 洁 的 代 码 : int my_strlen( const char* strdest ) return *strdest?1+strlen(strdest+1):0; 这 里 很 巧 妙 的 利 用 了 问 号 表 达 式, 但 是 没 有 做 参 数 入 口 校 验, 同 时 用 *strdest 来 代 替 ('\0' == *strdest) 也 不 是 很 好 所 以, 这 种 写 法 虽 然 很 简 洁, 但 不 符 合 我 们 前 面 所 讲 的 编 码 规 范 可 以 改 写 一 下 : int my_strlen( const char* strdest ) assert(null!= strdest); return ('\0'!= *strdest)?(1+my_strlen(strdest+1)):0; 上 面 的 问 题 利 用 函 数 递 归 的 特 性 就 轻 易 的 搞 定 了, 也 就 是 说 每 调 用 一 遍 my_strlen 函 数, 其 实 只 判 断 了 一 个 字 节 上 的 内 容 但 是, 如 果 传 入 的 字 符 串 很 长 的 话, 就 需 要 连 续 多 次 函 数 调 用, 而 函 数 调 用 的 开 销 比 循 环 来 说 要 大 得 多, 所 以, 递 归 的 效 率 很 低, 递 归 的 深 度 太 大 甚 至 可 能 出 现 错 误 ( 比 如 栈 溢 出 ) 所 以, 平 时 写 代 码, 不 到 万 不 得 已, 尽 量 不 要 用 递 归 即
126 便 是 要 用 递 归, 也 要 注 意 递 归 的 层 次 不 要 太 深, 防 止 出 现 栈 溢 出 的 错 误 ; 同 时 递 归 的 停 止 条 件 一 定 要 正 确, 否 则, 递 归 可 能 没 完 没 了
127 第 七 章 文 件 结 构 一 个 工 程 是 往 往 由 多 个 文 件 组 成 这 些 文 件 怎 么 管 理 怎 么 命 名 都 是 非 常 重 要 的 下 面 给 出 一 些 基 本 的 方 法, 比 较 好 的 管 理 这 些 文 件, 避 免 错 误 的 发 生 7.1, 文 件 内 容 的 一 般 规 则 规 则 7-1 每 个 头 文 件 和 源 文 件 的 头 部 必 须 包 含 文 件 头 部 说 明 和 修 改 记 录 源 文 件 和 头 文 件 的 头 部 说 明 必 须 包 含 的 内 容 和 次 序 如 下 : /************************************************************************ * File Name : FN_FileName.c/ FN_FileName.h * Copyright : XXXX Corporation, All Rights Reserved. * Module Name : Draw Engine/Display * * CPU : ARM7 * RTOS : Tron * * Create Date : 2008/10/01 * Author/Corporation : WhoAmI/your company name * * Abstract Description : Place some description here. * * Revision History * No Version Date Revised By Item Description * 1 V WhoAmI abcdefghijklm WhatUDo * ************************************************************************/ 规 则 7-2 各 个 源 文 件 必 须 有 一 个 头 文 件 说 明, 头 文 件 各 部 分 的 书 写 顺 序 下 : No. Item 1 Header File Header Section 2 Multi-Include-Prevent Section 3 Debug Switch Section 4 Include File Section 5 Macro Define Section 6 Structure Define Section 7 Prototype Declare Section 其 中 Multi-Include-Prevent Section 是 用 来 防 止 头 文 件 被 重 复 包 含 的 如 下 例 : #ifndef FN_FILENAME_H #define FN_FILENAME_H #endif 其 中 FN_FILENAME 一 般 为 本 头 文 件 名 大 写, 这 样 可 以 有 效 避 免 重 复, 因 为 同 一 工 程 中 不 可 能 存 在 两 个 同 名 的 头 文 件
128 /************************************************************************ * File Name : FN_FileName.h * Copyright : XXXX Corporation, All Rights Reserved. * Module Name : Draw Engine/Display * * CPU : ARM7 * RTOS : Tron * * Create Date : 2008/10/01 * Author/Corporation : WhoAmI/your company name * * Abstract Description : Place some description here. * * Revision History * No Version Date Revised By Item Description * 1 V WhoAmI abcdefghijklm WhatUDo * ************************************************************************/ /************************************************************************ * Multi-Include-Prevent Section ************************************************************************/ #ifndef FN_FILENAME_H #define FN_FILENAME_H /************************************************************************ * Debug switch Section ************************************************************************/ #define D_DISP_BASE /************************************************************************ * Include File Section ************************************************************************/ #include "IncFile.h" /************************************************************************ * Macro Define Section ************************************************************************/ #define MAX_TIMER_OUT (4) /************************************************************************ * Struct Define Section ************************************************************************/ typedef struct CM_RadiationDose unsigned char ucctgid; char cpatid_a[max_pati_len]; CM_RadiationDose_st, *CM_RadiationDose_pst; /************************************************************************ * Prototype Declare Section ************************************************************************/ unsigned int MD_guiGetScanTimes(void); #endif
129 规 则 7-3 源 文 件 各 部 分 的 书 写 顺 序 如 下 : No. Item 1 Source File Header Section 2 Debug Switch Section 3 Include File Section 4 Macro Define Section 5 Structure Define Section 6 Prototype Declare Section 7 Global Variable Declare Section 8 File Static Variable Define Section 9 Function Define Section /************************************************************************* * File Name : FN_FileName.c * Copyright : XXXX Corporation, All Rights Reserved. * Module Name : Draw Engine/Display * * CPU : ARM7 * RTOS : Tron * * Create Date : 2003/10/01 * Author/Corporation : WhoAmI/your company name * * Abstract Description : Place some description here. * * Revision History * No Version Date Revised By Item Description * 1 V WhoAmI abcdefghijklm WhatUDo * ************************************************************************/ /************************************************************************ * Debug switch Section ************************************************************************/ #define D_DISP_BASE /************************************************************************ * Include File Section ************************************************************************/ #include "IncFile.h" /************************************************************************ * Macro Define Section ************************************************************************/ #define MAX_TIMER_OUT (4) /************************************************************************
130 * Struct Define Section ************************************************************************/ typedef struct CM_RadiationDose unsigned char ucctgid; char cpatid_a[max_pati_len]; CM_RadiationDose_st, *pcm_radiationdose_st; /************************************************************************ * Prototype Declare Section ************************************************************************/ unsigned int MD_guiGetScanTimes(void); /************************************************************************ * Global Variable Declare Section ************************************************************************/ extern unsigned int MD_guiHoldBreathStatus; /************************************************************************ * File Static Variable Define Section ************************************************************************/ static unsigned int nuinavisysstatus; /************************************************************************ * Function Define Section ************************************************************************/ 规 则 7-4 需 要 对 外 公 开 的 常 量 放 在 头 文 件 中, 不 需 要 对 外 公 开 的 常 量 放 在 定 义 文 件 的 头 部 7.2, 文 件 名 命 名 的 规 则 规 则 7-5 文 件 标 识 符 分 为 两 部 分, 即 文 件 名 前 缀 和 后 缀 文 件 名 前 缀 的 最 前 面 要 使 用 范 围 限 定 符 模 块 名 ( 文 件 名 ) 缩 写 ; 规 则 7-6 采 用 小 写 字 母 命 名 文 件, 避 免 使 用 一 些 比 较 通 俗 的 文 件 名, 如 :public.c 等
《C语言基础入门》课程教学大纲
C 语 言 开 发 入 门 教 程 课 程 教 学 大 纲 课 程 编 号 :201409210011 学 分 :5 学 分 学 时 :58 学 时 ( 其 中 : 讲 课 学 时 :39 学 时 上 机 学 时 :19 学 时 ) 先 修 课 程 : 计 算 机 导 论 后 续 课 程 :C++ 程 序 设 计 适 用 专 业 : 信 息 及 其 计 算 机 相 关 专 业 开 课 部 门 : 计
说 明 为 了 反 映 教 运 行 的 基 本 状 态, 为 校 和 院 制 定 相 关 政 策 和 进 行 教 建 设 与 改 革 提 供 据 依 据, 校 从 程 资 源 ( 开 类 别 开 量 规 模 ) 教 师 结 构 程 考 核 等 维 度, 对 2015 年 春 季 期 教 运 行 基
内 部 资 料 东 北 师 范 大 教 运 行 基 本 状 态 据 报 告 2015 年 春 季 期 教 务 处 2015 年 10 月 27 日 说 明 为 了 反 映 教 运 行 的 基 本 状 态, 为 校 和 院 制 定 相 关 政 策 和 进 行 教 建 设 与 改 革 提 供 据 依 据, 校 从 程 资 源 ( 开 类 别 开 量 规 模 ) 教 师 结 构 程 考 核 等 维 度,
<433A5C446F63756D656E747320616E642053657474696E67735C41646D696E6973747261746F725CD7C0C3E65CC2DBCEC4CFB5CDB3CAB9D3C3D6B8C4CFA3A8BCF2BBAFA3A95CCAB9D3C3D6B8C4CF31302D31392E646F63>
( 一 ) 系 统 整 体 操 作 流 程 简 述 3 ( 二 ) 系 统 中 各 角 色 操 作 功 能 说 明 5 1. 学 院 管 理 员 5 2. 教 学 院 长 8 3. 指 导 教 师 10 4. 答 辩 组 组 长 12 5. 学 生 12 6. 系 统 管 理 员 15 ( 一 ) 论 文 系 统 常 见 问 题 16 ( 二 ) 论 文 查 重 常 见 问 题 22 1 2 主
龚 亚 夫 在 重 新 思 考 基 础 教 育 英 语 教 学 的 理 念 一 文 中 援 引 的 观 点 认 为 当 跳 出 本 族 语 主 义 的 思 维 定 式 后 需 要 重 新 思 考 许 多 相 连 带 的 问 题 比 如 许 多 发 音 的 细 微 区 别 并 不 影 响 理 解 和
语 音 语 篇 语 感 语 域 林 大 津 毛 浩 然 改 革 开 放 以 来 的 英 语 热 引 发 了 大 中 小 学 英 语 教 育 整 体 规 划 问 题 在 充 分 考 虑 地 区 学 校 和 个 体 差 异 以 及 各 家 观 点 的 基 础 上 遵 循 实 事 求 是 逐 级 定 位 逐 层 分 流 因 材 施 教 的 原 则 本 研 究 所 倡 导 的 语 音 语 篇 语 感 语 域
,,,,, :,, (.,, );, (, : ), (.., ;. &., ;.. &.., ;, ;, ),,,,,,, ( ) ( ),,,,.,,,,,, : ;, ;,.,,,,, (., : - ),,,, ( ),,,, (, : ),, :,
: 周 晓 虹 : - -., - - - -. :( ), -,.( ),,, -. - ( ).( ) ', -,,,,, ( ).( ),,, -., '.,, :,,,, :,,,, ,,,,, :,, (.,, );, (, : ), (.., ;. &., ;.. &.., ;, ;, ),,,,,,, ( ) ( ),,,,.,,,,,, : ;, ;,.,,,,, (., : - ),,,,
张 荣 芳 中 山 大 学 历 史 系 广 东 广 州 张 荣 芳 男 广 东 廉 江 人 中 山 大 学 历 史 系 教 授 博 士 生 导 师 我 们 要 打 破 以 前 学 术 界 上 的 一 切 偶 像 以 前 学 术 界 的 一 切 成 见 屏 除 我 们 要 实 地 搜 罗 材 料 到 民 众 中 寻 方 言 到 古 文 化 的 遗 址 去 发 掘 到 各 种 的 人 间 社 会 去
何 秋 琳 张 立 春 视 觉 学 习 研 究 进 展 视 觉 注 意 视 觉 感 知
第 卷 第 期 年 月 开 放 教 育 研 究 何 秋 琳 张 立 春 华 南 师 范 大 学 未 来 教 育 研 究 中 心 广 东 广 州 随 着 图 像 化 技 术 和 电 子 媒 体 的 发 展 视 觉 学 习 也 逐 步 发 展 为 学 习 科 学 的 一 个 研 究 分 支 得 到 研 究 人 员 和 教 育 工 作 者 的 广 泛 关 注 基 于 此 作 者 试 图 对 视 觉 学 习
I
机 电 一 级 注 册 建 造 师 继 续 教 育 培 训 广 东 培 训 点 网 上 报 名 操 作 使 用 手 册 (2013 年 1 月, 第 一 版 ) 第 一 章 个 人 注 册 与 个 人 信 息 管 理 1. 个 人 注 册 ( 请 每 人 只 申 请 一 个 注 册 号, 如 果 单 位 批 量 报 班 单 位 帮 申 请 注 册, 不 需 个 人 再 注 册 ) 首 次 报 班,
文 化 记 忆 传 统 创 新 与 节 日 遗 产 保 护 根 据 德 国 学 者 阿 斯 曼 的 文 化 记 忆 理 论 仪 式 与 文 本 是 承 载 文 化 记 忆 的 两 大 媒 体 在 各 种 仪 式 行 为 中 节 日 以 其 高 度 的 公 共 性 有 组 织 性 和 历 史 性 而 特 别 适 用 于 文 化 记 忆 的 储 存 和 交 流 节 日 的 文 化 功 能 不 仅 在 于
18 上 报 该 学 期 新 生 数 据 至 阳 光 平 台 第 一 学 期 第 四 周 至 第 六 周 19 督 促 学 习 中 心 提 交 新 增 专 业 申 请 第 一 学 期 第 四 周 至 第 八 周 20 编 制 全 国 网 络 统 考 十 二 月 批 次 考 前 模 拟 题 第 一 学
1 安 排 组 织 全 国 网 络 统 考 九 月 批 次 网 上 考 前 辅 导 第 一 学 期 第 一 周 统 考 考 前 半 个 月 2 下 发 全 国 网 络 统 考 九 月 批 次 准 考 证 第 一 学 期 第 一 周 导 出 下 半 年 成 人 本 科 学 士 学 位 英 语 统 一 考 试 报 考 3 信 息 第 一 学 期 第 一 周 4 教 学 计 划 和 考 试 计 划 上 网,
0 年 上 半 年 评 价 与 考 核 细 则 序 号 部 门 要 素 值 考 核 内 容 考 核 方 式 考 核 标 准 考 核 ( 扣 原 因 ) 考 评 得 3 安 全 生 产 目 30 无 同 等 责 任 以 上 道 路 交 通 亡 人 事 故 无 轻 伤 责 任 事 故 无 重 大 质 量
0 年 上 半 年 评 价 与 考 核 细 则 序 号 部 门 要 素 值 考 核 内 容 考 核 方 式 考 核 标 准 无 同 等 责 任 以 上 道 路 交 通 亡 人 事 故 3 无 轻 伤 责 任 事 故 目 标 30 及 事 无 重 大 质 量 工 作 过 失 故 管 无 其 他 一 般 责 任 事 故 理 在 公 司 文 明 环 境 创 建 中, 无 工 作 过 失 及 被 追 究 的
21 业 余 制 -- 高 起 专 (12 级 ) 75 元 / 学 分 网 络 学 院 学 生 沪 教 委 财 (2005)49 号 江 西 化 校 工 科 22 业 余 制 -- 高 起 专 (12 级 ) 70 元 / 学 分 网 络 学 院 学 生 沪 教 委 财 (2005)49 号 吉
1 普 通 高 校 学 费 5000 元 / 学 年 一 般 专 业 2 普 通 高 校 学 费 5500 元 / 学 年 特 殊 专 业 3 普 通 高 校 学 费 10000 元 / 学 年 艺 术 专 业 4 中 德 合 作 办 学 15000 元 / 学 年 本 科 生 本 科 学 费 5 ( 含 港 澳 修 读 第 二 专 业 辅 修 专 业 及 学 位 学 费 不 超 过 选 读 专 业
评 委 : 李 炎 斌 - 个 人 技 术 标 资 信 标 初 步 审 查 明 细 表 序 号 投 标 单 位 投 标 函 未 按 招 标 文 件 规 定 填 写 漏 填 或 内 容 填 写 错 误 的 ; 不 同 投 标 人 的 投 标 文 件 由 同 一 台 电 脑 或 同 一 家 投 标 单
评 委 : 李 炎 斌 - 个 人 清 标 评 审 明 细 表 评 审 因 素 序 号 投 标 单 位 清 标 评 审 1 深 圳 市 创 捷 科 技 有 限 合 格 2 四 川 川 大 智 胜 软 件 股 份 有 限 合 格 3 北 京 航 天 长 峰 科 技 工 业 集 团 有 限 公 司 合 格 4 深 圳 中 兴 力 维 技 术 有 限 合 格 5 深 圳 键 桥 通 讯 技 术 股 份 有
课程类 别
美 声 演 唱 方 向 培 养 方 案 一 培 养 目 标 本 方 向 要 求 学 生 德 智 体 美 全 面 发 展, 培 养 能 在 文 艺 团 体 从 事 声 乐 演 唱 及 能 在 艺 术 院 校 从 事 本 方 向 教 学 的 高 级 门 人 才 二 培 养 规 格 本 方 向 学 生 应 系 统 掌 握 声 乐 演 唱 方 面 的 理 论 和 技 能, 具 备 较 高 的 声 乐 演 唱
(2015-2016-2)-0004186-04205-1 140242 信 号 与 系 统 Ⅰ 学 科 基 础 必 修 课 37 37 1 教 203 17 周 2016 年 06 月 13 日 (08:00-09:35) (2015-2016-2)-0004186-04205-1 141011
关 于 2015-2016 学 年 第 二 学 期 期 末 周 内 考 试 时 间 地 点 安 排 选 课 课 号 班 级 名 称 课 程 名 称 课 程 性 质 合 考 人 数 实 际 人 数 考 试 教 室 考 试 段 考 试 时 间 (2015-2016-2)-0006178-04247-1 130101 测 试 技 术 基 础 学 科 基 础 必 修 课 35 35 1 教 401 17 周
深圳市新亚电子制程股份有限公司
证 券 代 码 :002388 证 券 简 称 : 新 亚 制 程 公 告 编 号 :2016-053 深 圳 市 新 亚 电 子 制 程 股 份 有 限 公 司 2016 年 第 二 次 临 时 股 东 大 会 决 议 公 告 本 公 司 及 董 事 会 全 体 成 员 保 证 公 告 内 容 真 实 准 确 和 完 整, 不 存 在 虚 假 记 载 误 导 性 陈 述 或 者 重 大 遗 漏 特
HSK( 一 级 ) 考 查 考 生 的 日 常 汉 语 应 用 能 力, 它 对 应 于 国 际 汉 语 能 力 标 准 一 级 欧 洲 语 言 共 同 参 考 框 架 (CEF) A1 级 通 过 HSK( 一 级 ) 的 考 生 可 以 理 解 并 使 用 一 些 非 常 简 单 的 汉 语
新 汉 语 水 平 考 试 HSK 为 使 汉 语 水 平 考 试 (HSK) 更 好 地 服 务 于 汉 语 学 习 者, 中 国 国 家 汉 办 组 织 中 外 汉 语 教 学 语 言 学 心 理 学 和 教 育 测 量 学 等 领 域 的 专 家, 在 充 分 调 查 了 解 海 外 实 际 汉 语 教 学 情 况 的 基 础 上, 吸 收 原 有 HSK 的 优 点, 借 鉴 近 年 来 国
<433A5C55736572735C6B73625C4465736B746F705CB9FABCCAD6D0D2BDD2A9D7A8D2B5B8DFBCB6BCBCCAF5D6B0B3C6C6C0C9F3C9EAC7EBD6B8C4CFA3A832303136CDA8D3C3B0E6A3A92E646F63>
附 件 1 国 际 中 药 专 业 高 级 技 术 职 称 评 审 条 件 及 报 名 材 料 一 系 列 ( 一 ) 中 1 高 级 专 科 ( 副 ) 高 级 专 科 ( 副 ) 1 取 得 中 专 科 职 称 后, 独 立 从 事 中 临 床 实 践 5 年 以 上 2 取 得 中 博 士 学 位 后, 临 床 实 践 2 年 以 上 3 取 得 中 硕 士 学 位 后, 临 床 实 践 7
科 学 出 版 社 科 学 出 版 社 前 言 本 书 是 针 对 普 通 高 等 院 校 经 济 类 和 工 商 管 理 类 本 科 专 业 财 务 管 理 学 的 教 学 需 求, 结 合 教 育 部 经 济 管 理 类 本 科 财 务 管 理 学 课 程 教 学 大 纲 编 写 而 成 的 本 书 执 笔 者 都 是 长 期 工 作 在 财 务 管 理 教 学 一 线 的 专 业 教 师,
2006年顺德区高中阶段学校招生录取分数线
2014 年 顺 德 区 高 中 阶 段 学 校 考 试 提 前 批 第 一 批 第 二 批 学 校 录 取 根 据 佛 山 市 办 提 供 的 考 生 数 据, 现 将 我 区 2014 年 高 中 阶 段 学 校 考 试 提 前 批 第 一 批 第 二 批 学 校 的 录 取 公 布 如 下 : 一 顺 德 一 中 录 取 分 第 1 志 愿, 总 分 585, 综 合 表 现 评 价 A, 考
一 公 共 卫 生 硕 士 专 业 学 位 论 文 的 概 述 学 位 论 文 是 对 研 究 生 进 行 科 学 研 究 或 承 担 专 门 技 术 工 作 的 全 面 训 练, 是 培 养 研 究 生 创 新 能 力, 综 合 运 用 所 学 知 识 发 现 问 题, 分 析 问 题 和 解 决
上 海 市 公 共 卫 生 硕 士 专 业 学 位 论 文 基 本 要 求 和 评 价 指 标 体 系 ( 试 行 ) 上 海 市 学 位 委 员 会 办 公 室 二 O 一 二 年 三 月 一 公 共 卫 生 硕 士 专 业 学 位 论 文 的 概 述 学 位 论 文 是 对 研 究 生 进 行 科 学 研 究 或 承 担 专 门 技 术 工 作 的 全 面 训 练, 是 培 养 研 究 生 创
珠江钢琴股东大会
证 券 代 码 :002678 证 券 简 称 : 珠 江 钢 琴 公 告 编 号 :2015-038 广 州 珠 江 钢 琴 集 团 股 份 有 限 公 司 2015 年 年 度 股 东 大 会 决 议 公 告 本 公 司 及 董 事 会 全 体 成 员 保 证 信 息 披 露 的 内 容 真 实 准 确 完 整, 没 有 虚 假 记 载 误 导 性 陈 述 或 重 大 遗 漏 特 别 提 示 :
马 克 思 主 义 公 正 观 的 基 本 向 度 及 方 法 论 原 则!! # #
马 克 思 主 义 公 正 观 的 基 本 向 度 及 方 法 论 原 则 马 俊 峰 在 社 会 公 正 问 题 的 大 讨 论 中 罗 尔 斯 诺 齐 克 哈 耶 克 麦 金 泰 尔 等 当 代 西 方 思 想 家 的 论 述 被 反 复 引 用 和 申 说 而 将 马 克 思 恩 格 斯 等 经 典 作 家 的 观 点 置 于 一 种 被 忽 视 甚 至 被 忘 却 的 状 态 形 成 这 种
Microsoft Word - 第7章 图表反转形态.doc
第 七 章 图 表 反 转 形 态 我 们 知 道 市 场 趋 势 共 有 三 种 : 上 升 趋 势 下 降 趋 势 和 横 向 整 理 市 场 的 价 格 波 动 都 是 运 行 在 这 三 种 趋 势 中, 所 有 的 走 势 都 是 这 三 种 趋 势 的 排 列 组 合 如 图 市 场 趋 势 结 构 示 意 图 7-1 所 示 市 场 趋 势 结 构 示 意 图 7-1 图 市 场 趋
( 二 ) 现 行 统 一 高 考 制 度 不 利 于 培 养 人 的 创 新 精 神,,,,,,,,,,,,, [ ],,,,,,,,,,, :, ;,,,,,,? ( 三 ) 现 行 统 一 高 考 制 度 不 利 于 全 体 学 生 都 获 得 全 面 发 展,, [ ],,,,,,,,,,,
( ) ( )... 李 雪 岩, 龙 耀 (. 广 西 民 族 大 学 商 学 院, 广 西 南 宁 ;. 中 山 大 学 教 育 学 院, 广 东 广 州 ) : 高 等 教 育 是 专 业 教 育 高 考 是 为 高 等 教 育 服 务 的, 是 为 高 等 专 业 教 育 选 拔 有 专 业 培 养 潜 质 的 人 才 现 行 高 考 制 度 忽 略 专 业 潜 质 的 因 素, 过 份 强
名 称 生 命 科 学 学 院 083001 环 境 科 学 1 生 物 学 仅 接 收 院 内 调 剂, 初 试 分 数 满 足 我 院 生 物 学 复 试 最 低 分 数 线 生 命 科 学 学 院 071300 生 态 学 5 生 态 学 或 生 物 学 生 命 科 学 学 院 040102
华 中 师 范 大 学 2016 年 接 收 校 内 外 优 秀 硕 士 研 究 生 调 剂 信 息 表 名 称 经 济 与 工 商 管 理 学 院 020101 政 治 经 济 学 1 经 济 学 类 毕 业 学 校 与 报 考 学 校 不 低 于 我 校 办 学 层 次 经 济 与 工 商 管 理 学 院 020105 世 界 经 济 学 1 经 济 学 类 毕 业 学 校 与 报 考 学 校
修改版-操作手册.doc
职 称 信 息 系 统 升 级 指 南 须 使 用 IE9 及 其 以 上 版 本 浏 览 器 或 谷 歌 浏 览 器 登 录 www.njrs.gov.cn 南 京 市 职 称 ( 职 业 资 格 ) 工 作 领 导 小 组 办 公 室 2016 年 5 月 目 录 一 申 报 人 员 操 作 指 南...1 1.1 职 称 初 定 申 报...1 1.1.1 职 称 初 定 基 础 信 息 填
中 中 中 中 部 中 岗 位 条 件 历 其 它 历 史 师 地 理 师 生 物 师 体 与 健 康 师 04 05 06 07 从 事 中 历 史 工 从 事 中 地 理 工 从 事 中 生 物 工 从 事 中 体 与 健 康 工 2. 课 程 与 论 ( 历 史 ); 2. 科 ( 历 史 )
中 中 中 部 中 26 年 系 统 事 业 公 开 计 划 岗 位 条 件 历 其 它 数 师 英 语 师 物 理 师 02 0 从 事 中 数 工 从 事 中 英 语 工 从 事 中 物 理 工 2. 课 程 与 论 ( 数 ); 2. 科 ( 数 );. 数 ; 4. 基 础 数 ; 5. 计 算 数 ; 6. 概 率 论 与 数 理 统 计 ; 7. 应 用 数 ; 8. 数. 课 程 与
对 当 前 小 说 艺 术 倾 向 的 分 析 陈 晓 明 人 民 性 是 一 个 现 代 性 概 念 近 年 来 艺 术 上 趋 于 成 熟 的 一 批 作 家 倾 向 于 表 现 底 层 民 众 苦 难 的 生 活 这 使 他 们 的 作 品 具 有 现 实 主 义 的 显 著 特 征 在 对 苦 难 生 活 的 把 握 中 对 人 物 性 格 和 命 运 的 展 示 中 这 些 小 说 在 人
<4D F736F F D D323630D6D0B9FAD3A6B6D4C6F8BAF2B1E4BBAFB5C4D5FEB2DFD3EBD0D0B6AF C4EAB6C8B1A8B8E6>
中 国 应 对 气 候 变 化 的 政 策 与 行 动 2013 年 度 报 告 国 家 发 展 和 改 革 委 员 会 二 〇 一 三 年 十 一 月 100% 再 生 纸 资 源 目 录 前 言... 1 一 应 对 气 候 变 化 面 临 的 形 势... 3 二 完 善 顶 层 设 计 和 体 制 机 制... 4 三 减 缓 气 候 变 化... 8 四 适 应 气 候 变 化... 20
评 委 : 徐 岩 宇 - 个 人 技 术 标 资 信 标 初 步 审 查 明 细 表 序 号 投 标 单 位 投 标 函 未 按 招 标 文 件 规 定 填 写 漏 填 或 内 容 填 写 错 误 的 ; 不 同 投 标 人 的 投 标 文 件 由 同 一 台 电 脑 或 同 一 家 投 标 单
评 委 : 徐 岩 宇 - 个 人 清 标 评 审 明 细 表 评 审 因 素 序 号 投 标 单 位 清 标 评 审 1 深 圳 市 创 捷 科 技 有 限 合 格 2 四 川 川 大 智 胜 软 件 股 份 有 限 合 格 3 北 京 航 天 长 峰 科 技 工 业 集 团 有 限 公 司 合 格 4 深 圳 中 兴 力 维 技 术 有 限 合 格 5 深 圳 键 桥 通 讯 技 术 股 份 有
<4D6963726F736F667420576F7264202D20B9D8D3DAB0BABBAAA3A8C9CFBAA3A3A9D7D4B6AFBBAFB9A4B3CCB9C9B7DDD3D0CFDEB9ABCBBE32303132C4EAC4EAB6C8B9C9B6ABB4F3BBE1B7A8C2C9D2E2BCFBCAE92E646F6378>
上 海 德 载 中 怡 律 师 事 务 所 关 于 昂 华 ( 上 海 ) 自 动 化 工 程 股 份 有 限 公 司 二 〇 一 二 年 年 度 股 东 大 会 法 律 意 见 书 上 海 德 载 中 怡 律 师 事 务 所 上 海 市 银 城 中 路 168 号 上 海 银 行 大 厦 1705 室 (200120) 电 话 :8621-5012 2258 传 真 :8621-5012 2257
2014年中央财经大学研究生招生录取工作简报
2015 年 中 央 财 经 大 学 研 究 生 招 生 录 取 工 作 简 报 一 硕 士 研 究 生 招 生 录 取 情 况 2015 年 共 有 8705 人 报 考 我 校 硕 士 研 究 生, 其 中 学 术 型 研 究 生 报 考 3657 人, 专 业 硕 士 研 究 生 报 考 5048 人 ; 总 报 考 人 数 较 2014 年 增 长 1.4%, 学 术 型 报 考 人 数 较
全国建筑市场注册执业人员不良行为记录认定标准(试行).doc
- 1 - - 2 - 附 件 全 国 建 筑 市 场 注 册 执 业 人 员 不 良 记 录 认 定 标 准 ( 试 行 ) 说 明 为 了 完 善 建 筑 市 场 注 册 执 业 人 员 诚 信 体 系 建 设, 规 范 执 业 和 市 场 秩 序, 依 据 相 关 法 律 法 规 和 部 门 规 章, 根 据 各 行 业 特 点, 我 部 制 订 了 全 国 建 筑 市 场 注 册 执 业 人
第2章 数据类型、常量与变量
第 2 章 数 据 类 型 常 量 与 变 量 在 计 算 机 程 序 中 都 是 通 过 值 (value) 来 进 行 运 算 的, 能 够 表 示 并 操 作 值 的 类 型 为 数 据 类 型 在 本 章 里 将 会 介 绍 JavaScript 中 的 常 量 (literal) 变 量 (variable) 和 数 据 类 型 (data type) 2.1 基 本 数 据 类 型 JavaScript
抗 战 时 期 国 民 政 府 的 银 行 监 理 体 制 探 析 % # % % % ) % % # # + #, ) +, % % % % % % % %
抗 战 时 期 国 民 政 府 的 银 行 监 理 体 制 探 析 王 红 曼 抗 战 时 期 国 民 政 府 为 适 应 战 时 经 济 金 融 的 需 要 实 行 由 财 政 部 四 联 总 处 中 央 银 行 等 多 家 机 构 先 后 共 同 参 与 的 多 元 化 银 行 监 理 体 制 对 战 时 状 态 下 的 银 行 发 展 与 经 营 安 全 进 行 了 大 规 模 的 设 计 与
伊 犁 师 范 学 院 611 语 言 学 概 论 全 套 考 研 资 料 <2016 年 最 新 考 研 资 料 > 2-2 语 言 学 纲 要 笔 记, 由 考 取 本 校 本 专 业 高 分 研 究 生 总 结 而 来, 重 点 突 出, 借 助 此 笔 记 可 以 大 大 提 高 复 习 效
伊 犁 师 范 学 院 611 语 言 学 概 论 全 套 考 研 资 料 ......2 伊 犁 师 范 学 院 802 文 学 概 论 全 套 考 研 资 料 ......2 伊 犁 师 范 学 院 702 普 通 物 理 全 套 考 研 资 料 ......3 伊 犁
3 复 试 如 何 准 备 4 复 试 成 绩 计 算 5 复 试 比 例 6 复 试 类 型 7 怎 么 样 面 对 各 种 复 试 04 05
1 复 试 流 程 2 复 试 考 查 形 式 02 03 3 复 试 如 何 准 备 4 复 试 成 绩 计 算 5 复 试 比 例 6 复 试 类 型 7 怎 么 样 面 对 各 种 复 试 04 05 2 怎 样 给 导 师 留 下 良 好 的 第 一 印 象 把 握 进 门 时 机 1 面 试 中 穿 着 的 瞒 天 过 海 3 无 声 胜 有 声 的 肢 体 语 言 育 4 眼 睛 是 心
1600 1000 40 50 2030 2000 采 取 行 动 的 机 会 90% 开 拓 成 功 的 道 路 2
简 略 版 本 :2015 3 10 2016 2021 全 球 卫 生 部 门 病 毒 性 肝 炎 战 略 2016 2021 2015 3 12 2012 2010 2014 2015 2016 2021 140 55% 35% 5 15% 5 20% 2.4 1.3 1.5 1 1600 1000 40 50 2030 2000 采 取 行 动 的 机 会 90% 开 拓 成 功 的 道 路
Microsoft Word - 第3章.doc
52 5 天 通 过 职 称 计 算 机 考 试 ( 考 点 视 频 串 讲 + 全 真 模 拟 ) Word 2003 中 文 字 处 理 ( 第 2 版 ) 第 3 章 3 字 符 格 式 需 要 掌 握 的 考 点 字 体 字 形 和 字 号 的 设 置 ; 上 标 下 标 空 心 字 等 字 体 效 果 的 使 用 ; 字 符 间 距 的 调 整 ; 改 变 字 符 颜 色 底 纹 添 加
朱 丽 明 柯 美 云 周 丽 雅 袁 耀 宗 罗 金 燕 候 晓 华 陈 旻 湖 滥 用 安 非 他 命 会 增 加 得 心 脏 病 的 风 险 据 美 国 科 技 新 闻 网 报 道 根 据 纽 约 路 透 社 报 道 一 份 新 的 研 究 显 示 青 年 及 成 年 人 若 滥 用 安 非 他 命 会 增 加 得 心 脏 病 的 风 险 美 国 德 州 大 学 西 南 医 学 中 心
一 从 分 封 制 到 郡 县 制 一 从 打 虎 亭 汉 墓 说 起
县 乡 两 级 的 政 治 体 制 改 革 如 何 建 立 民 主 的 合 作 新 体 制 县 乡 人 大 运 行 机 制 研 究 课 题 组 引 言 一 从 分 封 制 到 郡 县 制 一 从 打 虎 亭 汉 墓 说 起 二 密 县 在 周 初 是 两 个 小 国 密 国 和 郐 国 三 密 县 的 第 一 任 县 令 卓 茂 四 明 清 时 代 的 密 县 二 从 集 中 的 动 员 体
随着执业中医师资格考试制度的不断完善,本着为我校中医学专业认证服务的目的,本文通过对我校中医类毕业生参加2012年和2013年的中医执业医师考试成绩及通过率、掌握率进行分析,并与全国的平均水平进行差异比较分析,以此了解我校执业中医师考试的现状,进而反映我校中医类课程总体教学水平,发现考核知识模块教学中存在的不足,反馈给相关学院和教学管理部门,以此提高教学和管理水平。
2012-2013 中 医 类 别 执 业 医 师 综 合 笔 试 成 绩 分 析 反 馈 报 告 教 务 处 二 零 一 三 年 三 月 1 目 录 1 前 言 3 2 2012-2013 中 医 类 别 执 业 医 师 综 合 笔 试 成 绩 分 析 反 馈 报 告 4 附 件 1:2012 年 中 医 类 别 医 师 资 格 综 合 笔 试 院 校 学 科 成 绩 分 析 报 告 附 件 2:2013
作 为 生 产 者 式 文 本 的 女 性 主 义 通 俗 小 说 梅 丽 本 文 借 鉴 文 化 研 究 理 论 家 约 翰 费 斯 克 的 生 产 者 式 文 本 这 一 概 念 考 察 女 性 主 义 通 俗 小 说 的 文 本 特 征 写 作 策 略 和 微 观 政 治 意 义 女 性 主 义 通 俗 小 说 通 过 对 传 统 通 俗 小 说 的 挪 用 和 戏 仿 传 播 女 性 主 义
新, 各 地 各 部 门 ( 单 位 ) 各 文 化 事 业 单 位 要 高 度 重 视, 切 实 加 强 领 导, 精 心 组 织 实 施 要 根 据 事 业 单 位 岗 位 设 置 管 理 的 规 定 和 要 求, 在 深 入 调 查 研 究 广 泛 听 取 意 见 的 基 础 上, 研 究 提
广 西 壮 族 自 治 区 人 事 厅 广 西 壮 族 自 治 区 文 化 厅 文 件 桂 人 发 2009 42 号 关 于 印 发 广 西 壮 族 自 治 区 文 化 事 业 单 位 岗 位 设 置 结 构 比 例 指 导 标 准 的 通 知 各 市 人 事 局 文 化 局, 区 直 各 部 门 ( 单 位 ): 根 据 人 事 部 印 发 的 事 业 单 位 岗 位 设 置 管 理 试 行 办
类 似 地, 又 可 定 义 变 下 限 的 定 积 分 : ( ). 与 ψ 统 称 为 变 限 积 分. f ( ) d f ( t) dt,, 注 在 变 限 积 分 (1) 与 () 中, 不 可 再 把 积 分 变 量 写 成 的 形 式 ( 例 如 ) 以 免 与 积 分 上 下 限 的
5 ( 一 ) 微 积 分 学 基 本 定 理 当 函 数 的 可 积 性 问 题 告 一 段 落, 并 对 定 积 分 的 性 质 有 了 足 够 的 认 识 之 后, 接 着 要 来 解 决 一 个 以 前 多 次 提 到 过 的 问 题 在 定 积 分 形 式 下 证 明 连 续 函 数 必 定 存 在 原 函 数. 一 变 限 积 分 与 原 函 数 的 存 在 性 设 f 在 [,] 上
一 开 放 性 的 政 策 与 法 规 二 两 岸 共 同 的 文 化 传 承 三 两 岸 高 校 各 自 具 有 专 业 优 势 远 见 杂 志 年 月 日
河 北 师 范 大 学 学 报 新 时 期 海 峡 两 岸 高 校 开 放 招 生 问 题 探 讨 郑 若 玲 王 晓 勇 海 峡 两 岸 高 校 开 放 招 生 是 新 时 期 推 进 海 峡 两 岸 高 等 教 育 交 流 与 合 作 的 重 要 尝 试 系 统 梳 理 改 革 开 放 以 来 两 岸 招 生 政 策 与 就 学 人 数 发 展 变 化 的 历 史 进 程 可 发 现 促 进 两
编号:
编 号 : 企 业 内 高 技 能 人 才 培 养 评 价 实 施 方 案 ( 仅 适 用 于 企 业 特 有 行 业 特 有 工 种 ) 实 施 单 位 ( 公 章 ) 申 报 日 期 年 _ 月 日 1 企 业 内 高 技 能 人 才 培 养 评 价 项 目 实 施 方 案 申 报 表 项 目 名 称 等 级 项 目 性 质 课 时 申 报 单 位 联 系 人 通 讯 地 址 电 话 手 机 电
公 开 刊 物 须 有 国 内 统 一 刊 (CN), 发 表 文 章 的 刊 物 需 要 在 国 家 新 闻 出 版 广 电 总 局 (www.gapp.gov.cn 办 事 服 务 便 民 查 询 新 闻 出 版 机 构 查 询 ) 上 能 够 查 到 刊 凡 在 有 中 国 标 准 书 公 开
杭 教 人 2014 7 杭 州 市 教 育 局 关 于 中 小 学 教 师 系 列 ( 含 实 验 教 育 管 理 ) 晋 升 高 级 专 业 技 术 资 格 有 关 论 文 要 求 的 通 知 各 区 县 ( 市 ) 教 育 局 ( 社 发 局 ), 直 属 学 校 ( 单 位 ), 委 托 单 位 : 为 进 一 步 规 范 杭 州 市 中 小 学 教 师 系 列 ( 含 实 验 教 育 管
untitled
( 一 ) 深 刻 认 识 学 习 教 育 的 重 大 意 义 : - 3 - ( 二 ) 明 确 学 习 教 育 的 任 务 目 标 ( 三 ) 把 握 特 点 方 法 - 4 - ( 四 ) 坚 持 六 项 原 则 在 - 5 - ( 五 ) 着 力 解 决 问 题 - 6 - - 7 - - 8 - ( 一 ) 学 党 章 党 规, 进 一 步 明 确 党 员 标 准 树 立 行 为 规 范
3 月 30 日 在 中 国 证 券 报 上 海 证 券 报 证 券 时 报 证 券 日 报 和 上 海 证 券 交 易 所 网 站 上 发 出 召 开 本 次 股 东 大 会 公 告, 该 公 告 中 载 明 了 召 开 股 东 大 会 的 日 期 网 络 投 票 的 方 式 时 间 以 及 审
北 京 市 君 致 律 师 事 务 所 关 于 浪 潮 软 件 股 份 有 限 公 司 2015 年 度 股 东 大 会 的 法 律 意 见 书 致 : 浪 潮 软 件 股 份 有 限 公 司 北 京 市 君 致 律 师 事 务 所 ( 以 下 简 称 本 所 ) 受 浪 潮 软 件 股 份 有 限 公 司 ( 以 下 简 称 公 司 ) 的 委 托, 指 派 律 师 出 席 2016 年 4 月
际 联 考 的 非 美 术 类 本 科, 提 前 批 本 科 体 育 类 第 一 批 第 二 批 第 三 批 的 理 工 类 和 文 史 类 本 科 平 行 志 愿, 考 生 可 以 填 报 6 所 院 校 志 愿 符 合 贫 困 地 区 专 项 计 划 和 农 村 考 生 专 项 计 划 报 考
第 四 部 分 平 行 志 愿 57. 什 么 是 平 行 志 愿?/ 32 58. 我 省 在 哪 个 批 次 实 行 平 行 志 愿? 考 生 最 多 可 以 填 报 几 所 院 校 志 愿?/ 32 59. 第 一 二 三 批 本 科 平 行 志 愿 如 何 投 档?/ 32 60. 艺 术 本 科 ( 二 ) 艺 术 本 科 ( 三 ) 和 体 育 本 科 的 平 行 志 愿 如 何 投 档?/
反 学 校 文 化 与 阶 级 再 生 产 小 子 与 子 弟 之 比 较 周 潇 作 者 通 过 对 北 京 某 打 工 子 弟 学 校 的 田 野 调 查 后 发 现 在 农 民 工 子 弟 中 间 盛 行 着 类 似 学 做 工 中 所 描 述 的 工 人 阶 级 小 子 的 反 学 校 文 化 但 是 由 于 制 度 安 排 与 社 会 条 件 的 差 异 子 弟 与 小 子 的 反 学 校
黄 金 原 油 总 持 仓 增 长, 同 比 增 幅 分 别 为 4.2% 和 4.1% 而 铜 白 银 以 及 玉 米 则 出 现 减 持, 减 持 同 比 减 少 分 别 为 9.4%,9.4% 以 及 6.5% 大 豆, 豆 粕 结 束 连 续 4 周 总 持 仓 量 增 长, 出 现 小 幅
小 麦 净 多 持 仓 增 加, 豆 油 豆 粕 净 多 持 仓 减 少 美 国 CFTC 持 仓 报 告 部 门 : 市 场 研 究 与 开 发 部 类 型 : 量 化 策 略 周 报 日 期 :212 年 5 月 7 日 电 话 :592-5678753 网 址 :www.jinyouqh.com 主 要 内 容 : 根 据 美 国 CFTC 公 布 的 数 据, 本 报 告 中 的 11 个
附 件 : 上 海 市 建 筑 施 工 企 业 施 工 现 场 项 目 管 理 机 构 关 键 岗 位 人 员 配 备 指 南 二 一 四 年 九 月 十 一 日 2
公 开 上 海 市 城 乡 建 设 和 管 理 委 员 会 文 件 沪 建 管 2014 758 号 上 海 市 城 乡 建 设 和 管 理 委 员 会 关 于 印 发 上 海 市 建 筑 施 工 企 业 施 工 现 场 项 目 管 理 机 构 关 键 岗 位 人 员 配 备 指 南 的 通 知 各 区 县 建 设 和 交 通 委 员 会 : 为 进 一 步 加 强 对 建 设 工 程 施 工 现
物 流 从 业 人 员 职 业 能 力 等 级 证 书 分 为 四 个 级 别, 分 别 为 初 级 助 理 级 中 级 和 高 级 ; 采 购 从 业 人 员 职 业 能 力 等 级 证 书 分 为 三 个 级 别, 分 别 为 中 级 高 级 和 注 册 级 请 各 有 关 单 位 按 照 通
物 联 培 字 2016 16 号 各 有 关 单 位 : 为 适 应 国 家 一 带 一 路 战 略 实 施 和 物 流 产 业 转 型 升 级 对 人 才 的 新 要 求, 确 保 物 流 采 购 人 才 培 养 工 作 有 序 衔 接 和 持 续 健 康 发 展, 参 照 国 际 惯 例, 中 国 物 流 与 采 购 联 合 会 ( 以 下 简 称 中 物 联 ) 经 研 究 决 定, 以 物
金 不 少 于 800 万 元, 净 资 产 不 少 于 960 万 元 ; (3) 近 五 年 独 立 承 担 过 单 项 合 同 额 不 少 于 1000 万 元 的 智 能 化 工 程 ( 设 计 或 施 工 或 设 计 施 工 一 体 ) 不 少 于 2 项 ; (4) 近 三 年 每 年
工 程 设 计 与 施 工 资 质 标 准 一 总 则 建 筑 智 能 化 工 程 设 计 与 施 工 资 质 标 准 ( 一 ) 为 了 加 强 对 从 事 建 筑 智 能 化 工 程 设 计 与 施 工 企 业 的 管 理, 维 护 建 筑 市 场 秩 序, 保 证 工 程 质 量 和 安 全, 促 进 行 业 健 康 发 展, 结 合 建 筑 智 能 化 工 程 的 特 点, 制 定 本 标
教师上报成绩流程图
教 务 管 理 系 统 使 用 说 明 学 生 端 用 户 1 在 校 内 任 何 一 台 连 接 校 园 网 的 计 算 机 上 登 录 教 务 处 主 页 教 务 处 主 页 地 址 : http://jw.stdu.edu.cn/homepage 随 后 点 击 按 钮 ( 见 下 图 所 示 ), 即 可 进 入 综 合 教 务 管 理 系 统 2 在 综 合 教 务 管 理 区 域 内 键
第二讲 数列
Togisu XueD Persolized Eduio Developme Ceer 高 考 中 不 等 式 问 题 的 解 决 方 法 通 润 达 久 王 力 前 言 : 近 年 来 不 等 式 问 题 正 越 来 越 多 的 出 现 在 调 研 题 和 高 考 试 题 中 而 且 大 多 出 现 在 江 苏 高 考 的 填 空 压 轴 题 中 是 高 考 考 察 的 重 点 和 难 点 由 于
¹ º ¹ º 农 业 流 动 人 口 是 指 户 口 性 质 为 农 业 户 口 在 流 入 地 城 市 工 作 生 活 居 住 一 个 月 及 以 上 的 流 动 人 口 非 农 流 动 人 口 是 指 户 口 性 质 为 非 农 户 口 在 流 入 地 城 市 工 作 生 活 居 住 一 个
¹ 改 革 开 放 年 来 人 口 流 动 规 模 持 续 增 加 对 我 国 社 会 经 济 的 持 续 发 展 起 到 了 重 要 作 用 为 全 面 了 解 我 国 流 动 人 口 生 存 状 况 准 确 把 握 流 动 人 口 发 展 规 律 和 趋 势 不 断 加 强 流 动 人 口 服 务 管 理 引 导 人 口 有 序 流 动 合 理 分 布 国 家 人 口 计 生 委 于 年 月 启
登录、注册功能的测试用例设计.doc
注 册 登 陆 测 试 用 例 和 修 改 密 码 测 试 用 例 完 整 版 摘 自 网 络, 狗 狗 整 理 [email protected] 修 改 历 史 日 期 版 本 作 者 修 改 内 容 评 审 号 变 更 控 制 号 2010-11-25 1.0 初 稿 2011-09-17 2.0 整 理 一 注 册 测 试 用 例 序 号 : 1 控 件 名 称 : 功 能 描 述 : 注 册 编
Microsoft Word - 文件汇编.doc
北 京 市 中 医 管 理 局 二 一 五 年 四 月 ... 1... 18 2015... 30 京 中 医 政 字 [2014]160 号 1 2 一 充 分 认 识 中 医 健 康 乡 村 建 设 工 作 的 重 要 意 义 二 建 立 健 全 工 作 保 障 机 制 2014 12 15 三 做 好 工 作 启 动 的 准 备 事 宜 1 2014 12 15 5-10 2014 12 15
论 吉 卜 林 勇 敢 的 船 长 们 中 的 教 育 理 念 陈 兵 勇 敢 的 船 长 们 是 英 国 首 位 诺 贝 尔 文 学 奖 得 主 鲁 德 亚 德 吉 卜 林 的 一 部 教 育 小 说 通 过 主 人 公 哈 维 的 成 长 历 程 表 达 了 作 者 的 教 育 理 念 本 文 认 为 像 维 多 利 亚 时 代 晚 期 的 许 多 英 国 人 一 样 吉 卜 林 比 较 注 重
100566035515613 101 思 想 政 治 理 论 经 核 查 无 误 100566035715658 101 思 想 政 治 理 论 经 核 查 无 误 100566037615926 101 思 想 政 治 理 论 经 核 查 无 误 100566000100357 101 思 想
2016 年 天 津 大 学 硕 士 学 位 研 究 生 考 试 初 试 成 绩 复 核 结 果 公 示 考 生 编 号 科 目 码 科 目 名 称 复 核 结 果 100566000100858 101 思 想 政 治 理 论 经 核 查 无 误 100566000101151 101 思 想 政 治 理 论 经 核 查 无 误 100566000101348 101 思 想 政 治 理 论 经
微 积 分 ( 二 ) 教 学 大 纲 2 (2010 版 ) 课 程 编 码 :110861 课 程 名 称 : 微 积 分 学 时 / 学 分 :36/2 先 修 课 程 : 初 等 数 学 立 体 几 何 平 面 解 析 几 何 微 积 分 ( 一 ) 适 用 专 业 : 人 力 资 源 管
微 积 分 ( 二 ) 教 学 大 纲 2 (2010 版 ) 课 程 编 码 :110861 课 程 名 称 : 微 积 分 学 时 / 学 分 :36/2 先 修 课 程 : 初 等 数 学 立 体 几 何 平 面 解 析 几 何 微 积 分 ( 一 ) 适 用 专 业 : 人 力 资 源 管 理 等 专 业 开 课 教 研 室 : 大 学 数 学 教 研 室 执 笔 : 庄 乐 森 审 定 :
2015-2016 学 年 第 二 学 期 集 中 考 试 安 排 (18 周 ) 考 试 日 期 :6 月 27 日 星 期 一 8:10-9:50 第 二 公 共 教 学 楼 A 区 A303 10811046 高 等 数 学 ( 理 二 2) 复 材 1501-2 材 料 科 学 与 工 程
考 试 时 间 2015-2016 学 年 第 二 学 期 集 中 考 试 安 排 (18 周 ) 考 试 日 期 :6 月 27 日 星 期 一 考 场 所 在 教 学 楼 ( 教 学 区 ) 考 试 教 室 课 程 号 课 程 名 考 生 所 在 专 业 ( 班 级 ) 考 生 所 属 学 院 8:10-9:50 第 二 公 共 教 学 楼 A 区 A101 10811026 高 等 数 学 (
抗 日 战 争 研 究 年 第 期
田 子 渝 武 汉 抗 战 时 期 是 国 共 第 二 次 合 作 的 最 好 时 期 在 国 共 合 作 的 基 础 上 出 现 了 抗 日 救 亡 共 御 外 侮 的 局 面 这 个 大 好 局 面 的 出 现 与 中 共 长 江 局 的 丰 功 伟 绩 是 分 不 开 的 但 长 期 以 来 由 于 有 一 个 王 明 的 右 倾 错 误 直 接 影 响 了 对 它 的 全 面 科 学 准 确
第 期 李 伟 等 用 方 法 对 中 国 历 史 气 温 数 据 插 值 可 行 性 讨 论
李 伟 李 庆 祥 江 志 红 使 用 插 值 方 法 对 已 经 过 质 量 控 制 和 均 一 化 的 年 月 年 月 中 国 全 部 基 本 基 准 站 气 温 资 料 逐 月 进 行 空 间 插 值 通 过 站 点 的 实 际 序 列 与 插 值 后 格 点 序 列 进 行 比 较 针 对 相 关 系 数 和 线 性 趋 势 等 多 个 量 来 检 验 方 法 对 气 候 资 料 插 值 的
证券代码:000066 证券简称:长城电脑 公告编号:2014-000
证 券 代 码 :000066 证 券 简 称 : 长 城 电 脑 公 告 编 号 :2016-092 中 国 长 城 计 算 机 深 圳 股 份 有 限 公 司 2016 年 度 第 三 次 临 时 股 东 大 会 决 议 公 告 本 公 司 及 其 董 事 会 全 体 成 员 保 证 信 息 披 露 内 容 的 真 实 准 确 完 整, 没 有 虚 假 记 载 误 导 性 陈 述 或 重 大 遗
Microsoft Word - 资料分析练习题09.doc
行 测 高 分 冲 刺 练 习 题 资 料 分 析 ( 共 15 题, 参 考 时 限 10 分 钟 ) 材 料 题 - 1 2012 年 1 月 某 小 区 成 交 的 二 手 房 中, 面 积 为 60 平 方 米 左 右 的 住 宅 占 总 销 售 套 数 的 ( ) A.25% B.35% C.37.5% 长 沙 市 雨 花 区 侯 家 塘 佳 天 国 际 大 厦 北 栋 20 楼 第 1
工 程 勘 察 资 质 标 准 根 据 建 设 工 程 勘 察 设 计 管 理 条 例 和 建 设 工 程 勘 察 设 计 资 质 管 理 规 定, 制 定 本 标 准 一 总 则 ( 一 ) 本 标 准 包 括 工 程 勘 察 相 应 专 业 类 型 主 要 专 业 技 术 人 员 配 备 技 术
住 房 和 城 乡 建 设 部 关 于 印 发 工 程 勘 察 资 质 标 准 的 通 知 建 市 [2013]9 号 各 省 自 治 区 住 房 和 城 乡 建 设 厅, 北 京 市 规 划 委, 天 津 上 海 市 建 设 交 通 委, 重 庆 市 城 乡 建 设 委, 新 疆 生 产 建 设 兵 团 建 设 局, 总 后 基 建 营 房 部 工 程 局, 国 务 院 有 关 部 门 建 设 司,
导 数 和 微 分 的 概 念 导 数 的 几 何 意 义 和 物 理 意 义 函 数 的 可 导 性 与 连 续 性 之 间 的 关 系 平 面 曲 线 的 切 线 和 法 线 导 数 和 微 分 的 四 则 运 算 基 本 初 等 函 数 的 导 数 复 合 函 数 反 函 数 隐 函 数 以
2015 年 考 研 数 学 二 考 试 大 纲 考 试 科 目 : 高 等 数 学 线 性 代 数 考 试 形 式 和 试 卷 结 构 一 试 卷 满 分 及 考 试 时 间 试 卷 满 分 为 150 分, 考 试 时 间 为 180 分 钟. 二 答 题 方 式 答 题 方 式 为 闭 卷 笔 试. 三 试 卷 内 容 结 构 高 等 教 学 约 78% 线 性 代 数 约 22% 四 试 卷
ETF、分级基金规模、份额变化统计20130816
ETF 分 级 基 金 规 模 份 额 变 化 统 计 截 至 上 周 末, 全 市 场 股 票 型 ETF 规 模 约 1451 亿, 份 额 约 1215 亿,ETF 总 份 额 及 规 模 的 周 变 动 值 分 别 为 -23-44 亿, 份 额 与 规 模 均 下 降 ; 分 级 基 金 规 模 约 438 亿, 份 额 572 亿, 总 份 额 及 规 模 的 周 变 动 值 分 别 为
第二部分 阅读理解(Part II Reabing Comprehension)
吉 林 省 成 人 本 科 学 士 学 位 日 语 统 一 考 试 大 纲 总 则 为 适 应 成 人 高 等 教 育 本 科 毕 业 生 申 请 学 士 学 位 外 语 统 一 考 试 的 要, 根 据 国 务 院 学 位 委 员 会 关 于 授 予 成 人 高 等 教 育 本 科 毕 业 生 学 士 学 位 暂 行 规 定 和 国 务 院 学 位 委 员 会 原 国 家 教 育 委 员 会 关
上证指数
上 证 与 修 正 方 法 一 ( 一 ) 计 算 公 式 1. 上 证 指 数 系 列 均 采 用 派 许 加 权 综 合 价 格 指 数 公 式 计 算 2. 上 证 180 指 数 上 证 50 指 数 等 以 成 份 股 的 调 整 股 本 数 为 权 数 进 行 加 权 计 算, 计 算 公 式 为 : 报 告 期 指 数 =( 报 告 期 样 本 股 的 调 整 市 值 / 基 期 )
目 录 一 系 统 访 问... 1 二 门 户 首 页 申 报 用 户 审 核 用 户... 2 三 系 统 登 录 用 户 名 密 码 登 录 新 用 户 注 册 用 户 登 录 已 注 册 用
水 路 运 输 建 设 综 合 管 理 信 息 系 统 - 门 户 系 统 用 户 手 册 二 零 一 五 年 十 一 月 目 录 一 系 统 访 问... 1 二 门 户 首 页... 1 1. 申 报 用 户... 1 2. 审 核 用 户... 2 三 系 统 登 录... 4 1. 用 户 名 密 码 登 录... 4 1.1 新 用 户 注 册... 4 1.2 用 户 登 录... 7
外语
外 国 语 院 英 语 人 才 培 养 方 案 ( 代 码 :050201) 一 培 养 目 标 本 旨 在 培 养 具 有 扎 的 英 语 语 言 理 论 基 础 和 比 较 熟 练 的 听 说 读 写 译 的 践 能 力, 具 备 宽 泛 的 文 化 知 识 敏 锐 的 跨 文 化 交 际 意 识 开 阔 的 国 际 视 野 以 及 自 主 习 能 力 和 创 新 意 识 的 应 用 型 英 语
!!!!!
美 国 旧 金 山 湾 区 田 野 调 查 札 记 !!!!! ! 个 案 一 男 士 年 龄 岁 籍 贯 沈 阳! !! 个 案 二 女 士 年 龄 岁 籍 贯 沈 阳!! !!! 一 新 古 典 经 济 学 移 民 理 论 的 解 释!! 二 制 度 层 面 的 原 因! 三 社 会 资 本 理 论 与 东 北 人 移 民 网 络 !!!!!! 四 社 会 关 系 网 络 资 源 配 置 理 论
西 南 民 族 学 院 学 报 哲 学 社 会 科 学 版 第 卷 资 料 来 源 中 国 统 计 年 鉴 年 年 新 中 国 五 十 年 统 计 资 料 汇 编 中 国 人 口 统 计 年 鉴 年 数 据 资 料 来 源 中 国 统 计 年 鉴 中 国 统 计 出 版 社 年 版 资 料 来 源
郑 长 德 教 育 的 发 展 人 力 资 源 的 开 发 是 决 定 西 部 民 族 地 区 未 来 发 展 的 关 键 因 素 之 一 是 实 施 西 部 大 开 发 战 略 提 高 其 经 济 竞 争 力 和 综 合 实 力 的 重 要 保 障 本 文 从 西 部 民 族 地 区 教 育 发 展 的 现 状 入 手 指 出 中 华 人 民 共 和 国 成 立 多 年 来 西 部 民 族 地 区
现 场 会 议 时 间 为 :2016 年 5 月 19 日 网 络 投 票 时 间 为 :2016 年 5 月 18 日 -2016 年 5 月 19 日 其 中 通 过 深 圳 证 券 交 易 所 交 易 系 统 进 行 网 络 投 票 的 时 间 为 2016 年 5 月 19 日 9:30-
证 券 代 码 :300439 证 券 简 称 : 美 康 生 物 公 告 编 号 :2016-046 宁 波 美 康 生 物 科 技 股 份 有 限 公 司 2015 年 度 股 东 大 会 决 议 公 告 公 司 及 董 事 会 全 体 成 员 保 证 信 息 披 露 的 内 容 真 实 准 确 完 整, 没 有 虚 假 记 载 误 导 性 陈 述 或 重 大 遗 漏 特 别 提 示 : 1 2016
<4D6963726F736F667420576F7264202D2032303133C4EAB9A4B3CCCBB6CABFCAFDD1A7D7A8D2B5BFCEBFBCCAD4B4F3B8D9D3EBD2AAC7F3>
工 程 硕 士 数 学 考 试 大 纲 与 要 求 ( 包 括 高 等 数 学 和 线 性 代 数 ) 一 函 数 极 限 与 连 续 第 一 部 分 : 高 等 数 学 考 试 内 容 函 数 的 概 念 及 表 示 法 函 数 的 有 界 性 单 调 性 周 期 性 和 奇 偶 性 复 合 函 数 反 函 数 分 段 函 数 和 隐 函 数 基 本 初 等 函 数 的 性 质 及 其 图 形 初
春 天 来 了 静 悄 悄 的 没 有 鸟 语 没 有 花 香 到 处 死 一 样 的 沉 寂 雷 切 尔 卡 森
陈 小 红 加 里 斯 奈 德 被 深 层 生 态 学 家 视 为 他 们 的 桂 冠 诗 人 他 为 全 球 生 态 运 动 作 出 了 巨 大 的 贡 献 本 文 旨 在 研 究 其 诗 歌 中 所 体 现 出 的 独 特 的 生 态 观 主 要 从 四 方 面 来 阐 明 斯 奈 德 对 荒 野 的 热 爱 对 文 明 的 反 思 对 印 第 安 人 生 活 的 向 往 以 及 对 理 想 的
定 位 和 描 述 : 程 序 设 计 / 办 公 软 件 高 级 应 用 级 考 核 内 容 包 括 计 算 机 语 言 与 基 础 程 序 设 计 能 力, 要 求 参 试 者 掌 握 一 门 计 算 机 语 言, 可 选 类 别 有 高 级 语 言 程 序 设 计 类 数 据 库 编 程 类
全 国 计 算 机 等 级 考 试 调 整 方 案 2011 年 7 月, 教 育 部 考 试 中 心 组 织 召 开 了 第 五 届 全 国 计 算 机 等 级 考 试 (NCRE) 考 委 会 会 议, 会 议 完 成 NCRE 考 委 会 换 届 选 举, 并 确 定 了 下 一 步 改 革 和 发 展 的 目 标 在 新 的 历 史 时 期,NCRE 将 以 保 持 稳 定 为 前 提 以
三武一宗灭佛研究
四 川 大 学 博 士 学 位 论 文 三 武 一 宗 灭 佛 研 究 姓 名 : 张 箭 申 请 学 位 级 别 : 博 士 专 业 : 中 国 古 代 史 指 导 教 师 : 杨 耀 坤 20020101 三
资 料 来 源 延 边 中 级 人 民 法 院 小 野 和 子 指 出 年 实 施 婚 姻 法 后 的 年 间 中 国 有 万 人 因 婚 姻 问 题 自 杀 或 被 杀 离 婚 自 由 对 社 会 和 家 庭 稳 定 带 来 了 很 大 的 影 响 因 婚 姻 问 题 刑 事 案 件 频 发 已
以 延 边 朝 鲜 族 女 性 的 涉 外 婚 姻 为 例 本 研 究 运 用 交 换 理 论 以 延 边 朝 鲜 族 女 性 的 涉 外 婚 姻 为 例 探 讨 婚 姻 中 的 资 源 与 交 换 之 间 的 关 系 年 中 国 和 韩 国 建 交 后 在 延 边 朝 鲜 族 社 会 里 社 会 经 济 资 源 匮 乏 的 女 性 在 涉 外 婚 姻 中 将 自 身 的 年 轻 作 为 可 利 用
正 规 培 训 达 规 定 标 准 学 时 数, 并 取 得 结 业 证 书 二 级 可 编 程 师 ( 具 备 以 下 条 件 之 一 者 ) (1) 连 续 从 事 本 职 业 工 作 13 年 以 上 (2) 取 得 本 职 业 三 级 职 业 资 格 证 书 后, 连 续 从 事 本 职 业
1. 职 业 概 况 1.1 职 业 名 称 可 编 程 师 1.2 职 业 定 义 可 编 程 师 国 家 职 业 标 准 从 事 可 编 程 序 控 制 器 (PLC) 选 型 编 程, 并 对 应 用 进 行 集 成 和 运 行 管 理 的 人 员 1.3 职 业 等 级 本 职 业 共 设 四 个 等 级, 分 别 为 : 四 级 可 编 程 师 ( 国 家 职 业 资 格 四 级 ) 三
本 期 目 录 1. 一 图 看 懂 : 湖 南 大 学 两 学 一 做 学 习 教 育 实 施 方 案...1 2. 习 近 平 : 要 整 顿 不 合 格 基 层 党 组 织...9 3. 平 语 近 人 习 近 平 谈 党 章 党 规...12 4. 习 近 平 为 何 要 求 在 两 学 一
机 关 党 支 部 理 论 学 习 参 考 资 料 2016 年 第 5 期 ( 两 学 一 做 专 题 二 ) 中 共 湖 南 大 学 机 关 委 员 会 编 2016 年 4 月 11 日 本 期 目 录 1. 一 图 看 懂 : 湖 南 大 学 两 学 一 做 学 习 教 育 实 施 方 案...1 2. 习 近 平 : 要 整 顿 不 合 格 基 层 党 组 织...9 3. 平 语 近 人
抗 日 战 争 研 究 ( 年 第 期!!! #! %!! & ( % & ( (
曹 大 臣 年 月 至 年 月 日 本 利 用 宏 济 善 堂 在 华 中 进 行 毒 化 活 动 在 不 到 年 时 间 里 宏 济 善 堂 牟 取 了 亿 日 元 之 巨 利 为 侵 华 战 争 提 供 了 相 当 于 艘 航 空 母 舰 的 物 力 支 持 并 在 很 大 程 度 上 弱 化 了 中 国 人 民 的 抗 日 意 志 因 日 人 败 降 时 销 匿 了 宏 济 善 堂 相 关 资
GONGZUO JUJIAO 宝 山 区 领 军 人 才 名 单 宝 山 区 第 七 批 拔 尖 人 才 名 单 2
GONGZUO JUJIAO 宝 山 区 命 名 表 彰 领 军 人 才 第 七 批 拔 尖 人 才 和 青 年 尖 子 1 GONGZUO JUJIAO 宝 山 区 领 军 人 才 名 单 宝 山 区 第 七 批 拔 尖 人 才 名 单 2 GONGZUO JUJIAO 旻 3 GONGZUO JUJIAO 宝 山 区 第 七 批 青 年 尖 子 名 单 4 GONGZUO JUJIAO 宝 山
