关于C ++中全局内联函数的一个例子

时间:2013-11-15 12:14:35

标签: c++ pointers static inline global

考虑到以下设置,我遇到了一个非常奇怪的现象,我无法解释。使用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; 
}

事情是,

时崩溃消失了
  1. 我将内联函数setit()移动到未命名的命名空间
  2. 我让它静止
  3. 简而言之,我需要帮助才能理解原因。任何建议表示赞赏! (我知道,这个解决方案不是最好的部分之一,只是好奇。)

3 个答案:

答案 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;
}