当将变量定义为静态变量时,为什么要多次定义它?

问题描述 投票:2回答:3

我有这段代码,我注意到当我在没有静态属性的情况下定义“ arithmethicStruct arithmethicArray []”数组时,会出现多个定义的编译错误。使用static属性,它将进行编译。我可以解释一下这种全局静态变量在编译时的行为,为什么会抑制该错误,以及正常的全局变量之间的区别?使用多个文件时,我有些困惑。澄清“外部”也将有所帮助。在此先感谢!

//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;

  }
}
c++ static definition
3个回答
1
投票

您得到的错误不完全是编译错误,而是链接错误。这意味着每个translation unit(即包含所有.h的.c文件)您单独编译被编译器认为是正确的,但是当它出现时链接它们(将生成的.o文件放在一起以生成可执行文件整个文件),linker会发现这些文件之间存在一些不一致之处目标文件(.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);
}

prog.c


#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定义(因此,不同的地址在示例中报告)。


0
投票

静态变量声明告诉编译器不要将此变量放在对象符号表中,这允许在不同的cpp文件中包含多个具有相同名称的变量。这意味着这些变量对其他cpp文件不可见。在您的情况下,您声明并初始化.cpp中包含的.h中的变量,这将生成两个具有相同名称的不同变量,使编译器失败。当您放置静态时,编译器还会创建两个不同的变量,但通过不生成符号来隐藏它们。为了更正此声明变量在.cpp文件中并使用函数从其他cpp文件中检索值。


0
投票

简单案例

为了使事情变得简单,您的问题可以简化为以下内容:

现在有一个名为header.h的头文件:

// header.h
int a = 1;

这里变量a定义

现在让我们将启动器代码命名为main.cpp

// main.cpp
#include "header.h"

int main() {return 0;}

[每当编译这些行时,C(C ++)编译器COPIESheader.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时,还没有问题。当编译器尝试将两个目标文件链接在一起到ONE可执行文件中时,就会出现问题。具有相同名称a的变量定义了两次。

为什么STATIC起作用

大写,如果我们用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有效,应如何使用

我想您打算在头文件中使用全局变量。我认为最好的选择是使用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_,但是宏名称可以是任意的。如果多个头文件以复杂的方式相互包含,则很有用。

© www.soinside.com 2019 - 2024. All rights reserved.