我正在遍历整数数组,尝试查找非零元素并获取计数。这是我完整的代码。
#include <stdio.h>
#include <stdlib.h>
int countelem(int *a, int a_length){
int i, count = 0;
for (i=0; i<a_length; i++){
if (a[i] != 0) {
printf("element number: %d and element is: %d\n", i, a[i]);
count ++;
};
};
return count;
}
int main(){
int count, a_length = 5;
int *ptr_a, a[a_length];
ptr_a = calloc(a_length, 4);
ptr_a = a;
a[0] = 1;
count = countelem(ptr_a, a_length);
printf("number of non zeroes %d\n", count);
if (ptr_a){
printf("needs to be freed\n");
free(ptr_a);
}
return 0;
}
我正在使用命令
进行编译cc -Wall -std=c99 con.c -o con
在运行./con
时,我基本上遇到了两个问题。
if (a[i] != 0)
中的countelem函数中,a [i]产生与未初始化元素无关的结果。ptr_a
以来,调用free(ptr_a)
为何导致错误pointer being freed was not allocated
。这是标准输出
element number: 0 and element is: 1
element number: 1 and element is: 32767
element number: 2 and element is: 185925632
element number: 3 and element is: 1
number of non zeroes 4
needs to be freed
con(535,0x7ffff180a3c0) malloc: *** error for object 0x7fff54aaefe0: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Abort trap: 6
非常感谢您的帮助。
NB。我注意到的一件有趣的事情是,如果我使用double
而不是int
的数组,则a[i]
会提供正确的输出。
答案 0 :(得分:1)
好吧……“你在想什么?”
int *ptr_a, a[a_length];
让我们看两行,看看我们是否可以理解您在做什么:
ptr_a = calloc(a_length, 4);
ptr_a = a;
上面的第1行分配了一个内存块,其中calloc
的大小为a_length
的{{1}}个成员,每个成员4-bytes
的总数为20-bytes
。然后,它将新内存块的起始地址分配给a_length = 5;
。
上面的第2行,然后将数组ptr_a
(具有自动存储类型)的地址分配给a
覆盖 您刚刚分配的内存块(从而导致内存泄漏,因为不再有对该新块开始的引用,这意味着它永远无法释放)。
但是,由于ptr_a
现在指向先前未分配给ptr_a
,malloc
或calloc
的内存地址,因此当您将realloc
传递给ptr_a
景气! SegFault。
使用free (ptr_a);
分配的内存进行存储
您根本不需要VLA(可变长度数组)calloc
。您分配了足以容纳五个整数值的内存块,并将该块的起始地址分配给a
。您尝试使用ptr_a
时只需使用ptr_a
,例如
a
使用/输出示例
#include <stdio.h>
#include <stdlib.h>
#define NELEMENTS 5 /* if you need a constant, #define one (or more) */
int countelem (int *a, int a_length)
{
int i, count = 0;
for (i = 0; i < a_length; i++)
if (a[i] != 0) {
printf ("element number: %d and element is: %d\n", i, a[i]);
count++;
} /* no ; following block closure */
return count;
}
int main (void) {
int count = 0, /* initialize all variables */
a_length = NELEMENTS,
*ptr_a = NULL;
ptr_a = calloc(a_length, sizeof *ptr_a); /* allocate block of mem */
if (ptr_a == NULL) { /* validate & handle error before using block */
perror ("calloc-ptr_a");
return 1;
}
ptr_a[0] = 1; /* assign value 1 as first value in block of mem */
count = countelem (ptr_a, a_length);
printf ("number of non zeroes %d\n", count);
free(ptr_a); /* you validated the block above, just free */
return 0;
}
内存使用/错误检查
在您编写的任何动态分配内存的代码中,对于任何分配的内存块,您都有2个职责:(1)始终保留指向起始地址的指针因此,(2)当不再需要它时可以释放。
当务之急是使用一个内存错误检查程序来确保您不会尝试访问内存或在已分配的块的边界之外/之外进行写入,不要试图以未初始化的值读取或基于条件跳转,最后,以确认您释放了已分配的所有内存。
对于Linux,$ ./bin/alloccount
element number: 0 and element is: 1
number of non zeroes 1
是正常选择。每个平台都有类似的内存检查器。它们都很容易使用,只需通过它运行程序即可。
valgrind
始终确认已释放已分配的所有内存,并且没有内存错误。
使用VLA进行存储
相反,如果您确实想使用VLA,则不需要使用$ valgrind ./bin/alloccount
==7530== Memcheck, a memory error detector
==7530== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==7530== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==7530== Command: ./bin/alloccount
==7530==
element number: 0 and element is: 1
number of non zeroes 1
==7530==
==7530== HEAP SUMMARY:
==7530== in use at exit: 0 bytes in 0 blocks
==7530== total heap usage: 1 allocs, 1 frees, 20 bytes allocated
==7530==
==7530== All heap blocks were freed -- no leaks are possible
==7530==
==7530== For counts of detected and suppressed errors, rerun with: -v
==7530== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
分配内存。这是一个命题,但不是两个命题。
无论存储是由VLA提供还是由calloc
提供,您仍然可以使用calloc
指向该存储。但是,如果VLA提供了存储空间,则不要简单地使用ptr_a
进行分配:
calloc
这就是声明VLA和指向VLA的指针所需的全部内容。 (请注意:VLA值是不确定的,因此在使用前,您可能需要先添加int a[a_length], *ptr_a = a;
,然后再添加string.h
)
如果您使用可变长度数组作为存储而不是使用memset (a, 0, sizeof a);
进行分配,则代码将简化为:
calloc
(注意:如上所述,添加了#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NELEMENTS 5 /* if you need a constant, #define one (or more) */
int countelem (int *a, int a_length)
{
int i, count = 0;
for (i = 0; i < a_length; i++)
if (a[i] != 0) {
printf ("element number: %d and element is: %d\n", i, a[i]);
count++;
} /* no ; following block closure */
return count;
}
int main (void) {
int count = 0, /* initialize all variables */
a_length = NELEMENTS,
a[a_length], *ptr_a = a;
memset (a, 0, sizeof a); /* initialize a all zero */
ptr_a[0] = 1; /* assign value 1 as first element */
count = countelem (ptr_a, a_length);
printf ("number of non zeroes %d\n", count);
return 0;
}
。如果没有添加它,则任何访问memset
-a[1]
中不确定值的尝试都会导致未定义行为(以及发现的时髦值)
只需删除a[4]
并使用a_length
即可删除VLA,而是为存储提供一个普通数组,您可以在声明该数组时对其进行初始化,例如
NELEMENTS
(输出是相同的,但是不需要 int count = 0, /* initialize all variables */
a[NELEMENTS] = {0},
*ptr_a = a;
...
count = countelem (ptr_a, NELEMENTS);
或string.h
,也不需要通过内存/错误检查来运行它)
启用其他警告(至少memset
)
至少要为gcc / clang添加-Wextra
(尽管您还应该添加-Wextra
),以便VS使用-pedantic -Wshadow
。在没有任何警告的情况下,在干净地编译代码之前,不要接受代码。听您的编译器告诉您的内容,阅读并理解警告。它将为您提供精确的行,在该行上可以找到每个有问题的代码位。在进一步操作之前先对其进行修复。简单地解决所有警告可以并且将消除大多数问题,您会发现自己花费大量时间解决其他问题。
仔细研究一下,如果您有任何疑问,请告诉我。
答案 1 :(得分:0)
您已将ptr_a分配为指向a,堆栈rhat上的未初始化数组可能包含任何随机值。