从动态数组C中删除元素

时间:2018-05-30 01:28:16

标签: c

我正在为我的班级做一些练习,而且我遇到了问题。 练习要求使用文件中的数据填充动态数组,对数据进行排序,然后删除重复数据。 我的问题出在最后一个,我无法删除重复的数字。

这是我的代码:

int main(){
    FILE *f;
    int *v;
    int n=0,i,j;

    f=fopen("InteirosComRepetidos.txt","r");
    v=(int*)malloc(sizeof(int));

    while(!feof(f)){
        v=(int*)realloc(v,(n+1)*sizeof(int));
        fscanf(f,"%d\n",&v[n]);
        n++;
    }

    OrdenarQuicksort(v,0,n);

    for(i=0;i<n;i++){
        printf("%d\n",v[i]);
    }
    //Trying to remove the duplicate
    for(j=n;j>=0;j--){
        if(v[j]==v[j-1])
            v=(int*)realloc(v,(n-1)*sizeof(int));
    }

    printf("Retirados:\n");
    for(i=0;i<n;i++){
        printf("%d\n",v[i]);
    }
    system("pause");
    return 0;}  

任何人都可以帮助我吗?

3 个答案:

答案 0 :(得分:1)

此:

for(j=n;j>=0;j--){
    ...
}

应该是:

for(j=n-1;j>=0;j--){
    ...
}

否则你就会走出界限。

比这个:

if(v[j]==v[j-1])
     v=(int*)realloc(v,(n-1)*sizeof(int));

正在重新分配,但不会删除双打。如果你有

,那就意味着你
1 2 2 5

所有这些代码都会删除5,因此您最终会使用

1 2 2

如果您在内存中重新分配和缩小阵列将不会移动它将保持原样,因此在许多情况下您仍然可以访问5

答案 1 :(得分:1)

你有几个问题。首先,您的外观和if条件通过读取

