【问题标题】:Is casting a pointer to a double pointer acceptable within C?在 C 中是否可以接受指向双指针的指针?
【发布时间】:2018-04-16 22:43:39
【问题描述】:

我只是好奇将值 888 分配给 c 是否正确,如果不是,那么为什么。我没有发现任何说它不是的东西,当我查看 c 语言规范时,它看起来好像是正确的。

int** ppi;
int c = 6;
ppi = (int**)(&c);
*ppi = 888;

我在几个 IDE 和几个编译器中使用过它,但没有一个给我错误。不过有朋友说这段代码应该会报错。

我试图在不添加中间指针的情况下更改 c 的值。 我知道以下方法会起作用,但我不确定上述方法是否也能起作用。

int** ppi;
int* pi;
int c = 6;
pi = &c;
ppi = π
**ppi = 888;

【问题讨论】:

  • 显然不正确。它可以在大多数机器上“工作”,因为它将 c 设置为 888。不过,您为什么要这样做有点神秘。
  • 你不能使用非常好的编译器。我的警告我*ppi = 999 行,正如我所期望的那样。
  • 您想要完成什么?为什么不能使用int *pi
  • 它会抛出类型转换不兼容的警告,而您应该知道即使它是可编译的,它也永远不可用。
  • 铸造本身是完全可以接受的,只要您不违反任何对齐要求(否则,行为未定义)。但是你以后做的事情(即*ppi = 888)毫无意义。 *ppiint *。在 C 中,不允许将整数值 888 分配给指针对象。类型不同。这也需要一个明确的演员表。但即使有演员表,它仍然没有任何意义。你为什么要把888分配给一个指针

标签: c


【解决方案1】:

代码以 4 种不同的方式导致未定义的行为;正如其他一些答案似乎暗示的那样,这肯定不是“正确的”或“可接受的”。

首先,*ppi = 888; 尝试将 int 分配给 int * 类型的左值。这违反了赋值运算符的constraint 6.5.16.1/1,它列出了可以相互分配的类型;指向指针的整数不在列表中。

由于违反约束,编译器必须发出诊断信息并且可能拒绝编译程序。如果编译器确实生成了一个二进制文件,那么这超出了 C 标准的范围,即完全未定义。


某些编译器在其默认操作模式下会发出诊断信息,然后继续进行,就像您编写了 *ppi = (int *)888; 一样。这将我们带到下一组问题。

888 转换为int * 的行为是实现定义的。它可能没有正确对齐(导致未定义的行为),并且可能是陷阱表示(也导致未定义的行为)。此外,即使这些条件通过,也不能保证 (int *)888 与您的代码所依赖的 (int)888 具有相同的大小或表示形式。

下一个主要问题是代码违反了strict aliasing rule。声明为int c; 的对象是使用左值*ppi 编写的,它是int * 类型的左值;并且int *int 不兼容。

还有一个问题是写入可能会越界。如果int 是 4 字节,int * 是 8 字节,则您尝试将 8 字节写入 4 字节分配中。

程序前面的另一个问题是,如果c 未正确对齐int *ppi = (int**)(&c); 将导致未定义的行为,例如也许平台对int 有 4 字节对齐,对指针有 8 字节对齐。

【讨论】:

  • 在你答案的最后一部分,&c 什么时候不能正确对齐 int*? & 部分不是让它返回一个 int* 吗?如果这以任何方式显得粗鲁,我深表歉意。您的其余答案很有意义,并有助于解决问题。编辑:我刚刚意识到你的意思。如果 888 被转换为 int* 则 int 可能在内存中不匹配。
  • @KelbyDinkel int *int 对齐,而不是与 int * 对齐。如果该指针值存储在变量中,则该变量必须与int * 对齐。有些来源在这里的语言很草率,但希望您能识别这里涉及的两个不同的对齐值
  • 一个地址(即一个指针值)被“为 T 对齐”意味着它是 T 的基本对齐值的倍数;对于 int 可能是 4,对于指针可能是 8
【解决方案2】:

这是不可接受的。除非您有充分的理由知道有一个 int 存储在内存地址 888 中,否则这是无效代码,如果您取消引用指针两次(如果您不这样做,则会导致崩溃或未定义的行为)不打算这样做,使用int ** 没有什么意义。

【讨论】:

  • 代码从不使用指针6;它在使用前被888 覆盖。所以到那里,没有问题。 888 可能也未映射,但它在我所知道的每个架构上都正确对齐。
  • @rici 啊,你是对的。重点仍然是内存地址888 几乎可以肯定不会包含 OP 所期望的(即使 )。
【解决方案3】:

ppi 包含一个指向内存位置的指针,该内存位置本身包含一个指向int 的指针。 int c=6;int 创建存储并将值 6 放入该存储给:

ppi : [ some pointer ]
c   : [ 6 ]

线

ppi = (int**)(&c)

告诉编译器“不要介意&c是一个指向int的指针;假设它是一个指向int的指针;然后将它存储在ppi中。所以此时,ppi将包含c(不管是什么)。所以我们有

ppi : [ &c ]
c   : [ 6 ]

下一行

*ppi = 888;

告诉编译器:“将值888 存储在*ppi 指向的位置。”

所以ppi 指向包含6c,因此我们希望将c 的值修改为888。但是等等,c 是一个 int,所以取决于int 占用多少空间,它可能不足以存储一个指针。这是这里最大的问题。

【讨论】:

    【解决方案4】:
    int** ppi;
    int c = 6;
    ppi = (int**)(&c); // Cast from int* to int** may be lossy or trap due to alignment issues
    *ppi = 888; // 888 is not an int* nor implicitly convertible. Whether casting
                //  is allowed, and what that means, depends on the implementation
    

    关于编译给你一个错误:
    虽然最后一个赋值强制编译器给出诊断信息,但任何一个单数就足够了。这是否称为错误,它包含多少细节,以及是否会破坏构建由实现决定。可能有一些选择。

    【讨论】:

      猜你喜欢
      • 2016-06-06
      • 2011-04-24
      • 2020-01-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-14
      • 2016-12-23
      相关资源
      最近更新 更多