在Visual Studio C ++中初始化std :: string数组时崩溃

时间:2017-06-21 11:26:21

标签: c++ string visual-studio new-operator

问题:我正在尝试为std::string数组分配内存,然后使用new(...)[]()对其进行初始化。然后我尝试为数组元素赋值,这会导致应用程序崩溃(访问冲突)。问题是:我是否遗漏了一些编译器标志或明显的东西?

使用

进行编译
cl.exe /DEBUG /EHsc /MTd /Zi test.cc

生成崩溃的可执行文件(在VS 2017和VS 2012中测试)。另一方面,它在Linux上使用GCC正常工作。

#include <iostream>
#include <string>

struct S {
  int a,b;
  S() : a(99), b(299) {}
  S & operator=(const char * rhs) { a = 100; b = 300; return *this; }  
};
std::ostream & operator<<(std::ostream & os, const S & s) { os << "(" << s.a << "," << s.b << ")"; return os; }  


typedef std::string T;
//typedef S T;
int main(int argc, char ** argv) {
    size_t N = 100;
    std::allocator<T> mem;
    T * data = mem.allocate(N);
    new(data)T[N]();

    for (ptrdiff_t i = 0; i < N; ++i)
        data[i] = "HELLO WORLD";
    for (ptrdiff_t i = 0; i < N; ++i)
        std::cout << data[i] << std::endl;
}

我尝试使用其他typedef,在这种情况下,初始化完全按预期工作。

编辑:如果我使用C calloc代替std::allocator,我会看到相同的崩溃。

解决。事实证明问题是:C ++标准说new T[N]表达式至少 sizeof(T)*N字节分配。 Visual C分配额外的开销。展示位置新new(ptr)T[N]假设ptr中有额外开销。 下面答案的解决方案是分别循环和构建每个项目。

3 个答案:

答案 0 :(得分:1)

问题在于未完成的开销,如此处所述:Array placement-new requires unspecified overhead in the buffer?

快速修复

#include <iostream>
#include <string>

struct S {
  int a,b;
  S() : a(99), b(299) {}
  S & operator=(const char * rhs) { a = 100; b = 300; return *this; }  
};
std::ostream & operator<<(std::ostream & os, const S & s) { os << "(" << s.a << "," << s.b << ")"; return os; }  


typedef std::string T;
//typedef S T;
int main(int argc, char ** argv) {
    size_t N = 100;
    std::allocator<T> mem;
    T * datastor = mem.allocate(N+1);
    T * data = new(datastor)T[N]();

    for (ptrdiff_t i = 0; i < N; ++i)
        data[i] = "HELLO WORLD";
    for (ptrdiff_t i = 0; i < N; ++i)
        std::cout << data[i] << std::endl;
}

但这并不能保证始终有效,因为理论上“未指定的开销”可能大于sizeof(std::string)

没有未指定行为的版本将单独新建元素:

#include <iostream>
#include <string>

struct S {
  int a,b;
  S() : a(99), b(299) {}
  S & operator=(const char * rhs) { a = 100; b = 300; return *this; }  
};
std::ostream & operator<<(std::ostream & os, const S & s) { os << "(" << s.a << "," << s.b << ")"; return os; }  


typedef std::string T;
//typedef S T;
int main(int argc, char ** argv) {
    size_t N = 100;
    std::allocator<T> mem;
    T * data = mem.allocate(N);
    for (ptrdiff_t i = 0; i < N; ++i)
        new(&data[i]) T();
    for (ptrdiff_t i = 0; i < N; ++i)
        data[i] = "HELLO WORLD";
    for (ptrdiff_t i = 0; i < N; ++i)
        std::cout << data[i] << std::endl;
    for (ptrdiff_t i = 0; i < N; ++i)
        data[i].~T();
    mem.deallocate(data, N);
}

答案 1 :(得分:0)

你不应该使用构造吗?

#include <iostream>
#include <string>

struct S {
  int a,b;
  S() : a(99), b(299) {}
  S & operator=(const char * rhs) { a = 100; b = 300; return *this; }
};
std::ostream & operator<<(std::ostream & os, const S & s) { os << "(" << s.a << "," << s.b << ")"; return os; }


