将ptrdiff_t的正值分配给size_t是否安全?

时间:2018-08-03 11:44:08

标签: c language-lawyer

我正在尝试实现strlen函数,并且遇到了这种实现 在linux kernel中:

size_t strlen(const char *s)
{
    const char *sc;

    for (sc = s; *sc != '\0'; ++sc)
        /* nothing */;
    return sc - s;
}

根据C标准,sc - s操作的最佳拟合类型是 ptrdiff_t。

现在,在此代码段中,保证sc大于或等于0,这意味着 我们没有返回否定类型的风险。

对于正值,将sc - c返回为size_t是否安全?换句话说,是否有可能 在现实世界中,实现PTRDIFF_MAX大于SIZE_MAX


这是我对strlen的实现:

size_t strlen(const char *s)
{
    size_t size = 0;

    while (*s != '\0') {
        size++;
        s++;
    }

    return size;
}

3 个答案:

答案 0 :(得分:4)

是的,如果传递有效的对象是安全的,因为两个指针的减法只能用指向同一对象的指针来完成。

  

6.5.6加法运算符

     
      
  1. 减去两个指针时,都应指向同一数组对象的元素,   或数组对象的最后一个元素之后;结果是   两个数组元素的下标。 ...
  2.   

由于sizeof运算符最多可以返回SIZE_MAX,因此表达式sc - s的返回值永远不能大于该值。

答案 1 :(得分:2)

出于争论的目的,我将发表与当前接受的答案略有不同的观点,而不是说这实际上不是一个明显的问题。

  1. return sc - s;的错误之处不在于从ptrdiff_t的减法类型到size_t返回类型的隐式转换,例如 {{1} }可以按照标准溢出并it is undefined what happens when it does
  2. 这意味着“表达式sc - s中的推理永远不能返回大于[sc - s]”的说法是错误的:用了标准的措辞,以使指针之间的实际差异大于SIZE_MAX,然后减法调用未定义的行为。发生这种情况时,您可以将差异表示为PTRDIFF_MAX并不重要,因为最终转换为size_t并不会撤消size_t减法期间发生的不确定行为。
  3. 这仅是在实践中对于32位平台的担心,其中ptrdiff_t允许分配大于2GiB的块,并且程序可以有效地使用它。例如,在Linux内核中就不是这种情况。当Linux内核针对32位平台进行编译时,问题所在的函数malloc将永远不会在具有该大小的字符串上调用。
  4. 如果strlen的实现允许您创建大于2GiB的块,并且您在C程序中使用了该功能,那么即使避开减去多于2GiB的malloc指针,您仍在导航雷区此外,由于优化编译器假设各种优化even when the standard does not allow them to,内存块小于2GiB。

该标准还允许char大于PTRDIFF_MAX。在这种情况下,将应用指针减法的实际结果也必须小于SIZE_MAX并且可以安全地转换为SIZE_MAX的推理。 (我从未遇到过这样的平台,我认识的所有编译器对于size_tptrdiff_t都使用相同的宽度。)

答案 2 :(得分:-2)

要回答您的问题,在现实世界中可能发生任何事情,这取决于您在现实世界中如何使用和部署应用程序。

根据经验,如果您正在使用此代码做一些重要的事情,并且希望将来使用它,我强烈建议在所有情况下都稳健地实现它。

在读过here关于ptrdiff的信息后,就表明创建此类型是为了说明64位系统。

如果您不考虑特殊情况,则可能会遇到各种平台上的可移植性问题。

现在,对于size_t,"SIZE_MAX must be at least 65535",这是您在所有系统上都能找到的最小值,并且较旧的系统可能会受到限制。

c type limit网站上,查看:

PTRDIFF_MAX    = +9223372036854775807
SIZE_MAX       = 18446744073709551615

因此,是的,使用其他经过实践检验的实现可能会有风险。