诡异的extern问题。

sunnyqboy 2009-07-30 06:45:46
今天本来想研究一下extern的具体作用,因为一直很模糊。写了一个测试程序。发现了一个奇怪的问题。
aa.c


int i;
main()
{
//extern int i;
printf("%d\n", i);
}


bb.c

int i = 10;


编译命令: cc –o aa aa.c bb.c
结果:10

使用全局变量i,和在main函数里面使用extern int i结果一样都是10。

请问为什么将i设为全局变量只要联合编译就自动extern了?

2、另外,我们经常在头文件中定义
#define EXTERN extern

EXTERN int i;来说明某一个变量被外部引用。

在某些文件中使用
#define EXTERN
然后在引入定义i的头文件的时候说明其不是从外部引用?(我这块比较混乱,谁能讲一下?)
但是按照上面程序测试的做法,岂不是所有的都是extern?这样#define EXTERN不就没有用了?我很郁闷,请大家指点。


...全文
899 11 打赏 收藏 转发到动态 举报
写回复
用AI写文章
11 条回复
切换为时间正序
请发表友善的回复…
发表回复
invail 2010-03-03
  • 打赏
  • 举报
回复
楼上各位,很好很强大
morris88 2009-08-01
  • 打赏
  • 举报
回复
nm基本用法命令

  nm用来列出目标文件的符号清单。下面是nm命令的格式:

  nm [-a|--debug-syms] [-g|--extern-only] [-B][-C|--demangle] [-D|--dynamic] [-s|--print-armap][-o|--print-file-name] [-n|--numeric-sort][-p|--no-sort] [-r|--reverse-sort] [--size-sort][-u|--undefined-only] [-l|--line-numbers] [--help][--version] [-t radix|--radix=radix][-P|--portability] [-f format|--format=format][--target=bfdname] [objfile...]

  如果没有为nm命令指出目标文件,则nm假定目标文件是a.out。下面列出该命令的任选项,大部分支持"-"开头的短格式和"—"开头的长格式。
-A、-o或--print-file-name:在找到的各个符号的名字前加上文件名,而不是在此文件的所有符号前只出现文件名一次。

例如nm libtest.a的输出如下:
CPThread.o:
00000068 T Main__8CPThreadPv
00000038 T Start__8CPThread
00000014 T _._8CPThread
00000000 T __8CPThread
00000000 ? __FRAME_BEGIN__
.......................................

则nm -A 的输出如下:
libtest.a:CPThread.o:00000068 T Main__8CPThreadPv
libtest.a:CPThread.o:00000038 T Start__8CPThread
libtest.a:CPThread.o:00000014 T _._8CPThread
libtest.a:CPThread.o:00000000 T __8CPThread
libtest.a:CPThread.o:00000000 ? __FRAME_BEGIN__
..................................................................
-a或--debug-syms:显示调试符号。
-B:等同于--format=bsd,用来兼容MIPS的nm。
-C或--demangle:将低级符号名解码(demangle)成用户级名字。这样可以使得C++函数名具有可读性。
-D或--dynamic:显示动态符号。该任选项仅对于动态目标(例如特定类型的共享库)有意义。
-f format:使用format格式输出。format可以选取bsd、sysv或posix,该选项在GNU的nm中有用。默认为bsd。
-g或--extern-only:仅显示外部符号。
-n、-v或--numeric-sort:按符号对应地址的顺序排序,而非按符号名的字符顺序。
-p或--no-sort:按目标文件中遇到的符号顺序显示,不排序。
-P或--portability:使用POSIX.2标准输出格式代替默认的输出格式。等同于使用任选项-f posix。
-s或--print-armap:当列出库中成员的符号时,包含索引。索引的内容包含:哪些模块包含哪些名字的映射。
-r或--reverse-sort:反转排序的顺序(例如,升序变为降序)。
--size-sort:按大小排列符号顺序。该大小是按照一个符号的值与它下一个符号的值进行计算的。
-t radix或--radix=radix:使用radix进制显示符号值。radix只能为"d"表示十进制、"o"表示八进制或"x"表示十六进制。
--target=bfdname:指定一个目标代码的格式,而非使用系统的默认格式。
-u或--undefined-only:仅显示没有定义的符号(那些外部符号)。
-l或--line-numbers:对每个符号,使用调试信息来试图找到文件名和行号。对于已定义的符号,查找符号地址的行号。对于未定义符号,查找指向符号重定位入口的行号。如果可以找到行号信息,显示在符号信息之后。
-V或--version:显示nm的版本号。
--help:显示nm的任选项。

