当我创建各种案例时,我正在通过C和I中的指针我偶然发现了这个: (使用的IDE - Code :: Blocks
编译器 - GNU GCC)
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a=2;
int *pa;
pa=&a;
printf("1 %u\n", &a );
printf("2 %u\n", pa );
printf("3 %d\n", a );
printf("4 %u\n", &(pa));
//printf("\n4 %u\n", &(*pa)); // output not as expected
printf("End \n");
return 0;
}
这里的输出是:
1 2686748
2 2686748
3 2
4 2686744
结束
现在我将第4个printf更改为:
printf("\n4 %u\n", &(*pa));
输出更改为:
1 2686744
2 2686744
3 2
4 2686744
结束
在第二部分中, * pa 应该给出 2 而&amp;(* pa)应该给出 2686748 但此前的值已经改变了!
预期输出应为(&amp;(* pa)):2686748,2686748,2, 2686748
请解释一下,为什么我没有得到预期的输出以及我哪里出错?
我故意不在printf()中使用%p,所以请不要让我用它替换%u或%d。
如果需要,这里是指向IDEone的链接(也会生成类似的输出):
程序 - here只需删除/插入注释即可理解,首先包括printf,然后注释掉其中的1个。
忽略包含stdlib.h我懒得把它删除:P
请尽可能简单地保持:)
程序集:
First for:
printf("4 %u\n", &(pa));
// printf("\n4 %u\n", &(*pa));
0x00401334 push %ebp
0x00401335 mov %esp,%ebp
0x00401337 and $0xfffffff0,%esp
0x0040133A sub $0x20,%esp
0x0040133D call 0x401970 <__main>
0x00401342 movl $0x2,0x1c(%esp)
0x0040134A lea 0x1c(%esp),%eax
0x0040134E mov %eax,0x18(%esp)
0x00401352 lea 0x1c(%esp),%eax
0x00401356 mov %eax,0x4(%esp)
0x0040135A movl $0x403024,(%esp)
0x00401361 call 0x401be8 <printf>
0x00401366 mov 0x18(%esp),%eax
0x0040136A mov %eax,0x4(%esp)
0x0040136E movl $0x40302a,(%esp)
0x00401375 call 0x401be8 <printf>
0x0040137A mov 0x1c(%esp),%eax
0x0040137E mov %eax,0x4(%esp)
0x00401382 movl $0x403030,(%esp)
0x00401389 call 0x401be8 <printf>
**here>** 0x0040138E lea 0x18(%esp),%eax
0x00401392 mov %eax,0x4(%esp)
0x00401396 movl $0x403036,(%esp)
0x0040139D call 0x401be8 <printf>
0x004013A2 movl $0x40303c,(%esp)
0x004013A9 call 0x401be0 <puts>
0x004013AE mov $0x0,%eax
0x004013B3 leave
0x004013B4 ret
Second for :
// printf("4 %u\n", &(pa));
printf("\n4 %u\n", &(*pa));
0x00401334 push %ebp
0x00401335 mov %esp,%ebp
0x00401337 and $0xfffffff0,%esp
0x0040133A sub $0x20,%esp
0x0040133D call 0x401970 <__main>
0x00401342 movl $0x2,0x18(%esp)
0x0040134A lea 0x18(%esp),%eax
0x0040134E mov %eax,0x1c(%esp)
0x00401352 lea 0x18(%esp),%eax
0x00401356 mov %eax,0x4(%esp)
0x0040135A movl $0x403024,(%esp)
0x00401361 call 0x401be8 <printf>
0x00401366 mov 0x1c(%esp),%eax
0x0040136A mov %eax,0x4(%esp)
0x0040136E movl $0x40302a,(%esp)
0x00401375 call 0x401be8 <printf>
0x0040137A mov 0x18(%esp),%eax
0x0040137E mov %eax,0x4(%esp)
0x00401382 movl $0x403030,(%esp)
0x00401389 call 0x401be8 <printf>
**here>** 0x0040138E mov 0x1c(%esp),%eax
0x00401392 mov %eax,0x4(%esp)
0x00401396 movl $0x403036,(%esp)
0x0040139D call 0x401be8 <printf>
0x004013A2 movl $0x40303d,(%esp)
0x004013A9 call 0x401be0 <puts>
0x004013AE mov $0x0,%eax
0x004013B3 leave
0x004013B4 ret
Third one for:
printf("4 %u\n", &(pa));
printf("\n4 %u\n", &(*pa));
0x00401334 push %ebp
0x00401335 mov %esp,%ebp
0x00401337 and $0xfffffff0,%esp
0x0040133A sub $0x20,%esp
0x0040133D call 0x401980 <__main>
0x00401342 movl $0x2,0x1c(%esp)
0x0040134A lea 0x1c(%esp),%eax
0x0040134E mov %eax,0x18(%esp)
0x00401352 lea 0x1c(%esp),%eax
0x00401356 mov %eax,0x4(%esp)
0x0040135A movl $0x403024,(%esp)
0x00401361 call 0x401bf8 <printf>
0x00401366 mov 0x18(%esp),%eax
0x0040136A mov %eax,0x4(%esp)
0x0040136E movl $0x40302a,(%esp)
0x00401375 call 0x401bf8 <printf>
0x0040137A mov 0x1c(%esp),%eax
0x0040137E mov %eax,0x4(%esp)
0x00401382 movl $0x403030,(%esp)
0x00401389 call 0x401bf8 <printf>
0x0040138E lea 0x18(%esp),%eax
0x00401392 mov %eax,0x4(%esp)
0x00401396 movl $0x403036,(%esp)
0x0040139D call 0x401bf8 <printf>
**here>** 0x004013A2 mov 0x18(%esp),%eax
0x004013A6 mov %eax,0x4(%esp)
0x004013AA movl $0x40303c,(%esp)
0x004013B1 call 0x401bf8 <printf>
0x004013B6 movl $0x403043,(%esp)
0x004013BD call 0x401bf0 <puts>
0x004013C2 mov $0x0,%eax
0x004013C7 leave
0x004013C8 ret
答案 0 :(得分:4)
这可能是编译器的优化
如果您不使用&pa
,则pa
的所有用法都相当于&a
。
因此,编译器完全删除了pa
变量,并且它不再使用空格。
答案 1 :(得分:3)
Here in the 2nd part the the *pa should have given 2 and &(*pa) should have given 2686748 but here the previous values have been altered!
谁告诉过你,每次运行程序时,每次运行都会获得相同的内存位置?也可以使用%p
打印地址。
堆栈区域传统上与堆区域相邻并向相反方向增长;当堆栈指针遇到堆指针时,空闲内存耗尽。 (使用现代大地址空间和虚拟内存技术,它们几乎可以放置在任何地方,但它们通常仍然会朝着相反的方向发展。)
堆栈区域包含程序堆栈,LIFO结构,通常位于内存的较高部分。在标准的PC x86计算机体系结构上,它向零地址发展;在其他一些架构上,它朝着相反的方向发展。 “堆栈指针”寄存器跟踪堆栈的顶部;每次将值“推”到堆栈上时都会调整它。
存储自动变量的堆栈,以及每次调用函数时保存的信息。每次调用函数时,返回的地址和有关调用者环境的某些信息(例如某些机器寄存器)都会保存在堆栈中。新调用的函数然后在堆栈上为其自动和临时变量分配空间。
因此,可变点可以在任何地方分配,具体取决于每次运行。
答案 2 :(得分:0)
&a
会将指针地址打印到a
pa
也会将指针地址打印到a
,因为pa
的值是地址(*pa
是解除引用的值)
a
应该打印2,原因很明显
&(pa)
会打印地址pa
&(*pa)
是解除引用的pa的地址(a
的地址)
pa &(pa) *(&pa) *pa a &a
(a's address) (pa's address) (address of a) (value of a) (value of a) (address of a)
基本上,这是预期的行为。
答案 3 :(得分:0)
我解释下面的单个步骤:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a=2;
int *pa;
pa=&a; // (1)
printf("1 %u\n", &a ); // outputs the address of a
printf("2 %u\n", pa ); // outputs the address of a as well due to (1)
printf("3 %d\n", a ); // outputs the value of a
printf("4 %u\n", &(pa)); // outputs the value of the pointer variable pa
printf("4a %u\n", &(*pa)); // Outputs the address of the mempry pa points to, so it is esssentially pa. Output as expected!
printf("End\n");
return 0;
}
答案 4 :(得分:0)
第一个程序采用pa
的地址,但第二个程序没有。如果不接受对象的地址,则不需要C实现为其保留实际的内存空间,优化可能会避免将其放入内存中。该对象可能只存在于寄存器中,甚至可以通过其他优化完全消除。
因此,是否采用(并打印,强制它在程序外可观察)地址会改变实现必须使用的内存。这导致内存中的事物排列不同,从而产生不同的地址。
(当然,不保证会对您提供这些地址。实施可以自由选择以各种方式安排这些对象。)
答案 5 :(得分:0)
预期输出应为(&amp;(* pa)):2686748,2686748,2,2686748
请解释一下为什么我没有得到预期的输出和在哪里 我错了吗?
从OP我假设你的结果是可重复的,所以当你多次运行它时你会得到完全相同的输出。对?因此,您的操作系统不会进行地址(至少堆栈)随机化。
我同意BeniBela的理论,即由于编译器优化。在第二个程序中,局部变量pa被优化。它等同于:
int main()
{
int a=2;
printf("1 %u\n", &a );
printf("2 %u\n", &a );
printf("3 %d\n", a );
printf("\n4 %u\n", &(a)); // output not as expected
printf("End \n");
return 0;
}
因此,您的第一个和第二个程序的堆栈框架布局会有所不同。但是功能框的大小可能不会改变,因为默认情况下gcc的堆栈是16字节对齐的。
更新:从反汇编中,堆栈框架如下:
第一个
+-------+
0x0028FF00 | | <== (%esp)
+-------+
0x0028FF04 | |
+-------+
0x0028FF08 | |
+-------+
0x0028FF0c | |
+-------+
0x0028FF10 | |
+-------+
0x0028FF14 | |
+-------+
0x0028FF18 | | <== pa
+-------+
0x0028FF1c | 2 | <== a
+-------+
0x0028FF20 | |
+-------+
然后第二个:
+-------+
0x0028FF00 | | <== (%esp)
+-------+
0x0028FF04 | |
+-------+
0x0028FF08 | |
+-------+
0x0028FF0c | |
+-------+
0x0028FF10 | |
+-------+
0x0028FF14 | |
+-------+
0x0028FF18 | 2 | <== a
+-------+
0x0028FF1c | |
+-------+
0x0028FF20 | |