在函数内部使用realloc是否正确? (以及在失败的情况下使用free())

时间:2017-12-17 17:11:42

标签: c

我发现了一些与此相似的问题,但存在一些差异。 这是我的代码: student.h:

#define NUM_GRADES 5
#define NAME_LENGTH 21
#define ADDRESS_LENGTH 21
    typedef struct
{
    char name[NAME_LENGTH]; //name of a student - up to 20 chars.
    char add[ADDRESS_LENGTH]; //address - up to 20 chars.
    int grades[NUM_GRADES]; //stores the grades of a student.
}student;

//the current size of the students array.
extern int currentTotal;

//add a new student space, return 0 if failed, 1 if succeeded.
int addStudent(student **studentClass);

student.c:

    int addStudent(student **studentClass)
{
    //adds the count for the new student.
    currentTotal++; 
    //a temporary pointer to hold studentClass array in case realloc fails.
    student *temp=NULL;
    //reallocating space for the new student.
    if (!(temp = (student*)realloc(*studentClass, currentTotal * sizeof(student))))
    {
        printf("Not enough memory.\n");
        free(*studentClass);//free the original array.
        currentTotal = 0;
        return 0;
    }
    *studentClass = temp;//point class to the newly allocated space.
    printf("Added space for a student.\n");
    return 1;
}

main.c中:

#include <stdio.h>
#include <stdlib.h>
#include "student.h"
void main()
{
    student *studentClass=NULL;
....
if(addStudent(&studentClass) 
....

currentTotal是一个外部int变量。 realloc的使用是否正确? 而免费使用? 一旦我将指针的地址发送到另一个函数,我总是混淆我是否应该在函数内部使用*或**。 (即有一个像* studentClass这样的指针,然后将&amp; studentClass发送给另一个函数)。

如果这确实是正确的那么* studentClass在该行之前指向的原始数据会发生什么 &#34; * studentClass = temp;&#34; (在student.c中)? 它需要被释放吗?

编辑: 请不要混淆,最初* studentClass是NULL,它只是在开始时,addStudent()意味着在循环中调用,所以在第一次之后,* studentClass不再空值。 addStudent()在每次调用后增加* studentClass的大小。

感谢。

3 个答案:

答案 0 :(得分:1)

它&#34;保存&#34;从某种意义上说,它不会引入未定义的行为,也不会泄漏内存。您需要注意的是,如果<v-text-field :value="currentValue" @input="handleInput" :mask="###.###.###-##"></v-text-field> 失败,原始数据不会被释放(所以你这样做),并将realloc的结果存储在临时变量中,以免丢失指向原始数据的指针数据。到目前为止一切都还可以。

如果realloc失败,它包含addStudent调用者的陷阱。在这种情况下,您可以释放原始内存块而不提供新内存块,但不要将指针重置为realloc。因此,传递给NULL的变量仍指向某个内存,但此内存已被释放。调用者可能会尝试第二次释放此内存(然后产生未定义的行为)。

如果addStudent失败,我建议根据谁负责释放学生的阵列记忆来做两种选择中的任何一种:

一个。 realloc负责:释放原始内存并将指针设置为addStudent,这样外面没有人可以尝试两次释放内存。因此,您需要在NULL之后添加*studentClass=NULL

湾调用者负责:在free失败的情况下,不要释放原始内存;返回 - 正如您所做的那样 - 失败代码并让调用者完成其余的工作。因此,您要删除realloc

答案 1 :(得分:0)

这一切都很好。由于*studentClassNULLrealloc(3)的行为与malloc(3)相似。在realloc(3)失败的情况下,它不会修改传递的指针,这意味着*studentClass仍然是NULL。在free(3)指针上调用NULL是完全有效的,但不会发生任何事情(无操作)。然后函数返回,*studentClass仍为NULL。您只需要在调用addStudent后检查其值:如果是NULL,则添加学生失败,否则成功。

你使用双指针也是有效的。推理这种情况的一种方法是这样的。指针允许修改指向位置的数据。如果想要将整数传递给要修改的函数,可以将其作为int*传递。在函数内部,可以取消引用它以对其进行修改,例如*my_int = 0;。因此,如果想要修改函数内的student*,则必须将其作为student**传递,然后每当想要更改其内容时取消引用。

答案 2 :(得分:0)

除了Stephan Lechner提到的事实之外,代码应该单独保留分配或者将指针置空(而不是释放内存而不使指针无效),我还会看到一些其他问题:

  1. 如果不希望调用者从内存不足状态恢复,则分配函数在发生时不应返回。相反,他们应该发出故障信号(可能是通过提升信号)然后退出(如果发出故障信号并没有强制退出)。有一个功能保证它永远不会返回失败可以大大简化客户端代码。

  2. 如果函数要使用全局变量来保存分配的大小,它应该使用指向分配的全局指针。如果它将使用由传入地址标识的对象指针,则它应该使用也由传入地址标识的对象计数。

  3. 我可以看到解决第二个问题的四种好方法:

    1. 像现在一样传入双间接指针,但也传递指向包含学生数量的整数类型对象的指针。

    2. 传入一个指向结构的指针,该结构包含学生指针和学生数。

    3. 定义一个结构,该结构包含学生类型的计数和灵活的数组成员,并保留指向该结构而不是第一个学生的指针。

    4. 如上所述,但保留指向第一个学生的指针(而不是分配区域的开头)。这将要求调整使用malloc / realloc / free的任何代码来抵消与这些函数交换的指针。

    5. 如果学生人数可以增长而不是缩小,那么#3和#4方法可能会略有优势。如果分配区域的大小缩小为零,则无法确定对realloc区域大小为零字节的请求是否成功(如果该区域之前的大小为零,则标准将允许{{ 1}}在成功释放先前的分配后返回null,但标准也允许realloc(prevPtr,0)失败(并返回null)而不释放先前的分配。