收藏 分享(赏)

extern 编程难点详解课件.doc

上传人:无敌 文档编号:1058872 上传时间:2018-06-08 格式:DOC 页数:9 大小:36.52KB
下载 相关 举报
extern  编程难点详解课件.doc_第1页
第1页 / 共9页
extern  编程难点详解课件.doc_第2页
第2页 / 共9页
extern  编程难点详解课件.doc_第3页
第3页 / 共9页
extern  编程难点详解课件.doc_第4页
第4页 / 共9页
extern  编程难点详解课件.doc_第5页
第5页 / 共9页
点击查看更多>>
资源描述

1、extern 可以置于变量或者函数前,以表示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。另外,extern 也可用来进行链接指定。目录extern 变量编译、链接函数展开编 辑 本 段 extern 变 量在 一 个 源 文 件 里 定 义 了 一 个 数 组 : char a6;在 另 外 一 个 文 件 里 用 下 列 语 句 进 行 了 声 明 : extern char *a;请 问 , 这 样 可 以 吗 ?答 案 与 分 析 :1)、 不 可 以 , 程 序 运 行 时 会 告 诉 你 非 法 访 问 。 原 因 在 于 , 指 向 类 型

2、T 的指 针 并 不 等 价 于 类 型 T 的 数 组 。 extern char *a 声 明 的 是 一 个 指 针 变 量 而 不 是字 符 数 组 , 因 此 与 实 际 的 定 义 不 同 , 从 而 造 成 运 行 时 非 法 访 问 。 应 该 将 声 明 改为 extern char a 。2)、 例 子 分 析 如 下 , 如 果 a = “abcd“,则 外 部 变 量 a=0x12345678 (数 组的 起 始 地 址 ), 而 *a 是 重 新 定 义 了 一 个 指 针 变 量 a 的 地 址 可 能 是 0x87654321,直 接 使 用 *a 是 错 误 的

3、 .3)、 这 提 示 我 们 , 在 使 用 extern 时 候 要 严 格 对 应 声 明 时 的 格 式 , 在 实 际 编程 中 , 这 样 的 错 误 屡 见 不 鲜 。4)、 extern 用 在 变 量 声 明 中 常 常 有 这 样 一 个 作 用 , 你 在 *.c 文 件 中 声 明 了一 个 全 局 的 变 量 , 这 个 全 局 的 变 量 如 果 要 被 引 用 , 就 放 在 *.h 中 并 用 extern 来声 明 。编 辑 本 段 编 译 、 链 接1、 声 明 外 部 变 量现 代 编 译 器 一 般 采 用 按 文 件 编 译 的 方 式 , 因 此 在

4、 编 译 时 , 各 个 文 件 中 定 义的 全 局 变 量 是互 相 透 明 的 , 也 就 是 说 , 在 编 译 时 , 全 局 变 量 的 可 见 域 限 制 在 文 件 内 部 。下 面 举 一 个 简 单 的 例 子 。 创 建 一 个 工 程 , 里 面 含 有 A.cpp 和 B.cpp 两 个 简 单的 C+源 文 件 :/A.cppint i;void main()/B.cppint i;这 两 个 文 件 极 为 简 单 , 在 A.cpp 中 我 们 定 义 了 一 个 全 局 变 量 i, 在 B 中 我们 也 定 义 了 一 个 全 局 变 量 i。我 们 对 A

5、 和 B 分 别 编 译 , 都 可 以 正 常 通 过 编 译 , 但 是 进 行 链 接 的 时 候 , 却出 现 了 错 误 , 错 误 提 示 如 下 :Linking.B.obj : error LNK2005: “int i“ (?i3HA) already defined in A.objDebug/A.exe : fatal error LNK1169: one or more multiply defined symbols foundError executing link.exe.A.exe - 2 error(s), 0 warning(s)这 就 是 说 , 在 编

6、译 阶 段 , 各 个 文 件 中 定 义 的 全 局 变 量 相 互 是 透 明 的 , 编 译A 时 觉 察 不 到 B 中 也 定 义 了 i, 同 样 , 编 译 B 时 觉 察 不 到 A 中 也 定 义 了 i。但 是 到 了 链 接 阶 段 , 要 将 各 个 文 件 的 内 容 “合 为 一 体 ”, 因 此 , 如 果 某 些 文件 中 定 义 的 全 局 变 量 名 相 同 的 话 , 在 这 个 时 候 就 会 出 现 错 误 , 也 就 是 上 面 提 示的 重 复 定 义 的 错 误 。因 此 , 各 个 文 件 中 定 义 的 全 局 变 量 名 不 可 相 同 。

