-1

例如:

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 的原因吗?

4

2 回答 2

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无符号长整数。堆栈中的下一个值也恰好是 0。

TL/DR:由于您没有包含stdlib.h并忽略编译器警告,因此编译器假定strtoul应该返回int. 此外,函数参数的评估顺序在标准中没有明确规定。从那里开始,您调用未定义的行为,并且所有内容都可以打印出来。

你应该从中学到什么:

  • 不要忽略编译器警告
  • 不要忘记包含您使用的标准库的所有函数的头文件
于 2018-02-03T22:33:08.047 回答
1

的值可能在被调用之前errno被读取。 strtoul

参数评估的顺序未指定

任何 C 运算符的操作数的求值顺序,包括函数调用表达式中函数参数的求值顺序,以及任何表达式中子表达式的求值顺序都是未指定的(除非下文另有说明)。编译器将以任何顺序计算它们,并且当再次计算相同的表达式时可能会选择另一个顺序。
……

该标准具有以下示例:

在函数调用中

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

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

于 2018-02-03T21:29:24.963 回答