为什么将 strlen 重新实现为循环+减法?

2024-01-07

灵感来自这个问题 https://stackoverflow.com/q/6842130/57428关于 SQLite3 中的以下代码:

 static int strlen30(const char *z){
    const char *z2 = z;
    while( *z2 ){ z2++; }
    return 0x3fffffff & (int)(z2 - z);
 }

伴随着提交消息 http://www.sqlite.org/src/timeline?c=2008-12-10%2019:26:22说这个功能有帮助int溢出。

我对这部分特别感兴趣:

 const char *z2 = z;
 while( *z2 ){ z2++; }

对我来说这个循环前进z2 until z2指向空终止符。然后z2-z产生字符串长度。

为什么不使用strlen()对于这部分并重写如下:

return 0x3fffffff & (int)(strlen(z));

为什么用循环+减法而不是strlen()?循环+减法可以做什么strlen() can't?


我无法告诉你为什么他们必须重新实施它,以及为什么他们选择int相反,如果size_t作为返回类型。但关于功能:

/*
 ** Compute a string length that is limited to what can be stored in
 ** lower 30 bits of a 32-bit signed integer.
 */
static int strlen30(const char *z){
    const char *z2 = z;
    while( *z2 ){ z2++; }
    return 0x3fffffff & (int)(z2 - z);
}

有关截断、类型、溢出的标准参考

该标准在(ISO/IEC 14882:2003(E))中表示3.9.1 基本类型, 4.:

Unsigned integers, declared unsigned, shall obey the laws of arithmetic modulo 2n where n is the number of bits in the value representation of that particular size of integer. 41)

...

41): This implies that unsigned arithmetic does not overflow because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting unsigned integer type

标准的该部分没有定义有符号整数的溢出行为。如果我们看一下5. 表达式, 5.:

如果在计算表达式期间,结果未在数学上定义或不在其类型的可表示值范围内,则行为未定义,除非此类表达式是常量表达式 (5.19),在这种情况下程序是格式错误的。 [注:大多数现有的 C++ 实现都会忽略整数 溢出。除以零的处理、使用零除数形成余数以及所有浮点 例外情况因机器而异,通常可以通过库函数进行调整。 ]

到目前为止溢出。

至于减去两个指向数组元素的指针,5.7 加法运算符, 6.:

当两个指向同一数组对象的元素的指针相减时,结果是两个数组元素的下标之差。结果的类型是实现定义的有符号整型;该类型应与标头 (18.1) 中定义为 ptrdiff_t 的类型相同。 [...]

看着18.1:

内容与标准C库头文件stddef.h相同

那么让我们看看 C 标准(不过我只有 C99 的副本),7.17 通用定义:

  1. 用于 size_t 和 ptrdiff_t 的类型不应具有整数转换等级 大于signed long int 除非实现支持对象 大到足以使这一点成为必要。

不作进一步保证ptrdiff_t。然后,附录 E(仍在 ISO/IEC 9899:TC2 中)给出了最小震级对于有符号长整型,但不是最大值:

#define LONG_MAX +2147483647

现在最大值是多少int,返回类型为sqlite - strlen30()?让我们再次跳过将我们引向 C 标准的 C++ 引用,我们将在 C99 的附录 E 中看到,最小最大int:

#define INT_MAX +32767



关于截断部分的总结

  1. 通常,ptrdiff_t不大于signed long,不小于32位。
  2. int只是定义为至少 16 位长。
  3. 因此,两个指针相减可能会得到不适合的结果int您的平台。
  4. 我们从上面记得,对于有符号类型,不适合的结果会产生未定义的行为。
  5. strlen30确实对指针减结果应用按位或:

          | 32 bit                         |
ptr_diff  |10111101111110011110111110011111| // could be even larger
&         |00111111111111111111111111111111| // == 3FFFFFFF<sub>16</sub>
          ----------------------------------
=         |00111101111110011110111110011111| // truncated

That prevents undefiend behaviour by truncation of the pointer-subtraction result to a maximum value of 3FFFFFFF16 = 107374182310.

我不确定他们为什么选择这个值,因为在大多数机器上,只有最高有效位表示符号性 http://en.wikipedia.org/wiki/Signed_number_representations#One.27s_complement。相对于标准,选择最小值可能是有意义的INT_MAX,但是 1073741823 在不了解更多细节的情况下确实有点奇怪(尽管它当然完美地实现了其函数上面的注释:截断为 30 位并防止溢出)。

“为什么这部分不使用 strlen()”

并像这样重写:

return 0x3fffffff & (int)(strlen(z));

我的猜测是他们想避免潜在的间接。另一个优点可能是对标准库的依赖较少,如果您编写非托管应用程序,这可能会很有用。

顺便说一句,从上面的参考文献中可以看出,(int)(strlen(z))如果 ptrdiff_t > 的最大值,可能会产生未定义的行为INT_MAX, so (int)(0x3fffffff & strlen(z))会更好。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么将 strlen 重新实现为循环+减法? 的相关文章

