【问题标题】:array of structs created with malloc is not deleting nor resizing last element using free and realloc使用 malloc 创建的结构数组不会使用 free 和 realloc 删除或调整最后一个元素的大小
【发布时间】:2018-03-23 14:06:28
【问题描述】:

我的代码包含在一个应用程序中,该应用程序通过控制台一个客户端 (clientes) 接收并将其添加到使用 malloc 创建的数组中。在我们可以删除这个客户之后。我们还可以添加一个新的旅行 (viajes) 并将其删除(过程类似于代码中显示的过程)。

如前所述,我正在尝试删除使用 malloc 创建的结构数组的最后一项。我将向您展示代码(如何添加新的clientes 以及如何尝试删除它)。还说我得到的错误在产生错误的行中被注释,所以我认为它更直观。还说代码是 MCVE,所以只会运行复制代码。我的应用代码是:

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

// DEFINE STRUCTS
struct viaje {
    char *identificador;
    char *ciudadDestino;
    char *hotel;
    int numeroNoches;
    char *tipoTransporte;
    float precioAlojamiento;
    float precioDesplazamiento;
};

struct cliente {
    char *dni;
    char *nombre;
    char *apellidos;
    char *direccion;
    int totalViajes;
    struct viaje *viajes;
};

int main(){
    // Variables
    int i = 0;
    int totalClientes = 0;
    char dni[255];
    char nombre[255];
    char apellidos[255];
    char direccion[255];

    // Init array of struct cliente (in var clientes)
    struct cliente *clientes = (struct cliente *)malloc(0);

    printf("Input number of elements of struct clientes: ");
    scanf("%i", &totalClientes);
    fflush(stdin);

    // ADDING NEW ELEMENTS INTO CLIENTES
    for (i = 0; i < totalClientes; i++) {

        // Receive parameters
        printf("Input NIF: ");
        gets(dni);
        fflush(stdin);
        printf("Input NAME: ");
        gets(nombre);
        fflush(stdin);
        printf("Input SURNAME: ");
        gets(apellidos);
        fflush(stdin);
        printf("Input ADDRESS: ");
        gets(direccion);
        fflush(stdin);

        // Create memory for his child (MAX_TAM_* are #define int)
        clientes = (struct cliente *)realloc(clientes, (i+1)*sizeof(struct cliente));
        clientes[i].dni = (char *)malloc(200*sizeof(char));
        clientes[i].nombre = (char *)malloc(200*sizeof(char));
        clientes[i].apellidos = (char *)malloc(200*sizeof(char));
        clientes[i].direccion = (char *)malloc(200*sizeof(char));
        clientes[i].viajes = (struct viaje *)malloc(0); // Init clientes[i].viajes to 0 as previously done with clientes

        // Adding received element
        strcpy(clientes[i].dni, dni);
        strcpy(clientes[i].nombre, nombre);
        strcpy(clientes[i].apellidos, apellidos);
        strcpy(clientes[i].direccion, direccion);
    /* DANGER - ERROR HERE */
        clientes[i].totalViajes = 0; // HERE I GET A NOT EXPECTED BEHAVIOR - int is not added to clientes[i].totalViajes - Receive NULL
        // New element added sucessfully
    }

    // SHOW ELEMENTS CREATED
    for (i = 0; i < totalClientes; i++) {
        printf("\n");
        printf("%i.\n", i);
        printf("NIF: %s\n", clientes[i].dni);
        printf("NAME: %s\n", clientes[i].nombre);
        printf("SURNAME: %s\n", clientes[i].apellidos);
        printf("ADDRESS: %s\n", clientes[i].direccion);
        printf("TRAVELS_COUNT: %s\n", clientes[i].totalViajes);
       printf("\n");
    }

    // DELETING AN ELEMENT OF CLIENTES
    int posCliente = 0;

    printf("Input position of struct clientes that you wanna delete: ");
    // posCliente is the position (inside the array) of cliente we wanna remove
    scanf("%i", &posCliente);
    fflush(stdin);

    // Rewind one position array clientes since cliente we want to remove
    for (i = posCliente; i < totalClientes-1; i++) {
        // Rewind one element array clientes
        clientes[i] = clientes[i+1];
    }

    //freeing memory of this element (now, the element is the last of the array, we have moved it into last position with previously for)
    free(clientes[totalClientes].dni);
    free(clientes[totalClientes].nombre);
    free(clientes[totalClientes].apellidos);
    free(clientes[totalClientes].direccion);
    clientes[totalClientes].totalViajes = 0;
    // Remove / free memory of the element deleted
/* DANGER - ERROR HERE */
    //free(clientes[totalClientes]); // HERE I GET AN ERROR: `error: incompatible type for argument 1 of 'free'`

    // Now totalClientes is one less (we have deleted one element)
    totalClientes--;

    // Resize array clientes. It must have one less element (now totalClientes is totalClientes-1)
/* DANGER - ERROR HERE */
    //clientes = (struct cliente *)realloc(clientes, (totalClientes)*sizeof(struct cliente)); // HERE I DO NOT GET AN ERROR BUT PROGRAM CRASH

    // SHOW ELEMENTS AFTER DELETING
/* DANGER - ERROR HERE */
    // if the max legnth is totalClientes+1 we can see that the last element removed with free cointinues existing, so free is not freeing memory well
    for (i = 0; i < totalClientes; i++) {
        printf("\n");
        printf("%i.\n", i);
        printf("NIF: %s\n", clientes[i].dni);
        printf("NAME: %s\n", clientes[i].nombre);
        printf("SURNAME: %s\n", clientes[i].apellidos);
        printf("ADDRESS: %s\n", clientes[i].direccion);
        printf("TRAVELS_COUNT: %s\n", clientes[i].totalViajes);
        printf("\n");
    }
}

