我正在尝试实现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;
}
答案 0 :(得分:4)
是的,如果传递有效的对象是安全的,因为两个指针的减法只能用指向同一对象的指针来完成。
6.5.6加法运算符
- 减去两个指针时,都应指向同一数组对象的元素, 或数组对象的最后一个元素之后;结果是 两个数组元素的下标。 ...
由于sizeof
运算符最多可以返回SIZE_MAX
,因此表达式sc - s
的返回值永远不能大于该值。
答案 1 :(得分:2)
出于争论的目的,我将发表与当前接受的答案略有不同的观点,而不是说这实际上不是一个明显的问题。
return sc - s;
的错误之处不在于从ptrdiff_t
的减法类型到size_t
返回类型的隐式转换,例如 {{1} }可以按照标准溢出并it is undefined what happens when it does 。sc - s
中的推理永远不能返回大于[sc - s
]”的说法是错误的:用了标准的措辞,以使指针之间的实际差异大于SIZE_MAX
,然后减法调用未定义的行为。发生这种情况时,您可以将差异表示为PTRDIFF_MAX
并不重要,因为最终转换为size_t
并不会撤消size_t
减法期间发生的不确定行为。 ptrdiff_t
允许分配大于2GiB的块,并且程序可以有效地使用它。例如,在Linux内核中就不是这种情况。当Linux内核针对32位平台进行编译时,问题所在的函数malloc
将永远不会在具有该大小的字符串上调用。strlen
的实现允许您创建大于2GiB的块,并且您在C程序中使用了该功能,那么即使避开减去多于2GiB的malloc
指针,您仍在导航雷区此外,由于优化编译器假设各种优化even when the standard does not allow them to,内存块小于2GiB。该标准还允许char
大于PTRDIFF_MAX
。在这种情况下,将应用指针减法的实际结果也必须小于SIZE_MAX
并且可以安全地转换为SIZE_MAX
的推理。 (我从未遇到过这样的平台,我认识的所有编译器对于size_t
和ptrdiff_t
都使用相同的宽度。)
答案 2 :(得分:-2)
要回答您的问题,在现实世界中可能发生任何事情,这取决于您在现实世界中如何使用和部署应用程序。
根据经验,如果您正在使用此代码做一些重要的事情,并且希望将来使用它,我强烈建议在所有情况下都稳健地实现它。
在读过here关于ptrdiff的信息后,就表明创建此类型是为了说明64位系统。
如果您不考虑特殊情况,则可能会遇到各种平台上的可移植性问题。
现在,对于size_t,"SIZE_MAX must be at least 65535"
,这是您在所有系统上都能找到的最小值,并且较旧的系统可能会受到限制。
在c type limit网站上,查看:
PTRDIFF_MAX = +9223372036854775807
SIZE_MAX = 18446744073709551615
因此,是的,使用其他经过实践检验的实现可能会有风险。