当前位置: 首页 > 编程日记 > 正文

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

转载于:https://www.cnblogs.com/aquester/p/10143920.html

相关文章:

从内存溢出看Java 环境中的内存结构

作为有个java程序员&#xff0c;我想大家对下面出现的这几个场景并不陌生&#xff0c;倍感亲切&#xff0c;深恶痛绝&#xff0c;抓心挠肝&#xff0c;一定会回过头来问为什么为什么为什么会这样&#xff0c;嘿嘿&#xff0c;让我们看一下我们日常在开发过程中接触内存溢出的异…

【ACM】杭电OJ 1003。

运行环境VS2017 题目链接&#xff1a;杭电OJ 1003 主要思想是&#xff1a; 用d[i]来存放前i项中最大的和&#xff0c;得到end&#xff0c;然后再倒推&#xff0c;得起始的位置begin 然而在程序42行的疑问&#xff0c;大家可以讨论一下吗&#xff1f;&#xff1f;&#xff…

js 实现精确加减乘除运算之BigDecimal.js

在前端实际开发中&#xff0c;进行前端计算会出现丢失精度的问题&#xff0c;这里我们项目中运用了BigDecimal.js。 js计算丢失精度原因 计算机的二进制实现和位数限制有些数无法有限表示。就像一些无理数不能有限表示&#xff0c;如 圆周率 3.1415926...&#xff0c;1.3333...…

【ACM】杭电OJ 2012。

题目链接&#xff1a;杭电OJ 2012 思路很简单&#xff0c;但是有一种高效算法显示编译错误&#xff0c;不知道为什么 运行环境&#xff1a;VS2017 AC代码&#xff1a; #include <stdio.h> #include <math.h>int main() {int x, y, i,j,num,count,t;while (scan…

ubuntu配置jdk环境

1.下载jdk&#xff0c;解压 2.配置 ~/.bashrc&#xff0c;添加jdk路径 3.重启系统 4.当看到java -version有输出时&#xff0c;配置系统的默认java变量(默认jdk) sudo update-alternatives --install /usr/bin/java java /home/liweigao/software/jdk1.7.0_17/bin/java 300 …

解决 The mysql extension is deprecated and will be r

为什么80%的码农都做不了架构师&#xff1f;>>> 解决 The mysql extension is deprecated and will be removed in the future技术 maybe yes 发表于2015-07-27 15:19 原文链接 : http://blog.lmlphp.com/archives/132/Tutorial_of_solve_mysql_extension_is_depre…

Android控件系统(三)——Window与WindowMananger

Android版本&#xff1a;7.0(API27) [TOC] 澄清几个概念 窗口&#xff08;不是指的Window类&#xff09;&#xff1a;这是一个纯语义的说法&#xff0c;即程序员所看到的屏幕上的某个独立的界面&#xff0c;比如一个带有Title Bar的Activity界面、一个对话框、一个Menu菜单等&a…

Restore Volume 操作 - 每天5分钟玩转 OpenStack(60)

前面我们 backup 了 voluem&#xff0c;今天我们将讨论如何 restore volume。 restore 的过程其实很简单&#xff0c;两步走&#xff1a; 在存储节点上创建一个空白 volume。 将 backup 的数据 copy 到空白 voluem 上。 下面我们来看 restore 操作的详细流程&#xff1a; …

gdb 查找动态库方法

当GDB无法显示so动态库的信息或者显示信息有误时&#xff0c;通常是由于库搜索路径错误导致的&#xff0c;可使用set sysroot、set solib-absolute-prefix、set solib-search-path来指定库搜索路径。 1. set sysroot 与 set solib-absolute-prefix 是同一条命令&#xff0c;实…

【ACM】杭电OJ 1004

题目链接&#xff1a;杭电OJ 1004 运行环境&#xff1a;Dev-C 5.11 思路&#xff1a; 先把先把num数组全部赋值为1&#xff1b;第一个颜色单独输入&#xff0c;从第二个开始&#xff0c;需要与前面的进行比较&#xff0c;如果前面有相同的颜色&#xff0c;则在目前的num[i]上…

