【问题标题】:Explicit casting of ptr to "ptr to a ptr"将 ptr 显式转换为“ptr to a ptr”
【发布时间】:2012-04-15 18:23:27
【问题描述】:

我在一次采访中遇到了这段代码。

int main()
{
    int **p;
    p = (int **) new int(7);
    cout<<*p; 
    return 0;
}

我预计 *p 会出现一些运行时错误。但是当我运行代码时,它成功执行并输出“0x7”。有人可以解释一下这是如何工作的。谢谢。

【问题讨论】:

  • 你想做什么?一个基本的经验法则是,如果你必须施放,那你就做错了。由于new int(7) 返回int*,因此您需要进行转换。但是强制转换并不能使代码正确。它只是你弄错了事实的文件。移除演员表并从那里开始工作。
  • @David:代码可能来自采访,而不是来自 OP。
  • @David Heffernan:这是笔试中的面试题。有四个选项 compile time error , run time error , code will run fine ,以上都不是。甚至我都不明白他们试图通过这样的面试问题来衡量什么样的知识。
  • 是的,所以正确的答案应该是“依赖于实现”。例如,在指针与 int 大小不同的平台上,这将严重失败。
  • @Mr Lister:很可能,即使 sizeof (int) != sizeof(pointer) 也不会失败。它只会覆盖一些内存,但是这不会使程序崩溃的可能性很高。

标签: c++ pointers casting


【解决方案1】:

正确的答案是以上都不是,除非你有一些额外的限制。基本上,代码分配了一个int,并将该内存解释为一个int*(通过reinterpret_cast)。第一个问题是,作为reinterpret_cast,一般情况下结果是unspecified,如果int 的大小小于int* 的大小(想想64 位架构) 结果是未定义的行为,因为您正在阅读超出new 调用中分配的大小。

【讨论】:

  • 当您将 int(32 位)转换为 int*(64 位)时,结果将扩展为 64 位。
  • @gulyan 但是在这种情况下,编译器不知道它应该进行那种类型的转换。唯一发生的转换是从 int* 到 int**,在上面的行中!
  • @gulyan: 不,在 static_cast 从 32 位整数到 64 位整数时,编译器会执行扩展大小的转换,但在 reinterpret_cast 中是这样的,它只会将代码添加到 reinterpret 内存中。也就是说,它将获取指向int 的指针并读取接下来的64 位(8 字节),就好像它是int*,这是未定义的行为。这是 C 风格转换的最大问题之一,它不像显式 C++ 转换那么清楚
【解决方案2】:

您创建一个新的 int 并使用值 7 对其进行初始化。

int *x = new int(7);

您可以将其转换为指针(例如内存地址 7 或 0x07)

int **p = (int**) new int(7);

然后你用 cout 显示这个地址。

*p is equal to (int*)7

它是一个值为 7 的指针。

【讨论】:

  • 这似乎表明代码是正确的,或者至少它会在每种情况下产生相同的输出,而事实并非如此。 p 的取消引用会导致未定义的行为。
【解决方案3】:
new int(7);

为值为7int分配内存并返回一个指向它的指针。

int **p = (int **) new int(7);

告诉编译器将该内存解释为int**

cout << *p;

告诉编译器在地址*p 是一个int * 并输出它的值。值为0x07。 (它将 int 7 视为地址)。额外的取消引用会导致崩溃(准确地说,是未定义的行为)。

【讨论】:

  • 代码本身已经是未定义的行为...无需执行额外的取消引用。
  • @Luchian Grigore :谢谢,很有帮助
【解决方案4】:
int main()
{
    int **p;                  // declare pointer to pointer called p
    p = (int **) new int(7);  // new allocates integer initialized to value of 7 and returns a pointer.  Cast the pointer to a point to pointer.  p now represents a pointer to a pointer with contents of 0x7.  If you called **p you would get the contents at address 0x7.
    cout << *p; // dereference p, this yields a pointer, which is an int of value 0x7.
}

这个问题很可能是为了测试你对指针的了解,但似乎不太实用。

【讨论】:

    【解决方案5】:
    int main()
    {
        int **p = (int **) new int(7);
        cout << *p;
    }
    

    所以,new int(7) 分配了sizeof(int) 字节的内存并返回一个指向它的指针。假设内存恰好位于地址 X。然后将 X 强制转换为 (int**) 并存储在 p 中。

    *p 取消引用int**,这意味着它将地址X 处的int 值7 解释为int*

    • 如果 sizeof(int*) 大于 sizeof(int),则读取为 int* 将超出使用 new 分配的缓冲区 - 无论数据类型和任何重新诠释如何,始终未定义的行为。

    • 如果大小相同,CPU 将尝试将包含整数 7 的内存读取为 int* - 这通常会产生值 int*(7),但查看标准 5.2.10.5:

    整数类型或枚举类型的值可以显式转换为指针。* [脚注:转换值为 0 的整型常量表达式 (expr.const) 总是会产生一个空指针 (conv.ptr),但转换其他值为零的表达式不需要产生一个空指针。 ---结束脚注] 转换为足够大小的整数(如果实现中存在这样的整数)并返回相同指针类型的指针将具有其原始值;指针和整数之间的映射是由实现定义的。

    因此,整数值可以保证转换为某个指针值——没有任何未定义的值——但该值是实现定义的。尽管如此,作为一个可逆操作,int 7 将产生一个值为 7 的int* 极有可能 - 这解释了观察到的输出“7”的行为。

    • 如果指针大小小于int 大小,它将读取int 值的切片并将其解释为指针。根据字节顺序,切片可能是 0 或 7 甚至是其他值,但同样作为 int,它必须可转换为将显示的某个指针值。

    【讨论】:

      猜你喜欢
      • 2016-12-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-12
      • 2021-04-06
      • 1970-01-01
      相关资源
      最近更新 更多