0

这里我有两个文件。在第一个文件中,我在文件范围内声明externdemo1.cexterndemo2.c初始化了一个字符数组arr。但是我在第二个文件中声明了它,externdemo2.c没有extern关键字,并在函数中使用它display()。这是我由此引起的困惑。请回答这三个:

 //File No.1--externdemo1.c

#include<stdio.h>
#include "externdemo2.c"

extern int display();
char arr[3]={'3','4','7'};
//extern char arr[3]={'3','4','7'};

//extern int main()
int main()
{
    printf("%d",display());
}


//File No.2--externdemo2.c

char arr[3];

int display()
{
    return sizeof(arr);
}

1)为什么即使我在arr没有extern关键字 in的情况下声明程序也能正常编译externdemo2.c?我读过函数的默认链接是外部的,但我不确定变量是否也是如此。我只知道全局变量有extern存储班级。

2)extern storage class和之间的严格区别是什么extern linkage。我非常需要澄清这一点。在第一个文件中,我定义了数组arr,我没有使用关键字extern,但我知道它默认有extern storage类。但是在第二个文件,是不是有任何默认extern的,存储类或链接,关于全局变量arr,即在externdemo2.c

3)检查第一个文件中注释掉的行。只是externdemo1.c为了测试它,我使用了该行extern char arr[3]={'3','4','7'};。但是它给出了错误。'arr' initialized and declared 'extern'这个错误是什么意思?我还提到了一个注释行extern int main(),但它工作正常,没有错误或警告。那么为什么我们可以使用extern一个函数,即使一个函数是extern默认的,而不是一个变量,就像arr这里?

请花一些时间来帮我解决这个问题。它会清除我对整个事情的大部分挥之不去的疑虑。如果你能回答所有 3 位1)、2) 和 3),extern这将是巨大的帮助。Especially 3) is eating my brains out

4

3 回答 3

2

主要问题

  1. 基本上,因为您externdemo2.c在文件中包含了externdemo1.c.

  2. 这是个大问题。因为没有初始化器,所以char arr[3];in行externdemo2.c生成了数组的暂定定义arr。当遇到带有初始化的实际定义时,暂定定义不再是暂定的——但它也不是重复的定义。

    关于extern storage classvs extern linkage...Linkage 是指是否可以从定义它的源文件之外看到符号。一个符号extern linkage可以通过名称被其他适当声明它的源文件访问。就它的定义而言,extern storage class意味着“存储在函数范围之外”,因此独立于任何函数。用 exern 存储类定义的变量可能有也可能没有外部链接。

    因为没有用关键字定义static,所以数组arr有外部链接;它是一个全局变量。

  3. 在未注释掉的注释行中,您有一个数组的两个定义,这是不允许的。

我观察到你必须编译只是externdemo1.c为了创建一个程序——编译器包含来自的代码,externdemo2.c因为它是直接包含的。您可以从externdemo2.c. 但是,您不能通过链接两者的目标文件来创建程序,externdemo1.c因为externdemo2.c这会导致函数的多个定义display()

辅助问题

我已将这两个文件放在[同一目录] 中。如果我没有在第一个文件中包含第二个文件,那么当我编译第一个文件时,它会undefined reference显示错误。由于我在第一个文件中使用extern了该函数,即使我不包含第二个文件,链接器是否应该链接到它?或者链接器仅在默认文件夹中查找它?

这里有几个混淆。让我们尝试一次处理一个。

链接

链接器(通常由编译器启动)将链接在其命令行上指定的目标文件和库。如果你想要两个目标文件,调用它们externdemo1.objexterndemo2.obj,链接在一起,你必须告诉链接器(通过 IDE 中的构建系统)它需要处理这两个目标文件 - 以及它不拾取的任何库默认。(标准 C 库以及特定于平台的扩展通常会自动获取,除非您竭尽全力阻止这种情况发生。)

链接器没有义务花任何时间寻找可能满足引用的杂散目标文件;实际上,它应该只链接那些被告知链接的目标文件和库,而不是随心所欲地添加其他文件和库。关于库有一些注意事项(如果链接器被告知要链接的库之一具有内置到其他库的引用,则链接器可能会添加命令行中未提及的一些库),但链接器不会添加额外的目标文件混合。

带有模板实例化的 C++ 可能会被认为有点不同,但实际上它遵循的规则大致相同。

源代码

您应该有一个标题,externdemo.h其中包含:

#ifndef EXTERNDEMO_H_INCLUDED
#define EXTERNDEMO_H_INCLUDED

extern int display(void);

extern char arr[3];  // Or extern char arr[]; -- but NOT extern char *arr;

#endif /* EXTERNDEMO_H_INCLUDED */

然后,您应该修改源文件以包含标头:

//File No.1--externdemo1.c

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

char arr[3] = { '3', '4', '7' };

int main(void)
{
    printf("%d\n", display());
    return 0;
}

和:

//File No.2--externdemo2.c

#include "externdemo.h"

int display(void)
{
    return sizeof(arr);
}

这里唯一棘手的问题是“externdemo2.c真的知道 的大小arr吗?” 答案是“是”(至少在 Mac OS X 10.8.3 上使用 GCC 4.7.1)。但是,如果标头中的 extern 声明不包含大小 ( extern char arr[];),则会出现编译错误,例如:

externdemo2.c: In function ‘display’:
externdemo2.c:7:18: error: invalid application of ‘sizeof’ to incomplete type ‘char[]’
externdemo2.c:8:1: warning: control reaches end of non-void function [-Wreturn-type]
于 2013-05-07T03:21:52.813 回答
0

你的程序看起来有点错误。对我来说,#include "externdemo2.c" 行似乎无效。

以下是我所做的更正,它有效。

    //File No.1--externdemo1.c

    #include <stdio.h>

    extern char arr[3];

    extern int display();

    int main()
    {
        printf("%d", arr[0]);
        printf("%d",display());
    }

    //File No.2--externdemo2.c


    char arr[3]={'3','4','7'};

    int display()
    {
        return sizeof(arr);
    }

请点击以下链接以获得更好的理解:

于 2013-05-07T03:24:03.340 回答
0

如图所示使用#include将使两者仅作为一个文件。您可以使用 flag 检查中间文件-E,如下所示:

gcc -E externdemo1.c
于 2013-05-07T03:30:56.893 回答