使用extern关键字

时间:2012-12-22 16:55:20

标签: c gcc

我在一个文件中定义了一个变量,并使用extern关键字在另一个文件中声明了它。但我已用不同的数据类型声明它..

file1.c  
    char i;
    main()  
    {   
        foo();  
    }

file2.c

void foo()  
{  
    extern int i;  
    i = 130;  
    printf("%d", i);  
}

在上面的程序中,内存在main函数中为'i'分配为char数据类型。 执行后的答案应该是否定的(-127)。但它打印130.无论在foo()函数中分配给'i'的值是什么,不仅打印130。

7 个答案:

答案 0 :(得分:6)

“我在一个文件中定义了一个变量,并使用extern关键字”在另一个文件中声明了它。这不是真的。 char imain int ifoo中的char i无任何关联。

您在main内定义为main的变量没有链接。除了extern int i;函数之外,它不能从程序中的任何其他位置引用。在C中根本没办法做到这一点。它是一个局部变量。

foo内的char i;声明与main的本地i变量完全无关。它声明了一个完全不同的独立变量extern int ifoo中的i声明引用了具有外部链接的变量i,即指向全局变量i。您仍然需要在某处定义 i。必须在文件范围定义int i(因为这是使用外部链接定义变量的唯一方法)。如果你没有定义它,你的程序将无法编译。

具有外部链接的实体和没有链接的实体生活在完全独立的世界中。他们对彼此一无所知,从不互相干涉或互相干涉。

正如我上面所说,您在问题中发布的代码根本无法编译。链接器将告诉您忘记使用foo中使用的外部链接定义int i变量。然而,你的帖子表明代码编译成功。这只是意味着您发布的代码不准确或不完整。如果代码是准确的,那么代码中的其他地方也定义了全局变量foo。这是char i使用的全局变量,而main中的char i与任何内容完全无关。


编辑后,你现在拥有的是一个无效的程序,尽管原因不同。您在file1.c中定义了一个全局变量file2.c,然后您尝试在file2.c中链接到该变量i。但是,在int中,您通过告诉编译器全局变量char的类型为char来欺骗编译器,而实际上它的类型为130

此类程序的行为未定义。在声明具有外部链接的实体时使用不一致类型是非法的。如果在一个转换单元中声明了130类型的全局变量,则不允许在其他转换单元中声明与其他任何类型相同的全局变量。

某些编译器可能会捕获您的错误并拒绝编译该程序。但是,大多数C编译器都不会捕获此错误。相反,它会产生一个导致未定义行为的程序。这就是你的情况。在您将-127分配给变量后打印{{1}}时,未定义的行为恰好表现为自己的情况。为什么你突然觉得奇怪我不知道。为什么你期望它打印{{1}}我也不知道。

答案 1 :(得分:2)

分析 - 代码有什么问题

在您的代码中:

file1.c中

int main(void)  
{  
    char i;   // Warning: unused variable
    foo();  
}

file2.c中

void foo(void)  
{  
    extern int i;  
    i = 130;  
    printf("%d", i);  
}

i中的变量main()i中提到的foo()完全无关。 i中的变量main()严格属于main()的本地变量,不会传递给foo(); foo()无法看到i

在带有GCC 4.7.1的Mac OS X 10.7.5上,文件单独编译,但它们无法链接,因为没有变量i的定义。

如果我们将变量i移到main()之外,那么程序会链接:

file2.h

extern void foo(void);

file1.c中

#include <stdio.h>
#include "file2.h"

char i = 'A';

int main(void)
{
    printf("i = %d (%c)\n", i, i);
    foo();
    printf("i = %d (%c)\n", i, i);
    return(0);
}

file2.c中

#include "file2.h"
#include <stdio.h>

void foo(void)
{
    extern int i;
    i = 130;
    printf("i = %d (%c)\n", i, i);
}

现在链接并运行,但它会调用未定义的行为。链接器没有注意到i的大小应该是sizeof(int),而是sizeof(char)。但是,foo()中的赋值会超出已分配的i的范围。它不会对这个程序造成明显的伤害,但那是纯粹的运气。

当我在小端机器(Intel x86 / 64)上运行时,输出为:

i = 65 (A)
i = 130 (?)
i = -126 (?)

当我在大端机器(SPARC)上运行时,我得到了不同的结果(这并不令人惊讶;行为未定义,因此任何结果都有效):

i = 65 (A)
i = 130 (?)
i = 0 ()

4字节整数的最高有效字节写在1字节字符的唯一字节上,因此在第3行输出中为零。还要注意,幸运的是,角色被分配了一个4字节倍数的地址;这不能得到保证,int的地址错位会导致总线错误(尽管实验表明,即使i被强制转换为奇数地址,它也不会发生在SPARC上; I&I #39;我不确定发生了什么。)

综合 - 正确处理extern

处理此问题的正确方法是避免在任何源文件中编写extern int i;等外部变量声明;这样的声明应该只出现在需要使用变量的地方使用的标题中。

file2.h

extern char i;
extern void foo(void);

随着这种变化(以及相应的extern int i;被删除),输出是自洽的:

i = 65 (A)
i = -126 (?)
i = -126 (?)

请注意标题的作用,使file1.cfile2.c保持一致。它确保file1.c中的定义与file2.h中的声明匹配,file2.hfile2.c的使用确保其中使用的声明也是正确的。另请参阅What are extern variables in C

答案 2 :(得分:1)

foo中的extern声明与该本地char变量完全没有关系。

据我所知,extern可以放在变量声明之前。 也许这有点令人困惑。 这样做可以告诉编译器您希望在不同的.c文件之间共享变量。 但是,必须有一个.c文件,其中声明变量而前面没有extern

答案 3 :(得分:0)

注意:您的示例无论如何都不会起作用,因为extern对局部变量没有影响。

extern是一种告诉编译器你正在使用的变量在另一个目标文件中实现的方法,你保证在链接时链接。它通常用在涉及全局变量的库头文件中,以告诉编译器该变量实际上是在库文件中实现的。

例: file1.c中:

int thing;
int bork(int val) {
  return thing += val
}

file2.c中

extern int thing
int main() {
  thing + 2;
}

答案 4 :(得分:0)

如果i在一个文件中声明为global variable,则只有外部链接。

链接器仅查找将在链接时解析的符号i,但链接器不会解析类型,因此当Undefined Behavior具有不同类型和定义时,我认为它是i作为一个文件中的全局变量,extern声明在其他文件中完成。

答案 5 :(得分:-1)

在file1.c中,i全局定义(并隐式声明)。

char i;
main()  
{    
    foo();  
}

file2.c extern与变量i一起使用,它只声明未定义。

extern char i; 
void foo()  
{  
   printf("%d", i);  
}

答案 6 :(得分:-1)

您对编译器的i大小有所了解。编译器按照你在foo中告诉它的内容,所以你得到整数表示。不幸的是,它不会真正可靠地工作,因为你还写了超出char i;

的三个字节

如果您在致电char j = 11;后添加j,然后在主号码中打印foo();,则会注意到j不再是11。

对编译器说谎是一个坏主意。你迟早会被发现!