尝试打印指针时 Turbo C 和 gcc 中 printf() 的不同行为

Different behavior of printf() in Turbo C and gcc when trying to print a pointer

下面的代码给出了在Turbo C编译器中没有错误的输出,并给出了变量的地址和它的值:

int a=5,*fp;
fp=&a;
printf("%d %d\n",fp,*fp);

但是当我用 GCC 编译器在 Linux 中编译相同的代码时,它给出了一个错误:

`warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘int *’ [-Wformat=]
printf("%d %d\n",fp,*fp);`

但是带有 %p 格式说明符的相同代码在 GCC 编译器中有效,我同意这一点。问题是:它怎么能在Turbo C平台上工作?

P.S。问题不是(在 Turbo C 中)没有报告错误,而是在 Turbo C 上 它给出一个有符号整数值,该值在程序重复执行时不变;会不会是垃圾?

P.P.S Turbo C 在 MSDOS 平台上是 运行,在 64 位平台上是 GCC Linux,如果有帮助的话。

这是警告,不是错误。第一个编译器也可以这么说,但没有。

警告不会停止编译,所以它也会起作用。它们只是不同的编译器。此外,编译器接受您的程序并不意味着该程序是正确的。

printf() 假定您将为 %d 说明符传递 signed int

在您使用的平台和编译器上,整数和指针可能大小相同,printf() 能够正确显示。您的指针被重新解释为 int。

在不同的平台上编译和 运行 这将是未定义的行为,例如,int 是 32 位的,指针是 64 位的(例如 x64 上的 gcc)。如果你足够幸运,它会崩溃。否则,你会得到垃圾。

%d 转换说明符要求相应的参数具有类型 [signed] int。如果相应的参数实际上具有不同的类型,则该行为是明确未定义的。无论预期类型和实际类型的相对大小如何,也无论这些类型之间是否可以进行隐式或显式转换,情况都是如此。

当程序表现出未定义的行为时,编译器的行为和任何生成的已编译程序的任何部分的行为都未定义。诊断问题不需要 Turbo C。另一方面,gcc 可以对其进行诊断,甚至可以拒绝带有错误的源代码,而不仅仅是通过警告来提醒您。就 C 而言,如果触发任何未定义的行为,整个程序的 行为绝对可以是任何行为——从作者的意图(无论可能是什么)到发出粗鲁的评论机器的喇叭,而且远远超出。

实际上,如果预期类型和实际类型大小相同,并且转换说明符不是 %s,则未定义行为很可能(但绝不一定)以相对良性的方式表现出来。否则,所有赌注都将取消。请特别注意,许多 64 位平台的 C 实现具有 32 位 ints 和 64 位指针。

每个转换说明符,例如 %d,指定所需参数的类型和用于打印它的格式。

  • %d 需要一个类型为 int 的参数(等价于 signed int),并以十进制打印它。
  • %u 需要类型为 unsigned int 的参数并以十进制打印。
  • %x 需要类型为 unsigned int 的参数并以十六进制打印。

等等。

在您的代码中:

int a=5,*fp;
fp=&a;
printf("%d %d\n",fp,*fp);

第二个 %d 是正确的,因为相应的参数 *fpint 类型。但是第一个是不正确的,因为相应的参数 fp 是类型 int*.

如果您为转换说明符传递了错误类型的参数,则行为未定义。如果您这样做,编译器不需要警告您,因为在大多数情况下不可能检测到错误(格式字符串不必是字符串文字)。某些编译器(包括 gcc)将分析格式字符串(如果它们是字符串文字)并警告不匹配。 Turbo C 显然没有(这并不奇怪,它是一个非常古老的编译器)。

打印指针值的正确格式是%p。这需要一个 void* 类型的参数,并以实现定义的方式打印它。应转换 void* 以外类型的指针。

您的代码的正确版本是:

int a = 5, *fp;
fp = &a;
printf("%p %d\n", (void*)fp, *fp);