您可以对 windows 中的字符串进行 `memset()` 吗?

You can `memset()` over a string in windows?

我正在努力将一些移植到 linux,我在代码中发现了一个相当有趣的错误,它显然适用于 windows,但不适用于 linux。带有一些字符串成员的 class 正在通过 memset(this) 初始化,这显然适用于 windows,但在字符串析构函数中的 linux 上抛出分段错误。

是的,我知道使用 memset() 这是一种糟糕的做法,我正在修复它。

SSCCE:

#include <iostream>
#include <cstring>

int main()
{
    std::string tmp;
    std::cout << "String instantiated" << std::endl;
    memset(&tmp, 0, sizeof(tmp));
    std::cout << "String memset" << std::endl;

    return 0;
}

这在 windows 上运行良好,但字符串析构函数在 linux 上出现段错误。

编译器:

我知道这曾经是(现在也是)糟糕的做法,但它最初是如何运作的?

如果您使用调试器检查程序退出时(在 return 0),您实际上遇到了段错误,因为程序正在尝试释放它认为打开的 std::string tmp堆栈然后失败,因为它在该位置找不到有效的 std::string。

编辑:

它可能适用于一个 OS 而不是另一个,因为它不是由 C++ 标准指定的,并且释放 std::string 的任何具体实现都可能不同,只要它们符合标准。碰巧你在 Linux 上使用的实现并不能很好地处理这个问题,但它不需要,因为这不是应该使用 memset 的情况。如果要在字符串上使用 memset,请使用 C 样式字符串。

如果您想深入了解实现细节,MSVC 和 Clang(使用 libc++)使用 string 和短字符串优化,大致如下所示:

class string {
    size_t length;
    char* ptr;
    char short_buf[N];
};

所以如果它是 memset0,它的析构函数会认为它的长度为零并且可能什么都不做,而且即使它试图 delete[] ptr,它也赢了不会崩溃,因为 delete 可以很好地处理空指针。

GCC 相反,直到最近才使用完全不同的 string 实现,其中涉及写时复制和引用计数。所以它的内部结构要复杂得多,它在 memset.

之后崩溃也就不足为奇了

您问的是:

how did it ever work in the first place?

考虑如下实现:

class string
{
   public:

      string() : size_(0), data_(nullptr) {}

      string(char const* s) : size_(strlen(s)), data_(new char(size_+1))
      {
         strcpy(data_, s);
      }

      ~string()
      {
         if ( data_ )
         {
            delete [] data_;
         }
      }

   private:
      size_t size_;
      char* data_;
};

给定这样的实现,如果 nullptr0 表示(这是最常见的表示),

string s1;
memset(&s1, 0, sizeof(s1));  // Has no impact on s1

string s2("This is a test");
memset(&s2, 0, sizeof(s2));  // Makes s2 the same as a default
                             // constructed string with memory leak
                             // as a side effect. Still, it is
                             // not going to cause segementation
                             // fault.