【问题标题】:Casting dynamically allocated int pointer to struct pointer [closed]将动态分配的 int 指针转换为结构指针
【发布时间】:2013-03-29 07:27:46
【问题描述】:

我看到了如下代码。

typedef struct abc {
    int a;
    char b;
    float c;
} abc;

int main()
{
  abc *ab;
  int *i;
  i = (int*)malloc(sizeof(int));
  *i = 0;
  ab = (abc*) i;

  return 0;
}

倒数第二行ab = (abc*) i;,代码试图做什么?

如果我们要设置ab->a的值,那为什么要这样,而不是:

ab->a = (int)i;

如果ab = (abc*) i; 更新ab->a 的值,那么其他两个结构成员如何在不独占初始化的情况下初始化?

【问题讨论】:

  • 尝试做什么?或者它做什么。?它将i 的地址转换为与abc * 指针类型“兼容”,然后将该地址存储在ab 指针中。然后它立即对该强制转换不做任何事情,返回零,并泄漏在main() 的第三行分配的内存。
  • 你在问为什么混淆代码以一种混淆的方式做事? ab = (abc*)malloc(sizeof (int)); ab->a = 0; 会产生与这个疯狂代码相同的效果,但同样毫无意义。
  • 我想知道为什么这被否决了。

标签: c casting struct initialization


【解决方案1】:

那么其他两个结构成员将如何在不单独初始化它们的情况下被初始化?

他们不会。

您将在ab->bab->c 中获得垃圾值,因为i 不代表足够大小的内存块来代表abc 的实例。

ab->a 等于 0,因为当您这样做时:*i = 0,您将值 0 存储在 i 指向的内存位置中。当您使abc 指向与i 相同的内存位置时,没有写入内存,您只是更改了数据的位置。

由于 0 之前以 4 个字节存储在 i 指向的位置,并且由于 int ab::a 恰好占用 4 个字节并且恰好是结构的第一个成员,ab->a 将等于 0。

在内存中,相对于实例的位置,你的结构是这样排列的:

 ____ ____ ____ ____    ____    ____ ____ ____ ____
| 00 | 01 | 02 | 03 |  | 04 |  | 05 | 06 | 07 | 08 |
|____|____|____|____|  |____|  |____|____|____|____|
        int a          char b         float c

我希望这可以解决问题。

注意
您并不能真正保证将结构完全打包,就像我在上面的表示中所做的那样。虽然将保留顺序,但连续成员之间的空间并不总是 0 内存地址单元。阅读Alignment

【讨论】:

  • 实际上,结构内部的顺序不是特定于编译器的。它必须按照定义。所以从技术上讲,ab->a 被初始化为 0,但当然,访问 ab->a 是非法的(并导致未定义的行为),因为ab 指向的地址实际上不是abc 对象。
  • 感谢您解决这个问题。
  • @jogojapan:我很好奇 C 标准在哪里说访问 ab->a 会导致 UB。不知怎的,我没能找到那个段落。
  • @alk 我在想严格的别名规则禁止它。但是,事实证明,它确实允许这种特定情况。所以也许它毕竟不是未定义的行为。 (无论如何,我的评论主要是指答案的另一部分同时已更改。)
  • @jogojapan:您可能想看看 Jim Balter 的 cmets 来回答这个问题的另一个答案:stackoverflow.com/a/15699267/694576
【解决方案2】:

ab = (abc*) i; 行将int* 类型的指针变量i 转换为abc* 类型,并将该赋值给指针变量ab。但是,这当然不是您想要初始化结构中的数据成员的方式,特别是因为只为int 分配了足够的空间,而且我们使用的结构比int 占用更多空间.

归根结底,这是合法的代码,但非常可怕。我什至不确定您是否可以保证将数据成员int a 存储在ab 指向的地址中。我想说它依赖于实现,但也许其他人可以为我解决这个问题。

