【问题标题】:Why memory allocation for a structure in C works with any value given to malloc?为什么 C 中结构的内存分配适用于 malloc 的任何值?
【发布时间】:2020-05-03 10:11:52
【问题描述】:

我试图找到为如下结构动态分配内存的正确方法:

typedef struct myThread {
    unsigned int threadId;
    char threadPriority;
    unsigned int timeSlice;
    sem_t threadSem;
} myThread;

我记得,但我不确定,在一些学校论文中,我看到为这种情况分配内存的正确方法是:

myThread *node = (myThread *)malloc(sizeof(myThread *));

我试过了,它奏效了,但我不明白为什么。我的体系结构的 Sizeof 指针是 8 个字节,所以通过编写上面的指令,我分配了 8 个字节的连续内存,不足以容纳我的结构中所需的信息。所以我尝试分配 1 个字节的内存,如下所示:

myThread *node = (myThread *)malloc(1);

而且它仍在工作。 我试图找到这种行为的答案,但没有成功。为什么这行得通?除此之外,我还有几个问题:

  • 为结构动态分配内存的正确方法是什么?
  • 这种演员阵容有必要吗?
  • 结构如何存储在内存中?我知道 (*node).threadId 等价于 node->threadId 这让我有点困惑,因为通过解除对结构的指针的引用,我得到了整个结构,然后我必须访问一个特定的字段。我期望以这种方式访问​​知道结构地址的字段: *(node) 它是第一个元素的值, *(node + sizeof(firstElement)) 它是第二个元素的值,依此类推。我认为访问结构字段类似于访问数组值。

谢谢

稍后编辑:感谢您的回答,但我意识到我没有正确解释自己。通过说它有效,我的意思是它可以在结构的那些特定字段中存储值并在以后使用它们。我通过填写字段并在之后打印它们来测试它。我想知道为什么这是有效的,为什么我可以填充和使用我只为其分配一个字节内存的结构字段。

【问题讨论】:

  • myThread *node = (myThread *)malloc(sizeof(myThread *)); 必须是 myThread *node = (myThread *)malloc(sizeof(myThread)); 并且演员阵容没用,所以最后是 myThread *node = malloc(sizeof(myThread));
  • a.b 是访问结构对象b 中名为b 的字段的语法a
  • T *p = malloc( sizeof *p ); 是个好模式
  • 为什么会这样? 未定义的行为,任何事情都可能发生(包括“代码工作”)
  • sizeof(myThread)sizeof(*node)。后者更“健壮”,因为它允许您更改 *node 的类型而不更改此分配。

标签: c pointers memory malloc structure


【解决方案1】:

下面的工作是因为它们分配内存 - 但大小错误。

myThread *node = (myThread *)malloc(sizeof(myThread *));// wrong size,s/b sizeof(myThread) 
myThread *node = (myThread *)malloc(1);                 // wrong size 

为什么会这样?

当代码尝试将数据保存到该地址时,错误的大小可能会或可能不会变得明显。它是undefined behavior (UB)。

C 编码时没有training wheels。当代码有 UB 就像没有分配足够的内存并使用它时,它不一定会失败,它可能会失败,现在或稍后或下周二。

myThread *node = (myThread *)malloc(1);  // too small
node->timeSlice = 42;  // undefined behavior

为结构动态分配内存的正确方法是什么? @M.M

以下内容易于正确编码、查看和维护。

p = malloc(sizeof *p);  //no cast, no type involved.
// or
number_of_elements = 1;
p = malloc(sizeof *p * number_of_elements);

// Robust code does error checking looking for out-of-memory
if (p == NULL) {
  Handle_error();
}

这个演员有必要吗?

没有。 Do I cast the result of malloc?

结构是如何存储在内存中的?

每个成员后跟潜在的填充。它依赖于实现。

unsigned int
maybe some padding
char
maybe some padding
unsigned int
maybe some padding
sem_t
maybe some padding

我想知道为什么这是有效的,为什么我可以填充和使用我只为其分配一个字节内存的结构的字段。

OP 正在寻找它起作用的原因。

也许内存分配是以 64 字节的块或超过 sizeof *p 的块完成的,因此分配 1 与 sizeof *p 具有相同的效果。

也许现在由于代码使用少量分配而损坏的后来的内存区域将在以后表现出来。

也许分配器是一个恶毒的野兽,玩弄 OP,只是为了在下一个 April 1 中清除硬盘驱动器。 (恶意代码经常利用 UB 来感染系统——这并不牵强)

都是 UB。任何事情都可能发生。

【讨论】:

  • 感谢您的回答,请看一下编辑后尝试回答,我意识到我没有正确解释自己
  • OP和UB是什么意思?
  • @Andrei Deatcu OP:原始海报 - 在这种情况下就是你。
  • @AndreiDeatcu “这是未定义的行为 (UB)。”上面链接中引用的 UB 详细信息。实际上:代码违反了规则,编译器可以做任何想做的事情。
【解决方案2】:

由于 C 中的内存分配非常容易出错,我总是定义宏函数 NEWNEW_ARRAY,如下例所示。这使得内存分配更加安全和简洁。

