首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >处理char*字符串的正确方式是什么?

处理char*字符串的正确方式是什么?
EN

Stack Overflow用户
提问于 2013-05-02 21:05:10
回答 3查看 8.4K关注 0票数 7

我有一个第三方库,它使用char* (非常量)作为字符串值的占位符。为这些数据类型赋值的正确且安全的方法是什么?我有以下测试基准,它使用我自己的timer类来测量执行时间:

代码语言:javascript
复制
#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;

}

输出:

代码语言:javascript
复制
creating c-strings unsafe(?) way...
1.9164 ns
creating c-strings safe(?) way...
31.7406 ns

虽然“安全”的get方法摆脱了编译器的警告,但根据这个基准测试,它使代码慢了大约15-20倍(每次迭代1.9纳秒,而每次迭代31.7纳秒)。什么是正确的方式,这种“不推荐”的方式有什么危险?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-05-02 21:38:49

C++标准是明确的:

一个普通的字符串字面量的类型是“n个常量字符的数组”(C++11中的2.14.5.8节)。

尝试修改字符串文字的效果未定义(C++11中的第2.14.5.12节)。

对于在编译时已知的字符串,获取non-const char*的安全方法如下

代码语言:javascript
复制
char literal[] = "teststring";

然后您就可以安全地

代码语言:javascript
复制
char* ptr = literal;

如果在编译时你不知道字符串,但知道它的长度,你可以使用一个数组:

代码语言:javascript
复制
char str[STR_LENGTH + 1];

如果您不知道长度,那么您将需要使用动态分配。确保在不再需要字符串时释放内存。

只有当API没有取得你所传递的char*的所有权时,这才能起作用。

如果它试图在内部释放字符串,那么它应该在文档中说明,并通知您分配字符串的正确方式。您需要将您的分配方法与API内部使用的方法相匹配。

这个

代码语言:javascript
复制
char literal[] = "test";

将创建一个具有自动存储的5个字符的本地数组(这意味着当执行离开声明变量的作用域时,变量将被销毁),并使用字符't','e','s','t‘和'\0’初始化数组中的每个字符。

您可以稍后编辑这些字符:literal[2] = 'x';

如果你这样写:

代码语言:javascript
复制
char* str1 = "test";
char* str2 = "test";

然后,根据编译器的不同,str1str2可能是相同的值(即,指向相同的字符串)。

(是否所有字符串文字都是不同的(即存储在不重叠的对象中)是由实现定义的。在C++标准的第2.14.5.12节中)

它们存储在内存的只读部分中也可能是真的,因此任何修改字符串的尝试都将导致异常/崩溃。

它们实际上也是const char*类型的,所以这一行:

char* str = "test";

实际上丢弃了字符串上的常量,这就是编译器将发出警告的原因。

票数 10
EN

Stack Overflow用户

发布于 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的奇怪混合。

票数 5
EN

Stack Overflow用户

发布于 2013-05-02 21:17:47

你似乎对C字符串有一个根本性的误解。

代码语言:javascript
复制
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分配空间。

此外,第二个循环如此慢的原因是因为它在每次迭代中分配内存,而第一个循环只是将一个指针分配给静态分配的字符串文字。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16338772

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档