c - 使用 malloc 创建的结构数组不会使用 free 和 realloc 删除或调整最后一个元素的大小

标签 c arrays struct malloc free

我的代码包含一个应用程序,该应用程序由控制台一个客户端 (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");
    }
}

最佳答案

这里有几个问题。第一:

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

totalViajes 字段是一个整数,但您正在使用 %s 进行打印,它需要一个指向 null 终止的 char *字符串。值 0 被解释为 NULL 指针,这就是打印 NULL 的原因。使用 %d 打印整数:

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

其次,当您转到释放 已删除元素的字符串时,您正在删除错误的元素。您正在使用索引 totalClientes,它位于数组的末尾。读取数组末尾以及尝试释放 未从malloc 接收的内存时,将调用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);

关于c - 使用 malloc 创建的结构数组不会使用 free 和 realloc 删除或调整最后一个元素的大小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49451498/

相关文章:

c - 交叉编译时如何使用外部库?

c - 在 C 中错误地打印输出;故障和重复

c - 无法在 Windows 7 上创建高程 COM 对象

javascript - 将无类型数组编译为 C 的有效方法是什么?

arrays - 为什么 *S Slice 可以由文字 S 开头?

javascript - 如何使用 Ext.iterate (或 JS)迭代对象数组?

ios - Swift 3 - 使用具有空值的结构模型保存 json

javascript - 如何将一个数组排序为与另一个数组相同?

C如何将结构数组序列化为char数组?

c - 立即创建结构体变量和稍后创建变量有什么区别?