7、在 链 接 阶 段 , 各 个 文 件 的 内 容 ( 实 际 是 编 译 产 生 的 obj 文 件 ) 是 被 合 并 到一 起 的 , 因 而 , 定 义 于 某 文 件 内 的 全 局 变 量 , 在 链 接 完 成 后 , 它 的 可 见 范 围 被扩 大 到 了 整 个 程 序 。这 样 一 来 , 按 道 理 说 , 一 个 文 件 中 定 义 的 全 局 变 量 , 可 以 在 整 个 程 序 的 任何 地 方 被 使 用 , 举 例 说 , 如 果 A 文 件 中 定 义 了 某 全 局 变 量 , 那 么 B 文 件 中 应可 以 使 用 该 变 量 。 修 改 我 们 的

8、 程 序 , 加 以 验 证 :/A.cppvoid main()i = 100; /试 图 使 用 B 中 定 义 的 全 局 变 量/B.cppint i;编 译 结 果 如 下 :Compiling.A.cppC:Documents and Settingswangjian桌 面 try externA.cpp(5) : error C2065: i : undeclared identifierError executing cl.exe.A.obj - 1 error(s), 0 warning(s)编 译 错 误 。其 实 出 现 这 个 错 误 是 意 料 之 中 的 , 因 为

9、 : 文 件 中 定 义 的 全 局 变 量 的 可 见 性扩 展 到 整 个 程 序 是 在 链 接 完 成 之 后 , 而 在 编 译 阶 段 , 他 们 的 可 见 性 仍 局 限 于 各自 的 文 件 。编 译 器 的 目 光 不 够 长 远 , 编 译 器 没 有 能 够 意 识 到 , 某 个 变 量 符 号 虽 然 不 是本 文 件 定 义 的 , 但 是 它 可 能 是 在 其 它 的 文 件 中 定 义 的 。虽 然 编 译 器 不 够 远 见 , 但 是 我 们 可 以 给 它 提 示 , 帮 助 它 来 解 决 上 面 出 现 的问 题 。 这 就 是 extern 的

10、作 用 了 。extern 的 原 理 很 简 单 , 就 是 告 诉 编 译 器 : “你 现 在 编 译 的 文 件 中 , 有 一 个标 识 符 虽 然 没 有 在 本 文 件 中 定 义 , 但 是 它 是 在 别 的 文 件 中 定 义 的 全 局 变 量 , 你要 放 行 ! ”我 们 为 上 面 的 错 误 程 序 加 上 extern 关 键 字 :/A.cppextern int i;void main()i = 100; /试 图 使 用 B 中 定 义 的 全 局 变 量/B.cppint i;顺 利 通 过 编 译 , 链 接 。编 辑 本 段 函 数extern 函

11、数 1常 见 extern 放 在 函 数 的 前 面 成 为 函 数 声 明 的 一 部 分 , 那 么 , C 语 言 的 关键 字 extern 在 函 数 的 声 明 中 起 什 么 作 用 ?答 案 与 分 析 :如 果 函 数 的 声 明 中 带 有 关 键 字 extern, 仅 仅 是 暗 示 这 个 函 数 可 能 在 别 的 源文 件 里 定 义 , 没 有 其 它 作 用 。 即 下 述 两 个 函 数 声 明 没 有 明 显 的 区 别 :extern int f(); 和 int f();当 然 , 这 样 的 用 处 还 是 有 的 , 就 是 在 程 序 中 取

12、代 include “*.h”来 声 明 函 数 ,在 一 些 复 杂 的 项 目 中 , 我 比 较 习 惯 在 所 有 的 函 数 声 明 前 添 加 extern 修 饰 。extern 函 数 2当 函 数 提 供 方 单 方 面 修 改 函 数 原 型 时 , 如 果 使 用 方 不 知 情 继 续 沿 用 原 来 的extern 申 明 , 这 样 编 译 时 编 译 器 不 会 报 错 。 但 是 在 运 行 过 程 中 , 因 为 少 了 或 者多 了 输 入 参 数 , 往 往 会 造 成 系 统 错 误 , 这 种 情 况 应 该 如 何 解 决 ?答 案 与 分 析 :目