【讨论】:

  • “归根结底,这是法律法规”——我认为不是。 ab 指向一个不完整的对象,我很确定这是标准的 UB,即使您不访问其他字段。
  • @JimBalter:标准规定:“指向对象或不完整类型的指针可以转换为指向不同对象或不完整类型的指针。
  • @alk 没有关于不完整的对象,这与不完整的类型非常不同。
  • 标准中对语言的两次引用与我所写的内容无关......将int* 指针分配给abc* 指针本身并没有问题——对此没有异议。问题在于,作为赋值的结果,abc* 指针没有指向可以解释为abc 类型对象的内存。该标准说“对象由一个或多个字节的连续序列组成,其数量、顺序和编码要么是明确指定的,要么是实现定义的”。但是,sizeof(int) 字节不足以表示 abc
  • @alk “C 标准中没有“不完整的对象””——没有这个词,但没有这个概念。例如,struct {char a; char b;}* p = malloc(1)p 的值不是指向所需类型的 object 的指针,因为它不满足标准对对象的要求。标准说“如果一个左值在评估时没有指定一个对象,那么行为是未定义的”。在p->a 中,p 被评估,但它没有指定一个对象。一种可能的实现可以将*p 加载到工作内存中,然后提取a 成员......但*p 可能会失败。
【解决方案3】:

倒数第二行导致从(int *)(abc *) 的转换,并将结果分配给ab。这不是一个很好的代码行,因为旧的指向类型比新的指向类型小;使用此转换结果的一些尝试将是未定义的行为。将其保留为 (int *),或声明要转换为的前缀结构会是更好的主意。

abc * 中的* 表示该类型是“指向 abc 的指针”。 ab 不会指向 abc 对象,直到您告诉它指向一个对象。 ab = /* something */ 指定 ab 指向 something。在这个例子中,ab = malloc(sizeof *ab); 是有意义的。

这很愚蠢:i = (int*)malloc(sizeof(int));。您不需要强制转换 malloc 的返回值。唯一合理的原因是这段代码的作者忽略了#include <stdlib.h>。我建议包含该库,而不是强制转换返回值。您可以随意忽略我的建议,但在您阅读this page 之前,不要问任何关于奇怪错误的问题。

【讨论】:

    【解决方案4】:

    abc *ab;ab 是 struct abc 类型的指针; int *i; 是指向 malloc 返回的 int 的指针

    它的值是使用*i = 0;设置的

    ab = (abc*) i;
    

    这一行正在将 malloc 分配的位置地址分配给 ab。通过将i 类型转换为(abc*),表明ab 将用于读取struct abc 大小的内存块 ab = (abc*) i; 为 a 赋值。给你赋值ab->a = 5;

    已经存在的值是垃圾值(默认随机值)

    【讨论】:

    • 这不是垃圾。如果我要*i = 5ab = (abc*)i 然后ab->a = 5
    • b 和 c 将是垃圾
    【解决方案5】:
    ab = (abc*) i; updates the value of ab->a
    

    如果我们看结构布局,int 是 int 是第一个成员,所以它会被初始化为 0,所以 ans 是的!我尝试使用 VC 并将其初始化为 0,但这很棘手,如果是 struct if member change order 那么你就抢到了!

    还有内存泄漏,用 malloc 分配的内存从未释放!

    【讨论】:

    • “从未释放”:不是真的:stackoverflow.com/questions/5612095/…
    • 依赖操作系统来清理内存不是一个好习惯!
    • 我们可以辩论!这不是你可以宣布某人错的事情!您将建立对操作系统的依赖从来都不是一个好习惯,我曾参与过非常知名的商业项目,我知道您的意思,但正如我所说,这是值得商榷的!
    • 最后,我用一个 true 断言来纠正你的 false 断言——内存“从未被释放”。您立即将主题更改为是否根据操作系统释放内存是一种好习惯,避开操作系统确实释放内存(在大多数托管平台上)这一点。那是不好的行为。再见。
    • @JimBalter 很好,我将参加这场辩论。 C标准在哪里保证“您的进程的整个地址空间将被破坏”? C 标准在哪里保证会有操作系统来执行这样的操作?
    猜你喜欢
    • 2020-10-21
    • 1970-01-01
    • 2017-02-12
    • 1970-01-01
    • 2016-11-10
    • 1970-01-01
    • 2018-05-16
    • 1970-01-01
    • 2012-03-02
    相关资源
    最近更新 更多