以下代码行中的malloc是做什么用的?

4
我有以下实现来镜像二叉树。
#include<stdio.h>
#include<stdlib.h>

/* A binary tree node has data, pointer to left child
   and a pointer to right child */
struct node
{
    int data;
    struct node* left;
    struct node* right;
};

/* Helper function that allocates a new node with the
   given data and NULL left and right pointers. */
struct node* newNode(int data)

{
  struct node* node = (struct node*)
                       malloc(sizeof(struct node));
  node->data = data;
  node->left = NULL;
  node->right = NULL;

  return(node);
}


/* Change a tree so that the roles of the  left and
    right pointers are swapped at every node.

 So the tree...
       4
      / \
     2   5
    / \
   1   3

 is changed to...
       4
      / \
     5   2
        / \
       3   1
*/
void mirror(struct node* node)
{
  if (node==NULL)
    return; 
  else
  {
    struct node* temp;

    /* do the subtrees */
    mirror(node->left);
    mirror(node->right);

    /* swap the pointers in this node */
    temp        = node->left;
    node->left  = node->right;
    node->right = temp;
  }
}


/* Helper function to test mirror(). Given a binary
   search tree, print out its data elements in
   increasing sorted order.*/
void inOrder(struct node* node)
{
  if (node == NULL)
    return;

  inOrder(node->left);
  printf("%d ", node->data);

  inOrder(node->right);
} 


/* Driver program to test mirror() */
int main()
{
  struct node *root = newNode(1);
  root->left        = newNode(2);
  root->right       = newNode(3);
  root->left->left  = newNode(4);
  root->left->right = newNode(5);

  /* Print inorder traversal of the input tree */
  printf("\n Inorder traversal of the constructed tree is \n");
  inOrder(root);

  /* Convert tree to its mirror */
  mirror(root);

  /* Print inorder traversal of the mirror tree */
  printf("\n Inorder traversal of the mirror tree is \n"); 
  inOrder(root);

  getchar();
  return 0; 
}

我正在谈论以下这行代码:
  struct node* node = (struct node*)
                       malloc(sizeof(struct node));

我对C/C++有中级的了解,但是我非常害怕指针。即使尝试过几次,我仍然无法理解指针。我尽可能地避免使用它们,但是在实现树等数据结构时,没有其他选择。为什么我们在这里使用malloc和sizeof?还有为什么要进行(struct node*)的强制类型转换?


检查答案。如果您发现有任何遗漏,请随时提问。 - halkujabra
6个回答

7
首先,在C语言中使用malloc时不需要进行类型转换(请参见此处)。
您正在使用malloc进行分配堆内存,其大小为节点结构的大小。在C语言中,您必须牢记变量存储的位置,即栈和堆(请参见此处)。
在函数内部,变量被称为“局部变量”,它们存储在栈中。一旦退出函数,栈中的变量就会被清除。
为了能够在函数外部引用或使用局部变量,您必须在堆中分配内存,这就是您在此处所做的。您正在在堆中分配内存,以便在其他函数中重用相同的变量。
总之:
  • 函数内部的变量是局部的,因此被称为局部变量
  • 其他函数要访问局部变量,必须在堆中分配内存以与其他函数共享该变量。

为了给您一个例子,考虑以下代码:

#include <stdio.h>
#include <string.h>

char *some_string_func()
{
    char some_str[13]; /* 12 chars (for "Hello World!") + 1 null '\0' char */

    strcpy(some_str, "Hello World!");

    return some_str;
}

int main()
{
    printf("%s\n", some_string_func());
    return 0;
}

这很简单,main 函数只是调用了一个名为 some_str_func 的函数,该函数返回一个本地变量 some_str,编译上述代码可以工作,但会有警告:

test.c: In functionsome_string_func’:
test.c:11:9: warning: function returns address of local variable [enabled by default]

尽管编译通过,请注意some_str_func()中的some_str返回一个局部变量给函数(即在函数的堆栈中)。由于一旦离开函数some_str_func(),堆栈就会被清除,在main()中无法获取some_str的内容,即“Hello World”。如果尝试运行它,你将得到:
$ gcc test.c
$ ./a.out

$

它没有输出任何内容,因为它无法访问some_str。为了解决这个问题,您需要为字符串“Hello World”分配一些内存空间。就像这样:

#include <stdio.h>
#include <string.h>

char *some_string_func()
{
    char *some_str;

    /* allocate 12 chars (for "Hello World!") + 1 null '\0' char */
    some_str = calloc(13, sizeof(char));

    strcpy(some_str, "Hello World!");

    return some_str;
}

int main()
{
    char *str = some_string_func();
    printf("%s\n", str);

    free(str);  /* remember to free the allocated memory */
    return 0;
}

现在当您编译并运行它时,您会得到:
$ gcc test.c
$ ./a.out
Hello World!
$