13、 前 业 界 针 对 这 种 情 况 的 处 理 没 有 一 个 很 完 美 的 方 案 , 通 常 的 做 法 是 提 供方 在 自 己 的 xxx_pub.h 中 提 供 对 外 部 接 口 的 声 明 , 然 后 调 用 方 include 该 头 文件 , 从 而 省 去 extern 这 一 步 。 以 避 免 这 种 错 误 。宝 剑 有 双 锋 , 对 extern 的 应 用 , 不 同 的 场 合 应 该 选 择 不 同 的 做 法 。extern “C”在 C+环 境 下 使 用 C 函 数 的 时 候 , 常 常 会 出 现 编 译 器 无 法 找 到 obj 模 块中

14、的 C 函 数 定 义 , 从 而 导 致 链 接 失 败 的 情 况 , 应 该 如 何 解 决 这 种 情 况 呢 ?答 案 与 分 析 :C+语 言 在 编 译 的 时 候 为 了 解 决 函 数 的 多 态 问 题 , 会 将 函 数 名 和 参 数 联 合起 来 生 成 一 个 中 间 的 函 数 名 称 , 而 C 语 言 则 不 会 , 因 此 会 造 成 链 接 时 找 不 到 对应 函 数 的 情 况 , 此 时 C 函 数 就 需 要 用 extern “C”进 行 链 接 指 定 , 这 告 诉 编 译 器 ,请 保 持 我 的 名 称 , 不 要 给 我 生 成 用 于

15、 链 接 的 中 间 函 数 名 。下 面 是 一 个 标 准 的 写 法 :/在 .h 文 件 的 头 上#ifdef _cplusplus#if _cplusplusextern “C“#endif#endif /* _cplusplus */.h 文 件 结 束 的 地 方#ifdef _cplusplus#if _cplusplus#endif#endif /* _cplusplus */C+中 extern c 的 深 层 探 索C+语 言 的 创 建 初 衷 是 “a better C”, 但 是 这 并 不 意 味 着 C+中 类 似 C 语言 的 全 局 变 量 和 函 数 所

16、 采 用 的 编 译 和 连 接 方 式 与 C 语 言 完 全 相 同 。 作 为 一 种欲 与 C 兼 容 的 语 言 , C+保 留 了 一 部 分 过 程 式 语 言 的 特 点 ( 被 世 人 称 为 “不 彻底 地 面 向 对 象 ”) , 因 而 它 可 以 定 义 不 属 于 任 何 类 的 全 局 变 量 和 函 数 。 但 是 ,C+毕 竟 是 一 种 面 向 对 象 的 程 序 设 计 语 言 , 为 了 支 持 函 数 的 重 载 , C+对 全 局函 数 的 处 理 方 式 与 C 有 明 显 的 不 同 。2.从 标 准 头 文 件 说 起某 企 业 曾 经 给 出

17、 如 下 的 一 道 面 试 题 :面 试 题为 什 么 标 准 头 文 件 都 有 类 似 以 下 的 结 构 ?#ifndef _INCvxWorksh#define _INCvxWorksh#ifdef _cplusplusextern “C“ #endif/*.*/#ifdef _cplusplus#endif#endif /* _INCvxWorksh */分 析显 然 , 头 文 件 中 的 编 译 宏 “#ifndef _INCvxWorksh、 #define _INCvxWorksh、 #endif” 的 作 用 是 防 止 该 头 文 件 被 重 复 引 用 。那 么#if

18、def _cplusplusextern “C“ #endif#ifdef _cplusplus#endif的 作 用 又 是 什 么 呢 ? 我 们 将 在 下 文 一 一 道 来 。3.深 层 揭 密 extern “C“extern “C“ 包 含 双 重 含 义 , 从 字 面 上 即 可 得 到 : 首 先 , 被 它 修 饰 的 目 标 是“extern”的 ; 其 次 , 被 它 修 饰 的 目 标 是 “C”的 。 让 我 们 来 详 细 解 读 这 两 重 含 义 。被 extern “C“限 定 的 函 数 或 变 量 是 extern 类 型 的 ;extern 是 C/