v的范围之外的内容来调用未定义的行为
for(j=n;j>=0;j--){
    if(v[j]==v[j-1])

当您在j = n读取数组末尾1之后其中j = 0时,您会在数组开头之前读取1。更好的方法是:

    i = n - 1;      /* set i to last element */
    while (i--)     /* loop removing the duplicates */
        /* if current equals next */
        if (v[i] == v[i + 1]) { 
        ...

接下来,您应该使用realloc来删除重复项,而不是使用memmove从最后修剪块。 (realloc无法对1, 2, 2, 3, 4执行任何操作)而是简单地备份每个元素,检查重复项,并向前移动结束元素以覆盖副本。在删除所有重复项之前不要担心realloc - 然后realloc一次,例如

    i = n - 1;      /* set i to last element */
    while (i--)     /* loop removing the duplicates */
        /* if current equals next */
        if (v[i] == v[i + 1]) { /* use memmove - blocks overlap */
            memmove (&v[i], &v[i + 1], (n - i - 1) * sizeof *v);
            n--;    /* decrement count */
        }

    /* final reallocation */
    tmp = realloc (v, n * sizeof *v);   /* never realloc to original ptr */
    if (tmp == NULL) {                  /* validate reallocation */
        perror ("realloc-final");
        return 1;
    }
    v = tmp;        /* then assign new block to original pointer */

注意:不要realloc原始指针本身。而是使用临时指针,然后验证realloc成功之前将新内存块的地址分配给原始指针。

汇集了一个简短的例子,它使用了一个初始化的混乱数字数组而不是从文件中读取(因为没有提供),你可以做类似的事情:

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

/* simple qsort compare - sort int ascending */
int compare (const void *a, const void *b)
{
    return (*(int*)a > *(int*)b) - (*(int*)a < *(int*)b);
}

int main (void) {

    int a[] = {5,1,5,5,2,8,3,2,6,9,4,8,1,4,3,6,9,2,7,7},  /* numbers */
        n = sizeof a / sizeof *a,   /* number of elements */
        *v = NULL,                  /* pointer to allocate */
        i;
    void *tmp;  /* temp pointer to realloc, never realloc w/original */

    v = malloc (n * sizeof *v); /* don't cast return of malloc, always
                                * use 'n * sizeof dereferenced pointer'
                                */
    memcpy (v, a, n * sizeof *v);       /* copy a to v */
    qsort (v, n, sizeof *v, compare);   /* sort v */

    for (i = 0; i < n; i++){            /* output sorted v */
        printf ("a[%2d]: %d\n", i, v[i]);
    }

    i = n - 1;      /* set i to last element */
    while (i--)     /* loop removing the duplicates */
        /* if current equals next */
        if (v[i] == v[i + 1]) { /* use memmove - blocks overlap */
            memmove (&v[i], &v[i + 1], (n - i - 1) * sizeof *v);
            n--;    /* decrement count */
        }

    /* final reallocation */
    tmp = realloc (v, n * sizeof *v);   /* never realloc to original ptr */
    if (tmp == NULL) {                  /* validate reallocation */
        perror ("realloc-final");
        return 1;
    }
    v = tmp;        /* then assign new block to original pointer */

    printf ("Retirados:\n");
    for (i = 0; i < n; i++){
        printf ("a[%2d]: %d\n", i, v[i]);
    }

    free (v);   /* don't forget to free memory you allocate */
#if defined (_WIN32) || defined (_WIN64)
    getchar();  /* just use getchar() to hold terminal open on windows */
#endif

    return 0;
}

注意: qsort代替您的OrdenarQuicksort,因为您没有提供OrdenarQuicksort代码)

示例使用/输出

$ ./bin/array_rm_dups
a[ 0]: 1
a[ 1]: 1
a[ 2]: 2
a[ 3]: 2
a[ 4]: 2
a[ 5]: 3
a[ 6]: 3
a[ 7]: 4
a[ 8]: 4
a[ 9]: 5
a[10]: 5
a[11]: 5
a[12]: 6
a[13]: 6
a[14]: 7
a[15]: 7
a[16]: 8
a[17]: 8
a[18]: 9
a[19]: 9
Retirados:
a[ 0]: 1
a[ 1]: 2
a[ 2]: 3
a[ 3]: 4
a[ 4]: 5
a[ 5]: 6
a[ 6]: 7
a[ 7]: 8
a[ 8]: 9

内存使用/错误检查

在你编写的动态分配内存的任何代码中,你有2个职责关于任何分配的内存块:(1)总是保留一个指向起始地址的指针内存块,(2)当不再需要时,它可以释放

必须使用内存错误检查程序,以确保您不会尝试访问内存或写入超出/超出已分配块的范围,尝试读取或基于未初始化值的条件跳转,最后,确认您释放了所有已分配的内存。

对于Linux valgrind是正常的选择。每个平台都有类似的记忆检查器。它们都很简单易用,只需通过它运行程序即可。

$ valgrind ./bin/array_rm_dups
==26625== Memcheck, a memory error detector
==26625== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==26625== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==26625== Command: ./bin/array_rm_dups
==26625==
a[ 0]: 1
a[ 1]: 1
a[17]: 8
<snip>
a[18]: 9
a[19]: 9
Retirados:
a[ 0]: 1
a[ 1]: 2
a[ 2]: 3
a[ 3]: 4
a[ 4]: 5
a[ 5]: 6
a[ 6]: 7
a[ 7]: 8
a[ 8]: 9
==26625==
==26625== HEAP SUMMARY:
==26625==     in use at exit: 0 bytes in 0 blocks
==26625==   total heap usage: 2 allocs, 2 frees, 116 bytes allocated
==26625==
==26625== All heap blocks were freed -- no leaks are possible
==26625==
==26625== For counts of detected and suppressed errors, rerun with: -v
==26625== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

始终确认已释放已分配的所有内存并且没有内存错误。

验证所有文件打开&amp;读取

从文件中读取,您必须验证fopen,并且必须验证每个fscanf。没有理由在{em>格式字符串中包含'\n'"%d"(实际上所有数字)格式说明符使用前导空白(包括' ''\t''\n')。

使用mallocrealloc时,请勿投射结果 - 没有理由这样做。两者都返回void*,可以在没有强制转换的情况下分配给任何指针类型,请参阅:Do I cast the result of malloc?

将这些片段放在一起,你的开放和阅读可以完成如下:

    f = fopen ("InteirosComRepetidos.txt","r");
    if (f == NULL) {    /* validate file open for reading */
        perror ("fopen-InteirosComRepetidos.txt");
        return 1;   /* if it isn't, exit */
    }
    v = malloc (sizeof *v); /* don't cast return of malloc, and if you always
                             * use 'sizeof dereferenced pointer', times some
                             * amount, you will always get your allocations
                             * correct */

    while (fscanf (f, "%d", &v[n]) == 1) {  /* loop on validated scanf return */
        tmp = realloc (v, (n+1) * sizeof *v); /* never realloc ptr iself */
        if (tmp == NULL) {  /* validate realloc succeeded/handle failure */
            perror ("realloc-v");
            return 1;
        }
        v = tmp;    /* assign reallocated pointer to v */
        n++;        /* advance counter */
    }

注意:您永远不会realloc并将结果分配给原始指针(例如v = realloc (v, ...))。 realloc可能会失败,如果失败,则返回NULL。在这种情况下,您刚刚丢失了对原始内存块的引用 - 导致无法使用或free该内存(您已创建内存泄漏)。相反,始终realloc使用临时指针(例如void *tmp = realloc (v, ...))并在分配v = tmp;之前验证重新分配是否成功。这样,在realloc失败的情况下,您仍然可以引用v中的原始内存块(在您尝试{{1}之前仍然保留(或指向)所有原始值})

答案 2 :(得分:0)

您需要做的是将重复的元素移动到列表的末尾,然后将其删除。