随机推荐

  • 如何在 AWS 负载均衡器响应中禁用 Apache HTTP 标头信息?

    我在 Apache 服务器上使用 AWS Elastic Beanstalk 部署了一个 node js 环境 我对环境运行了 PCI 扫描 但出现了 2 次失败 Apache ServerTokens 信息披露 Web服务器HTTP头信息
  • 从逗号分隔字段中选择

    说我有一个subscribers表 每个用户都有一行 如下所示 id name subscribers 1 user1 user2 user3 user4 2 user2 user4 user5 user3 3 user3 user1 us
  • Apache Spark:核心数量与执行器数量

    我试图了解在 YARN 上运行 Spark 作业时核心数量和执行器数量的关系 测试环境如下 数据节点数量 3 Data node machine spec CPU Core i7 4790 核心数 4 线程数 8 内存 32GB 8GB x
  • Firebase 功能 HTTPS 403 禁止

    我使用 Node 和 Express 构建了 Firebase HTTP 事件函数 该函数正在工作 但是当我在客户端调用该函数时 我得到403 Forbidden 第一次调用该功能时 我被要求使用 Google 帐户登录 我使用与 Fire
  • Python xml - 处理未封闭的令牌

    我正在读取数百个 XML 文件并使用 xml etree ElementTree 解析它们 快速背景 这些 XML 文件在某一时刻是完全有效的 但不知何故 在历史上处理它们时 我复制 粘贴它们的过程可能已损坏它们 事实证明 这是一个冲洗问题
  • 防止更改 String.Empty 的值

    部分是出于好奇的破坏事物的角度 部分是出于防范潜在问题的目的 想象一下通过调用以下内容 或类似的内容 但是string Empty是一个很好的例子 typeof String GetField Empty BindingFlags Publ
  • Gradle 不包括已发布的 pom.xml 中的依赖项

    我有一个 Gradle 项目 我正在使用Maven 发布者用于将我的 Android 库安装到 Maven 本地和 Maven 存储库的插件 这可行 但生成的 pom xml 不包含任何依赖项信息 是否有解决方法来包含该信息 或者我是否被迫
  • 通过从当前日期减去秒来获取日期时间

    我想从日期中减去秒 例如 假设我有 1300 秒的无符号整数 我想获取当前日期和时间 从中减去 1 300 秒 最后得到 01 13 2012 2 15 PM 格式并不重要 我确实尝试过 DateTime dt new DateTime d
  • 为什么我无法截取 MPMoviePlayerController 的屏幕截图?

    我需要捕获在 mpmovieplayer 控制器中播放的视频的屏幕截图 但我得到的只是一个红色屏幕 我用红色背景和 0 5 alpha 制作了 coverView 这是代码 NSArray windows UIApplication sha
  • 使用 Groovy(Grails) 写入文件的某些行失败(断行)

    我正在使用 Groovy 在 csv 文件中进行大量写入 更具体地说 我有一个正在运行的 Quartz 作业 它创建一些发送到 RabbitMQ 队列的 Map 消息 该队列正在被 10 个消费者使用 并导致生成一些字符串列表 对于列表中的
  • 如何解决私有字节(本机内存)泄漏?

    我正在开发一个 C 应用程序 该应用程序似乎存在泄漏 我使用过内存分析器并发现我的 私有字节继续增加但是所有堆中的字节数不 这意味着可能是本机内存泄漏 现在我陷入困境 如何找到本机代码中的内存泄漏 首先 如果您有泄漏进程的转储 您可以在 W
  • 如何在按钮单击时使用 TypeScript

    我正在尝试在我的应用程序中使用打字稿 同样 我正在做一个 POC 在 POC 中我想在单击按钮时调用 TypeScript 类中定义的函数 可以调用函数吗 如果是的话怎么办 到目前为止 我已经看到了仅在页面加载时调用函数的示例 我需要在某些
  • 如何在 django 中构建 json REST API(没有 Django REST 框架)

    Preface 我有一个 Django 项目 我已经将它连接起来 以便它为一堆模型提供一堆视图 现在我想添加一个端点 它将数据库的很大一部分转储为 json 我假设您执行此操作的方法是将 URL 添加到视图类 方法 该视图类 方法返回充满
  • Transfuse 与 Dagger 相比如何?

    我正在尝试决定是使用 Transfuse 还是 Dagger 进行 Android 依赖注入 我从未使用过 Transfuse 并且对 Dagger 有基本了解 非常感谢 首先 我是输血 http androidtransfuse org因
  • 如何按特定文件类型过滤输入类型=“文件”对话框?

    当我单击浏览器的浏览按钮时 我想将浏览器限制为 JPG 文件
  • WCF 跨域使用 Jsonp 错误未捕获 SyntaxError:意外的标记:

    我正在尝试使用 jQuery 跨域调用 Web 服务 这是我调用服务的代码 document ready function ajax type GET async false contentType application json url
  • AWS Device Farm。如何将测试用例后生成的自定义报告保存到本地空间

    我正在使用 AWS 设备场 我的测试脚本在本地系统上运行时按预期工作 并在本地系统中的指定路径生成报告 现在 当我在设备场中运行代码时 不会生成报告 Am我错过了什么 这是我的测试代码 用于将测试用例写入 html 报告 package t
  • docs 文件夹中的 GitHub Pages 引用 docs 文件夹外部的文件

    我遵循了 GitHub 的文档并使用成功发布了我的项目页面docs我的项目存储库下的文件夹 但我想知道如何解决这个小问题 我正在开发一个 JavaScript 库wesa js http caiyi us wesa 源文件存储在src存储库
  • 如何使用 TypeORM 创建这个 ViewEntity?

    假设我正在构建一个有 4 个表的地址簿 user contact friend and stranger contact是链接表连接user与两个friend and stranger表 它的结构如下 id user id friend i
  • 为什么将 strlen 重新实现为循环+减法?

    灵感来自这个问题 https stackoverflow com q 6842130 57428关于 SQLite3 中的以下代码 static int strlen30 const char z const char z2 z while