为什么使用“strlen30()”而不是“strlen()”?

时间:2011-07-27 09:31:31

标签: c sqlite strlen

我已阅读并想知道sqlite的源代码

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

为什么使用strlen30()代替strlen()(在string.h中)??

3 个答案:

答案 0 :(得分:3)

进行此更改的commit message声明:

  

[793aaebd8024896c]登记入住的一部分[c872d55493]切勿使用strlen()。使用我们自己的内部sqlite3Strlen30(),保证永远不会溢出整数。额外的显式转换以避免滋扰警告消息。 (CVS 6007)(用户:drh branch:trunk)

答案 1 :(得分:2)

(这是我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。:

  

无符号整数,声明为无符号整数,应遵守算术模2 n 的定律,其中n是该特定整数大小的值表示中的位数。 41)

     

...

     

41):这意味着无符号算术不会溢出,因为无法用结果无符号整数表示的结果   type是以模数减少的模数,该数字大于可由结果无符号整数表示的最大值   型

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

  

如果在评估表达式期间,结果未在数学上定义或未在其类型的可表示值范围内,则行为未定义,除非此类表达式是常量表达式   (5.19),在这种情况下,该程序是不正确的。 [注意:大多数现有的C ++实现忽略整数   溢出。除零处理,使用零除数和所有浮点形成余数   机器之间的例外情况各不相同,通常可以通过库函数进行调整。 ]

到目前为止溢出。

至于减去两个指向数组元素的指针, 5.7 Additive operators ,6。:

  

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

查看 18.1

  

内容与标准C库标题 stddef.h

相同

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

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

ptrdiff_t没有进一步的保证。然后,附件E(仍在ISO / IEC 9899:TC2中)给出了有符号long int的最小幅度,但不是最大值:

#define LONG_MAX +2147483647

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

#define INT_MAX +32767



摘要

  1. 通常,ptrdiff_t不大于signed long,不小于32位。
  2. int被定义为至少16位长。
  3. 因此,减去两个指针可能会产生一个不适合您平台的int的结果。
  4. 我们从上面记得,对于签名类型,不适合的结果会产生未定义的行为。
  5. strlen30确实应用了bitwise或者指针 - 减法 - 结果:

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

    通过将指针减法结果截断为最大值3FFFFFFF 16 = 1073741823 10 来防止不良行为。

    我不确定他们为什么选择这个值,因为在大多数机器上只有most significant bit tells the signedness。选择最小INT_MAX可能比标准更有意义,但1073741823确实有点奇怪而不知道更多细节(尽管它当然完全符合其功能上面的注释:截断到30位并防止溢出)

答案 2 :(得分:1)

CVS提交消息说:

  

永远不要使用strlen()。使用我们自己的内部sqlite3Strlen30(),保证永远不会溢出整数。额外的显式转换以避免滋扰警告消息。 (CVS 6007)

我找不到任何进一步的参考这个提交或解释它们是如何在那个地方溢出的。我认为这是一些静态代码分析工具报告的错误。