Ну ... "О чем ты думал?"
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
.Не принимайте код, пока он не скомпилируется без единого предупреждения.Послушайте, что говорит ваш компилятор, прочитайте и поймите предупреждения.Это даст вам точную строку, в которой находится каждый проблемный фрагмент кода.Иди починить, прежде чем идти дальше.Простое устранение всех предупреждений может устранить и устранит большинство проблем, на которые вы потратите много времени, решая иначе.
Просмотрите все и дайте мне знать, если у вас есть какие-либо вопросы.