为什么在c中出现 more '%' conversions than data arguments 错误时输出随机整数?
Why random integer is outputed when more '%' conversions than data arguments error occurs in c?
我从 printf()
调用中取出年龄变量只是为了看看会发生什么。然后我用 make 编译它。似乎它只抛出关于转换百分比多于数据参数和未使用的年龄变量的警告,但没有编译错误。然后我 运行 可执行文件,它确实 运行。只是每次我运行它,它returns不同的随机整数。我想知道是什么导致了这种行为?
#include <stdio.h>
int main(int argc, char *arg[]) {
int age = 10;
int height = 72;
printf("I'm %d years old\n");
printf("I'm %d inches tall\n", height);
return 0;
}
根据 printf()
规范,如果所需格式说明符的参数数量不足,它会调用 undefined behavior.
那么,你的代码
printf("I'm %d years old\n");
缺少 %d
所需的参数,调用 UB 并且不保证产生任何有效结果。
交叉引用,C11
标准,章节 §7.21.6.1
[..] If there are insufficient arguments for the format, the behavior is
undefined. [..]
根据 C 标准(7.21.6.1 fprintf 函数 - 同样对 printf 有效)
- ...If there are insufficient arguments for the format, the behavior is undefined. If the format is exhausted while arguments
remain, the excess arguments are evaluated (as always) but are
otherwise ignored.
使用cdecl的printf,使用堆栈参数。如果你向函数暗示你正在使用一个参数,它将被从运行时堆栈中拉出,如果你没有把你的数字放在那里,这个地方可能会包含一些垃圾数据。所以将要打印的参数是一些任意数据。
除了我所知道的一个例外,C 标准没有对在某些看似合理的实现中可能有用地捕获的任何操作强加任何要求。不难想象 C 编译器会传递一个可变参数函数,如 printf
以指示它传递了哪些参数,实现者也不难想象让编译器在以下情况下触发陷阱可能是有用的:当相应的参数是其他类型或根本不存在时,代码会尝试检索某种类型的可变参数。因为在这种情况下让编译器陷入陷阱可能很有用,并且因为这种陷阱的行为不在标准的管辖范围内,所以标准没有对可变参数函数尝试接收参数时可能发生或不发生的情况强加任何要求没有传递给它。
在实践中,大多数编译器并没有让可变参数函数知道它们收到了多少参数,而是简单地使用约定来描述非可变参数的位置与后续可变参数的位置之间的关系。生成的代码不知道函数是否已经收到,例如两个 int
类型的参数,但它会知道每个这样的参数,如果它存在 ,将被存储在某个地方。在这样的编译器上,使用过多的格式说明符通常会导致生成的代码查看如果存在额外参数本应存储的位置。在许多情况下,这个位置将被用于其他目的,然后被遗弃,并且可能保存为此目的存储在那里的最后一个值,但通常没有理由期望任何特别关于被遗弃内存内容的内容。
我从 printf()
调用中取出年龄变量只是为了看看会发生什么。然后我用 make 编译它。似乎它只抛出关于转换百分比多于数据参数和未使用的年龄变量的警告,但没有编译错误。然后我 运行 可执行文件,它确实 运行。只是每次我运行它,它returns不同的随机整数。我想知道是什么导致了这种行为?
#include <stdio.h>
int main(int argc, char *arg[]) {
int age = 10;
int height = 72;
printf("I'm %d years old\n");
printf("I'm %d inches tall\n", height);
return 0;
}
根据 printf()
规范,如果所需格式说明符的参数数量不足,它会调用 undefined behavior.
那么,你的代码
printf("I'm %d years old\n");
缺少 %d
所需的参数,调用 UB 并且不保证产生任何有效结果。
交叉引用,C11
标准,章节 §7.21.6.1
[..] If there are insufficient arguments for the format, the behavior is undefined. [..]
根据 C 标准(7.21.6.1 fprintf 函数 - 同样对 printf 有效)
- ...If there are insufficient arguments for the format, the behavior is undefined. If the format is exhausted while arguments remain, the excess arguments are evaluated (as always) but are otherwise ignored.
使用cdecl的printf,使用堆栈参数。如果你向函数暗示你正在使用一个参数,它将被从运行时堆栈中拉出,如果你没有把你的数字放在那里,这个地方可能会包含一些垃圾数据。所以将要打印的参数是一些任意数据。
除了我所知道的一个例外,C 标准没有对在某些看似合理的实现中可能有用地捕获的任何操作强加任何要求。不难想象 C 编译器会传递一个可变参数函数,如 printf
以指示它传递了哪些参数,实现者也不难想象让编译器在以下情况下触发陷阱可能是有用的:当相应的参数是其他类型或根本不存在时,代码会尝试检索某种类型的可变参数。因为在这种情况下让编译器陷入陷阱可能很有用,并且因为这种陷阱的行为不在标准的管辖范围内,所以标准没有对可变参数函数尝试接收参数时可能发生或不发生的情况强加任何要求没有传递给它。
在实践中,大多数编译器并没有让可变参数函数知道它们收到了多少参数,而是简单地使用约定来描述非可变参数的位置与后续可变参数的位置之间的关系。生成的代码不知道函数是否已经收到,例如两个 int
类型的参数,但它会知道每个这样的参数,如果它存在 ,将被存储在某个地方。在这样的编译器上,使用过多的格式说明符通常会导致生成的代码查看如果存在额外参数本应存储的位置。在许多情况下,这个位置将被用于其他目的,然后被遗弃,并且可能保存为此目的存储在那里的最后一个值,但通常没有理由期望任何特别关于被遗弃内存内容的内容。