https://files-cdn.cnblogs.com/files/aquester/std之string的find问题研究.pdf

目录

目录 1

1. 前言 1

2. find字符串 1

3. find单个字符 2

4. 问题分析 3

4.1. gcc-4.1.2 3

4.2. gcc-4.8.2 4

5. a.cpp源代码 5

6. 单个字符版本find源码 5

7. 字符串版本find源码 6

7.1. gcc-4.1.2 6

7.2. gcc-4.8.2 6

8. 结论 7

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。

std::string的find问题研究的更多相关文章

  1. std::string的拷贝赋值研究

    说明:以下涉及的std::string的源代码摘自4.8.2版本.结论:std::string的拷贝复制是基于引用计数的浅拷贝,因此它们指向相同的数据地址. // std::string类定义type ...

  2. 【转】标准C++类std::string的内存共享和Copy-On-Write技术

    1.             概念 Scott Meyers在<More Effective C++>中举了个例子,不知你是否还记得?在你还在上学的时候,你的父母要你不要看电视,而去复习功 ...

  3. QString 和std::string互转

    std::string cstr; QString qstring; //****从std::string 到QString qstring = QString(QString::fromLocal8 ...

  4. std::string的split函数

    刚刚要找个按空格分离std::string的函数, 结果发现了stackoverflow上的这个问题. 也没仔细看, 直接拿来一试, 靠, 不对啊, 怎么分离后多出个空字符串, 也就是 "a ...

  5. could not deduce template argument for 'const std::_Tree<_Traits> &' from 'const std::string'

    VS2008, 写一个简单的demo的时候出现了这个: 1>------ Build started: Project: GetExportTable, Configuration: Relea ...

  6. 源码阅读笔记 - 3 std::string 与 Short String Optimization

    众所周知,大部分情况下,操作一个自动(栈)变量的速度是比操作一个堆上的值的速度快的.然而,栈数组的大小是在编译时确定的(不要说 C99 的VLA,那货的 sizeof 是运行时计算的),但是堆数组的大 ...

  7. CString std::string相互转换

    CString->std::string 例子: CString strMfc=“test“; std::string strStl; strStl=strMfc.GetBuffer(0); s ...

  8. 计算std:string的字节长度

    如果项目本身是使用 Unicode 字符集和utf8编码,std::string的length(),size()甚至是c的strLen取到的都是字节长度了,比如三个汉字,就是9, 以上情况不满足的话, ...

  9. 【原】error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'std::string'

    今天遇到一个非常难以排查的BUG,谷歌度娘都问过了依旧无解,最后自己重新尝试之后找到解决方案: 先看一下报错信息: 1>.\lenz.cpp(2197)  error C2679: binary ...

随机推荐

  1. stark组件开发之自动生成URL

    app01\model.py from django.db import models # Create your models here. class Depart(models.Model): i ...

  2. tomcat/eclipse提速[z]

    在使用Eclipse开发项目过程中,一度使Eclipse陷入瘫痪状态,Tomcat启动项目时也异常缓慢,增加了超时限制并没有用,有时候项目根本运行不起来,简直让人崩溃,可能我电脑内存小(4G),配置低 ...

  3. c# usercontrol ,networkcomms3.0 Invoke总结

    1: accordionControl 添加了自定义控件页面 之后,切换到别的页面后,原先打开的页面对象还是存在的没有被销毁,再打开时重新引用即可 private void accordionCont ...

  4. opencv 3.2 vs2015 debug assertion __acrt_first_block == header

    网上复制了一个转直方图的代码 ,说来也奇怪, 用imshow 显示 图片在独立窗体内,不存在问题, 要注释掉这段代码就出现了下边的错误. 网上查了查,原来是程序中 有个std::vector<c ...

  5. 7.Mysql存储引擎

    7.表类型(存储引擎)的选择7.1 Mysql存储引擎概述 mysql支持插件式存储引擎,即存储引擎以插件形式存在于mysql库中. mysql支持的存储引擎包括:MyISAM.InnoDB.BDB. ...

  6. docker 运行nginx并进入容器内部、端口映射

    一.docker运行容器 1.从网易蜂巢镜像仓库下载nginx镜像 : 2.拉取镜像到本地,并查看本地的镜像: $ docker pull hub.c..com/library/node:latest ...

  7. win10安装.net3.5

    近日有网友反映在windows10_64位系统电脑上安装Net framework3.5,操作时总失败,怎么办呢?小编下面就介绍win10 64位系统无法安装Net framework3.5的两种解决 ...

  8. eclipse手动安装alibaba代码规范插件

    如果你的开发环境无法访问外网,那么手动安装阿里巴巴的代码规范插件是一个不错的选择.另外,很多教程说该插件需要jdk1.8以上,我试了一下jdk1.7也是可以运行的,更低的版本就不知道了,貌似jdk1. ...

  9. BZOJ1228或洛谷2148 [SDOI2009]E&D

    BZOJ原题链接 洛谷原题链接 完全不会呀.. 写了这题才知道\(SG\)函数原来也能打表找规律... 题解请看大佬的博客 #include<cstdio> using namespace ...

  10. ubuntu 下通过ftp命令下载文件

    /*连接*/ $ ftp 192.168.180.2Connected to 192.168.180.2.Name (192.168.180.2:rivsidn): admin  Password: ...