#include <semaphore.h> /*POSIX*/
#include <stdio.h>
#include <stdlib.h>

#define NEW_ARRAY(ptr, n) \
    { \
        (ptr) = malloc((sizeof (ptr)[0]) * (n)); \
        if ((ptr) == NULL) { \
            fprintf(stderr, "error: Memory exhausted\n"); \
            exit(EXIT_FAILURE); \
        } \
    }

#define NEW(ptr) NEW_ARRAY((ptr), 1)

typedef struct myThread {
    unsigned int threadId;
    char threadPriority;
    unsigned int timeSlice;
    sem_t threadSem;
} myThread;

int main(void)
{
    myThread *node;
    myThread **nodes;
    int nodesLen = 100;

    NEW(node);
    NEW_ARRAY(nodes, nodesLen);
    /*...*/
    free(nodes);
    free(node);
    return 0;
}

【讨论】:

  • 我不会调用使用宏进行分配“明确表达”,但我猜它自己的偏好:)
  • @TonyTannous 重点是每次分配动态内存时不必摆弄 sizeof 运算符。
  • "更安全" --> 这种方法的一个弱点是当大小比n 更复杂时。考虑int r; int c; NEW_ARRAY(nodes, r*c); 使用int 数学执行r*c 乘法。对于大的r,c,这可能会溢出,而sizeof (ptr)[0] * r *c 可能不会。 r,c,n as size_t 避免了这种情况以及重新排序宏的乘法 sizeof (ptr)[0] * n 或具有显式的 NEW_ARRAY_2D(nodes, r, c);sizeof (ptr)[0] * (r) * (c)
  • @chux-ReinstateMonica 感谢您指出这一点。我现在已经更正了 malloc 参数中的乘法顺序。
  • @AugustKarlstrom Er。 (sizeof (ptr)[0]) * (n)(n) * (sizeof (ptr)[0]) 的顺序没有区别,因为 () 强制 int multiplication. The idea of sizeof (ptr)[0] * n, with the ()` 将导致 NEW_ARRAY(nodes, r*c) 乘以 ``sizeof (ptr)[0] * r` 首先。一般来说,我认为在极端情况下没有双赢的方法,因此更喜欢代码与宏。但我确实喜欢关于宏的想法。
【解决方案3】:

malloc 保留内存供您使用。

当您尝试使用比请求更多的内存时,可能会出现多种结果,包括:

  • 您的程序访问了不应访问的内存,但没有任何中断。
  • 您的程序访问了不应访问的内存,这会损坏您的程序需要的其他数据,因此您的程序会失败。
  • 您的程序尝试访问未映射到其虚拟地址空间的内存,并导致陷阱。
  • 编译器的优化会以意想不到的方式转换您的程序,并出现奇怪的错误。

因此,当您未能分配足够的内存时,您的程序似乎可以运行,或者您的程序在您未能分配足够的内存时中断,这并不奇怪。

为结构动态分配内存的正确方法是什么?

好的代码是myThread *node = malloc(sizeof *node);

这个演员有必要吗?

不,不在 C 中。

结构是如何存储在内存中的?我知道 (*node).threadId 等价于 node->threadId 这让我有点困惑,因为通过解除对结构的指针的引用,我得到了整个结构,然后我必须访问一个特定的字段。我期望以这种方式访问​​知道结构地址的字段: *(node) 它是第一个元素的值, *(node + sizeof(firstElement)) 它是第二个元素的值,依此类推。我认为访问结构字段类似于访问数组值。

该结构以字节序列的形式存储在内存中,就像 C 中的所有对象一样。您不需要进行任何字节或指针计算,因为编译器会为您完成。例如,当您编写node-&gt;timeSlice 时,编译器获取指针node,将偏移量添加到成员timeSlice,并使用结果访问存储成员timeSlice 的内存。

【讨论】:

    【解决方案4】:

    你没有分配正确的大小做

    myThread *node = (myThread *)malloc(sizeof(myThread *));
    

    正确的方法可以是例如

    myThread *node = (myThread *)malloc(sizeof(myThread));
    

    演员阵容没用,所以最后

    myThread *node = malloc(sizeof(myThread));
    

    或如对您问题的评论中所说的那样

    myThread *node = malloc(sizeof(*node));
    

    原因是你分配了一个 myThread 而不是一个指向的指针,所以要分配的大小是 myThread

    的大小

    如果您分配sizeof(myThread *),则意味着您需要myThread ** 而不是myThread *

    我知道 (*node).threadId 等价于 node->threadI

    是的,-&gt; 取消引用,而 . 没有

    拥有myThread node; 来访问字段threadId 你做node.threadId,但是有一个指向你的指针需要以任何方式尊重


    稍后编辑:...

    当您访问已分配的块时分配不足,行为未定义,这意味着任何事情都可能发生,包括立即可见的任何不良情况

    【讨论】:

      猜你喜欢
      • 2015-11-09
      • 1970-01-01
      • 1970-01-01
      • 2015-06-27
      • 2015-01-05
      • 2014-04-15
      • 2018-11-08
      • 2021-05-29
      • 1970-01-01
      相关资源
      最近更新 更多