http://dev.csdn.net/article/69/69405.shtm
sunnyqboy 2009-08-01
  • 打赏
  • 举报
回复
弱弱的问一下。nm是什么?
mymtom 2009-07-31
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 fox000002 的回复:]
其实用 nm 可以看看

C/C++ code// a.cint i;// 0000000000000004 C i// b.cint i=10;// 0000000000000000 D i


C/C++ code"C" The symbolis common.

Common symbols are uninitialized data. When linking, multiple
common symbols may appear with the same name. If the symbolis
defined anywhere, the common symbols are treatedas undefined references."D" The symbolisin the initialized data section.

如果改为

C/C++ code// a.cint i=10;// b.cint i=10;

那就会出现重复定义的错误了

[/Quote]
用nm可以帮助理解三种不同的形式的变量说明:

int x; /* C (Common) 声明(可能同时也定义)变量, 到了链接阶段, */
/* 1. 如果没在其他模块发现同名(D)符号,作为未初始化的全局变量, 放在BSS */
/* 2. 如果有在其他模块发现同名(D)符号,作为已初始化的全局变量, 放在数据段 */
int y = 1; /* D (Data) 定义变量,作为已初始化的全局变量, 放在数据段 */
extern int z;  /* U (Undefined), 声明变量, 到了链接阶段 */
/* 1. 如果在其他模块有且仅有一个类型(D)的同名符号,将此符号解析到此 */
/* (D)符号,作为已初始化的全局变量, 放在数据段 */
/* 2. 如果在其他模块有两个或更多的类型(D)的同名符号,*/
/* 报告“重复定义”,链接失败 */
/* 3. 如果在其他模块未发现类型(D)的同名符号,但是至少有一个类型(C) */
/* 的同名符号, 将此符号解析到此(C)符号上,
/* 作为未初始化的全局变量, 放在BSS */

简单来说,
int x; 可以在多个模块,
int x = 1; 只能在一个模块
extern int x; 可以在多个模块, 且至少还需要在其他模块有一个 int x; 或 int x = 1;
morris88 2009-07-31
  • 打赏
  • 举报
回复
楼上各位,很好很强大
fox000002 2009-07-30
  • 打赏
  • 举报
回复
其实用 nm 可以看看

// a.c

int i; // 0000000000000004 C i

// b.c

int i = 10; // 0000000000000000 D i



 "C" The  symbol  is common. 

Common symbols are uninitialized data. When linking, multiple
common symbols may appear with the same name. If the symbol is
defined anywhere, the common symbols are treated as undefined references.

"D" The symbol is in the initialized data section.


如果改为

// a.c
int i = 10;

// b.c
int i = 10;


那就会出现重复定义的错误了
ShowMan 2009-07-30
  • 打赏
  • 举报
回复
这就是extern的用法, 把全局变量扩展到其他的c文件中。。
晨星 2009-07-30
  • 打赏
  • 举报
回复
总结一下吧:

extern int i;
——外部引用声明;声明引用一个名字为i的整形变量;

int i;
——全局变量定义;默认具有外部连接性,可从另一个文件中通过extern声明来引用。
注意做全局变量时它将被默认初始化成0。
(当然,在本文件内部,定义本身同时也是声明。不管是定义过还是仅仅声明过,都能使名字变得“可见”。)

extern int i = 10;
——注意带初始化式的extern,这个是定义,而不仅仅是声明。

static int i;
static int i = 10;
——静态变量定义。
static全局变量是具有内连接属性,另一个文件用extern引用也引用不到;注意第一个会被默认初始化成0。

const int i = 10;
——在C++语言中,const全局变量是个特例,它默认具有内部连接性的,而不像一般全局变量那样默认是外部连接性的。因此这种可以像宏一样直接扔到头件中,即使被多个文件include也不会造成符号重定义错误。
晨星 2009-07-30
  • 打赏
  • 举报
回复
(2)
按照你说的这种用法,在某些文件中,如果
#define EXTERN
那就是没有什么修饰了,单单一个:
int i;
此时它就成了变量定义,而非外部引用声明。
——毕竟总得有个地方有定义嘛,不能全是声明。否则连接器又要抱怨找不到符号了。
晨星 2009-07-30
  • 打赏
  • 举报
回复
(1)
全局变量默认就是extern的,只有明确指定为static才能成为static的。而且并不是说联合编译就变成extern了。即使你不联合编译,它也是extern的,只不是会发生一些连接时的错误,告诉你某个符号(extern的)找不到。

23,124

社区成员

发帖
与我相关
我的任务
社区描述
Linux/Unix社区 应用程序开发区
社区管理员
  • 应用程序开发区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