C - Целочисленный массив, дающий различные значения в цикле for - PullRequest
0 голосов
/ 28 октября 2018

Я перебираю целочисленный массив и пытаюсь найти ненулевые элементы и получаю счетчик.Вот мой полный код.

#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. В функции countelem в операторе if (a[i] != 0) a [i] выдает не относящиеся к делу результаты для неинициализированных элементов.
  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

Ваша помощь очень ценится.

NB.Одна интересная вещь, которую я заметил, что если я использую массив double вместо int, a[i] обеспечивает правильный вывод.

Ответы [ 2 ]

0 голосов
/ 28 октября 2018

Ну ... "О чем ты думал?"

    int *ptr_a, a[a_length];

Давайте посмотрим на две строки и посмотрим, сможем ли мы понять, что вы делаете:

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

Строка 1 выше выделяет блок памяти с calloc, определяя размер блока для a_length членов 4-bytes каждый на общую сумму 20-bytes при заданном a_length = 5;.Затем он назначает начальный адрес для нового блока памяти на ptr_a.

в строке 2 выше, а затем назначает адрес массива a (с автоматическим типом хранения) на ptr_a перезаписывает адрес памяти для только что выделенного блока памяти (что вызывает утечку памяти , потому что больше нет ссылок на начало этого нового блока, что означает, что он можетникогда не будет освобожден).

Однако, поскольку ptr_a теперь указывает на адрес памяти, который НЕ был ранее выделен с malloc, calloc или realloc, когда вы передаете ptr_a в free (ptr_a); boom! SegFault.

Использование памяти, выделенной 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

Использование памяти /Проверка ошибок

В любом написанном вами коде, который динамически выделяет память, у вас есть 2 обязанностей в отношении любого выделенного блока памяти: (1) всегда сохраняйте указатель наначальный адрес для блока памяти, поэтому (2) он может быть освобожден , когда он больше не нужен.

Крайне важно, чтобы вы использовали программу проверки ошибок памяти дляубедитесь, что вы не пытаетесь получить доступ к памяти или писать за пределами / за пределами выделенного блока, пытаться прочитать или основать условный переход на неинициализированном значении и, наконец, подтвердить, что вы освобождаете всю выделенную память.

Для Linux valgrind - нормальный выбор.Для каждой платформы есть похожие проверки памяти.Все они просты в использовании, просто запустите вашу программу через него.

$ 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, не следует выделять с помощью calloc, просто:

int a[a_length], *ptr_a = a;

Это все, что требуется для объявления VLA, а затем указателя на VLA.(примечание: значения VLA являются неопределенными, поэтому перед использованием вы можете включить 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] приведет к Undefined Behavior (и вывод значений в стиле фанк, как вы нашли))

Простое удаление a_length и использование NELEMENTS вместо этого удаляет VLA и вместо этого предоставляет хранилище с обычныммассив, который вы можете инициализировать при объявлении, например,

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

(вывод такой же, но нет необходимости в string.h или memset или необходимости запускать его через проверку памяти / ошибок)

Включить дополнительные предупреждения (-Wextra минимум)

Как минимум, добавьте -Wextra для gcc / clang (хотя вы должны также добавить -pedantic -Wshadow) для использования VS /W3.Не принимайте код, пока он не скомпилируется без единого предупреждения.Послушайте, что говорит ваш компилятор, прочитайте и поймите предупреждения.Это даст вам точную строку, в которой находится каждый проблемный фрагмент кода.Иди починить, прежде чем идти дальше.Простое устранение всех предупреждений может устранить и устранит большинство проблем, на которые вы потратите много времени, решая иначе.

Просмотрите все и дайте мне знать, если у вас есть какие-либо вопросы.

0 голосов
/ 28 октября 2018

Вы назначили ptr_a для указания на неинициализированный массив в стеке, который может содержать любые случайные значения.

...