19、C+语 言 中 表 明 函 数 和 全 局 变 量 作 用 范 围 ( 可 见 性 ) 的 关 键 字 ,该 关 键 字 告 诉 编 译 器 , 其 声 明 的 函 数 和 变 量 可 以 在 本 模 块 或 其 它 模 块 中 使 用 。记 住 , 下 列 语 句 :extern int a;仅 仅 是 一 个 变 量 的 声 明 , 其 并 不 是 在 定 义 变 量 a, 并 未 为 a 分 配 内 存 空 间 。变 量 a 在 所 有 模 块 中 作 为 一 种 全 局 变 量 只 能 被 定 义 一 次 , 否 则 会 出 现 连 接 错 误 。通 常 , 在 模 块 的 头 文 件

20、 中 对 本 模 块 提 供 给 其 它 模 块 引 用 的 函 数 和 全 局 变 量以 关 键 字 extern 声 明 。 例 如 , 如 果 模 块 B 欲 引 用 该 模 块 A 中 定 义 的 全 局 变 量和 函 数 时 只 需 包 含 模 块 A 的 头 文 件 即 可 。 这 样 , 模 块 B 中 调 用 模 块 A 中 的 函数 时 , 在 编 译 阶 段 , 模 块 B 虽 然 找 不 到 该 函 数 , 但 是 并 不 会 报 错 ; 它 会 在 连 接阶 段 中 从 模 块 A 编 译 生 成 的 目 标 代 码 中 找 到 此 函 数 。与 extern 对 应

21、的 关 键 字 是 static, 被 它 修 饰 的 全 局 变 量 和 函 数 只 能 在 本 模块 中 使 用 。 因 此 , 一 个 函 数 或 变 量 只 可 能 被 本 模 块 使 用 时 , 其 不 可 能 被extern “C”修 饰 。被 extern “C“修 饰 的 变 量 和 函 数 是 按 照 C 语 言 方 式 编 译 和 连 接 的 ;未 加 extern “C”声 明 时 的 编 译 方 式首 先 看 看 C+中 对 类 似 C 的 函 数 是 怎 样 编 译 的 。作 为 一 种 面 向 对 象 的 语 言 , C+支 持 函 数 重 载 , 而 过 程 式

22、语 言 C 则 不 支持 。 函 数 被 C+编 译 后 在 符 号 库 中 的 名 字 与 C 语 言 的 不 同 。 例 如 , 假 设 某 个函 数 的 原 型 为 :void foo( int x, int y );该 函 数 被 C 编 译 器 编 译 后 在 符 号 库 中 的 名 字 为 _foo, 而 C+编 译 器 则 会产 生 像 _foo_int_int 之 类 的 名 字 ( 不 同 的 编 译 器 可 能 生 成 的 名 字 不 同 , 但 是 都 采用 了 相 同 的 机 制 , 生 成 的 新 名 字 称 为 “mangled name”) 。_foo_int_i

23、nt 这 样 的 名 字 包 含 了 函 数 名 、 函 数 参 数 数 量 及 类 型 信 息 , C+就 是 靠 这 种 机 制 来 实 现 函 数 重 载 的 。 例 如 , 在 C+中 , 函 数 void foo( int x, int y )与 void foo( int x, float y )编 译 生 成 的 符 号 是 不 相 同 的 , 后 者 为_foo_int_float。同 样 地 , C+中 的 变 量 除 支 持 局 部 变 量 外 , 还 支 持 类 成 员 变 量 和 全 局 变 量 。用 户 所 编 写 程 序 的 类 成 员 变 量 可 能 与 全 局

24、变 量 同 名 , 我 们 以 “.“来 区 分 。 而 本质 上 , 编 译 器 在 进 行 编 译 时 , 与 函 数 的 处 理 相 似 , 也 为 类 中 的 变 量 取 了 一 个 独一 无 二 的 名 字 , 这 个 名 字 与 用 户 程 序 中 同 名 的 全 局 变 量 名 字 不 同 。未 加 extern “C“声 明 时 的 连 接 方 式假 设 在 C+中 , 模 块 A 的 头 文 件 如 下 :/ 模 块 A 头 文 件 moduleA.h#ifndef MODULE_A_H#define MODULE_A_Hint foo( int x, int y );#en

25、dif在 模 块 B 中 引 用 该 函 数 :/ 模 块 B 实 现 文 件 moduleB.cpp#include “moduleA.h“foo(2,3);实 际 上 , 在 连 接 阶 段 , 连 接 器 会 从 模 块 A 生 成 的 目 标 文 件 moduleA.obj中 寻 找 _foo_int_int 这 样 的 符 号 !加 extern “C“声 明 后 的 编 译 和 连 接 方 式加 extern “C“声 明 后 , 模 块 A 的 头 文 件 变 为 :/ 模 块 A 头 文 件 moduleA.h#ifndef MODULE_A_H#define MODULE_A

