考虑以下示例: 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副本。
答案 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.c
,file1.c
和file2.c
构建到单个可执行文件中:
gcc -o test0 file1.c file2.c Header.c
然后在整个程序中只存在data
的单个实例,因此file1.c
中的代码对file2.c
中的代码所做的任何更改都将由file1.c
中的代码获取-versa。
如果您想保证,file2.c
和data
正在处理get
的单独实例,无论是否'重新链接到相同的可执行文件,然后正确的方法是更改edit
和MyStruct
函数,以便他们将// 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.c
和file2.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
如果链接器在多个转换单元(文件)中看到相同的文件范围对象名称声明,它将尝试将这些多个声明映射到单个对象实例。如果我们在static
和file1.c
中声明file2.c
而没有 data
关键字,那么链接器只会创建{{1}的单个实例我们将回到我们开始的地方 1 。
static
关键字告诉编译器不要将对象名称导出到链接器;它有效地使data
的实例“私有”到源文件。因此,当file1.c
和file2.c
链接到同一个可执行文件时,它们每个都有自己的“私有”data
实例,因此代码对data
进行了更改file1.c
中的代码无法显示file2.c
中的代码,反之亦然。
请注意,只要有可能, 就不希望在文件范围内创建对象。通常,函数应使用参数和返回值进行通信;除非绝对必要,否则他们不应该通过全球价值来分享国家。
<小时/>
答案 2 :(得分:0)
您不能为同一参考中的每个保留不同的值。为此,您可以定义结构数组,如:
int n = 2;
My_Struct structsOfMine[n];
答案 3 :(得分:0)
假设你在file1.c和file2.c中都有以下两行,你将获得99。
♯包含“Header.h”
extern My_Struct数据;
答案 4 :(得分:0)
如果file1.c
和file2.c
仅包含标题,那么当您将它们编译为目标文件并将它们链接以生成程序时,由于未解析的符号get
而导致它们失败, edit
。
如果将两个对象对象文件与Header.c
的转换链接到一个程序中,则它们共享一个名为data
的对象的单个实例,该实例由整个单个外部定义提供程序:定义My_Struct data={0};
。 Header.h
文件仅声明类型My_Struct
。
您可以将file1.c
与Header.c
关联到一个程序中,然后将file1.c
和Header.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
存储类说明符,edit
,get
和static
的这些定义具有内部链接,使得它们可以从其他翻译单元中隐藏。