首页 > 解决方案 > C - 在 for 循环中产生不同值的整数数组

问题描述

我正在遍历一个整数数组并尝试查找非零元素并获取计数。这是我的完整代码。

#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时我基本上遇到了两个问题。

  1. 在语句if (a[i] != 0) a[i] 的函数 countelem 中,对未初始化的元素产生了不相关的结果。
  2. 由于我正在为指针分配内存,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

非常感谢您的帮助。

注意。我注意到的一件有趣的事情是,如果我使用 array ofdouble而不是int,会a[i]提供正确的输出。

标签: carrayspointerscalloc

解决方案


嗯…… “你在想什么?”

    int *ptr_a, a[a_length];

让我们看两行,看看我们是否能理解你在做什么:

    ptr_a = calloc(a_length, 4);
    ptr_a = a;

上面的第 1 行分配了一块内存,并为每个成员分配了块calloc大小,总共给定的. 然后它将新内存块的起始地址分配给.a_length4-bytes20-bytesa_length = 5;ptr_a

上面的第 2 行,然后分配数组的地址a(具有自动存储类型)以ptr_a 覆盖您刚刚分配的内存块的内存地址(从而导致内存泄漏,因为不再引用该新块的开头意味着它永远无法被释放)。

但是,由于ptr_anow 指向的内存地址之前未分配malloc,callocrealloc, 当您传递ptr_afree (ptr_a); 繁荣!段错误。

使用calloc为存储分配的内存

您根本不需要 VLA(可变长度数组)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;
}

示例使用/输出

$ ./bin/alloccount
element number: 0 and element is: 1
number of non zeroes 1

内存使用/错误检查

在您编写的任何动态分配内存的代码中,对于分配的任何内存块,您有两个责任:(1)始终保留指向内存块起始地址的指针,(2)它可以在没有时被释放更需要。

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

对于 Linuxvalgrind是正常的选择。每个平台都有类似的内存检查器。它们都易于使用,只需通过它运行您的程序即可。

$ 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 进行存储

相反,如果您确实想使用 VLA,则无需使用calloc. 这是一个或另一个,但不是两者兼而有之的命题。

无论存储是由 VLA 还是由 提供calloc,您仍然可以使用ptr_a来指向该存储。但是,如果存储由 VLA 提供,则不要分配 with calloc,只需:

int a[a_length], *ptr_a = a;

这就是声明 VLA 和指向 VLA 的指针所需的全部内容。(注意:VLA 值是不确定的,所以在使用之前,您可能需要包含string.hthen 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和使用NELEMENTS会删除 VLA,而是为存储提供一个普通数组,您可以在声明它时对其进行初始化,例如

    int count = 0,              /* initialize all variables */
        a[NELEMENTS] = {0},
        *ptr_a = a;
    ...
    count = countelem (ptr_a, NELEMENTS);

(输出是相同的,但不需要string.hmemset不需要通过内存/错误检查来运行它)

启用附加警告(-Wextra至少)

至少,为 VS use添加-Wextragcc/clang (尽管您也应该另外添加) 。在编译干净之前不要接受代码——没有任何警告。听听你的编译器告诉你什么,阅读并理解警告。它将为您提供找到每个有问题的代码位的精确行。在继续之前先修复它。简单地解决所有警告可以并且将消除您会发现自己花费大量时间以其他方式解决的大多数问题。-pedantic -Wshadow/W3

看看事情,如果你有任何问题,请告诉我。


推荐阅读