如果你很难理解C语言,我知道许多人认为Brian W. Kernighan和Dennis Ritchie的《C程序设计语言》是一个非常好的参考书,然而一本更现代化和图形化(甚至有趣可读!真的)的书是David和Dawn Griffiths的《Head First C》,他们解释了许多重要的C概念,如堆和栈、动态和静态C库之间的区别、为什么使用Makefiles是个好主意以及Make是如何工作的等等,这些概念在普通的C书中没有被解释过,绝对值得一看。
另一个好的在线资源是Zed Shaw的《学习C语言的艰辛方式》,其中提供了良好的代码示例和注释。

1
@user1425223 malloc 的结果是指向未初始化内存的指针,与 new 不同。你可以认为 new T(args) 约等于 malloc(sizeof(T)) + T(args) - Elazar
3
值得一提的是,“堆”和“栈”的所有内容都没有在标准中提到。例如,在某些嵌入式平台上,不存在堆,而malloc()仅返回指向“不是栈”的某些内存的指针(例如,请参见AVR-libc中的malloc()实现)。 - user529758
@H2CO3 类似于freestore。对的,抓住了。 - Deepak Ingole
@H2CO3,“堆”和“栈”是常见的术语,对于大多数实现和开发人员来说也是准确的。没有任何问题。这就像“NULL是地址0x0”。 - Elazar
1
Chutsu,如果你使用strcpy()函数,就不需要显式地终止字符串,因此从代码中删除some_str[12] = '\0'; /* remember to null terminate */。无论如何,回答得很好。 - Grijesh Chauhan
显示剩余4条评论

3

阅读:void *malloc(size_t size);

malloc()函数分配size字节,并返回指向已分配内存的指针。内存未初始化。如果size为0,则malloc()返回NULL或一个唯一的指针值,该值后来可以成功传递给free()

因此,在

struct node* node = (struct node*)malloc(sizeof(struct node));
  //                                     ^----size---------^

您正在分配 size = sizeof node 字节的内存块,并将从 malloc 返回的地址存储在 node 指针中。

注意:您的变量名称有误,不应该是 node,因为它是结构体名称。虽然可以这样做,但这不是好的编程实践,建议使用 sizeof(*pointer) 而不是 sizeof(Type),以防类型更改。

顺便提一下:安全起见,最好避免对 malloc 和 calloc 函数返回的地址进行强制类型转换。参考:Do I cast the result of malloc?

因此,以上陈述的正确首选形式是:

struct node* nd = malloc(sizeof *nd);
  //                     ^----size-^

两个修改:(1)删除类型转换,(2)将变量名称更改为nd


1
你有一个错误,变量名不能是node,因为它是结构体的名称。他可以这样做,但不是好的习惯。此外,在类型被更改的情况下,sizeof(*pointer)sizeof(Type)更可取。 - user529758
@H2CO3 是吗?...有趣。我又从你这里复制了 :) 谢谢! - Grijesh Chauhan
@H2CO3 非常感谢你,H2co3先生。接下来在C++中不应该正确,因为在C++中我们可以使用结构名称而不需要struct关键字。我是正确的吗? - Grijesh Chauhan
@H2CO3 在C++中,类命名空间和变量命名空间是不同的。太棒了! - Grijesh Chauhan
@Elazar 抱歉我没有尝试过,我是从这里学习的,请检查并建议纠正。 - Grijesh Chauhan
显示剩余2条评论

2

使用 sizeof-

sizeof(T)会告诉您存储类型T的变量所需的字节数。

使用 malloc-

动态分配内存是指在运行时(当 CPU 实际执行程序并且程序存在于内存中时)分配内存。我们通常在不确定运行时需要多少内存时使用它。因此,我们使用 malloc 在运行时动态分配它。

使用 (struct node*)-

malloc 返回一个指向请求的空间大小的内存块的指针。这个空间只是内存中的一些空间。因此,该指针没有与其关联的类型。我们将此指针转换为 (struct node*),因为它将让机器知道类型为 (struct node) 的变量将被保存在此内存中。


谢谢!使用(struct node*)正是我想要的! - Rosemary

0
void* malloc (size_t size);

分配内存块
为大小为size字节的内存块分配空间,返回指向该块开始处的指针。新分配的内存块的内容未初始化,保持不定值。如果size为0,则返回值取决于特定的库实现(它可能是一个空指针,也可能不是)。但返回的指针不得被取消引用。
同时请注意,不要对malloc的结果进行强制类型转换。
您还需要释放这段内存:
void free (void* ptr);

释放内存块 之前通过 malloc、calloc 或 realloc 调用分配的内存块被释放,从而可以再次用于进一步的分配。

0
struct node* node = (struct node*)malloc(sizeof(struct node)); 

分配足够的空间来容纳一个node结构


0

通常使用malloc来让指针有东西可以指向。

指针就像一个街道地址,而建筑物是由malloc构建的——或者至少是构建建筑物所需的大小——你在那里建造什么取决于你。

在你的例子中,树中的每个节点都是使用malloc分配的一定数量的字节,节点的大小是容纳所有内容所需的字节数。

二叉树将使用malloc为其每个节点分配内存,内存位置无关紧要,这可能是使用malloc和指针有点棘手的事情。只要有指向这些位置的指针,一切都很好。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接