前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >std::string的find问题研究

std::string的find问题研究

作者头像
一见
发布2018-12-27 16:16:06
1.3K0
发布2018-12-27 16:16:06
举报
文章被收录于专栏:蓝天蓝天

1. 前言

一次偶然,发现完全同一份代码,在不同机器上find出现两个不同执行结果,本文旨在研究find的“诡异”行为,找出背后的原因。

2. find字符串

测试代码:

代码语言:javascript
复制
// g++ -g -o x x.cpp
#include
#include
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):

代码语言:javascript
复制
n=4294967295, m=1

这里m值为1,是一个非期望的值。

i86_64编译成64位输出结果(gcc (GCC) 4.8.5):

代码语言:javascript
复制
n=18446744073709551615, m=18446744073709551615

i86_64编译成32位输出结果(gcc (GCC) 4.8.5):

代码语言:javascript
复制
n=4294967295, m=4294967295

i386上编译放到i86_64上执行的输出结果:

代码语言:javascript
复制
n=4294967295, m=4294967295

i386上编译成共享库后放到i86_64上执行的输出结果:

代码语言:javascript
复制
// g++ -g -o libx.so -fPIC -shared x.cpp
n=4294967295, m=4294967295

3. find单个字符

测试代码:

代码语言:javascript
复制
// g++ -g -o x x.cpp
#include
#include
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):

代码语言:javascript
复制
n=4294967295, m=4294967295

i86_64编译成64位输出结果(gcc (GCC) 4.8.5):

代码语言:javascript
复制
n=18446744073709551615, m=18446744073709551615

i86_64编译成32位输出结果(gcc (GCC) 4.8.5):

代码语言:javascript
复制
n=4294967295, m=4294967295

i386上编译放到i86_64上执行的输出结果:

代码语言:javascript
复制
n=4294967295, m=4294967295

i386上编译成共享库后放到i86_64上执行的输出结果:

代码语言:javascript
复制
n=4294967295, m=4294967295

4. 问题分析

对于字符串版本的find,出现不同的结果。小技巧:加上编译选项“-D_GLIBCXX_DEBUG”,方可DEBUG进入find。

4.1. gcc-4.1.2

以下为i386环境。

代码语言:javascript
复制
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::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::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环境。

代码语言:javascript
复制
// 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::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::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源代码

代码语言:javascript
复制
// g++ -g -o a a.cpp -ldl -m32
#include
#include
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的实现相同。

代码语言:javascript
复制
// basic_string.tcc
template
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

代码语言:javascript
复制
// /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
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不同了,新的实现不存在溢出漏洞。

代码语言:javascript
复制
// /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
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。

window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-12-19 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 前言
  • 2. find字符串
  • 3. find单个字符
  • 4. 问题分析
    • 4.1. gcc-4.1.2
      • 4.2. gcc-4.8.2
      • 5. a.cpp源代码
      • 6. 单个字符版本find源码
      • 7. 字符串版本find源码
        • 7.1. gcc-4.1.2
          • 7.2. gcc-4.8.2
          • 8. 结论
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档