一种特别的 itoa 实现
一般实现
一般我们想实现 int 转 char*,往往采用下面的形式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
   | #include <algorithm> #include <iostream>
  void itoa(char buf[], int value) {     bool negative = value < 0;     unsigned int abs_value = value >= 0 ? value : -value;
      char* p = buf;     do {         unsigned int lsd = abs_value % 10;         abs_value /= 10;         *p++ = char('0' + lsd);     } while (abs_value > 0);
      if (negative) {         *p++ = '-';     }     *p = '\0';     std::reverse(buf, p); }
  void f(int i) {     char buf[1024];     memset(buf, 0, sizeof(buf));
      itoa(buf, i);     std::cout << buf << std::endl; }
  int main() {     f(0);             f(1);             f(-1);            f(INT_MAX);       f(INT_MIN);   }
 
  | 
 
特别实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
   | void itoa(char buf[], int value) {     static const char digits[] = {'9', '8', '7', '6', '5', '4', '3', '2', '1', '0',                                   '1', '2', '3', '4', '5', '6', '7', '8', '9'};     static const char* zero = digits + 9;
      int i = value;     char* p = buf;     do {         int lsd = i % 10;         i /= 10;         *p++ = zero[lsd];     } while (i != 0);
      if (value < 0) {         *p++ = '-';     }     *p = '\0';     std::reverse(buf, p); }
 
  | 
 
此算法基于 (-1) / 10 = 0 (-1) % 10 = -1 的规则。(C99 和 C++11 都规定商向 0 取整)
再探 std::string
直接拷贝(eager copy)
实现 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14
   | class string { public:     const_pointer data() const  { return start; }     iterator begin()            { return start; }     iterator end()              { return finish; }     size_type size() const      { return finish - start; }     size_type capacity() const  { return end_of_storage - start; }
 
  private:     char* start;     char* finish;     char* end_of_storage; };
 
  | 
 
实现 2
1 2 3 4 5 6 7 8 9 10 11 12 13 14
   | class string { public:     const_pointer data() const  { return start; }     iterator begin()            { return start; }     iterator end()              { return start + size_; }     size_type size() const      { return size_; }     size_type capacity() const  { return capacity_; }
 
  private:     char* start;     size_t size_;     size_t capacity_; };
 
  | 
 
由于 sizeof(char*) == sizeof(size_t),所以在空间占用上,std::string 实现 2 与实现 1 没有多大的改变。但是单个字符串往往不到几百兆字节,在 64-bit 下可以将 size_t 改成 uint32_t 从而减小对象的大小,由 24 字节减小为 16 字节。
写时复制(copy-on-write)
1 2 3 4 5 6 7 8 9
   | class cow_string {     struct Rep {         size_t size;         size_t capacity;         size_t refcount;         char*  data[1];     };     char* start; };
 
  | 
 
start = data[0]
COW 时间复杂度不一定符合直觉,它拷贝字符串是 O(1) 时间,但拷贝之后第一次 operator[] 有可能是 O(N) 时间。
段字符串优化(SSO)
1 2 3 4 5 6 7 8 9
   | class sso_string {     char* start;     size_t size;     static const int kLocalSize = 15;     union {         char buffer[kLocalSize + 1];         size_t capacity;     } data; };
 
  | 
 
短字符串(size <= 15)放在栈上,长字符串放在堆上。
SSO string 在 64-bits 中有个小小的优化空间:如果允许字符串 max_size() 不大于 4GiB 的话,可以用 uint32_t 来表示长度和容量,这样同样是 32 字节的 string 对象,local buffer 可以增大至 19 字节。
1 2 3 4 5 6 7 8 9 10 11
   | class sso_string {     char* start;     uint32_t size;
      static const int kLocalSize = sizeof(void*) == 8 ? 19 : 15;
      union {         char buffer[kLocalSize + 1];         uint32_t capacity;     } data; };
 
  | 
 
总结
建议针对不同的应用负载选用不同的 string:
- 对于短字符串,用 SSO string;
 
- 对于中等长度的字符串,用 eager copy;
 
- 对于长字符串,用 COW。