当前位置: 首页 > 知识库问答 >
问题:

当调用'string::c_str()'时,实际会执行什么操作?

连时铭
2023-03-14

调用string::c_str()时,实际会执行什么操作?

  1. 字符串::c_str()将分配内存,复制字符串对象的内部数据并在新分配的内存中追加一个以null结尾的字符?

或者

在这个问题的答案的评论中有人说,C11要求std::string为后面的'\0'分配一个额外的char。因此,第二种选择似乎是可能的。

另一个人说,std::string操作——例如迭代、串联和元素突变——不需要零终止符。除非将字符串传递给需要以零结尾的字符串的函数,否则可以忽略该字符串。

为什么实现者通常让. data()和.c_str()做同样的事情?

因为这样做效率更高。这是唯一的办法。data()返回不以null结尾的内容,这将是必须的。c_str()或。data()复制它们的内部缓冲区,或者只使用两个缓冲区。拥有一个以null结尾的缓冲区总是意味着在实现std::string时,始终可以只使用一个内部缓冲区。

所以我现在真的很困惑,当调用string::c_str()时,实际会做什么?

更新:

如果c_str()实现为简单地返回指针,则它已经被分配和管理。

A.由于c_str()必须以null结尾,因此内部缓冲区需要始终以null结尾,即使对于空的std::string,例如:std::string demo_str;,demo_str的内存中应该有一个\0。我说得对吗?

B.调用std::string::substr()时会发生什么?自动将\0附加到子字符串?

共有3个答案

裘禄
2023-03-14

我们已经提供了很多很好的答案和评论。但是为了证明std::string通常由以null结尾的字符串支持,我提供了一个简单但幼稚的实现。它不完整,不进行错误检查,当然也没有优化。但它已经足够完整,可以向您展示字符串类通常是如何使用以null结尾的缓冲区作为成员变量来实现的。

class string
{
public:

    string()
    {
        assign("", 0);
    }

    string(const char* s)
    {
        assign(s, strlen(s));
    }

    string(const char* s, size_t len)
    {
        assign(s, len);
    }

    string(const string& s)
    {
        assign(s._ptr, s._len);
    }

    ~string()
    {
       delete [] _ptr;
    }

    string& operator=(const string& s)
    {
        const char* oldptr = _ptr;
        assign(s._ptr, s._len);
        delete [] oldptr;
    }

    const char* data()
    {
        return _ptr;
    }

    const char* c_str()
    {
       return _ptr;
    }

    size_t length()
    {
        return _len;
    }

    // substr always returns a new string
    std::string substr(size_t pos, size_t count)
    {
        std::string s(_ptr+pos, count);
        return s;  
    }

private:
    char* _ptr;
    size_t _len;

    void assign(const char* ptr, size_t len)
    {
        _len = len;        
        _ptr = new char[_len+1]; // +1 for null termination
        memcpy(_ptr, ptr, len); 
        _ptr[_len] = '\0';       // always null terminate
    }
};
南门正业
2023-03-14

这里有一个经验“证明”的复杂性。c_str()是o(1):

#include <stdio.h>
#include <string>
using namespace std;
int main(int argc, char **argv)
{
    std::string x(5000000, 'b'); // <--- single time allocation
    // std::string x(5, 'b'); // <--- compare to a much shorter string
    for (unsigned int i=0;i<1000000;i++)
    {
        const char *y = x.c_str(); // <--- copy entire content ?
    }
}
  • 使用-O0编译以避免优化任何内容
  • 计时2个版本:我获得相同的性能
  • 这是一个经验“证明”(至少我的机器的实现)
    • 提取以null结尾的字符串的内部表示形式
    • 不会每次都复制内容。调用c_str()

斜和硕
2023-03-14

由于C11,std::字符串::c_str()std::字符串::data()都需要返回指向字符串内部缓冲区的指针。由于c_str()(但不是data())必须以null结尾,这实际上要求内部缓冲区始终以null结尾,尽管null终止符不按size()/long()计数,也不按std::字符串迭代器等返回。

在C11之前,c_str()的行为在技术上是特定于实现的,但是我见过的大多数实现都是这样工作的,因为这是实现它的最简单和最明智的方法。C11只是标准化了已经广泛使用的行为。

使现代化

从C 11开始,缓冲区总是以null结尾,即使对于空字符串也是如此。然而,这并不意味着当字符串为空时需要动态分配缓冲区。它可以指向SSO缓冲区,甚至可以指向单个静态nul字符。无法保证字符串内容更改时,c_str()/data()返回的指针仍指向同一内存地址。

std::string::substr()返回一个新的std::string,它有自己的以null结尾的缓冲区。从中复制的字符串不受影响。

 类似资料:
  • 本文向大家介绍请说明当PHP switch case执行case 0时会发生什么?,包括了请说明当PHP switch case执行case 0时会发生什么?的使用技巧和注意事项,需要的朋友参考一下 PHP是一种松散类型的语言。当与大小写0匹配时,字符串与最接近的整数匹配。 假设我们有以下开关表达式- 现在,我们将匹配情况0- 我们还将匹配非零情况- 示例 输出结果

  • 我有一个ViewModel处理我的业务逻辑,我正在使用Koin将它注入到我的活动和每个片段中。然而,在我从片段A-片段B导航并导航回片段A之后,我的观察者再次被触发。为什么会发生这种情况?当我返回时,如何阻止这种onChanged被触发? 我尝试将'this'和'view LifecycleOwner'设置为LiveData的LifecycleOwner。 我还尝试将observable移动到on

  • 根据对条件dplyr评估的讨论,我想根据传递的数据帧中是否存在参考列,有条件地在管道中执行一个步骤。 和生成的结果应该是相同的。 对于可用列,传递的对象与初始数据帧不对应。原始代码返回错误消息: :未找到对象 我尝试过其他语法(运气不佳): 我想扩展这个问题,以解释调用中右侧的评估。例如,下面的语法试图过滤第一个可用值。mtcars% 预期的是,调用的结果是错误消息: 中出错:结果的长度必须为32

  • 我在写一个玩家轮流参加的游戏。在一个回合结束时,我将我的数据发送到服务器,并更新我的数据库,让我知道现在轮到另一个玩家了。问题是,如果有人在中途扼杀了应用程序怎么办?我是说去找任务经理然后杀了它。 编辑:我还应该提到这是在一个片段中,我正在检查这个,但不要认为这会有什么不同。

  • 问题内容: 我阅读的所有资料都提到了几个案例,并以“其他一些案例”作了总结。在视图/活动中调用onSaveInstanceState方法时,所有情况是什么? 问题答案: 该文档称 在活动被杀死之前将调用此方法,以便将来在将来返回某个时间时可以恢复其状态。

  • 问题内容: 当某些调用工作而另一些调用失败时,$ q.all()会发生什么? 我有以下代码: 当所有$ http调用都工作时,则allResponses数组将填充数据。 当我失败时,据我了解,第二个函数将被调用,并且错误变量将给出详细信息。 但是,有人可以帮我解释一下,如果某些响应有效而其他响应失败了怎么办? 问题答案: 我相信,由于Promise库是基于实现的,因此,一旦第一个Promise被拒