zabbix 监控mysql(实例)

修改zabbix_agentd.conf UnsafeUserParameters1 UserParametermysql.version,mysql -V UserParametermysql.status[*],/usr/local/zabbix/share/zabbix/alertscripts/chk_mysql.sh $1 UserParametermysql.ping,mysqladmin ping | grep -c alive 设置mysql的链接 ln -s /data/my…

linux查看系统版本信息命令

几种查看Linux版本信息的方法&#xff1a; 1. uname -a 2. cat /proc/version 3. cat /etc/issue 4. lsb_release -a 详解 lsb_release -a 登录到服务器执行 lsb_release -a &#xff0c;即可列出所有版本信息&#xff0c;例如&#xff1a; 1. [root3.5.5Biz-46 ~]# lsb_releas…

【ACM】杭电OJ 1005

题目链接&#xff1a;杭电OJ 1005 超时代码如下&#xff08;而且开辟的数组空间大小不够&#xff09;: #include <stdio.h> int m[100000]; int f(int n,int a,int b) {m[1] 1;m[2] 1;for (int i 3; i < n; i){m[i] (a * m[i - 1] b * m[i - 2]) % 7;}return …

PostgreSQL:Java使用CopyManager实现客户端文件COPY导入

在MySQL中&#xff0c;可以使用LOAD DATA INFILE和LOAD DATA LOCAL INFILE两种方式导入文本文件中的数据到数据库表中&#xff0c;速度非常快。其中LOAD DATA INFILE使用的文件要位于MySQL所在服务器上&#xff0c;LOAD DATA LOCAL INFILE则使用的是客户端的文件。 LOAD DATA I…

c, c++函数名编译符号修饰符说明

C 编译器的函数名修饰规则 函数名字修饰&#xff08;Decorated Name&#xff09;方式 函数的名字修饰&#xff08;Decorated Name&#xff09;就是编译器在编译期间创建的一个字符串。用来指明函数的定义或原型。 LINK程序或其它工具有时须要指定函数的名字修饰来定位函数的正确…

VS Code - Debugger for Chrome调试JavaScript的两种方式

VS Code - Debugger for Chrome调试JavaScript的两种方式 最近由于出差的缘故&#xff0c;博客写的不是很多&#xff0c;一直想写一篇VS Code - Debugger for Chrome相关的文章&#xff0c;没想到一直拖到了今天。VS Code 开源以后确实在社区得到了很多人的支持&#xff0c;当中…

【ACM】杭电OJ 2018

题目链接&#xff1a;杭电OJ 2018 从n>4开始&#xff0c;每一年的牛的数量前一年的牛的数量三年前的牛的数量 问&#xff1a;为什么是三年前&#xff1f; 答&#xff1a;假设三年前有一头小牛出生&#xff0c;出生的那一年即为第一年&#xff0c;到了第四年&#xff0c;即…

python 帮助文档、自我解释

现在让我们以交互方式使用 Python 来开始研究。当我们从命令行启动 Python 时&#xff0c;就进入了 Python shell&#xff0c;在这里可以输入 Python 代码&#xff0c;而且立刻会从 Python 解释器获得响应。 清单 1. 以交互方式启动 Python 解释器 Python 2.7.15rc1 (default, …

RCF远程调用框架

介绍 RCF&#xff08;远程调用框架&#xff09;是一个C IPC框架&#xff0c;提供了一种在C 程序中实现进程间通信的简单而一致的方法。它基于强类型的客户端/服务器接口的概念&#xff0c;这是基于IDL的中间件&#xff08;如CORBA和DCOM&#xff09;的用户熟悉的概念。然而&am…

Python变量类型(l整型,长整形,浮点型,复数,列表,元组,字典)学习