26、_Hextern “C“ int foo( int x, int y );#endif在 模 块 B 的 实 现 文 件 中 仍 然 调 用 foo( 2,3 ), 其 结 果 是 :( 1) 模 块 A 编 译 生 成 foo 的 目 标 代 码 时 , 没 有 对 其 名 字 进 行 特 殊 处 理 ,采 用 了 C 语 言 的 方 式 ;( 2) 连 接 器 在 为 模 块 B 的 目 标 代 码 寻 找 foo(2,3)调 用 时 , 寻 找 的 是 未 经修 改 的 符 号 名 _foo。如 果 在 模 块 A 中 函 数 声 明 了 foo 为 extern “C“类 型 , 而

27、模 块 B 中 包 含 的是 extern int foo( int x, int y ) , 则 模 块 B 找 不 到 模 块 A 中 的 函 数 ; 反 之 亦 然 。所 以 , 可 以 用 一 句 话 概 括 extern “C”这 个 声 明 的 真 实 目 的 ( 任 何 语 言 中 的任 何 语 法 特 性 的 诞 生 都 不 是 随 意 而 为 的 , 来 源 于 真 实 世 界 的 需 求 驱 动 。 我 们 在思 考 问 题 时 , 不 能 只 停 留 在 这 个 语 言 是 怎 么 做 的 , 还 要 问 一 问 它 为 什 么 要 这 么做 , 动 机 是 什 么 ,

28、这 样 我 们 可 以 更 深 入 地 理 解 许 多 问 题 ) :实 现 C+与 C 及 其 它 语 言 的 混 合 编 程 。明 白 了 C+中 extern “C“的 设 立 动 机 , 我 们 下 面 来 具 体 分 析 extern “C“通常 的 使 用 技 巧 。4.extern “C“的 惯 用 法( 1) 在 C+中 引 用 C 语 言 中 的 函 数 和 变 量 , 在 包 含 C 语 言 头 文 件 ( 假设 为 cExample.h) 时 , 需 进 行 下 列 处 理 :extern “C“#include “cExample.h“而 在 C 语 言 的 头 文 件

29、 中 , 对 其 外 部 函 数 只 能 指 定 为 extern 类 型 , C 语 言中 不 支 持 extern “C“声 明 , 在 .c 文 件 中 包 含 了 extern “C“时 会 出 现 编 译 语 法 错误 。笔 者 编 写 的 C+引 用 C 函 数 例 子 工 程 中 包 含 的 三 个 文 件 的 源 代 码 如 下 :/* c 语 言 头 文 件 : cExample.h */#ifndef C_EXAMPLE_H#define C_EXAMPLE_Hextern int add(int x,int y);#endif/* c 语 言 实 现 文 件 : cExa

30、mple.c */#include “cExample.h“int add( int x, int y )return x + y;/ c+实 现 文 件 , 调 用 add: cppFile.cppextern “C“#include “cExample.h“int main(int argc, char* argv)add(2,3);return 0;如 果 C+调 用 一 个 C 语 言 编 写 的 .DLL 时 , 当 包 括 .DLL 的 头 文 件 或 声 明 接口 函 数 时 , 应 加 extern “C“ 。( 2) 在 C 中 引 用 C+语 言 中 的 函 数 和 变 量

31、 时 , C+的 头 文 件 需 添 加extern “C“, 但 是 在 C 语 言 中 不 能 直 接 引 用 声 明 了 extern “C“的 该 头 文 件 , 应该 仅 将 C 文 件 中 将 C+中 定 义 的 extern “C“函 数 声 明 为 extern 类 型 。笔 者 编 写 的 C 引 用 C+函 数 例 子 工 程 中 包 含 的 三 个 文 件 的 源 代 码 如 下 :/C+头 文 件 cppExample.h#ifndef CPP_EXAMPLE_H#define CPP_EXAMPLE_Hextern “C“ int add( int x, int y );#endif/C+实 现 文 件 cppExample.cpp#include “cppExample.h“int add( int x, int y )return x + y;/* C 实 现 文 件 cFile.c/* 这 样 会 编 译 出 错 : #include “cExample.h“ */extern int add( int x, int y );int main( int argc, char* argv )add( 2, 3 );return 0;

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 企业管理 > 经营企划

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报