1

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

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

文件2.c

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

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

4

7 回答 7

6

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

char i您在内部定义的变量main没有链接。main除了该函数之外,程序中的任何其他地方都不能通过名称引用它。在 C 中根本没有办法做到这一点。它是一个局部变量。

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

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

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


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

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

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

于 2012-12-22T17:04:53.077 回答
2

分析——代码有什么问题

在您的代码中:

文件1.c

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

文件2.c

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

变量iin与 in 所引用的变量main()完全无关。变量in是严格本地的,不会传递给; 无法看到。ifoo()imain()main()foo()foo()i

在带有 GCC 4.7.1 的 Mac OS X 10.7.5 上,文件单独编译,但无法链接,因为没有 variable 的定义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分配的范围。它不会在该程序中造成明显的损坏,但这纯粹是运气。

当我在 little-endian 机器(Intel x86/64)上运行它时,输出为:

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

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

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

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

合成——正确处理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相互一致方面的作用。它确保 in 中的定义与 infile1.c中的声明相匹配file2.h,并且 in 的使用file2.h确保file2.c在那里使用的声明也是正确的。另请参阅什么是externC 中的变量

于 2012-12-22T17:20:44.747 回答
1

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

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

于 2012-12-22T16:59:14.550 回答
0

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

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

例如:file1.c:

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

文件2.c

extern int thing
int main() {
  thing + 2;
}
于 2012-12-22T17:05:03.827 回答
0

i在一个文件中声明的情况下,global variable只有它具有外部链接。

链接器只查找将在链接时解析的符号i,但链接器不解析类型,所以我认为它Undefined Behaviori不同类型的,并在一个文件中定义为全局变量,而 extern 声明在另一个文件中完成。

于 2012-12-22T17:05:51.477 回答
-1

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

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

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

extern char i; 
void foo()  
{  
   printf("%d", i);  
}
于 2012-12-22T17:04:34.073 回答
-1

你在向编译器撒谎关于i. 编译器按照您在 foo 中告诉它的内容运行,因此您将获得整数表示。不幸的是,它不会真正可靠地工作,因为您还写了三个字节超出您的char i;

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

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

于 2012-12-22T17:06:35.057 回答