【问题讨论】:

  • 不要尝试释放最后一个元素,只需将数组向下重新分配一个。
  • @MartinJames 但需要提前释放内存删除最后一个元素,不是吗?但是,当我尝试调整数组大小时,程序崩溃没有错误,所以我不知道发生了什么
  • 全局变量i 是个坏主意,尤其是在单功能程序中。当它被用作main() 中的循环控制变量时更是如此。
  • 是的,我知道,这只是我的代码的一个示例功能。在我的原始代码中i 是本地的,不是全局的
  • 注意Using fflush(stdin)的约束。

标签: c arrays struct malloc free


【解决方案1】:

这里有几个问题。第一:

printf("TRAVELS_COUNT: %s\n", clientes[i].totalViajes);

字段totalViajes 是一个整数,但您使用%s 进行打印,它期望char * 指向一个以空值结尾的字符串。值 0 被解释为 NULL 指针,这就是打印 NULL 的原因。使用%d 打印整数:

printf("TRAVELS_COUNT: %d\n", clientes[i].totalViajes);

其次,当您转到free 已删除元素的字符串时,您删除的是错误的。您正在使用索引totalClientes,它位于数组的末尾。读取数组末尾之后,以及尝试访问未从malloc 接收的free 内存,调用undefined behavior

您大概想对索引为totalClientes-1 的最后一个元素执行此操作,但这也不正确,因为它包含列表中最后一个元素的字符串。指向您要删除的内存的指针位于索引posCliente 中,您在移动元素时会覆盖它,从而导致内存泄漏。将对free 的调用移动到移动元素的循环之前,并使用posCliente 作为索引进行清理:

// Remove / free memory of the element deleted
free(clientes[posCliente].dni);
free(clientes[posCliente].nombre);
free(clientes[posCliente].apellidos);
free(clientes[posCliente].direccion);

// Rewind one position array clientes since cliente we want to remove
for (i = posCliente; i < totalClientes-1; i++) {
    // Rewind one element array clientes
    clientes[i] = clientes[i+1];
}

最后,您不能在数组的最后一个元素上调用 free,因为 1) 它不是指针,并且 2) 即使您获取了它的地址,malloc 也不会返回该地址。您注释掉的realloc 很好。它崩溃的原因是您的程序中早先出现的未定义行为。

另外,fflush(stdin) 无效,gets is insecure and should not be used 因为它可能允许您溢出缓冲区。使用 scanf 代替长度修饰符来指定最大大小:

    printf("Input NIF: ");
    scanf("%254s", dni);
    printf("Input NAME: ");
    scanf("%254s", nombre);
    printf("Input SURNAME: ");
    scanf("%254s", apellidos);
    printf("Input ADDRESS: ");
    scanf("%254s", direccion);

【讨论】:

  • 好的,非常感谢!!当我更改免费线路时,问题解决了,两件事情都很好!我很感激。另外,您的信息太好了,我会照顾变更项目的!再次感谢你 :)。我有一个问题:如果我在 totalClientes++ 后面加上“free”行;这也很好用吧? (它必须删除clientes 的最后一项)。我试过了,没有报错。但是如果我打印最后一个元素,继续存在,不会被删除
  • @JuMoGar 取消引用已经被释放的指针是未定义的行为。物理数据可能仍然存在,但无法保证。
  • 哦,好的,谢谢!那么,这样做也是正确的:totalClientes--;免费之后呢?
  • @JuMoGar 是的,但是如果你把 totalClientes-- 放在循环之前,你将不得不调整循环,它将所有东西移动一个位置。
猜你喜欢
  • 1970-01-01
  • 2023-03-27
  • 2014-08-01
  • 1970-01-01
  • 2012-09-19
  • 1970-01-01
  • 2013-03-15
  • 2019-12-03
  • 2018-07-24
相关资源
最近更新 更多