【轉】當心stringstream.str()字符串用法的陷阱

--------------------- 
做者:心中那自由的世界 
來源:CSDN 
原文:https://blog.csdn.net/119365374/article/details/77446786 

ios

在編寫應用程序時,咱們常常要使用到字符串。C++標準庫中的<string>和<sstream>爲咱們操做字符串提供了不少的方便,例如:對象封裝、安全和自動的類型轉換、直接拼接、沒必要擔憂越界等等。但今天咱們並不想長篇累牘得去介紹這幾個標準庫提供的功能,而是分享一下stringstream.str()的一個有趣的現象。咱們先來看一個例子:

安全

 1    #include <string>
 2    #include <sstream>
 3    #include <iostream>
 4  
 5    using namespace std;
 6  
 7    int main()
 8    {
 9        stringstream ss("012345678901234567890123456789012345678901234567890123456789");
10       stringstream t_ss("abcdefghijklmnopqrstuvwxyz");
11       string str1(ss.str());
12  
13       const char* cstr1 = str1.c_str();
14       const char* cstr2 = ss.str().c_str();
15       const char* cstr3 = ss.str().c_str();
16       const char* cstr4 = ss.str().c_str();
17       const char* t_cstr = t_ss.str().c_str(); 
18  
19       cout << "------ The results ----------" << endl
20            << "cstr1:\t" << cstr1 << endl 
21            << "cstr2:\t" << cstr2 << endl
22            << "cstr3:\t" << cstr3 << endl
23            << "cstr4:\t" << cstr4 << endl
24            << "t_cstr:\t" << t_cstr << endl
25            << "-----------------------------"  << endl;
26  
27       return 0;
28   }

 

