繁体   English   中英

extern关键字的工作

[英]working of extern keyword

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

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

文件2.c

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

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

“我已经在一个文件中定义了一个变量,并使用extern关键字在另一个文件中声明了它” 这不是真的。 char imainfooint i没有任何关系。

您在main内部定义为char i的变量没有链接。 除该main功能外,不能从程序的任何其他位置使用名称进行引用。 用C语言根本无法做到这一点。它是一个局部变量。

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

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

如前所述,您在问题中发布的代码根本无法编译。 链接器将告诉您,您忘记了使用foo声明的外部链接定义int i变量。 但是,您的帖子表明代码已成功编译。 这仅表示您发布的代码不正确或不完整。 如果代码是正确的,那么在代码的其他地方,您还定义了一个全局变量int i 这是您的foo使用的全局变量,而mainchar i绝对与之无关。


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

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

一些编译器可能会捕获您的错误,并拒绝编译该程序。 大多数C编译器不会捕获此错误。 而是会产生一个导致未定义行为的程序。 这就是您遇到的情况。 在您的情况下,在为变量分配130之后,通过打印130出现了未定义的行为。 为什么你突然觉得奇怪,我不知道。 为什么您希望它打印-127我也不知道。

分析-代码有什么问题

在您的代码中:

文件1.c

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

文件2.c

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

main()的变量ifoo()引用的i完全无关。 main()的变量i严格来说是main()局部变量,不会传递给foo() foo()无法看到i

在带有GCC 4.7.1的Mac OS X 10.7.5上,文件分别编译,但是由于没有变量i定义,因此无法链接。

如果将变量i移动到main() ,则程序链接:

文件2.h

extern void foo(void);

文件1.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);
}

文件2.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 (?)

当我在big-endian计算机(SPARC)上运行它时,得到了不同的结果(这并不奇怪;行为是不确定的,因此任何结果都是有效的):

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

4字节整数的最高有效字节被写入1字节字符的唯一字节,因此第三行输出中的零。 还要注意,幸运的是为该字符分配了一个4字节倍数的地址。 那是不能保证的,并且int地址未对齐会导致总线错误(尽管实验表明即使i被迫使用奇数地址也不会在SPARC上发生这种情况;我不确定这是怎么回事)。

综合— extern正确处理

解决此问题的正确方法是避免编写外部变量声明,例如extern int i; 在任何源文件中; 此类声明应仅出现在需要使用变量的所有位置使用的标头中。

文件2.h

extern char i;
extern void foo(void);

进行适当的更改(以及相应的extern int i;将其删除),则输出是自洽的:

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

请注意,标头在保持file1.cfile2.c彼此file1.c方面的作用。 它确保file1.c中的定义与file2.h中的声明匹配,并且file2.cfile2.h的使用也确保那里使用的声明也是正确的。 另请参见什么是C中的extern变量

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

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

注意:您的示例仍然无法正常工作,因为extern对局部变量没有影响。

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

例如:file1.c:

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

文件2.c

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

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

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

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

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

file2.c extern与变量i ,仅声明未定义。

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

您正在向编译器撒谎关于i的大小。 编译器将按照您在foo中告诉您的内容进行操作,因此您将获得整数表示形式。 不幸的是,它不能真正可靠地工作,因为您还已经在char i;之外写入了三个字节char i;

如果添加char j = 11; 然后在调用foo();之后在main中打印j foo(); ,您会注意到j不再是11。

欺骗编译器是一个坏主意。 您迟早会被发现的!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM