(这是我来自Why reimplement strlen as loop+subtraction? 的回答,但已关闭)
我不能告诉你为什么他们必须重新实现它,以及为什么他们选择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.:
声明为无符号的无符号整数应遵守算术模 2n 的定律,其中 n 是该特定整数大小的值表示中的位数。 41)
...
41):这意味着无符号算术不会溢出,因为结果不能由生成的无符号整数表示
type 以比结果无符号整数可以表示的最大值大一的数字为模减少
输入
标准的那部分没有定义有符号整数的溢出行为。如果我们看5。表达式, 5.:
如果在计算表达式期间,结果未在数学上定义或不在其类型的可表示值范围内,则行为未定义,除非此类表达式是常量表达式
(5.19),在这种情况下,程序是非良构的。 [注意:大多数现有的 C++ 实现忽略整数
溢出。除以零的处理,使用零除数形成余数,以及所有浮点数
例外情况因机器而异,通常可以通过库函数进行调整。 ]
到目前为止溢出。
至于两个指向数组元素的指针相减,5.7 加法运算符,6.:
当两个指向同一个数组对象的元素的指针相减时,结果是两个数组元素的下标之差。结果的类型是实现定义的有符号整数类型;此类型应与 cstddef 标头 (18.1) 中定义为 ptrdiff_t 的类型相同。 [...]
看18.1:
内容同标准C库头文件stddef.h
那么让我们看看 C 标准(不过我只有 C99 的副本),7.17 通用定义:
- 用于 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
总结
- 通常
ptrdiff_t不大于signed long,不小于32bits。
-
int 被定义为至少 16 位长。
- 因此,减去两个指针可能会得到不适合您平台的
int 的结果。
- 我们从上面记得,对于有符号类型,不适合的结果会产生未定义的行为。
-
strlen30 确实按位或在指针减结果上应用:
| 32 bit |
ptr_diff |10111101111110011110111110011111| // could be even larger
& |00111111111111111111111111111111| // == 3FFFFFFF<sub>16</sub>
----------------------------------
= |00111101111110011110111110011111| // truncated
通过将指针减法结果截断为最大值 3FFFFFFF16 = 107374182310 来防止未定义行为。
我不确定他们为什么选择这个值,因为在大多数机器上,只有most significant bit tells the signedness。与标准相比,选择最小值 INT_MAX 可能是有意义的,但 1073741823 在不了解更多细节的情况下确实有点奇怪(尽管它当然完美地完成了他们函数上方的评论所说的:截断到 30 位并防止溢出)。