【问题标题】:Copying the address of a static union member gives the incorrect address复制静态联合成员的地址会给出不正确的地址
【发布时间】:2012-09-15 10:37:50
【问题描述】:

我无法在可以展示的较小程序中复制此问题,因此我将通过屏幕截图说明问题。

我有一个声明联合的类和联合的静态成员:

class 
{ 
/* rest of the class */

    union EmptyString
    {
      char8   m_Empty8[1];
      uchar8  m_EmptyU8[1];
      char32  m_Empty32[1];
    };

    static const EmptyString sm_emptyString;
};

  // Definition
  template <typename T>
  const typename StringBase<T>::EmptyString 
    StringBase<T>::sm_emptyString = { 0 };

然后我有一个返回字符串地址的函数。出于调试目的,我添加了变量address,以便可以将其添加到监视窗口。这是函数(T 这里是 char8 其中char8 是 char 的 typedef):

  template <typename T>
  const char8* StringBase<T>::GetEmptyString( char8 )
  {
    const char8* address = sm_emptyString.m_Empty8;
    //const char8* address = &(sm_emptyString.m_Empty8[0]);
    return address;
    //return sm_emptyString.m_Empty8;
  }

如您所见,最初我有一个 return 语句,我将其注释掉以便调试 sn-p。被注释掉的另一行是获取数组的第一个(也是唯一一个)元素的地址;这条被注释掉的行与它之前的行做同样的事情,即:const char8* address = sm_emptyString.m_Empty8;。

程序崩溃是因为我没有通过上述函数获得正确的地址,这让我很困惑。下面我将开始我的调试会话的截图:

在上面,你可以看到我在地址被复制到指针之前就中断了。以下是sm_emptyString 和address 在此休息时间的观察窗口值。

到目前为止,没有什么特别的。下面是单步执行一行代码后的截图。

以及对应的观察窗口:

如您所见,复制到address 的值是错误的。另外需要注意的是,这个函数被调用了几次,静态变量地址到address 的副本是正确的。当地址不正确并由函数返回时,我的程序会崩溃(因为稍后在程序中对地址进行了假设)。

这里发生了什么?我是否在某处覆盖了内存?我该如何调试呢?

编辑:

反汇编(根据调试器,sm_emptyString的地址是129F184,与反汇编显示的128F134不同):

template <typename T>
  const char8* StringBase<T>::GetEmptyString( char8 )
  {
0119B850  push        ebp  
0119B851  mov         ebp,esp 
0119B853  push        ecx  
0119B854  mov         dword ptr [ebp-4],0CCCCCCCCh 
    const char8* address = sm_emptyString.m_Empty8;
0119B85B  mov         dword ptr [address],offset StringBase<char>::sm_emptyString (128F134h) 
    //const char8* address = &(sm_emptyString.m_Empty8[0]);
    return address;
0119B862  mov         eax,dword ptr [address] 
    //return sm_emptyString.m_Empty8;
  }

我正在使用什么: Windows 7,VC++ 2008,调试版本

【问题讨论】:

  • 如果您将联合成员从 1 元素数组更改为只是一个简单的变量会发生什么?即union { char8 m_Empty8; ... },然后将地址分配为const char8* address = &amp;(sm_emptyString.m_Empty8);。你还遇到同样的问题吗?
  • 如果你把 union 改成 struct 会发生什么? (只需用结构替换 union 关键字)。你还遇到同样的问题吗?
  • 除此之外,尝试将char8 m_Empty8[1] 和朋友更改为直接指针。所以改为char8 *m_Empty8。
  • 您实际上是定义您的static 成员还是您只声明了它?鉴于您既没有显示定义也没有给包含 union 的类命名,这可能是问题所在。
  • @DietmarKühl 如果他没有定义它就不会编译是吗?

标签: c++ memory pointers


【解决方案1】:

使用函数?您需要的空字符串与StringBase&lt;T&gt; 无关,也与它的状态和模板参数无关。

inline const char8* GetEmptyString( char8 )     
{
    static const char8 address[1] = {0};
    return address;
}

编辑:为什么我建议像你的代码那样使用联合是错误的。 C99 标准所说的一些事情:

  • 6.2.5p20,联合有一组重叠的成员对象
  • 6.7.2.1p14,任何时候最多可以将一个成员的值存储在一个联合对象中
  • 附件 J.1 未指定存储到的最后一个以外的联合成员的值。

C++ 也是如此,只是现在手头没有标准。所以你不能使用联合来同时在同一个地方存储 3 个字符串。虽然我不确定究竟是什么导致了这些问题,但很可能完全在其他地方,你应该摆脱这些有问题的结构。

【讨论】:

  • 我确实喜欢这个建议,但它并没有解决原来的问题。如果我用这个函数得到了正确的地址,那么我只是在地毯下扫除错误(可能在其他地方并在给定的代码中表现出来)(可以这么说)。
  • 最初的问题是您的代码包含不可避免的未定义行为。您可能无法读取不是最后一个写入的工会成员的值。不在 C 中,不在 C++ 中。所以我试图提出理智的解决方案。
  • 啊,我不知道这是未定义的行为 (+1)。实际上,我不知道“this”是什么意思:您可能不会读取非最后一个所写的 union 成员的值。如果您能详细说明,我将不胜感激。
  • 好的,我在回答中加入了一些标准的话。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多