无法理解指针输出的指针:15 15

时间:2017-11-15 20:16:09

标签: c pointers undefined-behavior

有人可以帮助理解以下代码的输出是如何生成15 15的吗?

#include <stdio.h>
void foo(int **p1);
int main()
{
    int i = 10;
    int *p = &i;
    foo(&p);  //address of constant pointer p passed to function
    printf("%d\n", *p);

}
void foo(int **p1)
{
    int j = 15;
    *p1 = &j; //I don't get this line
    printf("%d\n", **p1); 
}

让我们假设addr(i)= ff2,addr(指针p)= ff4,addr(j)= ff6

i = 10,j = 15

p =地址(i),所以p = ff2

p1 =地址(p),所以p1 = ff4

2 个答案:

答案 0 :(得分:1)

程序有不确定的行为。

要理解程序中的错误,让我们为指针类型引入一个typedef。

这是一个示范程序。

#include <stdio.h>

typedef int * T;

int main(void) 
{
    int i = 10;
    const T p = &i;

    printf( "%d\n", *p );

    return 0;
}

程序输出

10

正如所见,变量p是用限定符const。

声明的

现在让我们尝试添加函数foo

#include <stdio.h>

typedef int * T;

void foo( T *pp )
{

}

int main(void) 
{
    int i = 10;
    const T p = &i;

    foo( &p );

    printf( "%d\n", *p );

    return 0;
}

糟糕,编译器发出错误

prog.c:15:7: error: passing argument 1 of ‘foo’ discards ‘const’ qualifier from pointer target type [-Werror=discarded-qualifiers]
  foo( &p );
       ^
prog.c:5:6: note: expected ‘int **’ but argument is of type ‘int * const*’
 void foo( T *pp )

实际上变量p是使用限定符const

声明的
    const T p = &i;

因此,指向变量的指针必须具有类型const T *

#include <stdio.h>

typedef int * T;

void foo( const T *pp )
{

}

int main(void) 
{
    int i = 10;
    const T p = &i;

    foo( &p );

    printf( "%d\n", *p );

    return 0;
}

现在,如果要取消引用函数中的参数pp,我们会得到一个可能无法更改的常量对象。

#include <stdio.h>

typedef int * T;

void foo( const T *pp )
{
    int j = 15;
    *pp = &j;
}

int main(void) 
{
    int i = 10;
    const T p = &i;

    foo( &p );

    printf( "%d\n", *p );

    return 0;
}

所以编译器会再次发出错误

prog.c: In function ‘foo’:
prog.c:8:6: error: assignment of read-only location ‘*pp’
  *pp = &j;
      ^

此外,您正在尝试分配一个本地变量的地址,该地址变量在退出该函数后将无效。因此,即使指针在任何情况下都不是常量,程序也有不确定的行为。

编辑: 在你从指针const的声明中删除p限定符的问题中认真改变你的程序(这是一个坏主意)后,我已经说过程序有未定义的行为,因为指针已分配使用局部变量的值。因此,指针将具有无效值,因为退出函数后该变量不再存在,但您可以获得预期的结果。

void foo(int **p1)
{
    int j = 15;
    // Assigning the original variable p pointed to by the parameter p1
    // the address of the local variable j.
    *p1 = &j; //I don't get this line
    printf("%d\n", **p1); 
}

至于这句话

*p1 = &j;

然后指针p1指向原始指针p。因此,取消引用指针p1,您将获得分配了局部变量p的地址的原始指针j

答案 1 :(得分:1)

忽略未定义的行为 1 一分钟,这是正在发生的事情,或者至少是该示例的 intent

首先,您有一个包含值i的整数对象10

   +----+
i: | 10 |
   +----+

您创建指向p的指针对象i

   +---+          +----+
p: |   | ----> i: | 10 |
   +---+          +----+

您将p的地址传递给foo。函数参数p1指向p

    +---+          +---+          +----+
p1: |   | ----> p: |   | ----> i: | 10 |
    +---+          +---+          +----+

foo内,您可以使用值j创建整数对象15

   +----+
j: | 15 |
   +----+

现在,这是有趣的部分 - 您通过取消引用 pj设置为指向p1,并将j的地址指定给结果。 *p1 == p,因此扩展名*p1 = &j实际上与撰写p = &j相同。在该行之后,您有以下情况:

    +---+          +---+          +----+
p1: |   | ----> p: |   | ----> j: | 15 |
    +---+          +---+          +----+

这就是为什么在**p1输出foo中输出p1 == &p的值为*p1 == p == &j,所以**p1 == *p == j == 15,所以foo

现在,当函数j退出时,p 不再存在,因此j不再是有效指针 - 它不再指向活动对象。由于它不再有效,因此取消引用它的行为是 undefined ;您的代码可能会崩溃,或者它可能会损坏数据,或者它可能看起来像预期的那样工作,或者完全做其他事情。

逻辑对象15不再存在,但它曾经占用的内存仍然存在,并且将包含写入它的最后一个东西,即值{{1 }}。只要没有其他内容覆盖该内存,您将继续看到*p == 15。但是这种行为并不能保证,你不应该依赖它。

正如弗拉德和其他人所指出的那样,这远远不是这个代码的唯一问题。但同样,这基本上就是发生了什么。

<小时/>

  1. 包括但不限于:foo(函数返回int)的隐式声明与{的明确定义之间的类型不匹配{1}}(函数返回foo),尝试通过非void表达式写入const - 限定指针并解除引用无效指针。