#codingutf-8 __author__ Administrator#Python变量类型#Python数字&#xff0c;python支持四种不同的数据类型 int整型 long长整型 float浮点型 complex复数 var1 10; #表示整型 var2 678L #表示长整型 var3 12.34;#表示浮点型 var4 123j #复数 var5 12345j #复数print(…

【ACM】杭电OJ 2020(排序)

题目链接&#xff1a;杭电OJ 2020 排序可以有冒泡排序&#xff0c;选择排序&#xff0c;或者直接调用函数。 下面是选择排序&#xff1a; #include <stdio.h> #include <math.h> int main () {int n,i,j,t,a[100],temp;while(scanf("%d",&n)!EOF)…

使用Novell.Directory.Ldap.NETStandard在.NET Core中验证AD域账号

Novell.Directory.Ldap.NETStandard是一个在.NET Core中&#xff0c;既支持Windows平台&#xff0c;又支持Linux平台&#xff0c;进行Windows AD域操作的Nuget包。 首先我们新建一个.NET Core控制台项目&#xff1a;NetCoreLdap&#xff0c;并下载如下Nuget包&#xff1a; Nove…

android AIDL IPC深入分析

深入分析AIDL原理 博客分类&#xff1a; Android 在上一篇文章&#xff08;Service使用方式&#xff09;中&#xff0c;介绍了Android进程间通信&#xff08;IPC&#xff09;的使用&#xff0c;并给出了一个示例。但并没有深入分析aidl是怎样可以做到进程间通信的&#xff0c;…

xml常用操作(js、sql、vb)

我们经常会用到xml操作&#xff0c;如下介绍了js、sql、vb等对xml的操作。 JS创建xml对象 //创建对象 function getDataXML() {var objTds $("TEXTAREA");var count objTds.length;var jsonData;var xmlDoc new ActiveXObject("Microsoft.XMLDOM");//创…

【ACM】杭电OJ 2019

可以用链表写&#xff0c;也可以用顺序表写&#xff1a; 下面是链表的AC代码&#xff1a; #include <stdio.h> #include <stdlib.h> typedef struct node {int info;struct node* next; }N; N* init() {return NULL; } N* creat(N* head,int n) {int i,x;N *p, *…

Nancy in .Net Core学习笔记 - 初识Nancy

原文:Nancy in .Net Core学习笔记 - 初识Nancy前言 去年11月份参加了青岛MVP线下活动&#xff0c;会上老MVP衣明志介绍了Nancy, 一直没有系统的学习一下&#xff0c;最近正好有空&#xff0c;就结合.NET Core学习总结了一下。 注&#xff1a; 本文中大部分内容都是对官网文档的…

rpath和runpath的区别

考虑翻译Qt官方blog中的RPATH and RUNPATH这篇文章&#xff0c;在继续之前&#xff0c;我需要先验证自己的理解是正确的&#xff0c;至少能自圆其说&#xff0c;能说服自己。 用例子说话 二进制 对应源码 有一个程序 a.out main.c 需要加载插件A libA.so liba…

scanf(%s,a)和gets(a)的差别

gets()和scanf()的区别在于输入的字符串是否中间有空格&#xff1a;对于前者&#xff0c;只有遇到"\n"时才停止输入&#xff0c;而对于后者&#xff0c;出现"\n"或空格都停止输入。

阿里注册中心nacos使用整合Dubbo-原创

阿里注册中心nacos是今年开源的框架&#xff0c;一开始以为就是个zk。后面看了图才明白他对标的竟然是consul\eureka&#xff0c;最重要是完美支持dubbo。我想今年开源它也是别有用意 。&#xff08;目前nacos0.7版本&#xff09; Dubbo 融合 Nacos 成为注册中心 Nacos 作为 Du…

UBUNTU adb连接android设备

1sudo vi /etc/udev/rules.d/70-android.rules 2最新修改方法&#xff0c;不用去看设备的ID&#xff0c;直接在rules.d下增加一个文件51-android.rules&#xff0c;内容为&#xff1a; SUBSYSTEM"usb" ENV{DEVTYPE}"usb_device", MODE"0666" …