c++ - 为什么链接器在链接共享和静态时不会抱怨多个定义
问题描述
我最近在我正在开发的代码中遇到了静态初始化失败的问题。这让我意识到我缺乏关于链接过程和全局变量初始化的知识,所以我在 cppcon上发现了这个非常有趣的关于全局变量、链接以及将静态库嵌入共享和同时链接两者的可能问题(使用全局变量定义)。基本上说的是,当具有以下结构时:
//static.hpp
#pragma once
#include <string>
extern std::string globalString;
//static.cpp
#include "static.hpp"
std::string globalString;
//shared.hpp
#pragma once
#include "static.hpp"
#include <string>
std::string& getGlobalString();
//shared.cpp
#include "shared.hpp"
#include "static.hpp"
std::string& getGlobalString(){
return globalString;
}
//main.cpp
#include "static.hpp"
#include "shared.hpp"
#include <iostream>
int main(){
std::cout << "Global variable: " << globalString << std::endl;
std::cout << "Global var acccesed through shared lib: " << getGlobalString() << std::endl;
return 0;
}
所有编译:
clang++ -c -std=c++14 -fpic static.cpp
clang++ -c -std=c++14 -fpic shared.cpp
clang++ -c -std=c++14 main.cpp
ar rsc libstatic.a static.o
clang++ -shared -o libshared.so shared.o -L./ -lstatic
clang++ -L./ -Wl,-rpath=./ main.o -lstatic -lshared
由于多次调用同一对象的构造函数和析构函数,我导致分段错误。这是令人惊讶的,因为我认为每个对象构造函数都将被调用一次是不变的!
- 关于多次调用构造函数/析构函数的标准是什么?
- 如果在这种情况下,全局静态 i 嵌入在 main.o 和 libshared.so 中,这是否意味着有一个定义的规则被打破了?为什么链接器在链接 main 时不抱怨第二个定义可用?
- 当在单独的翻译单元中至少有两个全局时,建立构造和销毁顺序的建议方法之一是在第一次使用时进行初始化。然而,据我所知,这只能确保施工顺序。毁灭令呢?如何控制?
解决方案
这是令人惊讶的,因为我认为每个对象构造函数都将被调用一次是不变的!
在正确构造的二进制文件中,这是正确的。在具有 ODR 违规的二进制文件中不一定是正确的。
关于多次调用构造函数/析构函数的标准是什么?
该标准规定,在正确构造的程序中,构造函数/析构函数只被调用一次。在违反 ODR 的程序中,任何事情都可能发生(未定义的行为)。
如果在这种情况下,全局静态 i 嵌入在 main.o 和 libshared.so 中,这是否意味着有一个定义的规则被打破了?
是的。
为什么链接器在链接 main 时不抱怨第二个定义可用?
就链接器而言,您的程序没有任何问题。从链接器的角度来看,在静态库和共享库中定义一个全局是完全合乎情理的。另请参阅此答案。
当在单独的翻译单元中至少有两个全局时,建立构造和销毁顺序的建议方法之一是在第一次使用时进行初始化。然而,据我所知,这只能确保施工顺序。
正确的。这是针对不同问题的解决方案:两个全局变量 A 和 B 在单独的翻译单元中定义。
你没有那个问题,你有一个完全不同的问题(违反 ODR)。
毁灭令呢?如何控制?
在正确构造的程序中,破坏顺序保证与构造顺序相反。
推荐阅读
- cucumber - Cucumber 在多个站点上运行测试
- java - 使用 java FileWriter 时我尝试解决并发问题是否正确?
- python - Foluim - 在等值线覆盖上绘制圆圈
- python - 使用 Pandas 根据数据框中的另一列值获取特定值的计数和总数
- pandas - 如果列包含字符串,熊猫会删除行
- javascript - 手机和 safari 桌面中的幻灯片动画问题
- ios - 将 HTML 文档转换为文本?迅速
- c - C分配问题:将十进制数转换为位的函数
- javascript - 为什么 getElementById 找不到我的 ReactJs 按钮
- rust - 使用 None 值打开 HashMap 时出现恐慌