例如:
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 的原因吗?
如果您在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
无符号长整数。堆栈中的下一个值也恰好是 0。
TL/DR:由于您没有包含stdlib.h
并忽略编译器警告,因此编译器假定strtoul
应该返回int
. 此外,函数参数的评估顺序在标准中没有明确规定。从那里开始,您调用未定义的行为,并且所有内容都可以打印出来。
你应该从中学到什么:
的值可能在被调用之前errno
被读取。 strtoul
参数评估的顺序未指定:
任何 C 运算符的操作数的求值顺序,包括函数调用表达式中函数参数的求值顺序,以及任何表达式中子表达式的求值顺序都是未指定的(除非下文另有说明)。编译器将以任何顺序计算它们,并且当再次计算相同的表达式时可能会选择另一个顺序。
……
该标准具有以下示例:
在函数调用中
(*pf[f1()]) (f2(), f3() + f4())
函数
f1
,f2
,f3
和f4
可以按任何顺序调用。所有副作用都必须在调用指向的函数之前完成pf[f1()]
。