1

我只是用 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);
}

并在执行后打印输出,如:

number = 4 and pointer = 8724
pointer = 8724 and number = 9415

那么,为什么值仅针对打印方向的变化而变化。我无法证明这条规则的合理性。

4

4 回答 4

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);

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

(有一些警告潜伏着。我假设对于 64 位编译,CHAR_BIT == 8,sizeof(int) == 4sizeof(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);

如果你想查看地址,那么你应该使用%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);

如果要控制指针的格式,则需要 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);

示例代码

#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;
}

带有警告的编译

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);
     ^

示例输出

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

有趣的是,系统设法4在前两行输出中两次打印数字。我还没有研究过汇编器来看看它是如何工作的——但是调用“未定义行为”的好处之一是你不能抱怨结果;任何结果都是有效的,因为所需的行为是未定义的。

于 2013-10-04T04:24:33.107 回答
2

这可能是由于未定义的行为!当您为地址传递不正确的格式字符串时。

用于%p打印地址并将其排版为void*.

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

printf的部分引用的 fprintf 函数用于格式字符串段落:

如果转换规范无效,则行为未定义。[...]

取自@Shafik Yaghmour回答的文字

于 2013-10-04T04:16:33.673 回答
1

使用*pointer而不是pointer.

Pointer打印地址并*pointer打印值。

printf("number = %d and pointer = %d\n",number, *pointer);  
                                                ^ 
printf("pointer = %d and number = %d",*pointer,number);
                                      ^
于 2013-10-04T04:13:05.963 回答
1

看来您正在使用一个平台,其中 anint的大小和指针的大小不同,或者指针和 int 的参数传递不太可能不同。您尝试打印的类型 ( %d) 与您传递的类型(第一种情况下的 int/pointer 和第二种情况下的指针/int)之间存在不匹配。

于 2013-10-04T04:18:19.970 回答