我正在运行一个包含声明的程序:
#include <stdio.h>
#include <string.h>
main() {
printf("%d","Hello"+1);
}
它给出4196445作为输出。它是正确的..请解释逻辑
答案 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。