我有这段代码,并且我注意到,当我定义我的arithmethicStruct arithmethicArray[]
数组而没有static
属性时,会出现多个定义的编译错误。使用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;
}
}
答案 0 :(得分:1)
您得到的错误不完全是编译错误,而是链接错误。
这意味着每个翻译单元(即包含所有.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文件中
如果此文件包含在许多翻译单元中,则每次翻译
单元对此变量具有 own 定义(因此,不同的地址
在示例中报告)。
答案 1 :(得分:0)
静态变量声明告诉编译器不要将此变量放在对象符号表中,这允许在不同的cpp文件中包含多个具有相同名称的变量。这意味着这些变量对其他cpp文件不可见。 在您的情况下,您声明并初始化.cpp中包含的.h中的变量,这将生成两个具有相同名称的不同变量,使编译器失败。当您放置静态时,编译器还会创建两个不同的变量,但通过不生成符号来隐藏它们。 为了更正.cpp文件中的声明变量,并使用函数从其他cpp文件中检索值。
答案 2 :(得分:0)
为使事情变得简单,您的问题可以简化为以下内容:
现在有一个名为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++; }
当两个源代码文件被每个编译时,还没有问题。当编译器尝试将两个目标文件链接在一起成为 ONE 可执行文件时,会发生此问题。具有相同名称a
的变量定义了两次。
大写的情况下,如果我们用a
修改header.h
文件中static
的定义:
// 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
具有相同的名称,它们也完全是 Different 。
我想您打算在头文件中使用全局变量。我认为最好的选择是使用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
,并且在两个目标文件时找到定义链接在一起。
在您认为有问题的地方出现语法错误:您需要=
。
arithmethicStruct arithmethicArray[] = {
{sum, '+'},
{diff, '-'},
{mult, '*'},
{div, '/'}
};
最好用自己的宏将内容包装在每个头文件中,如下所示:
#ifndef _HEADER_H_
#define _HEADER_H_
extern int a;
#endif
我在这里使用_HEADER_H_
,但是宏名称可以是任意的。如果多个头文件以复杂的方式相互包含,将非常有用。