关于C语言中字符串缺陷的补充

目录

最重要的一点:

 string类的length

string类中的capacity

为什么是3而不是5

下期内容:新的章节:C++的基本容器。


这一章我们来补充上一章没有提到的,但是我认为还是有必要明确的内容。

上期链接  C语言字符串缺陷


最重要的一点:

在C++中,std::string的length成员函数不需要像strlen那样遍历字符串以寻找空字符(\0)来确定字符串的长度,这带来了几个显著的好处:

1.效率提升:由于std::string内部已经维护了一个表示字符串长度的字段,因此length函数可以直接返回这个值,其时间复杂度为O(1)。相比之下,strlen需要遍历整个字符串直到找到\0,其时间复杂度与字符串的长度成正比,即O(n)。因此,对于非常长的字符串,使用length可以显著提高效率。

2.安全性:由于std::string自动管理内存并包含字符串的结束标志(虽然这个标志对于length来说不是必需的),它避免了缓冲区溢出的风险,这是C风格字符串(使用strlen)时常见的问题之一。如果程序员不小心越界访问了C风格字符串的数组,可能会读取到未定义的数据,导致程序崩溃或安全漏洞。

3.易用性:std::string提供了更丰富的功能,如字符串连接、查找、替换等,这些操作都是基于其内部维护的字符串长度和其他元数据来高效完成的。相比之下,C风格字符串的操作更加原始和低效,需要程序员手动管理内存和字符串的结束。

4.类型安全:std::string是一个类类型,它提供了类型安全,这意味着在编译时就可以检查到很多潜在的错误,比如尝试将std::string对象与C风格字符串进行不兼容的操作。而C风格字符串只是字符数组,它们缺乏类型信息,因此在编译时难以发现与它们相关的错误。

综上所述,std::string的length成员函数不需要寻找\0来确定字符串长度,这带来了效率、安全性、易用性和类型安全方面的好处。这也是C++推荐使用std::string而不是C风格字符串的原因之一。

所以完美解决字符串缺陷的是redis库。
 


 string类的length

性能更高(对比strlen)

std::string类:在C++中,std::string类是一个用于表示和操作字符串的容器。与C风格字符串不同,std::string对象内部存储了字符串的长度信息,因此它不受\0字符的影响。当你调用std::string对象的length()或size()成员函数时,它们会返回字符串中字符的数量,包括任何可能的\0字符(尽管在正常情况下,你不会在std::string对象中包含\0字符,因为这样做通常没有实际意义)。但是,如果出于某种原因,std::string对象确实包含了\0字符,这些字符仍会被计入长度。

内部存储:std::string 类内部通常会维护一个表示字符串长度的成员变量,这意味着 length() 或 size() 方法的调用几乎是常数时间的操作,因为它们只需要返回这个内部存储的长度值。而 strlen 函数需要遍历整个字符串,直到找到null字符为止,这可能导致线性时间的性能开销,特别是对于长字符串。


string类中的capacity

关于这个我认为有必要说一下,因为这个和之前的sizeof,length,strlen这三个有点相像的意味。

在C++中,std::string 类用于表示和操作字符串。关于 capacity() 成员函数,它并不是用来直接获取字符串的长度(这由 size() 或 length() 成员函数完成),而是用来获取字符串当前分配的内存空间(以字符为单位)的大小,即在不重新分配内存的情况下,字符串可以增长到的大小。

(预分配,预内存,可以这么理解。害怕后续对存储的内容扩展而导致内存不足,但是有上限。)

字符串的内存管理是由C++标准库自动进行的,以优化性能和空间利用率。当你向一个 std::string 对象添加字符时,如果超出了当前的 capacity(),则可能会重新分配更大的内存空间以容纳新的字符,此时 capacity() 可能会增加。但是,减少字符串的长度(比如使用 erase() 方法)不会自动减少 capacity(),除非进行某些特定操作(如 shrink_to_fit()),它会请求库释放不需要的内存,但这仅仅是请求,实际行为依赖于库的实现。

