'printf("%n %d", &a, a)' 定义明确吗?
Is 'printf("%n %d", &a, a)' well defined?
#include <stdio.h>
int main(void) {
int i = 0;
printf("abc %n %d", &i, i);
printf("\n%d\n", i);
}
当我执行它时,我得到了以下结果。
abc 0
4
我认为这个结果是有意的
但是当我执行下一个时,我得到了不同的结果。
int main(void) {
int i; // not initialize
printf("abc %n %d", &i, i);
printf("\n%d\n", i);
}
产生了:
abc 1
4
不知道为什么第一个printf()
中i
的结果是1
。
甚至,我发现了更多奇怪的行为:
int main(void) {
int sdf;
printf("abc %n %d", &sdf, sdf);
printf("\n%d\n", sdf);
int i;
printf("abc %n %d", &i, i);
printf("\n%d\n", i);
}
使用此输出:
abc 1
4
abc Random_Value
4
第一个总是显示 1
但其他显示随机值(我认为这是垃圾值)。
我认为垃圾值是有意的,但我不明白为什么第一个有不同的结果。
结果定义明确。 i
和 i
的地址被传递给 printf
。然后 printf 将值赋给 i。但是因为i
是在它之前传递的,所以printf会在调用之前打印i
的值。
后面的代码片段使用未初始化的变量,这个未确定的值将首先被打印出来。稍后的行为将与每个片段 1 相同。
未初始化的变量将具有未确定的值。如果变量的类型有陷阱表示,它也可以是一个 UB(这里不是这种情况)但是因为执行环境是相同的,所以很可能你会看到相同的值。如果您 运行 它在其他 OS、其他计算机上或将使用其他编译器,它们可能会有所不同。
Is printf(“%n %d”, &a, a)
defined well in printf?
如果 a
的类型为 int
(等价于 signed int
)并且不是 const
限定的,则为是,否则为否。在well-defined的情况下,打印调用时a
的值,调用后returns,a
的值为0.
也许这里的关键点是函数调用的参数在进入被调用函数之前被评估,并且它们是按值传递的。在参数求值和函数 body 中第一条语句的执行之间存在一个序列点,因此 a
由调用函数读取并由 printf
写入这一事实确实不存在任何特定问题。
[...] when I execute next one, I got a different result.
int main(void)
{
int i; // not initialize
printf("abc %n %d", &i, i);
printf("\n%d\n", i);
}
abc 1
4
I don't know why the result of 'i' in the first printf() is 1.
没有人这样做。一个既没有初始化也没有赋值的自动变量的值是indeterminate。与标题问题相同的注意事项适用于此处:printf
的参数列表中的表达式 i
在 printf
的任何部分执行之前被计算,因此 printf
稍后会为它赋值,不影响它接收到的值。
存储classauto
的一个未初始化的局部整型变量,存储在栈区的值为undefined .出于这个原因,打印它,一个随机值是绝对期望的。
这就是为什么,在你的输出中
abc 1
4
abc Random_Value
4
值 1
也是垃圾。
实际上它是堆栈中的第一个位置,它的值可能因系统and/or编译器的不同而不同。我的 猜测 是它的值为 1,因为它代表一个“幽灵 argc
”,作为一个非常特殊的函数,即使定义该函数时没有参数,它也会存在,它的值为 1.
由于 argc
表示用于从命令行调用您的程序的参数数量(至少 1:可执行文件名称)有一种方法可以验证这个假设:以这种方式调用您的程序
executableName foo
这将使 argc 变为 2,因此第一个 printf
显示的值也应变为 2
。
出于好奇,我通过在我的机器上编译你的第二个示例来测试这个假设(DevC++ 和 gcc
编译器在 W10 64 位 OS 下)。我确认了两个陈述:
- 当发生未定义行为时,改变环境会导致输出改变
argc
存在于栈中,影响未初始化局部变量的初始值
执行后的输出uninitVars.exe
abc
0
abc
1
执行后的输出uninitVars.exe dog
abc
0
abc
2
执行后的输出uninitVars.exe dog cat
abc
0
abc
3
所以我的堆栈的前四个字节似乎总是设置为 0(它们是 return 值的位置吗?)而第二个 实际上是 argc
,即使在main()
原型中没有明确定义。
相反,第二个打印显示不同的值,因为它是在两次 printf
调用之后定义的,并且它们的执行在堆栈中写入几个字节(其中一些是地址,这解释了为什么值是总是不同的,因为进程的地址是虚拟的并且总是不同的)。
首先,你不能在同一行打印%n
的结果,i
会保留它之前的值。关于随机值,虽然您没有初始化 i
和 sdf
,但它们有一个随机值(很可能是 0 但没有保证)。在C语言中,全局变量如果没有初始化就会有0值,但是局部变量会有未初始化的值(随机)。
由于所讨论的变量未被初始化,行为充其量是未指定的,最坏的情况是未定义的。
局部变量i
和sdf
是未初始化的,也就是说它们的值是不确定的。正式定义在 C standard 的第 3.19 节中,如下所示:
3.19.2
1 indeterminate value
either an unspecified value or a trap representation
3.19.3
1 unspecified value
valid value of the relevant type where this International Standard imposes no requirements on
which value is chosen in any instance
2 NOTE An unspecified value cannot be a trap representation.
3.19.4
1 trap representation
an object representation that need not represent a value of the object type
这基本上意味着该值是不可预测的。事实上,在某些情况下,简单地读取一个不确定的值可能会导致 undefined behavior。如果不确定值恰好是如上定义的陷阱表示,就会发生这种情况。
如果相关变量的地址从未被占用,也可能是未定义的行为,但这在这种情况下不适用,因为您确实占用了地址。此行为记录在第 6.3.2.1p2 节中:
Except when it is the operand of the sizeof
operator, the
_Alignof
operator, the unary &
operator, the ++
operator, the
--
operator, or the left operand of the .
operator or an
assignment operator, an lvalue that does not have array type
is converted to the value stored in the designated object
(and is no longer an lvalue); this is called lvalue
conversion. If the lvalue has qualified type, the value has
the unqualified version of the type of the lvalue; additionally,
if the lvalue has atomic type, the value has the non-atomic version
of the type of the lvalue; otherwise, the value has the
type of the lvalue. If the lvalue has an incomplete type and does
not have array type, the behavior is undefined. If the lvalue
designates an object of automatic storage duration that could
have been declared with the register
storage class (never had its
address taken), and that object is uninitialized (not declared
with an initializer and no assignment to it has been
performed prior to use), the behavior is undefined.
因此假设您的实现没有陷阱表示,sdf
和 i
的值未指定,这意味着它们可以是任何值,包括 0 或 1。例如,你得到值 1 和 sdf
和 i
的(一些随机值)。当我 运行 相同的代码时,我得到这个:
abc 0
4
abc 0
4
如果我使用设置更高优化级别的 -O3
进行编译,我会得到:
abc 1446280512
4
abc 0
4
如您所见,运行读取未指定值的代码在不同的机器上可能会产生不同的结果,甚至在具有不同编译器设置的同一台机器上也会有不同的结果。
我得到的值0和你得到的值1没有什么特别的。它们与 1446280512 一样随机。
#include <stdio.h>
int main(void) {
int i = 0;
printf("abc %n %d", &i, i);
printf("\n%d\n", i);
}
当我执行它时,我得到了以下结果。
abc 0
4
我认为这个结果是有意的
但是当我执行下一个时,我得到了不同的结果。
int main(void) {
int i; // not initialize
printf("abc %n %d", &i, i);
printf("\n%d\n", i);
}
产生了:
abc 1
4
不知道为什么第一个printf()
中i
的结果是1
。
甚至,我发现了更多奇怪的行为:
int main(void) {
int sdf;
printf("abc %n %d", &sdf, sdf);
printf("\n%d\n", sdf);
int i;
printf("abc %n %d", &i, i);
printf("\n%d\n", i);
}
使用此输出:
abc 1
4
abc Random_Value
4
第一个总是显示 1
但其他显示随机值(我认为这是垃圾值)。
我认为垃圾值是有意的,但我不明白为什么第一个有不同的结果。
结果定义明确。 i
和 i
的地址被传递给 printf
。然后 printf 将值赋给 i。但是因为i
是在它之前传递的,所以printf会在调用之前打印i
的值。
后面的代码片段使用未初始化的变量,这个未确定的值将首先被打印出来。稍后的行为将与每个片段 1 相同。
未初始化的变量将具有未确定的值。如果变量的类型有陷阱表示,它也可以是一个 UB(这里不是这种情况)但是因为执行环境是相同的,所以很可能你会看到相同的值。如果您 运行 它在其他 OS、其他计算机上或将使用其他编译器,它们可能会有所不同。
Is
printf(“%n %d”, &a, a)
defined well in printf?
如果 a
的类型为 int
(等价于 signed int
)并且不是 const
限定的,则为是,否则为否。在well-defined的情况下,打印调用时a
的值,调用后returns,a
的值为0.
也许这里的关键点是函数调用的参数在进入被调用函数之前被评估,并且它们是按值传递的。在参数求值和函数 body 中第一条语句的执行之间存在一个序列点,因此 a
由调用函数读取并由 printf
写入这一事实确实不存在任何特定问题。
[...] when I execute next one, I got a different result.
int main(void) { int i; // not initialize printf("abc %n %d", &i, i); printf("\n%d\n", i); }
abc 1 4
I don't know why the result of 'i' in the first printf() is 1.
没有人这样做。一个既没有初始化也没有赋值的自动变量的值是indeterminate。与标题问题相同的注意事项适用于此处:printf
的参数列表中的表达式 i
在 printf
的任何部分执行之前被计算,因此 printf
稍后会为它赋值,不影响它接收到的值。
存储classauto
的一个未初始化的局部整型变量,存储在栈区的值为undefined .出于这个原因,打印它,一个随机值是绝对期望的。
这就是为什么,在你的输出中
abc 1
4
abc Random_Value
4
值 1
也是垃圾。
实际上它是堆栈中的第一个位置,它的值可能因系统and/or编译器的不同而不同。我的 猜测 是它的值为 1,因为它代表一个“幽灵 argc
”,作为一个非常特殊的函数,即使定义该函数时没有参数,它也会存在,它的值为 1.
由于 argc
表示用于从命令行调用您的程序的参数数量(至少 1:可执行文件名称)有一种方法可以验证这个假设:以这种方式调用您的程序
executableName foo
这将使 argc 变为 2,因此第一个 printf
显示的值也应变为 2
。
出于好奇,我通过在我的机器上编译你的第二个示例来测试这个假设(DevC++ 和 gcc
编译器在 W10 64 位 OS 下)。我确认了两个陈述:
- 当发生未定义行为时,改变环境会导致输出改变
argc
存在于栈中,影响未初始化局部变量的初始值
执行后的输出uninitVars.exe
abc
0
abc
1
执行后的输出uninitVars.exe dog
abc
0
abc
2
执行后的输出uninitVars.exe dog cat
abc
0
abc
3
所以我的堆栈的前四个字节似乎总是设置为 0(它们是 return 值的位置吗?)而第二个 实际上是 argc
,即使在main()
原型中没有明确定义。
相反,第二个打印显示不同的值,因为它是在两次 printf
调用之后定义的,并且它们的执行在堆栈中写入几个字节(其中一些是地址,这解释了为什么值是总是不同的,因为进程的地址是虚拟的并且总是不同的)。
首先,你不能在同一行打印%n
的结果,i
会保留它之前的值。关于随机值,虽然您没有初始化 i
和 sdf
,但它们有一个随机值(很可能是 0 但没有保证)。在C语言中,全局变量如果没有初始化就会有0值,但是局部变量会有未初始化的值(随机)。
由于所讨论的变量未被初始化,行为充其量是未指定的,最坏的情况是未定义的。
局部变量i
和sdf
是未初始化的,也就是说它们的值是不确定的。正式定义在 C standard 的第 3.19 节中,如下所示:
3.19.2
1 indeterminate value
either an unspecified value or a trap representation
3.19.3
1 unspecified value
valid value of the relevant type where this International Standard imposes no requirements on which value is chosen in any instance
2 NOTE An unspecified value cannot be a trap representation.
3.19.4
1 trap representation
an object representation that need not represent a value of the object type
这基本上意味着该值是不可预测的。事实上,在某些情况下,简单地读取一个不确定的值可能会导致 undefined behavior。如果不确定值恰好是如上定义的陷阱表示,就会发生这种情况。
如果相关变量的地址从未被占用,也可能是未定义的行为,但这在这种情况下不适用,因为您确实占用了地址。此行为记录在第 6.3.2.1p2 节中:
Except when it is the operand of the
sizeof
operator, the_Alignof
operator, the unary&
operator, the++
operator, the--
operator, or the left operand of the.
operator or an assignment operator, an lvalue that does not have array type is converted to the value stored in the designated object (and is no longer an lvalue); this is called lvalue conversion. If the lvalue has qualified type, the value has the unqualified version of the type of the lvalue; additionally, if the lvalue has atomic type, the value has the non-atomic version of the type of the lvalue; otherwise, the value has the type of the lvalue. If the lvalue has an incomplete type and does not have array type, the behavior is undefined. If the lvalue designates an object of automatic storage duration that could have been declared with theregister
storage class (never had its address taken), and that object is uninitialized (not declared with an initializer and no assignment to it has been performed prior to use), the behavior is undefined.
因此假设您的实现没有陷阱表示,sdf
和 i
的值未指定,这意味着它们可以是任何值,包括 0 或 1。例如,你得到值 1 和 sdf
和 i
的(一些随机值)。当我 运行 相同的代码时,我得到这个:
abc 0
4
abc 0
4
如果我使用设置更高优化级别的 -O3
进行编译,我会得到:
abc 1446280512
4
abc 0
4
如您所见,运行读取未指定值的代码在不同的机器上可能会产生不同的结果,甚至在具有不同编译器设置的同一台机器上也会有不同的结果。
我得到的值0和你得到的值1没有什么特别的。它们与 1446280512 一样随机。