C++ Can";“首次使用时建造”;成语在任何情况下都会失败吗?

C++ Can";“首次使用时建造”;成语在任何情况下都会失败吗?,c++,static,linker,static-initialization,C++,Static,Linker,Static Initialization,我正在使用一些静态库构建我的程序(实际上是测试)。 此库包含一个文件,其中包含如下函数: string& GetString() { static string strFilename; return strFilename; } void PrintToScreen() { printf("String: %s\n", GetString().c_str()) } 然后在我的main.cpp(图书馆外)中,我正在做: GetString() = "abc";

我正在使用一些静态库构建我的程序(实际上是测试)。
此库包含一个文件,其中包含如下函数:

string& GetString() {
    static string strFilename;
    return strFilename;
}

void PrintToScreen() {
    printf("String: %s\n", GetString().c_str())
}
然后在我的main.cpp(图书馆外)中,我正在做:

GetString() = "abc";
printf("String: %s\n", GetString().c_str());
PrintToScreen();
我得到这个输出:

String: abc
String:
看起来像是对函数的第二次调用 (但从库中的不同文件执行) 以某种方式清除以前的值,重新初始化它,或使用它自己的副本。
我将GetString函数改为使用“new”,但结果完全相同(顺便说一句,程序从未崩溃)。
但我不明白这有可能吗?
知道我做错了什么吗?

-------------------------------更新------------------------------------------

  • 测试是在单线程环境下完成的
  • 它在某些平台上工作,在某些平台上不工作(在windows、MacOS和AIX上工作,在linux、HP_UX、Solaris、FreeBSD上不工作…)
  • 我在执行过程中验证了strFilename的地址(GetString中的printf),看起来它是一个没有重复项的变量(地址总是相同的)
  • 但是,在最后一个库中使用nm时,我得到了类似的结果:
  • 00000000000000 30 T_Z16GetLogprintfFilev
    0000000000000008 b_zgvz16GetLogPrintOffile16StrLogPrintOffile
    00000000000000 18 b_ZZ16GetLogprintfFilevE16strLogprintfFile
    U_Z16GetLogprintfFilev

    在我的基本库中使用nm(由最终库使用),我得到:

    00000000000000 30 T_Z16GetLogprintfFilev
    0000000000000008 b_zgvz16GetLogPrintOffile16StrLogPrintOffile

    00000000000000 18 b_zz16GetLogPrintffile16StrLogPrintffile

    是静态链接时很可能出现这种情况

    例如:

     libA.a   // contains GetString()
              // Contains. PrintToScreen()
              // Here the reference has been resolved.
    
     libX.so  // Contains a call to GetString()
              // This library is linked with libA.a
              // Thus pulls in the function GetString() into this library.
    
     libY.so  // Contains a call to PrintToScreen()
              // This library is linked with libA.a
              // Thus pulls in the function PrintToScreen and GetString() into this library.
    
     a.out    // linked against libY.so libX.so
              // This has two distinct versions of GetString()
    
    在上面的示例中,如果a.out包含一个调用Get getString(),那么将调用哪个版本的getString()是操作系统特有的。在大多数系统上,使用单个共享库的加载顺序,但在其他系统上,它将对共享库进行深度优先搜索(即lib X加载XA XB和Y加载YA YB。搜索顺序可以是X XA XB Y YA YB或X Y XA XB YA YB)。您需要查阅每个OS共享库文档,以了解如何在运行时搜索符号

    这里的解决方案是仅针对共享库进行链接(大多数情况下为默认设置)。
    这样,您只获得一个libA副本(假设您将libA设置为共享lib),其内容只加载到运行时一次(没有副本)

    注意:这不是语言级别的失败。

    这是由于链接导致的失败,链接超出了C/C++语言的范围。

    实际上,示例中缺少一个想法。应该是这样的:

    string& GetString() {
      static string strFilename;
      return strFilename;
    }
    
    extern "C" {
      void PrintToScreen() {
        printf("String: %s\n", GetString().c_str())
      }
    }
    
    extern "C" {
      string* GetString() {
        static string strFilename;
        return &strFilename;
      }
    
      void PrintToScreen() {
        printf("String: %s\n", GetString()->c_str())
      }
    }
    
    奇怪。无论如何,我将其重构为如下内容:

    string& GetString() {
      static string strFilename;
      return strFilename;
    }
    
    extern "C" {
      void PrintToScreen() {
        printf("String: %s\n", GetString().c_str())
      }
    }
    
    extern "C" {
      string* GetString() {
        static string strFilename;
        return &strFilename;
      }
    
      void PrintToScreen() {
        printf("String: %s\n", GetString()->c_str())
      }
    }
    
    现在它可以正常工作了
    然而,我觉得奇怪的是,编译器并没有抱怨
    多亏了大家的贡献,问题现在解决了

    ----------------------------------编辑----------------------------------

    我后来又遇到了这个问题,因此无法正确修复。
    真正的问题是某个已初始化的单例
    同时,在类构造函数中有:

    GetString() = "";
    

    所以,这个问题很简单,但很难跟踪…

    顺便问一下,这是您的实际代码还是一个简化的示例?您是否尝试过在调试器中单步执行您的代码,以查看调用的位置以及它们访问的数据?@kuki:嗯,这个简化的示例正如预期的那样工作。因此,结构与实际问题不完全相同。请给出一个简单的例子来重现这个问题。在构造示例时,很可能会发现bug。这段代码很好。您的错误出现在您没有显示给我们的代码中。@kuki:它的工作方式与您发布的不同,即它应该打印“abc”两次。您的代码肯定包含一些其他的bug,当您构建一个复制问题的最小示例时,您会发现这些bug。如果您没有,我们将很乐意在您更新问题时帮助您找到它。或者,我建议您删除这个问题,并在调试代码后发布一个新的带有好例子的问题,但没有发现问题。一、 首先,我投票决定关闭这个库。一开始我的想法完全一样,但我仔细检查了一下,我没有两次链接同一个库。好吧,我想我明天第三次再查…@kuki:如果你能告诉我们确切涉及的图书馆以及它们是如何联系在一起的,那将很有趣。如果您在*nix-like框中,
    nm
    将转储符号,以便查看GetString()是否位于多个位置。这不要求函数是静态的吗?否则它不应该给出链接器错误吗?@bitmask:不,这是不必要的(但这是实现相同效果的另一种方式)。在上述情况下,每个共享库都是独立的,并且可以有重叠的导出标识符,并且可以成功地与可执行文件链接(链接阶段完全独立于编译,并且只尝试解析符号(而不是重复检测)。@Tux-D:I have shared.lib(其中包含两个函数)另外两个库合并到一个server.lib库中。最后,这个server.lib与测试文件链接。不幸的是,整个过程有点复杂(我们之前正在重新链接server.lib,等等)。我想我会像你说的那样检查nm输出,以及在执行过程中打印出strFilename的地址。更新将在12小时后到来;)很好,你找到了解决方案。接受答案,这样它将被提升到第一位,人们将能够很容易地看到它。我会这样做,但我得到的信息是,我可以明天这样做;)我不认为这解决了问题(它存在)。问题是由链接和加载共享库的方式引起的。你刚刚隐藏了问题。不幸的是,这个问题与