示例代码:

#include <iostream>  
#include <string>  
  
int main() {  
    std::string str = "Hello";  
    std::cout << "Initial capacity: " << str.capacity() << std::endl; // 初始容量可能因实现而异  
  
    // 向字符串添加字符,可能需要重新分配内存  
    str += " World!";  
    std::cout << "After appending, capacity: " << str.capacity() << std::endl; // 容量可能增加  
  
    // 减少字符串的长度,但capacity不一定会减少  
    str.resize(5); // 仅保留 "Hello"  
    std::cout << "After resizing, capacity: " << str.capacity() << std::endl; // 容量可能未变  
  
    // 尝试减少capacity到与size相同(具体效果取决于实现)  
    str.shrink_to_fit();  
    std::cout << "After shrink_to_fit, capacity: " << str.capacity() << std::endl; // 容量可能减少  
  
    return 0;  
}

请注意,capacity() 的确切值和行为可能会根据标准库的具体实现(如 libc++、libstdc++ 等)而有所不同。特别是,对于小的字符串,库可能会优化内存使用,即使字符串的实际长度很小,也可能会分配比实际需要更多的内存。这是因为小字符串的内存分配通常成本较高,库可能会采取一些策略来减少这种开销。


为什么是3而不是5

这里回答一下上次代码里hel\0lo的length是3的问题。

在C++中,当你使用字符串字面量(如"hel\0lo")来初始化std::string对象时,字符串字面量中的\0(空字符)并不会像它在C风格的字符串(即字符数组)中那样被特别处理为字符串的终结符。然而,在这个特定的上下文中,\0实际上在字符串字面量中就被忽略了,因为字符串字面量在编译时就被转换成了字符数组,而\0通常只会在字符数组(C风格字符串)的末尾出现,以标记字符串的结束。

但是,当字符串字面量被用来初始化std::string对象时,std::string的构造函数会读取直到字符串字面量末尾(在C++中,字符串字面量的“末尾”是由编译器自动添加的\0,但这个\0不会成为std::string内容的一部分)的所有字符,并在内部存储这些字符。然而,在这个特定的例子中,由于字符串字面量内部显式地包含了一个\0字符(位于'l''o'之间),这个\0字符在C++标准中并不会阻止后面的字符(在这个例子中是'o')被读取。但是,实际上,当编译器处理这个字符串字面量时,它可能只会将"hel"作为初始化std::string的有效部分,因为\0在字符串字面量内部的位置是不寻常的,并且它通常不会出现在这样的上下文中(除了作为字符串的结尾)。

然而,重要的是要理解,在标准的C++实现中,直接使用"hel\0lo"这样的字符串字面量来初始化std::string通常不会按你期望的方式工作,因为字符串字面量中的\0不会被特别处理为字符串内容的一部分。相反,由于\0的存在,它可能会导致编译器、链接器或运行时环境中的混淆或错误(尽管在这个特定情况下,它可能只是被简单地忽略了)。

但是,在你的例子中,输出是3而不是5的原因是std::string的构造函数只看到了"hel"这三个字符,因为它在内部处理字符串字面量时,可能遇到了\0并停止了读取(尽管这不是std::string构造函数的标准行为,但可能是某些实现或特定上下文的特殊行为)

如果你确实想要std::string包含\0字符作为其内容的一部分,你需要使用不同的方法,比如:

string zz2 = string("hel") + char(0) + string("lo"); // 注意这里显式地添加了char(0)  
// 但通常不建议这样做,因为std::string不期望包含'\0'作为内容  
// 如果你确实需要处理包含'\0'的字符串,考虑使用std::vector<char>或其他容器

然而,请注意,上面的代码虽然可以工作,但它并不是处理包含\0字符的字符串的标准或推荐方式。在C++中,如果你需要处理可能包含\0字符的字符串,使用std::vector<char>或类似的容器可能更合适。

这就是这章的内容,有点长,但是应该会为你解决一些疑惑。

下期内容:新的章节:C++的基本容器。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值