在C中的多个源文件中包含头文件

时间:2016-10-05 13:47:36

标签: c

考虑以下示例: Header.h:

typedef struct
{
    int value;
}My_Struct;

void edit(int num);
int get();

Header.c

My_Struct data={0};

void edit(int num)
{
      data.value = num;
}
int get()
{
     Return data.value;
}

如果两个文件file1.c和file2.c都包含header.h,而file1.c调用edit函数将值更改为99,那么从file2.c调用的get函数是返回99还是初始化为0?

我希望它会返回0并且每个文件都有自己的My_Struct副本。

5 个答案:

答案 0 :(得分:1)

假设file1.c和file2.c与header.c链接到一个可执行文件中,它们将引用相同的变量。因此,如果从file1.c中的函数调用值为99的edit,则从file2.c中的函数调用get将返回99.

唯一可以获得不同结果的方法是,如果要将My_Struct的实例传递给这些函数,那么每个调用函数都可以使用不同的实例。

另一方面,如果file1.c和file2.c分别链接到单独的可执行文件中,那么每个(实际上每个运行的实例)都将拥有自己的data副本。

答案 1 :(得分:1)

问题不在于Header.h文件;问题出在Header.c文件中:

My_Struct data={0};

void edit(int num)
{
      data.value = num;
}
int get()
{
     Return data.value;
}

您已创建data变量的单个实例,并将其设为全局(意味着可以通过其他翻译单元的名称访问)。假设您将Header.cfile1.cfile2.c构建到单个可执行文件中:

gcc -o test0 file1.c file2.c Header.c

然后在整个程序中只存在data的单个实例,因此file1.c中的代码对file2.c中的代码所做的任何更改都将由file1.c中的代码获取-versa。

如果您想保证file2.cdata正在处理get 的单独实例,无论是否'重新链接到相同的可执行文件,然后正确的方法是更改​​editMyStruct函数,以便他们将// Header.h typedef struct { int value; } MyStruct; void edit( int num, MyStruct *data); int get( MyStruct data); // Header.c void edit( int num, MyStruct *data) { data->value = num; } int get( MyStruct data) { return data.value; } 作为参数:

data

然后在file1.cfile2.c内创建// file1.c #include "Header.h" static MyStruct data = {0}; // static instance, not visible outside of file1.c, // exists over lifetime of program void foo( void ) { ... edit( 99, &data ); ... } void bar( void ) { ... printf( "data contains %d\n", get( data ) ); ... } // file2.c #include "Header.h" static MyStruct data = {0}; // Another static instance, not visible outside of file2.c // exists over lifetime of program void bletch( void ) { ... edit( 42, &data ); ... } void blurga( void ) { ... printf( "data contains %d\n", get( data ) ); ... } 的单独实例:

data

如果链接器在多个转换单元(文件)中看到相同的文件范围对象名称声明,它将尝试将这些多个声明映射到单个对象实例。如果我们在staticfile1.c中声明file2.c 而没有 data关键字,那么链接器只会创建{{1}的单个实例我们将回到我们开始的地方 1

static关键字告诉编译器不要将对象名称导出到链接器;它有效地使data的实例“私有”到源文件。因此,当file1.cfile2.c链接到同一个可执行文件时,它们每个都有自己的“私有”data实例,因此代码对data进行了更改file1.c中的代码无法显示file2.c中的代码,反之亦然。

请注意,只要有可能, 就不希望在文件范围内创建对象。通常,函数应使用参数和返回值进行通信;除非绝对必要,否则他们不应该通过全球价值来分享国家。

<小时/>

  1. 实际上,我不确定当我们有多个定义声明时会发生什么,就像我们在这种情况下那样(初始化器的存在使它成为一个定义声明)。我想说链接器会抱怨,但我不是100%肯定。

答案 2 :(得分:0)

您不能为同一参考中的每个保留不同的值。为此,您可以定义结构数组,如:

int n = 2;
My_Struct structsOfMine[n];

答案 3 :(得分:0)

假设你在file1.c和file2.c中都有以下两行,你将获得99。

♯包含“Header.h”

extern My_Struct数据;

答案 4 :(得分:0)

如果file1.cfile2.c仅包含标题,那么当您将它们编译为目标文件并将它们链接以生成程序时,由于未解析的符号get而导致它们失败, edit

如果将两个对象对象文件与Header.c的转换链接到一个程序中,则它们共享一个名为data的对象的单个实例,该实例由整个单个外部定义提供程序:定义My_Struct data={0};Header.h文件仅声明类型My_Struct

您可以将file1.cHeader.c关联到一个程序中,然后将file1.cHeader.c关联到另一个程序中。然后这两个单元在不同的程序中,并且不共享相同的data

如果您想在一个程序中使用效果,可以像这样更改Header.h

typedef struct
{
    int value;
}My_Struct;

static My_Struct data={0};

static void edit(int num)
{
      data.value = num;
}

static int get()
{
     Return data.value;
}

现在,包含此标头的每个翻译单元都有自己的data外部定义和两个引用相同data的函数。由于data存储类说明符,editgetstatic的这些定义具有内部链接,使得它们可以从其他翻译单元中隐藏。