在看這段代碼的輸出結果以前,先問你們一個問題,這裏cstr一、cstr二、cstr3和cstr4 打印出來結果是同樣的麼?(相信讀者內心會想:結果確定不同的嘛,不然不用在這裏「故弄玄虛」了。哈哈函數

接下來,咱們來看一下這段代碼的輸出結果:spa

 

    ------ The results ----------
    cstr1:  012345678901234567890123456789012345678901234567890123456789
    cstr2:  012345678901234567890123456789012345678901234567890123456789
    cstr3:  abcdefghijklmnopqrstuvwxyz
    cstr4:  abcdefghijklmnopqrstuvwxyz
    t_cstr: abcdefghijklmnopqrstuvwxyz
    -----------------------------

 

這裏,咱們驚奇地發現cstr3和cstr4居然不是ss所表示的數字字符串,而是t_ss所表示的字母字符串,這也太詭異了吧,但咱們相信「真相只有一個」。下面咱們經過再加幾行代碼來看看,爲何會出現這個「詭異」的現象。.net

 

   #include <string>
   #include <sstream>
   #include <iostream>
 
   using namespace std;
 
   #define PRINT_CSTR(no) printf("cstr" #no " addr:\t%p\n",cstr##no)
   #define PRINT_T_CSTR(no) printf("t_cstr" #no " addr:\t%p\n",t_cstr##no)
 
  int main()
  {
      stringstream ss("012345678901234567890123456789012345678901234567890123456789");
      stringstream t_ss("abcdefghijklmnopqrstuvwxyz");
      string str1(ss.str());
 
      const char* cstr1 = str1.c_str();
      const char* cstr2 = ss.str().c_str();
      const char* cstr3 = ss.str().c_str();
      const char* cstr4 = ss.str().c_str();
      const char* t_cstr = t_ss.str().c_str(); 
 
      cout << "------ The results ----------" << endl
           << "cstr1:\t" << cstr1 << endl 
           << "cstr2:\t" << cstr2 << endl
           << "cstr3:\t" << cstr3 << endl
           << "cstr4:\t" << cstr4 << endl
           << "t_cstr:\t" << t_cstr << endl
           << "-----------------------------"  << endl;
      printf("\n------ Char pointers ----------\n");
      PRINT_CSTR(1);
      PRINT_CSTR(2);
      PRINT_CSTR(3);
      PRINT_CSTR(4);
      PRINT_T_CSTR();
 
      return 0;
  }

在上述代碼中,咱們把那幾個字符串對應的地址打印出來,其輸出結果爲:code

    ------ The results ----------
    cstr1:  012345678901234567890123456789012345678901234567890123456789
    cstr2:  012345678901234567890123456789012345678901234567890123456789
    cstr3:  abcdefghijklmnopqrstuvwxyz
    cstr4:  abcdefghijklmnopqrstuvwxyz
    t_cstr: abcdefghijklmnopqrstuvwxyz
    -----------------------------
 
    ------ Char pointers ----------
    cstr1 addr:     0x100200e4
    cstr2 addr:     0x10020134
    cstr3 addr:     0x10020014
    cstr4 addr:     0x10020014
    t_cstr addr:    0x10020014

 

從上面的輸出,咱們發現cstr3和cstr4字串符的地址跟t_cstr是同樣,所以,cstr三、cstr4和t_cstr的打印結果是同樣的。按照咱們一般的理解,當第17-19行調用ss.str()時,將會產生三個string對象,其對應的字符串也將會是不一樣的地址。對象

而打印的結果告訴咱們,真實狀況不是這樣的。其實,streamstring在調用str()時,會返回臨時的string對象。而由於是臨時的對象,因此它在整個表達式結束後將會被析構。因爲緊接着調用的c_str()函數將獲得的是這些臨時string對象對應的C string,而它們在這個表達式結束後是不被引用的,進而這塊內存將被回收而可能被別的內容所覆蓋,所以咱們將沒法獲得咱們想要的結果。雖然有些狀況下,這塊內存並無被別的內容所覆蓋,因而咱們仍然可以讀到咱們指望的字符串,(這點在這個例子中,能夠經過將第20行刪除來體現)。但咱們要強調的是,這種行爲的正確性將是不被保證的。blog

經過上述分析,咱們將代碼修改以下:生命週期

 

   #include <string>
   #include <sstream>
   #include <iostream>
 
   using namespace std;
 
   #define PRINT_CSTR(no) printf("cstr" #no " addr:\t%p\n",cstr##no)
   #define PRINT_T_CSTR(no) printf("t_cstr" #no " addr:\t%p\n",t_cstr##no)
 
  int main()
  {
      stringstream ss("012345678901234567890123456789012345678901234567890123456789");
      stringstream t_ss("abcdefghijklmnopqrstuvwxyz");
      string str1(ss.str());
 
      const char* cstr1 = str1.c_str();
      const string& str2 = ss.str();
      const char* cstr2 = str2.c_str();
      const string& str3 = ss.str();
      const char* cstr3 = str3.c_str();
      const string& str4 = ss.str();
      const char* cstr4 = str4.c_str();
      const char* t_cstr = t_ss.str().c_str(); 
 
      cout << "------ The results ----------" << endl
           << "cstr1:\t" << cstr1 << endl 
           << "cstr2:\t" << cstr2 << endl
           << "cstr3:\t" << cstr3 << endl
           << "cstr4:\t" << cstr4 << endl
           << "t_cstr:\t" << t_cstr << endl
           << "-----------------------------"  << endl;
      printf("\n------ Char pointers ----------\n");
      PRINT_CSTR(1);
      PRINT_CSTR(2);
      PRINT_CSTR(3);
      PRINT_CSTR(4);
      PRINT_T_CSTR();
 
      return 0;
  }

如今咱們將得到咱們所指望的輸出結果了:內存

    ------ The results ----------
    cstr1:  012345678901234567890123456789012345678901234567890123456789
    cstr2:  012345678901234567890123456789012345678901234567890123456789
    cstr3:  012345678901234567890123456789012345678901234567890123456789
    cstr4:  012345678901234567890123456789012345678901234567890123456789
    t_cstr: abcdefghijklmnopqrstuvwxyz
    -----------------------------
 
    ------ Char pointers ----------
    cstr1 addr:     0x100200e4
    cstr2 addr:     0x10020134
    cstr3 addr:     0x10020184
    cstr4 addr:     0x100201d4
    t_cstr addr:    0x10020014

 

如今咱們知道stringstream.str()方法將返回一個臨時的string對象,而它的生命週期將在本表達式結束後完結。當咱們須要對這個string對象進行進一步操做(例如得到對應的C string)時,咱們須要注意這個可能會致使非預期結果的「陷阱」。:)

最後,咱們想強調一下:因爲臨時對象佔用內存空間被從新使用的不肯定性,這個陷阱不必定會明顯暴露出來。但不暴露出來不表明行爲的正確性,爲了不「詭異」問題的發生,請儘可能採用能保證正確的寫法。

正確的寫法應該是string str = stringstream.str();

相關文章
相關標籤/搜索