在 Visual Studio C++ 中初始化 std::string 数组时崩溃
Crash when initializing a std::string array in Visual Studio C++
问题:我正在尝试为 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
中可用。
以下答案的解决方案是循环并分别构造每个项目。
你不是应该使用 construct 吗?
#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;
}
它与 GCC 一起“工作”的说法有些夸张:
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。但是 naked placement-new 看起来很奇怪 - 如果你使用分配器,你应该使用它的 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);
问题在于此处描述的未指定开销: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;
}
但这不能保证总是有效,因为 "unspecified overhead" 理论上可能大于 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);
}
问题:我正在尝试为 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
中可用。
以下答案的解决方案是循环并分别构造每个项目。
你不是应该使用 construct 吗?
#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;
}
它与 GCC 一起“工作”的说法有些夸张:
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。但是 naked placement-new 看起来很奇怪 - 如果你使用分配器,你应该使用它的 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);
问题在于此处描述的未指定开销: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;
}
但这不能保证总是有效,因为 "unspecified overhead" 理论上可能大于 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);
}