我有这段代码,我注意到当我在没有static
属性的情况下定义我的arithmethicStruct arithmethicArray[]
数组时,我得到多个定义的编译错误。使用static
属性进行编译。我能否解释一下这个全局静态变量在编译时的行为方式,为什么会抑制错误,以及普通全局变量之间的区别?使用多个文件时我有点困惑。澄清extern
也会有所帮助。
//main.cpp
#include "functionPointers.h"
#include <iostream>
int main() {
int a = getNumber();
char op = getOperatrion();
int b = getNumber();
arithmethicFunc func = getArithmeticFunct(op);
std::cout << a << " " << op << " " << b << " = " << func(a, b) << "n";
return 0;
}
//functionPointers.h
int getNumber();
char getOperatrion();
typedef int (*arithmethicFunc)(int, int&);
int sum(int a, int&b);
int diff(int a, int&b);
int mult(int a, int&b);
int div(int a, int&b);
arithmethicFunc getArithmeticFunct(char op);
struct arithmethicStruct {
arithmethicFunc func;
char op;
};
//here is the question, with static it compiles
arithmethicStruct arithmethicArray[] {
{sum, '+'},
{diff, '-'},
{mult, '*'},
{div, '/'}
};
//functionpointers.cpp
#include "functionPointers.h"
#include <iostream>
int getNumber() {
int a;
std::cout << "Enter a number : ";
std::cin >> a;
return a;
}
char getOperatrion() {
char a;
std::cout << "Enter an operation (+, -, * , /) : ";
std::cin >> a;
return a;
}
int sum(int a, int&b) { return a+b; }
int diff(int a, int&b) { return a-b; }
int mult(int a, int&b) { return a*b; }
int div(int a, int&b) { return a/b; }
arithmethicFunc getArithmeticFunct(char op) {
switch (op) {
case '+': return sum;
case '-': return diff;
case '*': return mult;
case '/': return div;
}
}
简单案例
为了方便起见,您的问题可以简化如下:
现在有一个名为header.h
的头文件:
// header.h
int a = 1;
这里定义了一个变量a
。
现在让我们有代号为main.cpp
的启动器:
// main.cpp
#include "header.h"
int main() {return 0;}
每当编译这些行时,C(C++) 编译器都会将header.h
文件中的内容复制到以下内容中:
// main.cpp after pre-compile stage
int a = 1; // <- this line is from header.h
int main() {return 0;}
该定义在这种情况下有效,因为没有其他源文件。让我们通过添加新的源代码文件来使其更加复杂header_impl.cpp
// header_impl.cpp: original source code
#include "header.h"
int inc_a() { a++; }
然后扩展后,header_impl.cpp
变成:
// header_impl.cpp after pre-compile stage
int a = 1; // <- this line is from header.h
int inc_a() { a++; }
当两个源代码文件编译为EACH时,还没有问题。当编译器尝试将两个目标文件链接到一个可执行文件中时,会出现此问题。具有相同名称的变量a
定义两次。
为什么静态有效
在大写的情况下,如果我们使用static
修改文件中header.h
a
的定义:
// header.h
static int a = 1;
在预编译阶段之后,其他两个文件变为:
// main.cpp after pre-compile stage
static int a = 1; // <- this line is from header.h
int main() {return 0;}
和:
// header_impl.cpp after pre-compile stage
static int a = 1; // <- this line is from header.h
int inc_a() { a++; }
那么为什么编译器不再抛出链接错误呢?答案是,static
意味着此变量或函数对其他源代码文件不可见。变量a
定义的作用域仅限于每个文件。也就是说,即使名称相同,两个a
也是完全不同的。
为什么 EXTERN 工作以及如何使用它
我猜你打算在头文件中使用全局变量。我认为最好的选择是使用extern
关键字。下面列出了三个源代码文件:
// header.h
extern int a; // only declaration
// main.cpp
#include "header.h"
int main() {return 0;}
// header_impl.cpp
#include "header.h"
int a = 1; // definition here
int inc_a() { a++; }
经过预编译阶段,这两个源代码文件(头文件不编译为目标文件)如下所示:
// main.cpp after pre-compile stage
extern int a; // <- this line is from header.h
int main() {return 0;}
// header_impl.cpp after pre-compile stage
extern int a; // <- this line is from header.h
int a = 1; // definition here
int inc_a() { a++; }
这里编译器知道编译主源码文件时有一个外部变量a
,当两个目标文件链接在一起时,就找到了定义。
PS
1.一点语法错误
在你认为有问题的地方有一个语法错误:你需要一个=
。
arithmethicStruct arithmethicArray[] = {
{sum, '+'},
{diff, '-'},
{mult, '*'},
{div, '/'}
};
2. 使用宏防止多个包含
最好使用自己的宏包装每个头文件中的内容,如下所示:
#ifndef _HEADER_H_
#define _HEADER_H_
extern int a;
#endif
这里我使用_HEADER_H_
,但宏名称可能是任意的。 如果多个头文件以复杂的方式相互包含,这将非常有用。
您得到的错误不完全是编译错误,而是链接错误。
这意味着每个翻译单元(即包含所有.h的.c文件) 编译器认为您单独编译是正确的,但是当它来的时候 链接它们(将生成的 .o 文件放在一起以生成可执行文件 文件作为一个整体)链接器发现这些不同的不一致 目标文件(.o 文件)。
我将尝试在一个更简单的例子上重现它(在 C 中是通用的,因为它在 C 和 C++ 中相似)。
my_global.h
#ifndef MY_GLOBAL_H
#define MY_GLOBAL_H
static int my_global_with_static[]={10, 20, 30, 40};
extern int my_global_with_extern[4];
void
show_array(const char *title,
const int *array,
int count);
void
another_function(void);
#endif
my_global.c
#include "my_global.h"
#include <stdio.h>
int my_global_with_extern[4]={1, 2, 3, 4};
void
show_array(const char *title,
const int *array,
int count)
{
printf("%s:n", title);
printf(" at %p:", (void *)array);
for(int i=0; i<count; ++i)
{
printf(" %d", array[i]);
}
printf("n");
}
void
another_function(void)
{
show_array("my_global_with_static from another translation unit",
my_global_with_static, 4);
show_array("my_global_with_extern from another translation unit",
my_global_with_extern, 4);
}
程序
#include "my_global.h"
#include <stdio.h>
int
main(void)
{
show_array("my_global_with_static from main translation unit",
my_global_with_static, 4);
show_array("my_global_with_extern from main translation unit",
my_global_with_extern, 4);
another_function();
return 0;
}
运行此程序时,我得到此结果
my_global_with_static from main translation unit:
at 0x5630507e10a0: 10 20 30 40
my_global_with_extern from main translation unit:
at 0x5630507e1200: 1 2 3 4
my_global_with_static from another translation unit:
at 0x5630507e11c0: 10 20 30 40
my_global_with_extern from another translation unit:
at 0x5630507e1200: 1 2 3 4
我们可以看到my_global_with_extern
完全相同 从主翻译单元或从 另一个;事实上,不仅价值观相同,而且这个 数组在同一地址可见(在此运行期间0x5630507e1200)。
另一方面,my_global_with_static
看起来是一样的 在两个翻译单元中(值相同),但包含 实际上在两个不同的数组中,它们有自己的内存位置 在每个翻译单元中(在此运行中0x5630507e10a0和0x5630507e11c0)。
extern
关键字意味着我们只声明变量。
这是对编译器阅读这一行的承诺,在某处 否则,此变量被定义,因此我们可以表达一些使用 它(完全像函数声明:原型而不是代码)。
因此,在一个且唯一的翻译单元中,我们必须提供唯一的定义 这个变量(在这里my_global.c)。
另一方面,static
关键字意味着以下定义 应被视为当前翻译单元的本地翻译。
符号不会导出,因此不会与其他符号冲突 在另一个翻译单元中具有相同的名称。
结果是,如果此static
定义出现在 .h 文件中 如果此文件包含在许多翻译单元中,则每个翻译 单位对此变量有自己的定义(因此不同的地址 示例中报告)。
静态变量声明告诉编译器不要将此变量放在对象符号表中,这允许在不同的 CPP 文件中有多个具有相同名称的变量。 这意味着这些变量对其他 CPP 文件不可见。 在您的情况下,您在 .h 中声明并初始化变量,该变量包含在 to .cpp 这将生成两个具有相同名称的不同变量,使编译器失败。当你把静态时,编译器也会创建两个不同的变量,但通过不生成符号来隐藏它们。 为了更正此声明.cpp文件中的变量,并使用函数从其他CPP文件中检索值。