std::string的find問題研究
https://download.csdn.net/download/aquester/10861914
目錄
1. 前言
一次偶然,發現完全同一份程式碼,在不同機器上find出現兩個不同執行結果,本文旨在研究find的“詭異”行為,找出背後的原因。
2. find字串
測試程式碼:
// g++ -g -o x x.cpp #include <string> #include <iostream>
extern "C" int main() { std::string::size_type n = std::string::npos; std::string str = "123"; std::string::size_type m = str.find("2", n); // 按照期望,m值應為npos std::cout << "n=" << n << ", m=" << m << std::endl; return 0; } |
i386輸出結果(gcc (GCC) 4.1.2):
n=4294967295, m=1 |
這裡m值為1,是一個非期望的值。
i86_64編譯成64位輸出結果(gcc (GCC) 4.8.5):
n=18446744073709551615, m=18446744073709551615 |
i86_64編譯成32位輸出結果(gcc (GCC) 4.8.5):
n=4294967295, m=4294967295 |
i386上編譯放到i86_64上執行的輸出結果:
n=4294967295, m=4294967295 |
i386上編譯成共享庫後放到i86_64上執行的輸出結果:
// g++ -g -o libx.so -fPIC -shared x.cpp n=4294967295, m=4294967295 |
3. find單個字元
測試程式碼:
// g++ -g -o x x.cpp #include <string> #include <iostream>
extern "C" int main() { std::string::size_type n = std::string::npos; std::string str = "123"; std::string::size_type m = str.find('2', n); std::cout << "n=" << n << ", m=" << m << std::endl; return 0; } |
i386輸出結果(gcc (GCC) 4.1.2):
n=4294967295, m=4294967295 |
i86_64編譯成64位輸出結果(gcc (GCC) 4.8.5):
n=18446744073709551615, m=18446744073709551615 |
i86_64編譯成32位輸出結果(gcc (GCC) 4.8.5):
n=4294967295, m=4294967295 |
i386上編譯放到i86_64上執行的輸出結果:
n=4294967295, m=4294967295 |
i386上編譯成共享庫後放到i86_64上執行的輸出結果:
n=4294967295, m=4294967295 |
4. 問題分析
對於字串版本的find,出現不同的結果。小技巧:加上編譯選項“-D_GLIBCXX_DEBUG”,方可DEBUG進入find。
4.1. gcc-4.1.2
以下為i386環境。
g++ -g -o x x.cpp -D_GLIBCXX_DEBUG Breakpoint 2, main () at x.cpp:6 6 std::string::size_type n = std::string::npos; (gdb) n 7 std::string str = "123"; (gdb) 8 std::string::size_type m = str.find("2", n); (gdb) s std::string::find (this=0xbfb54a10, __s=0x804b8f2 "2", __pos=4294967295) at /usr/include/c++/4.1.2/bits/basic_string.h:1579 1579 return this->find(__s, __pos, traits_type::length(__s)); (gdb) s std::char_traits<char>::length (__s=0x804b8f2 "2") at /usr/include/c++/4.1.2/bits/char_traits.h:257 257 { return strlen(__s); } (gdb) finish Run till exit from #0 std::char_traits<char>::length (__s=0x804b8f2 "2") at /usr/include/c++/4.1.2/bits/char_traits.h:257 0x0804a8d9 in std::string::find (this=0xbfb54a10, __s=0x804b8f2 "2", __pos=4294967295) at /usr/include/c++/4.1.2/bits/basic_string.h:1579 1579 return this->find(__s, __pos, traits_type::length(__s)); Value returned is $1 = 1 (gdb) s std::string::find (this=0xbfb54a10, __s=0x804b8f2 "2", __pos=4294967295, __n=1) at /usr/include/c++/4.1.2/bits/basic_string.tcc:721 721 size_type __ret = npos; 723 if (__pos + __n <= __size) (gdb) p __pos $2 = 4294967295 (gdb) p __n $3 = 1 (gdb) p __size $4 = 3 (gdb) p __pos + __n $5 = 0 |
4.2. gcc-4.8.2
以下為x86_64環境。
// g++ -g -o x x.cpp -m32 -D_GLIBCXX_DEBUG Breakpoint 1, main () at x.cpp:6 6 std::string::size_type n = std::string::npos; Missing separate debuginfos, use: debuginfo-install glibc-2.17-196.tl2.3.i686 libgcc-4.8.5-4.el7.i686 libstdc++-4.8.5-4.el7.i686 (gdb) n 7 std::string str = "123"; (gdb) 8 std::string::size_type m = str.find("2", n); (gdb) s std::string::find (this=0xffffd300, __s=0x80499d8 "2", __pos=4294967295) at /usr/include/c++/4.8.2/bits/basic_string.h:1864 1864 return this->find(__s, __pos, traits_type::length(__s)); (gdb) s std::char_traits<char>::length (__s=0x80499d8 "2") at /usr/include/c++/4.8.2/bits/char_traits.h:259 259 { return __builtin_strlen(__s); } (gdb) finish Run till exit from #0 std::char_traits<char>::length (__s=0x80499d8 "2") at /usr/include/c++/4.8.2/bits/char_traits.h:259 0x0804931f in std::string::find (this=0xffffd300, __s=0x80499d8 "2", __pos=4294967295) at /usr/include/c++/4.8.2/bits/basic_string.h:1864 1864 return this->find(__s, __pos, traits_type::length(__s)); Value returned is $1 = 1 (gdb) s std::string::find (this=0xffffd300, __s=0x80499d8 "2", __pos=4294967295, __n=1) at /usr/include/c++/4.8.2/bits/basic_string.tcc:740 740 const size_type __size = this->size(); |
5. a.cpp原始碼
// g++ -g -o a a.cpp -ldl -m32 #include <dlfcn.h> #include <stdio.h>
int main() { typedef int (*X)();
void* p = dlopen("./libx.so", RTLD_NOW); if (!p) printf("dlopen failed: %s\n", dlerror()); else { X x = (X)dlsym(p, "main"); (*x)(); } return 0; } |
6. 單個字元版本find原始碼
gcc-4.1.2版本的find原始碼,gcc-4.8.2的實現相同。
// basic_string.tcc template<typename _CharT, typename _Traits, typename _Alloc> typename basic_string<_CharT, _Traits, _Alloc>::size_type basic_string<_CharT, _Traits, _Alloc>:: find(_CharT __c, size_type __pos) const _GLIBCXX_NOEXCEPT { size_type __ret = npos; const size_type __size = this->size();
if (__pos < __size) { const _CharT* __data = _M_data(); const size_type __n = __size - __pos; const _CharT* __p = traits_type::find(__data + __pos, __n, __c); if (__p) __ret = __p - __data; }
return __ret; } |
7. 字串版本find原始碼
7.1. gcc-4.1.2
// /usr/include/c++/4.1.2/bits/basic_string.h 1575 size_type 1576 find(const _CharT* __s, size_type __pos = 0) const 1577 { 1578 __glibcxx_requires_string(__s); 1579 return this->find(__s, __pos, traits_type::length(__s)); 1580 }
// /usr/include/c++/4.1.2/bits/basic_string.tcc 715 template<typename _CharT, typename _Traits, typename _Alloc> 716 typename basic_string<_CharT, _Traits, _Alloc>::size_type 717 basic_string<_CharT, _Traits, _Alloc>:: 718 find(const _CharT* __s, size_type __pos, size_type __n) const 719 { 720 __glibcxx_requires_string_len(__s, __n); 721 size_type __ret = npos; 722 const size_type __size = this->size(); 723 if (__pos + __n <= __size) // 這裡溢位 724 { 725 const _CharT* __data = _M_data(); 726 const _CharT* __p = std::search(__data + __pos, __data + __size, 727 __s, __s + __n, traits_type::eq); 728 if (__p != __data + __size || __n == 0) 729 __ret = __p - __data; 730 } 731 return __ret; 732 } |
7.2. gcc-4.8.2
實現和gcc-4.1.2不同了,新的實現不存在溢位漏洞。
// /usr/include/c++/4.8.2/bits/basic_string.h 1860 size_type 1861 find(const _CharT* __s, size_type __pos = 0) const 1862 { 1863 __glibcxx_requires_string(__s); 1864 return this->find(__s, __pos, traits_type::length(__s)); 1865 }
// /usr/include/c++/4.8.2/bits/basic_string.tcc 734 template<typename _CharT, typename _Traits, typename _Alloc> 735 typename basic_string<_CharT, _Traits, _Alloc>::size_type 736 basic_string<_CharT, _Traits, _Alloc>:: 737 find(const _CharT* __s, size_type __pos, size_type __n) const 738 { 739 __glibcxx_requires_string_len(__s, __n); 740 const size_type __size = this->size(); 741 const _CharT* __data = _M_data(); 742 743 if (__n == 0) 744 return __pos <= __size ? __pos : npos; 745 746 if (__n <= __size) // 這裡不存在溢位 747 { 748 for (; __pos <= __size - __n; ++__pos) 749 if (traits_type::eq(__data[__pos], __s[0]) 750 && traits_type::compare(__data + __pos + 1, 751 __s + 1, __n - 1) == 0) 752 return __pos; 753 } 754 return npos; 755 } |
8. 結論
一些低版本的find實現存在bug,存在溢位。注:std::string::size_type實際為size_t,是一個無符號整數型別,在i386上為4位元組無符號整數型別,在x86_84上為8位元組無符號整數型別,對應的有符號型別為ssize_t。
相關文章
- string的find()與npos
- std::io::BufReader 物件借用和引用的問題物件
- 遇到的cannot find module 'xxx' 問題
- string型別資料的find函式型別函式
- LeetCode 438. Find All Anagrams in a StringLeetCode
- Folly解讀(零) Fbstring—— 一個完美替代std::string的庫
- 【譯】對Rust中的std::io::Error的研究RustError
- 關於/usr/bin/ld: cannot find -lXX的問題
- 雖然包含string標頭檔案但未用std::
- [LeetCode] 1545. Find Kth Bit in Nth Binary StringLeetCode
- String型別函式傳遞問題型別函式
- char[] 轉換string時的自動截斷問題
- 深入研究Java StringJava
- [20210624]find -mtime +N N -N的時間範圍問題.txt
- std::reserve和std::resize的區別
- `std::packaged_task`、`std::thread` 和 `std::async` 的區別與聯絡Packagethread
- [20210708]find -mtime +0 0 -0時間問題補充.txt
- [20210626]find -mtime +N N -N時間問題補充.txt
- [20210625]find -mtime +N N -N時間問題補充.txt
- (轉)leetcode:Find All Anagrams in a String 滑動視窗方法總結LeetCode
- spring boot 註解物件的問題 待研究Spring Boot物件
- 問題解決:TypeError: unsupported format string passed to NoneType.__format__ErrorORMNone
- (轉載)關於usr/bin/ld: cannot find -lxxx問題總結
- std::vector 和 std::list 區別
- std::bind與std::ref, why and how
- c++11:std::boolalpha、std::noboolalphaC++
- JSP中String a = request.getParameter(“ “),判斷a是否為null或空的問題JSNull
- ECode1024 | String拼接方法concat與+效率比較問題
- C++ 標準庫 std::set std::multiset swap()的使用C++
- Martyr2專案實現——Number部分的問題求解 (1) Find Pi to Nth DigitGit
- Doris建立表報錯Failed to find enough host with storage medium問題解決AI
- 深入剖析go中字串的編碼問題——特殊字元的string怎麼轉byte?Go字串字元
- [Python3] 關於Bytes與String 寫檔案遇到的編碼問題Python
- 效能測試中的唯一標識問題研究
- Long型別框架自動序列化成String失效問題排查型別框架
- android SDK Manager無法啟動閃退問題(環境變數配置正確,find_java.bat沒問題)Android變數JavaBAT
- 好程式設計師Java教程分享Java中String型別的10個問題程式設計師Java型別
- Electron打包的時候路徑出現問題!include: could not find: "C:UsersxxxxAppDataLocalTemp -TH3KzBAPP