std::string的find问题研究
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问题研究的更多相关文章
- std::string的拷贝赋值研究
说明:以下涉及的std::string的源代码摘自4.8.2版本.结论:std::string的拷贝复制是基于引用计数的浅拷贝,因此它们指向相同的数据地址. // std::string类定义type ...
- 【转】标准C++类std::string的内存共享和Copy-On-Write技术
1. 概念 Scott Meyers在<More Effective C++>中举了个例子,不知你是否还记得?在你还在上学的时候,你的父母要你不要看电视,而去复习功 ...
- QString 和std::string互转
std::string cstr; QString qstring; //****从std::string 到QString qstring = QString(QString::fromLocal8 ...
- std::string的split函数
刚刚要找个按空格分离std::string的函数, 结果发现了stackoverflow上的这个问题. 也没仔细看, 直接拿来一试, 靠, 不对啊, 怎么分离后多出个空字符串, 也就是 "a ...
- could not deduce template argument for 'const std::_Tree<_Traits> &' from 'const std::string'
VS2008, 写一个简单的demo的时候出现了这个: 1>------ Build started: Project: GetExportTable, Configuration: Relea ...
- 源码阅读笔记 - 3 std::string 与 Short String Optimization
众所周知,大部分情况下,操作一个自动(栈)变量的速度是比操作一个堆上的值的速度快的.然而,栈数组的大小是在编译时确定的(不要说 C99 的VLA,那货的 sizeof 是运行时计算的),但是堆数组的大 ...
- CString std::string相互转换
CString->std::string 例子: CString strMfc=“test“; std::string strStl; strStl=strMfc.GetBuffer(0); s ...
- 计算std:string的字节长度
如果项目本身是使用 Unicode 字符集和utf8编码,std::string的length(),size()甚至是c的strLen取到的都是字节长度了,比如三个汉字,就是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 ...
随机推荐
- PAT 1049 数列的片段和(20)(代码+思路分析)
1049 数列的片段和(20)(20 分) 给定一个正数数列,我们可以从中截取任意的连续的几个数,称为片段.例如,给定数列{0.1, 0.2, 0.3, 0.4},我们有(0.1) (0.1, 0.2 ...
- PAT 1002 写出这个数 (20)(代码)
1002 写出这个数 (20)(20 分) 读入一个自然数n,计算其各位数字之和,用汉语拼音写出和的每一位数字. 输入格式:每个测试输入包含1个测试用例,即给出自然数n的值.这里保证n小于10^100 ...
- andorid 表格布局
tablelayout.xml表格布局 <?xml version="1.0" encoding="utf-8"?> <TableLayout ...
- Linux下动态库使用小结
转自:https://blog.csdn.net/jaylong35/article/details/6132087 1. 静态库和动态库的基本概念 静态库,是在可执行程序连接时就已经加 ...
- java mina框架使用
1.目前为止,看到写mina最清晰的一篇博客:https://my.oschina.net/ielts0909/blog/85946! 2.官网的开发文档:http://mina.apache.org ...
- bowtie:短序列比对的新工具
bowtie:短序列比对的新工具(转) (2014-11-17 22:15:24) 转载▼ 标签: 转载 原文地址:bowtie:短序列比对的新工具(转)作者:玉琪星兆 Bowtie是一个超级快速 ...
- [Robot Framework] 调用ExcelLibrary
安装ExcelLibrary for Robot Framework 参考 : http://navinet.github.io/robotframework-excellibrary/ 打开wind ...
- Linux 禁止root 用户登录启用sudo
1.添加sudo用户执行visudo命令,找到: 复制代码 代码如下: root ALL=(ALL) ALL 在下面增加:(注意,qianyunlai 是Linux新增的一个普通用户),没有的话可创建 ...
- jvm相关知识点
1.hotspot虚拟机结构:类加载器.堆.栈.方法区.垃圾回收系统.执行引擎.本地方法栈.pc寄存器. 类加载器:负责将class文件从文件系统加载到方法区. 堆:存放对象的一块区域,所有线程共用. ...
- centos安装tomcat7.0.70
抄自:https://www.cnblogs.com/www1707/p/6592504.html apache-tomcat-7.0.70jdk-7u67-linux-x64 下载tomcathtt ...