我有一个第三方库,它使用char* (非常量)作为字符串值的占位符。为这些数据类型赋值的正确且安全的方法是什么?我有以下测试基准,它使用我自己的timer类来测量执行时间:
#include "string.h"
#include <iostream>
#include <sj/timer_chrono.hpp>
using namespace std;
int main()
{
sj::timer_chrono sw;
int iterations = 1e7;
// first method gives compiler warning:
// conversion from string literal to 'char *' is deprecated [-Wdeprecated-writable-strings]
cout << "creating c-strings unsafe(?) way..." << endl;
sw.start();
for (int i = 0; i < iterations; ++i)
{
char* str = "teststring";
}
sw.stop();
cout << sw.elapsed_ns() / (double)iterations << " ns" << endl;
cout << "creating c-strings safe(?) way..." << endl;
sw.start();
for (int i = 0; i < iterations; ++i)
{
char* str = new char[strlen("teststr")];
strcpy(str, "teststring");
}
sw.stop();
cout << sw.elapsed_ns() / (double)iterations << " ns" << endl;
return 0;
}输出:
creating c-strings unsafe(?) way...
1.9164 ns
creating c-strings safe(?) way...
31.7406 ns虽然“安全”的get方法摆脱了编译器的警告,但根据这个基准测试,它使代码慢了大约15-20倍(每次迭代1.9纳秒,而每次迭代31.7纳秒)。什么是正确的方式,这种“不推荐”的方式有什么危险?
发布于 2013-05-02 21:38:49
C++标准是明确的:
一个普通的字符串字面量的类型是“n个常量字符的数组”(C++11中的2.14.5.8节)。
和
尝试修改字符串文字的效果未定义(C++11中的第2.14.5.12节)。
对于在编译时已知的字符串,获取non-const char*的安全方法如下
char literal[] = "teststring";然后您就可以安全地
char* ptr = literal;如果在编译时你不知道字符串,但知道它的长度,你可以使用一个数组:
char str[STR_LENGTH + 1];如果您不知道长度,那么您将需要使用动态分配。确保在不再需要字符串时释放内存。
只有当API没有取得你所传递的char*的所有权时,这才能起作用。
如果它试图在内部释放字符串,那么它应该在文档中说明,并通知您分配字符串的正确方式。您需要将您的分配方法与API内部使用的方法相匹配。
这个
char literal[] = "test";将创建一个具有自动存储的5个字符的本地数组(这意味着当执行离开声明变量的作用域时,变量将被销毁),并使用字符't','e','s','t‘和'\0’初始化数组中的每个字符。
您可以稍后编辑这些字符:literal[2] = 'x';
如果你这样写:
char* str1 = "test";
char* str2 = "test";然后,根据编译器的不同,str1和str2可能是相同的值(即,指向相同的字符串)。
(是否所有字符串文字都是不同的(即存储在不重叠的对象中)是由实现定义的。在C++标准的第2.14.5.12节中)
它们存储在内存的只读部分中也可能是真的,因此任何修改字符串的尝试都将导致异常/崩溃。
它们实际上也是const char*类型的,所以这一行:
char* str = "test";
实际上丢弃了字符串上的常量,这就是编译器将发出警告的原因。
发布于 2013-05-02 21:07:13
这种不安全的方法适用于编译时已知的所有字符串。
你的“安全”方式会泄漏内存,这是相当可怕的。
通常你会有一个接受const char *的C,所以你可以在C++中使用合适的安全方式,即std::string和它的c_str()方法。
如果你的C应用程序接口接管了字符串的所有权,那么你的“安全方式”就有另一个缺陷:你不能混合使用new[]和free(),不允许将使用C++ new[]操作符分配的内存传递给期望对其调用free()的C应用程序接口。如果C应用编程接口不想在字符串后面调用free(),那么在C++端使用new[]就可以了。
这也是C++和C的奇怪混合。
发布于 2013-05-02 21:17:47
你似乎对C字符串有一个根本性的误解。
cout << "creating c-strings unsafe(?) way..." << endl;
sw.start();
for (int i = 0; i < iterations; ++i)
{
char* str = "teststring";
} 在这里,你只是将一个指针赋给一个字符串常量。在C和C++中,字符串字面值的类型为char[N],您可以将指针分配给字符串字面值数组,因为数组"decay“。(但是,不建议将非常数指针赋给字符串文字。)
但是,将指针赋给字符串文字不是您想要做的事情。您的API需要一个非常量字符串。字符串字面值为const。
为这些char*字符串赋值的正确且安全的方法是什么?
这个问题没有通用的答案。无论何时使用C字符串(或一般的指针),都需要处理所有权的概念。使用std::string,C++会自动为您处理此问题。在内部,std::string拥有一个指向char*数组的指针,但它为您管理内存,因此您不需要关心它。但是当你使用原始的C字符串时,你确实需要把精力放在内存管理上。
如何管理内存取决于您对程序所做的操作。如果你用new[]分配一个C字符串,那么你需要用delete[]释放它。如果你用malloc分配它,那么你必须用free()释放它。在C++中使用C字符串的一个很好的解决方案是使用智能指针,它获得分配的C字符串的所有权。(但您需要使用通过delete[]释放内存的deleter )。或者,您可以直接使用std::vector<char>。和往常一样,不要忘记为终止的null char分配空间。
此外,第二个循环如此慢的原因是因为它在每次迭代中分配内存,而第一个循环只是将一个指针分配给静态分配的字符串文字。
https://stackoverflow.com/questions/16338772
复制相似问题