1

我想在标题中定义一个变量并能够在多个文件中使用它。例如,在a某处定义了变量,并且能够在 p1.cpp 和 p2.cpp 中使用/更改它

这是一个示例,其中包含我正在尝试做的 3 个简单文件。

// vars.hpp
#ifndef VARS_HPP
#define VARS_HPP

int a = 1;
float b = 2.2;
void double_vars();

#endif



// vars.cpp
#include "vars.hpp"
void double_vars () {
    a *= 2;
    b *= 2;
}

// vars_main.cpp
#include <cstdio>
#include "vars.hpp"
int main () {
    printf("a: %d; b: %f\n", a, b);
    double_vars();
    printf("a: %d; b: %f\n", a, b);
}

现在,编译上面的内容:

g++ -Wall -W -g vars.cpp vars_main.cpp -o vars && ./vars

给我以下错误:

/tmp/ccTPXrSe.o:(.data+0x0): multiple definition of `a'
/tmp/ccnc1vof.o:(.data+0x0): first defined here
/tmp/ccTPXrSe.o:(.data+0x4): multiple definition of `b'
/tmp/ccnc1vof.o:(.data+0x4): first defined here

有人可以向我解释为什么会这样吗?据我所知,我在头文件中有警卫,它应该只包含一次,所以不应该有多个声明

4

3 回答 3

4

在头文件中,这样写:

extern int a;
extern float b;

这将声明变量。

在其中一个 CPP 文件中,写入以下内容:

int a = 1;
float b = 2.2;

这将定义变量。将定义放在哪个 CPP 文件中并不重要,只要变量只定义一次即可。

于 2012-11-03T13:09:49.483 回答
4

首先,不要这样做。全局可变状态通常是一件坏事。

问题是,一旦链接器完成合并包含语句中的文件,它就会看到两次,一次在 main.cpp 中,一次在 vars_main.cpp 中:

int a = 1;
float b = 2.2;

如果您只将它放在头文件中,它会显示在多个翻译单元中,每个cpp包含您的头文件的文件一次。编译器不可能知道您在谈论哪个 a 和 b。

解决此问题的方法是extern在标头中声明它们:

extern int a;
extern float b;

这告诉编译器 a 和 b 是在某处定义的,不一定在这部分代码中。

然后,在您的一个 cpp 文件中,您将定义它们:

int a = 1;
float b = 2.2;

这样,就有一个明确定义的存储ab生存位置,编译器可以正确连接这些点。

于 2012-11-03T13:12:33.987 回答
1

Collin 已经描述了问题和解决方案,但我想强调这是一种多么糟糕的风格。

正如您从这个简单的练习中发现的那样,以这种方式使用全局变量通常是一件坏事。除了您已经发现的编译器问题之外,使用全局变量在函数之间共享状态会导致以下问题:

  • 它使函数本身更难在其他程序中重用,因为函数期望这些符号的存在;

  • 对一个函数的更改可能会导致共享该状态的其他函数出现问题(即,更改未本地化到该函数);

  • 代码变得更难调试,因为任何人都可以随时a修改b

理想情况下,函数应该只通过参数、返回值和异常(如果适用)进行通信;有时共享状态是正确的答案,但这种情况相对较少而且相差甚远。

由于您显然使用的是 C++ 编译器,因此我将以 C++ 的方式执行此操作,使用引用而不是指针。我还将使用 C++iostream例程而不是cstdio(编写 C 或 C++;尝试将两者混合只会导致胃灼热)。

变量.hpp:

#ifndef VARS_H  // Include guard to prevent multiple inclusion
#define VARS_H

void double_vars(int &, float &);

#endif

变量.cpp:

#include "vars.hpp"

void double_vars(int &x, float &y)
{
  x *= 2;    
  y *= 2.0;  
}

主.cpp:

#include <iostream>
#include "vars.hpp"

int main(void)
{
  int a = 1;
  float b = 2.2;

  std::cout << "a: " << a << "; b: " << b << std::endl;
  double_vars(a, b);
  std::cout << "a: " << a << "; b: " << b << std::endl;

  return 0;
}

由于xandy被声明为引用类型(使用&),写入表达式 xyindouble_vars等同于写入abin main

这样做的 C 方法是使用指针而不是引用:

变量.h:

#ifndef VARS_H  // Include guard to prevent multiple inclusion
#define VARS_H

void double_vars(int *, float *); 
#endif

变量.c:

#include "vars.h"

void double_vars(int *x, float *y)
{
  *x *= 2;    
  *y *= 2.0;  
}

主.c:

#include <stdio.h>
#include "vars.h"

int main(void)
{
  int a = 1;
  float b = 2.2;

  printf("a: %d; b: %f\n", a, b);
  double_vars(&a, &b);
  printf("a: %d; b: %f\n", a, b);
  return 0;
}

在这种情况下,我们使用指针而不是引用,因此我们需要将表达式 &a的结果传递&bdouble_vars; 在函数内,写入表达式*xand*y与写入abin相同main

于 2012-11-03T15:08:10.433 回答