C和C++安全编码(原书第2版)
上QQ阅读APP看书,第一时间看更新

2.4.4 C++ std::basic_string

此前我们描述了一个常见的编程漏洞,它使用C++的提取操作符operator>>从标准的std::cin iostream对象读入输入,并写入一个字符数组。虽然设置字段宽度消除了缓冲区溢出漏洞,但它没有解决截断的问题。此外,达到最大字段宽度且输入流中剩余的字符被提取操作符的下一次调用使用时,可能会导致意想不到的程序的行为。

C++程序员可以选择使用在ISO/IEC 14882中定义的标准的std::string类。std::string类是std::basic_string模板在char类型上的一个特化。std::wstring类是st::basic_string模板在wchar_t类型上的一个特化。

basic_string类代表一个字符序列。它支持序列操作以及字符串操作,如搜索和串联,并由字符类型参数化。

basic_string类使用一种动态方法,按需要为字符串分配内存—这意味着在所有的情况下,size()<=capacity()。basic_string类很方便,因为语言直接支持这个类。此外,许多现有的库已经使用这个类,这简化了集成工作。

basic_string类实现了“由被调用者分配,由被调用者释放”的内存管理策略。这是最安全的方法,但它仅支持C++。因为basic_string管理内存,所以调用者并不需要担心内存管理的细节。例如,字符串串联的简单处理如下所示。


1  string str1 = "hello, ";
2  string str2 = "world";
3  string str3 = str1 + str2;

在其内部,basic_string使用动态分配内存的方法,缓冲区总是自动调整大小以容纳所需的数据,通常是通过调用realloc()函数。这些方法扩展性优于对应的C函数,而且不丢弃超出的数据。

下面的程序显示了一个解决方案,从std::cin提取字符到一个std::string中,它使用一个std::string对象来代替一个字符数组:


01  #include <iostream>
02  #include <string>
03  using namespace std;
04
05  int main(void) {
06    string str;
07
08    cin >> str;
09    cout << "str 1: " << str << '\n';
10  }

这个程序简单而优雅,处理了缓冲区溢出和字符串截断问题,并且其行为方式是可预见的。你还有什么想要的呢?

basic_string类比以空字符结尾的字节串更不容易有安全漏洞,但编码错误仍然可能导致的安全漏洞。使用basic_string类时,应考虑的领域之一是迭代器。可以使用迭代器遍历一个字符串的内容,如下所示。


1  string::iterator i;
2  for (i = str.begin(); i != str.end(); ++i) {
3    cout << *i;
4  }