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

发布于 2024-12-23 10:36:33 字数 1424 浏览 4 评论 0原文

让我们使用头文件 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; }
  };

和两个源文件,第一个 lib.cpp

#include "var.h"
var A;

和第二个 app.cpp

#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

因此在同一内存地址创建了不同的变量!例如,当库和应用程序期望它们具有不同的值(在本例中为对象字段的值)时,这可能会带来严重的麻烦。

if class var do 内存分配/删除 valgrind 会警告访问最近删除的块中的内存。

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

而且,顺便说一句,dlload 会陷入同样的​​麻烦吗?

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

Let's take header file 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; }
  };

and two source files, first lib.cpp

#include "var.h"
var A;

and second app.cpp

#include "var.h"

var A;

int main ()
  {return 0;
  }

then, if I attempt to compile them

g++ -c app.cpp
g++ -c lib.cpp
g++ -o app app.o lib.o

linker return multiply defined variable error. But, if I compile it onto shared library + main app

g++ -fPIC -c lib.cpp
g++ --shared -o liblib.so lib.o
g++ -fPIC -c app.cpp
g++ -o app -llib -L . app.o

it links without error. However program doesn't work properly:

./app
Creating var at 0x6013c0
Creating var at 0x6013c0
Deleting var at 0x6013c0
Deleting var at 0x6013c0

so different variables was created at the same memory address! It might put into serious trouble, for example, in a case when library and application expect them to have different values (values of object fields in this case).

if class var do memory allocation/deleting valgrind warns about accessing memory in recently deleted block.

Yes, I know I could put static var A; instead of var A; and both ways to compile will work properly. My question is: why one can't use same-named variables (or even functions?) in different libraries? Library creators might know nothing about names each other use and not to be warned to use static. Why GNU linked doesn't warn about this conflict?

And, BTW, could dlload put into same trouble?

UPD. Thank you all for explaining about namespaces and extern, I see why same symbols are placed into same memory address, but still I can't get why no linking error or even warning about doubly defined variable is shown but wrong code produced in second case.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

肤浅与狂妄 2024-12-30 10:36:33

我的问题是:为什么不能使用同名变量(甚至函数?)
在不同的库中?

你可以。您缺少的是声明

var A;

没有定义在库中使用的符号 A 。他们正在定义要导出的符号,以便任何其他编译单元都可以引用它!

例如,如果在 app.cpp 中,您声明

extern var A;

这将意味着声明“A 是一个 var 类型的变量,其他编译单元将使用该变量定义和导出”——通过对您的设置进行此修改,这将使 app.cpp 显式请求使用 lib.cpp< 中名为 A 的对象/代码> 已导出。

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

Why GNU linked doesn't warn about this conflict?

因为 GNU 无法知道您希望 A 成为编译单元私有的变量,除非您告诉 GNU 它应该是编译单元私有的。这就是static 在此上下文中的含义。

My question is: why one can't use same-named variables (or even functions?)
in different libraries?

You can. The thing you're missing is that the declarations

var A;

aren't defining the symbol A for use in the library. They're defining the symbol to be exported for so that any other compilation unit can reference it!

e.g. if, in app.cpp, you declared

extern var A;

this would mean declare "A is a variable of type var that some other compilation unit is going to define and export" -- with this modification to your setup, this would make app.cpp explicitly request to use the object named A that lib.cpp exported.

The problem with your setup is that you have two different compilation units both trying to export the same symbol A, which leads to a conflict.

Why GNU linked doesn't warn about this conflict?

Because GNU can't know that you wanted A to be a variable private to your compilation unit unless you tell GNU that it should be private to your compilation unit. That's what static means in this context.

情话难免假 2024-12-30 10:36:33

目前尚不清楚您是在问这是否应该发生,或者其理由是什么。

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

如果您问这条规则的基本原理是什么,那么它通常就是您想要的。您的编译器可能有一个选项,如果多个定义未标记为 extern,则会发出警报。

It's not clear if you're asking whether this is supposed to happen or what the rationale is.

First, it is required behavior. Per the "one definition rule", section 3.2 of the C++ standard, if multiple translation units contain identical definitions (and certain other requirements are met), then the program shall behave as if there were a single definition. In any other case where there are multiple definitions, the behavior is undefined.

If you're asking what the rationale for this rule is, it's that it's usually what you want. Your compiler may have an option to alert if more than one definition isn't marked extern.

不念旧人 2024-12-30 10:36:33

不同的库应该有不同命名的全局变量和全局函数,否则会发生非常不愉快的事情(例如,当多次使用dlopen时...... )。

按照惯例,行为良好的库在 C 中使用公共前缀(如 gtk ),或在 C++ 中使用命名空间。

库应该最小化全局状态(在 C++ 中,它可能应该是类内的静态数据)。

您还可以使用 visibility 函数属性被海湾合作委员会接受。

Different libraries should have differently named global variables and global functions, otherwise very unpleasant things happen (e.g. when dlopen-ing it several times...).

Conventionally, well behaved libraries use a common prefix (like gtk) in C, or a namespace in C++.

And libraries should minimize global state (in C++, it probably should be static data inside classes).

You could also use the visibility function attribute accepted by GCC.

旧人 2024-12-30 10:36:33

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

定义的存在或不存在不会改变访问哪个对象。程序员负责安排声明和定义,以便对象始终在使用前声明并始终只定义一次(单一定义规则)。

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

lib.cpp

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

应用程序.cpp

#include "var.h"

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

int main ()
  {return 0;}

Symbols with extern linkage (which is the default in this case) are visible to other translation units. This is to allow interfaces between source files, libraries, etc.

The existence or non-existence of a definition does not change which object is accessed. The programmer is responsible for arranging declarations and definitions such that an object always declared before use and always defined exactly once (the one-definition rule).

The best solution is to put private globals into unnamed namespaces, so that definitions that look the same can still be different.

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;}
远昼 2024-12-30 10:36:33

稍微简化的答案:“库”是一个实现细节。所有目标文件在执行之前都组合(链接)到一个单个单元(可执行文件)。链接完成后,就不再有库、原始源文件等的踪迹——重要的是最终的可执行文件。

现在,您似乎感到惊讶的是,程序中的相同全局名称(= 将所有内容连接在一起的最终结果)始终引用同一个对象。如果不是这样的话,不是会很混乱吗?

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

Somewhat simplified answer: "libraries" are an implementation detail. All object files are combined (linked) to a single unit (executable) prior to execution. After linking has been done, there is no trace of libraries, original source files, etc. any longer -- all that matters is the final executable.

Now, you seem to be surprised that the same global name in the program (= the final result of linikng everything together) always refers to the same object. Wouldn't it be confusing if it were otherwise?

If file1.cpp and file2.cpp both defined a variable A with external linkage, how are the compiler and linker supposed to know whether you want one or two different objects? More importantly, how is the human reading the code supposed to know whether the original author wanted to create one or two objects?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文