考虑到以下设置,我遇到了一个非常奇怪的现象,我无法解释。使用Visual Studio 2005,以下代码导致崩溃。我想知道原因。
playground.cpp
static int local=-1;
#include "common.h"
int main(int arg)
{
setit();
docastorUpdate();
return 0;
}
COMMON.H
#include <stdio.h>
#include <iostream>
void docastorUpdate();
static int *gemini;
inline void setit()
{
gemini = &local;
}
castor.cpp
static int local = 2;
#include "common.h"
void docastorUpdate() {
setit();
// crashing here, dereferencing a null pointer
std::cout << "castor:" << *gemini << std::endl;
}
事情是,
时崩溃消失了简而言之,我需要帮助才能理解原因。任何建议表示赞赏! (我知道,这个解决方案不是最好的部分之一,只是好奇。)
答案 0 :(得分:9)
这会因为违反单定义规则而中断。单定义规则表明,在程序中,跨所有翻译单元,任何给定函数只有一个定义。 inline
是这个规则的一个例外,它或多或少意味着“亲爱的编译器,这个函数会有几个定义,但它们都是一样的,我保证 ”
static
,在此用于local
时,意味着“亲爱的编译器,这是一个只有这个翻译单元才能看到的内部细节;请不要将它与名为{{1}的变量混淆来自其他翻译单位“
所以你向编译器承诺local
的所有定义都是相同的,并要求编译器为每个翻译单元提供它自己的setit
变量。
但是,由于local
函数使用名为setit
的任何变量在范围内,因此最终结果是local
的两个不同定义,每个定义使用不同的变量。 你刚违背诺言。编译器信任你,结果是一个完全混乱的程序。它认为它可以根据你的承诺对代码做某些事情,但是既然你把它们背在背后,它试图用代码做的那些东西根本不起作用。
答案 1 :(得分:5)
您的代码以非常微妙的方式调用未定义的行为。
[C++11: 7.1.2/4]:
内联函数应在每个使用ODR的翻译单元中定义,并且在每种情况下都应具有完全相同的定义。 [..]
虽然定义看起来相同,因为它被#include
词汇复制粘贴到每个翻译单元中,但并不是因为&local
在每种情况下都没有采用相同变量的地址。
因此,当您运行程序时,任何事情都可能发生,包括将您辛苦赚来的所有积蓄转移到我的银行帐户中,或者取走所有Jon Skeet的代表。
这就是使非函数inline
解决问题的原因;另外,将它放在一个未命名的命名空间中,使其成为每个翻译单元中不同的函数。
答案 2 :(得分:0)
你可以避免在所有地方使用静态,你可以在你的情况下使用extern
:
COMMON.H:
extern int *gemini;
common.cpp;
int *gemini = nullptr;
也避免像这样使用local
,而是可以这样做:
inline void setit(int * p)
{
gemini = p;
}
void docastorUpdate()
{
static int local = 2;
setit(&local);
std::cout << "castor:" << *gemini << std::endl;
}