C++ 为什么是c++;将在不同模块中定义的相同命名变量放入内存中的相同地址?

C++ 为什么是c++;将在不同模块中定义的相同命名变量放入内存中的相同地址?,c++,g++,C++,G++,让我们看一下头文件var.h #include <iostream> class var {public: var () {std::cout << "Creating var at " << this << std::endl; } ~var () {std::cout << "Deleting var at " << this << std::endl; } }; 第二个a

让我们看一下头文件
var.h

#include <iostream>

class var
  {public:
      var () {std::cout << "Creating var at " << this << std::endl; }
      ~var () {std::cout << "Deleting var at " << this << std::endl; }
  };
第二个
app.cpp

#include "var.h"
var A;
#include "var.h"

var A;

int main ()
  {return 0;
  }
然后,如果我试图编译它们

g++ -c app.cpp
g++ -c lib.cpp
g++ -o app app.o lib.o
链接器返回多定义变量错误。但是,如果我把它编译到共享库+主应用程序上

g++ -fPIC -c lib.cpp
g++ --shared -o liblib.so lib.o
g++ -fPIC -c app.cpp
g++ -o app -llib -L . app.o
它链接无误。但是,该程序无法正常工作:

./app
Creating var at 0x6013c0
Creating var at 0x6013c0
Deleting var at 0x6013c0
Deleting var at 0x6013c0
所以在同一个内存地址创建了不同的变量!例如,当库和应用程序希望它们具有不同的值(本例中的对象字段值)时,它可能会遇到严重的问题

如果
class var
do memory allocation/deleting valgrind警告访问最近删除的块中的内存

是的,我知道我可以把
静态变量A而不是
var A和两种编译方法都可以正常工作。我的问题是:为什么不能在不同的库中使用相同的命名变量(甚至函数?)?库创建者可能对彼此使用的名称一无所知,并且不会被警告使用
static
。为什么GNU linked没有警告此冲突

顺便说一句,
dlload
也会遇到同样的麻烦吗


UPD。感谢大家对名称空间和外部的解释,我明白了为什么相同的符号被放在相同的内存地址中,但我仍然不明白为什么没有显示关于双定义变量的链接错误甚至警告,但在第二种情况下生成了错误的代码。

不同的库应该有不同的全局变量和全局函数名称,否则会发生非常不愉快的事情(例如,
dlopen
-ing多次…)

传统上,良好的库使用C中的公共前缀(如代码> GTK < /C> >,或C++中的命名空间。

和库应该尽量减少全局状态(C++中,它可能是类内的静态数据)。 您还可以使用GCC接受的
可见性

我的问题是:为什么不能使用相同的命名变量(甚至函数?) 在不同的图书馆

你可以。你缺少的是声明

var A; 这意味着声明“
A
是另一个编译单元将定义和导出的
var
类型的变量”--通过对设置的修改,这将使
app.cpp
明确请求使用导出的
lib.cpp
名为
A
的对象

设置的问题是,有两个不同的编译单元都试图导出相同的符号
A
,这会导致冲突

Why GNU linked doesn't warn about this conflict? 为什么GNU linked没有警告此冲突?
因为GNU不知道您希望
A
成为编译单元私有的变量,除非您告诉GNU它应该是编译单元私有的。这就是本文中静态的意思。

不清楚你是在问这是不是应该发生的或者原因是什么

首先,这是必需的行为。根据“一个定义规则”,C++标准的第3.2节,如果多个翻译单元包含相同的定义(以及满足某些其他要求),则程序应该表现为好像有一个单一的定义。在存在多个定义的任何其他情况下,行为是未定义的


如果你问这个规则的基本原理是什么,那就是它通常是你想要的。如果有多个定义未标记为
extern

时,编译器可能会有一个选项来发出警报。答案有点简化:“库”是一个实现细节。在执行之前,所有对象文件都合并(链接)到单个单元(可执行文件)。链接完成后,不再有库、原始源文件等的踪迹——重要的是最终的可执行文件

现在,您似乎很惊讶,程序中相同的全局名称(=将所有内容连接在一起的最终结果)总是指同一个对象。如果不是这样,会不会让人困惑


如果file1.cpp和file2.cpp都定义了一个带有外部链接的变量
a
,那么编译器和链接器如何知道您想要一个还是两个不同的对象?更重要的是,阅读代码的人应该如何知道原始作者是否想要创建一个或两个对象?

具有
外部链接的符号(在本例中为默认链接)对其他翻译单元可见。这是为了允许源文件、库等之间的接口

定义的存在与否不会改变访问哪个对象。程序员负责安排声明和定义,使对象总是在使用前声明,并且总是精确定义一次(一个定义规则)

最好的解决方案是将私有全局变量放入未命名的名称空间,这样看起来相同的定义仍然可以不同

lib.cpp

#include "var.h"
namespace { // unnamed namespace
    var A; // object inaccessible to other translation units
}
app.cpp

#include "var.h"

namespace { // different unnamed namespace
    var A; // different object
}

int main ()
  {return 0;}

哦,我有关于外部的,它应该通过图书馆工作,不是吗?但我仍然不明白为什么链接器在没有警告的情况下提供无效代码…我不知道。唉,我对共享库和gcc没有足够的知识,不知道你是否不应该以这种方式使用它们(因此,如果你有奇怪的行为,这是你自己的想法),或者这是gcc中的一个实际错误。你不允许定义同一个对象两次。不过你可以申报两次。他的程序格式不正确,因为
A
应该在两个翻译单元中命名相同的对象,并且两个单元也定义
A
。奇怪的是,GCC在第一个构建中正确地抱怨,但在第二个构建中却没有抱怨在多个对象文件中,并且不会有任何链接器错误。无论如何,正如我所说,这是一个有点简化/不精确的答案
#include "var.h"

namespace { // different unnamed namespace
    var A; // different object
}

int main ()
  {return 0;}