什么是printf的输出(“%d”,“你好”+1);?

时间:2014-06-04 05:04:05

标签: c

我正在运行一个包含声明的程序:

#include <stdio.h>
#include <string.h>
main() {
  printf("%d","Hello"+1);  
}

它给出4196445作为输出。它是正确的..请解释逻辑

3 个答案:

答案 0 :(得分:4)

  

printf的输出是什么(“%d”,“Hello”+1);?

其他人声称它将“打印字符串的地址加一”。 可能是也可能不是。原因是您的代码调用未定义的行为,因为您正在使用格式打印char *类型的对象说明符仅适用于int

因此,编译器和随后的代码可以做任何事情(包括擦除硬盘或下载最新的Justin Bieber专辑。)

答案 1 :(得分:0)

虽然有些人会说“根据标准,这是未定义的行为”,但这是事实上发生的事情。请注意,这是一个非常一般的描述,根据您的平台(编译器,CPU架构,操作系统,MMU,标准输出控制器等)可能略有不同:

  • 编译器生成以空字符结尾的字符串("Hello"),并将其放在程序的代码部分(RO数据部分更准确)中。

  • 每次创建进程并将可执行映像加载到内存中时(即运行程序时),包含字符的字符串 'H''e''l''l''o''\0'位于逻辑内存地址4196444.此字符串的物理内存地址可以通过将该值添加到Base-Address寄存器的值来计算(尽管这对您来说无关紧要,因为您的程序对此无视)。

  • 由于此字符串的逻辑地址将在程序的每次执行过程中保持为4196444,因此编译器可以将"Hello"+1的计算替换为常数值4196445。

  • 所以你可以想象编译器编译printf("%d","Hello"+1)而不是编译printf("%d",4196445)。实际上,由于"%d"字符串也是位于程序代码部分的常量字符串,因此它也会替换为某个常量值。

  • 顺便说一句,如果您使用的是指向"Hello"字符串的变量,那么编译器无法在编译期间确定该值,而是会生成代码以在运行时计算它。计算本身将使用堆栈或通用寄存器(或可能是两者的组合)来执行。下面是如何通过堆栈计算此值的典型示例(这是程序的另一部分 - 类似于代码部分,但具有写权限):

    • 变量的值被压入堆栈。

    • 将值1推入堆栈。

    • 前两个元素从堆栈中弹出并添加。

    • 结果被推回堆栈。

  • 在任何情况下,调用printf("%d","Hello"+1)时:

    • 字符串"%d"的地址被压入堆栈。

    • 字符串"Hello"加1的地址被推入堆栈。

    • 程序计数器(或有人称之为指令指针)跳转到内存中函数printf的地址,并从那里继续执行。

    • 对于传递给函数%的第一个参数指向的字符串中的每个printf字符,该函数从堆栈加载相应的参数,然后 - 基于之后指定的类型%字符 - 计算要打印的数据。

    • 最后,结果被发送到屏幕(为了更准确,对于结果中的每个字符,生成标准输出中断,导致PC(程序计数器)/ IP(指令指针)到跳转到IV(中断向量),其中调用指定的ISR(中断服务程序),它本质上是一个函数,就像代码中的任何其他函数一样,然后将输入字符写入标准的FIFO队列中 - 输出控制器)。


正如@hvd在以下评论之一中暗示的那样:

在64位系统上,%d%会将"Hello"+1的结果从64位值截断为32位值。使用%lld可以解决这个问题,但当然 - 正确的解决方案是使用%p

答案 2 :(得分:-2)

这将打印出值       (字符串的地址(&#34; Hello&#34;)+ 1)

所以,在你的情况下,字符串的基地址&#34;你好&#34;是4196444。 这就是打印输出的原因4196445。