为什么用C打印输出变量会改变它们的值?

use*_*121 1 c pointers

我只是用C编写了一个简单的程序,如下所示:

#include <stdio.h>
void main()
{
  int *pointer;
  int number = 4;
  printf("number = %d\n",number);
  pointer = &number;
  printf("number = %d and pointer = %d\n",number,pointer);
  printf("pointer = %d and number = %d",pointer,number);
}
Run Code Online (Sandbox Code Playgroud)

执行后,将输出如下所示:

number = 4 and pointer = 8724
pointer = 8724 and number = 9415
Run Code Online (Sandbox Code Playgroud)

因此,为什么只为打印方向改变而改变值。我不能证明这个规则。

Jon*_*ler 5

您使用的是64位编译吗?

int number = 4;
int *pointer = &number;
printf("number = %d and pointer = %d\n", number, pointer);
printf("pointer = %d and number = %d\n", pointer, number);
Run Code Online (Sandbox Code Playgroud)

如果是这样,则printf()上面代码中的第一个调用将4字节的int值和8字节的int *值压入堆栈,并告知printf()打印两个4字节的数量。这些显然是int8字节地址的4字节和4字节。第二个printf()调用将8字节int *和4字节压int入堆栈,并告知printf()再次打印两个4字节数量。但是这一次,它们是分别int *打印的8字节值的两半。严格来说,这是在调用不确定的行为,但这是一种可能的可能性,可以解释您看到的结果。

(有一些注意事项潜伏在我假设的64位编译。CHAR_BIT == 8sizeof(int) == 4以及sizeof(int *) == 8;也是落实堆栈传递参数,并且可能不严格C标准但保证一些相关的假设通常适用于64位编译器。)

您应该决定要做什么。如果您想看4两次,则:

int number = 4;
int *pointer = &number;
printf("number = %d and pointer = %d\n", number, *pointer);
printf("pointer = %d and number = %d\n", *pointer, number);
Run Code Online (Sandbox Code Playgroud)

如果您想查看地址,则应使用%p该地址(并严格转换为void *):

int number = 4;
int *pointer = &number;
printf("number = %d and pointer = %p\n", number, (void *)pointer);
printf("pointer = %p and number = %d\n", (void *)pointer, number);
Run Code Online (Sandbox Code Playgroud)

如果要控制指针的格式,则需要C99 #include <inttypes.h>和:

int number = 4;
int *pointer = &number;
printf("number = %d and pointer = 0x%.16" PRIXPTR "\n", number, (uintptr_t)pointer);
printf("pointer = 0x%.16" PRIXPTR " and number = %d\n", (uintptr_t)pointer, number);
Run Code Online (Sandbox Code Playgroud)

范例程式码

#include <stdio.h>
#include <inttypes.h>

int main(void)
{
    int number = 4;
    int *pointer = &number;
    printf("number = %d and pointer = %d\n", number, pointer);
    printf("pointer = %d and number = %d\n", pointer, number);

    printf("number = %d and pointer = %d\n", number, *pointer);
    printf("pointer = %d and number = %d\n", *pointer, number);

    printf("number = %d and pointer = %p\n", number, (void *)pointer);
    printf("pointer = %p and number = %d\n", (void *)pointer, number);

    printf("number = %d and pointer = 0x%.16" PRIXPTR "\n", number, (uintptr_t)pointer);
    printf("pointer = 0x%.16" PRIXPTR " and number = %d\n", (uintptr_t)pointer, number);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

带有警告的编译

Mac OS X 10.8.5上的GCC 4.8.1。同样也给出了类似的警告clang

gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition   it.c -o it
it.c: In function ‘main’:
it.c:8:5: warning: format ‘%d’ expects argument of type ‘int’, but argument 3 has type ‘int *’ [-Wformat=]
     printf("number = %d and pointer = %d\n", number, pointer);
     ^
it.c:9:5: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘int *’ [-Wformat=]
     printf("pointer = %d and number = %d\n", pointer, number);
     ^
Run Code Online (Sandbox Code Playgroud)

输出示例

number = 4 and pointer = 1417245952
pointer = 1417245952 and number = 4
number = 4 and pointer = 4
pointer = 4 and number = 4
number = 4 and pointer = 0x7fff54797500
pointer = 0x7fff54797500 and number = 4
number = 4 and pointer = 0x00007FFF54797500
pointer = 0x00007FFF54797500 and number = 4
Run Code Online (Sandbox Code Playgroud)

有趣的是,系统设法4在输出的前两行中两次打印该数字。我没有研究汇编程序来了解它是如何工作的,但是调用“不确定行为”的优点之一是您不能抱怨结果。任何结果都是正确的,因为所需的行为是不确定的。

  • @GrijeshChauhan是的。 (2认同)
  • @GrijeshChauhan:PRIXPTR是宏,用于定义要以大写十六进制打印的uintptr_t的正确转换说明符。它与`PRIu32`有关,因为它来自`&lt;inttypes.h&gt;`。 (2认同)