我正在为我的班级做一些练习,而且我遇到了问题。 练习要求使用文件中的数据填充动态数组,对数据进行排序,然后删除重复数据。 我的问题出在最后一个,我无法删除重复的数字。
这是我的代码:
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;}
任何人都可以帮助我吗?
答案 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'
)。
使用malloc
或realloc
时,请勿投射结果 - 没有理由这样做。两者都返回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)
您需要做的是将重复的元素移动到列表的末尾,然后将其删除。