c,strtoul()在出错/溢出时返回无效值

时间:2018-02-03 21:25:59

标签: c strtoull

例如:

int main()
{
    errno = 0;
    printf("val = %lu err: %d\n", strtoul("12345678901234", NULL, 10), errno);
}

输出:

val = 1942892530错误:0

手册页说strtoul()应该返回ULONG_MAX(4294967295UL)的溢出值,有人可以解释strtoul()在这种情况下不返回ULONG_MAX的原因吗?

2 个答案:

答案 0 :(得分:2)

如果你所在的系统unsigned long使用64位,而int只使用32位,如果你没有包含stdlib.h,编译器会认为strtoul是一个函数返回一个int值。

在这种情况下,strtoul("12345678901234", NULL, 10)将返回12345678901234L或十六进制0xb3a73ce2ff2。假设函数返回一个int,它将转换为32位int。这种转换是实现定义的,并且通用实现只保留32个低位,这将给出0x73ce2ff2或十进制1942892530。您传递一个32位的int,其中64位是预期的,因此结果是未定义的,但是常见的实现只是使用堆栈中的下一个值来完成该值。由于我假设您的实现是小端,并且下一个值为0,因为errno在前一行设置为0,并且参数的评估顺序未定义,因此您仍然获得1942892530 as一个无符号的长。堆栈中的下一个值恰好也是0。

TL / DR:由于您没有包含stdlib.h并忽略了编译器警告,编译器认为strtoul应该返回int。另外,函数的参数的评估顺序明确地未通过标准指定。从那以后,你调用未定义的行为,一切都可以打印出来。

你应该从中学到什么:

  • 不要忽略编译器警告
  • 不要忘记为您使用的标准库的所有功能添加标题

答案 1 :(得分:1)

在调用 errno之前,strtoul的值很可能是

参数评估的顺序是unspecified

  

任何C运算符的操作数的评估顺序,包括函数调用表达式中函数参数的评估顺序,以及任何表达式中子表达式的评估顺序都是未指定的(除非另有说明)。编译器将以任何顺序对它们进行评估,并且可以在再次评估相同的表达式时选择另一个顺序   ....

标准有以下例子:

  

在函数调用中

(*pf[f1()]) (f2(), f3() + f4())
     

可以按任何顺序调用函数f1f2f3f4。在调用pf[f1()]指向的函数之前,必须完成所有副作用。