typedef std::string T;
//typedef S T;
int main(int argc, char ** argv) {
    size_t N = 100;
    std::allocator<T> mem;
    T * data = mem.allocate(N);
    //new(data)T[N]();

    for (ptrdiff_t i = 0; i < N; ++i)
        mem.construct(data + i, "HELLO WORLD");
        //data[i] = "HELLO WORLD";
    for (ptrdiff_t i = 0; i < N; ++i)
        std::cout << data[i] << std::endl;
}

答案 2 :(得分:0)

声称它与海湾合作委员会“有效”是夸张的说法:

g++ -std=c++17 -fPIC -g -Wall -Wextra -Wwrite-strings -Wno-parentheses -Wpedantic -Warray-bounds -Weffc++      16486983.cpp    -o 16486983
16486983.cpp: In member function ‘S& S::operator=(const char*)’:
16486983.cpp:7:30: warning: unused parameter ‘rhs’ [-Wunused-parameter]
   S & operator=(const char * rhs) { a = 100; b = 300; return *this; }
                              ^~~
16486983.cpp: In function ‘int main(int, char**)’:
16486983.cpp:20:10: error: ‘ptrdiff_t’ was not declared in this scope
     for (ptrdiff_t i = 0; i < N; ++i)
          ^~~~~~~~~
16486983.cpp:20:10: note: suggested alternatives:
In file included from /usr/include/c++/6/iostream:38:0,
                 from 16486983.cpp:1:
/usr/include/x86_64-linux-gnu/c++/6/bits/c++config.h:202:28: note:   ‘std::ptrdiff_t’
   typedef __PTRDIFF_TYPE__ ptrdiff_t;
                            ^~~~~~~~~
/usr/include/x86_64-linux-gnu/c++/6/bits/c++config.h:202:28: note:   ‘std::ptrdiff_t’
16486983.cpp:20:27: error: ‘i’ was not declared in this scope
     for (ptrdiff_t i = 0; i < N; ++i)
                           ^
16486983.cpp:22:10: error: ‘ptrdiff_t’ was not declared in this scope
     for (ptrdiff_t i = 0; i < N; ++i)
          ^~~~~~~~~
16486983.cpp:22:10: note: suggested alternatives:
In file included from /usr/include/c++/6/iostream:38:0,
                 from 16486983.cpp:1:
/usr/include/x86_64-linux-gnu/c++/6/bits/c++config.h:202:28: note:   ‘std::ptrdiff_t’
   typedef __PTRDIFF_TYPE__ ptrdiff_t;
                            ^~~~~~~~~
/usr/include/x86_64-linux-gnu/c++/6/bits/c++config.h:202:28: note:   ‘std::ptrdiff_t’
16486983.cpp:22:27: error: ‘i’ was not declared in this scope
     for (ptrdiff_t i = 0; i < N; ++i)
                           ^
16486983.cpp:14:14: warning: unused parameter ‘argc’ [-Wunused-parameter]
 int main(int argc, char ** argv) {
              ^~~~
16486983.cpp:14:28: warning: unused parameter ‘argv’ [-Wunused-parameter]
 int main(int argc, char ** argv) {
                            ^~~~

这些需要修复;谢天谢地,这并不难:

#include <cstddef>
#include <iostream>
#include <string>

struct S {
  int a,b;
  S() : a(99), b(299) {}
  S & operator=(const char*) { a = 100; b = 300; return *this; }
};
std::ostream & operator<<(std::ostream & os, const S & s) { os << "(" << s.a << "," << s.b << ")"; return os; }


//typedef std::string T;
typedef S T;
int main() {
    std::size_t N = 100;
    std::allocator<T> mem;
    T * data = mem.allocate(N);
    new(data)T[N]();

    for (std::size_t i = 0; i < N; ++i)
        data[i] = "HELLO WORLD";
    for (std::size_t i = 0; i < N; ++i)
        std::cout << data[i] << std::endl;
}

现在它运行了,差不多是Valgrind-clean。但裸露的位置看起来很奇怪 - 如果你使用的是分配器,你应该使用它的construct()

for (std::size_t i = 0; i < N; ++i)
    mem.construct(data+i);

¹别忘了释放内存:

for (std::size_t i = 0; i < N; ++i)
    mem.destroy(data+i);
mem.deallocate(data, N);