C中的指针更改以前的值

时间:2013-10-27 08:35:23

标签: c pointers gcc printf

当我创建各种案例时,我正在通过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

6 个答案:

答案 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计算机体系结构上,它向零地址发展;在其他一些架构上,它朝着相反的方向发展。 “堆栈指针”寄存器跟踪堆栈的顶部;每次将值“推”到堆栈上时都会调整它。

存储自动变量的堆栈,以及每次调用函数时保存的信息。每次调用函数时,返回的地址和有关调用者环境的某些信息(例如某些机器寄存器)都会保存在堆栈中。新调用的函数然后在堆栈上为其自动和临时变量分配空间。

因此,可变点可以在任何地方分配,具体取决于每次运行。

Image

答案 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 |       |