我想在标题中定义一个变量,并能够在多个文件中使用它。
例如在某处定义变量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
有人可以向我解释为什么会这样吗?我在头文件中有警卫,所以据我所知它应该只包含一次所以不应该有多个声明
答案 0 :(得分:4)
在头文件中,写下:
extern int a;
extern float b;
这将声明变量。
在其中一个CPP文件中,写下:
int a = 1;
float b = 2.2;
这将定义变量。只要变量只定义一次就无论在哪个CPP文件中放置定义都无关紧要。
答案 1 :(得分:4)
首先,不要这样做。全球可变状态通常是一件坏事。
问题是,一旦链接器完成了你的include语句中的文件组合,它已经看过两次了,一次在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;
这样,a
和b
存储有一个明确定义的存放位置,编译器可以正确连接点。
答案 2 :(得分:1)
Collin已经描述了问题和解决方案,但我想强调一下它的风格有多糟糕。
正如你从这个简单的练习中发现的那样,以这种方式使用全局变量通常是一件坏事。除了您已经发现的编译器问题之外,使用全局变量在函数之间共享状态会导致以下问题:
它使得函数本身更难在其他程序中重用,因为函数期望存在这些符号;
对一个函数的更改可能会导致共享该状态的其他函数出现问题(即,更改未本地化为该函数);
代码变得更难调试,因为任何人都可以随时修改a
和b
。
理想情况下,函数应该通过参数,返回值和异常(如果适用)进行通信;有时候共享状态是正确的答案,但是那些相对较少且相差甚远。
由于你显然使用的是C ++编译器,我将采用C ++方式,使用引用而不是指针。我也将使用C ++ iostream
例程而不是cstdio
(编写C或C ++;尝试将两者混合起来导致胃灼热)。
vars.hpp:
#ifndef VARS_H // Include guard to prevent multiple inclusion
#define VARS_H
void double_vars(int &, float &);
#endif
vars.cpp:
#include "vars.hpp"
void double_vars(int &x, float &y)
{
x *= 2;
y *= 2.0;
}
main.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;
}
由于x
和y
被声明为引用类型(使用&
),因此写入表达式 {{ {}}和x
中的y
相当于在double_vars
中写入a
和b
。
这样做的方法是使用指针而不是引用:
vars.h:
main
vars.c:
#ifndef VARS_H // Include guard to prevent multiple inclusion
#define VARS_H
void double_vars(int *, float *);
#endif
main.c中:
#include "vars.h"
void double_vars(int *x, float *y)
{
*x *= 2;
*y *= 2.0;
}
在这种情况下,我们使用指针而不是引用,因此我们需要将表达式 #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
的结果传递给&b
;在函数内,写入double_vars
和*x
表达式与写入*y
中的a
和b
相同。