Могу ли я сделать динамический массив таким образом?
Итак, мой код такой:
int a, b;
printf("Give dimensions\n");
scanf("%d %d", &a, &b);
double array1[a][b];
printf("Give values\n");
int i, j;
for(i=0; i<a; i++)
{
for(j=0; j<b; j++)
{
scanf("%lf", &array1[i][j]);
}
}
Моя проблема в том, что мне сказали, что это неправильный способ выделения памяти и что я должен использовать malloc. Я должен был создать динамический массив, используя измерения пользователя.
Редактировать: остальная часть программы:
double sum=0;
for(i=0; i<a; i++)
{
for(j=0; j<b; j++)
{
sum = sum + array1[i][j];
}
printf("Line %d: sum = %lf\n", i+1, sum);
sum=0;
}
4 ответа
Зависит от того, как вы определяете "правильно". Это законный C с C99.
Проблема, однако, в том, что если a
а также b
слишком велики, массив переполнит стек вызовов. Если это вероятный сценарий, вы должны предпочесть malloc
, Стек вызовов обычно выделяется достаточно маленьким по сравнению с кучей. Так что это, вероятно, источник совета.
Обратите внимание, что вы все еще можете наслаждаться нотацией индекса массива с динамически размещаемыми массивами:
double (*array)[b] = malloc(sizeof(double[a][b]));
Массив находится в одном непрерывном блоке, и указатель VLA приведет к a[i][j]
ссылка на правильный элемент.
Нет, это совершенно правильный и правильный способ, если вы используете версию / среду компилятора, которая поддерживает массив переменной длины.
Это была обязательная функция, как на C99
но снова сделал необязательным C11
,
Основные различия между использованием VLA и "комбинацией функций указателя и выделения памяти" заключаются в
- VLA имеют блочную область. VLA не находится за пределами области действия, поэтому его нельзя вернуть из функции и использовать в вызывающей стороне, в отличие от подхода указателя и malloc.
- Обычно VLA выделяются в стеке для всех основных реализаций, так что размер ограничен.
Компилятор C может обрабатывать его как допустимый код, хотя стандарт C11 сделал поддержку VLA необязательной.
Основная проблема заключается в том, что на самом деле у вас нет возможности проверить, было ли выделение успешным или нет, особенно когда размер неизвестен. Это также главное преимущество malloc/calloc
:
double (*array1)[b] = malloc(a * sizeof(*array1));
if (!array1) {
// handle allocation failure
}
scanf("%d %d", &a, &b);
double array1[a][b];
Это действительно для стандарта C99, хотя сначала вы захотите выполнить некоторую проверку работоспособности ваших входов (т. Е. Убедиться, что оба входа действительно прочитаны, убедитесь, что ни один из них не отрицателен, убедитесь, что они находятся в разумных пределах, так далее.).
array1
является массивом переменной длины (VLA), который впервые был представлен в C99. Начиная с C2011 они стали необязательными, но я думаю, что почти все размещенные реализации все еще поддерживают их. Чтобы убедиться, проверьте __STDC_NO_VLA__
макрос - если он определен, то реализация не поддерживает VLA.
size_t a, b;
if ( scanf( "%zu %zu", &a, &b ) < 2 )
// bail out with an error message
#if defined( __STDC_VERSION__ ) && __STDC_VERSION__ >= 199901L && !defined( __STDC_NO_VLA__ )
/**
* This implementation supports VLAs. We need to do an additional
* sanity check on a and b to make sure we don't allocate an
* object too large for the stack (even though VLAs
* don't have to be allocated on the stack, that's how many
* implementations choose to do it).
*/
if ( a > SOME_MAX_LIMIT || b > SOME_MAX_LIMIT )
// bail out with an error
double array1[a][b];
#else
/**
* This implementation does not support VLAs, so we'll have to use
* dynamic memory allocation. Note that memory allocated this way
* is *not* contiguous - rows are not adjacent, so the object immediately
* following array1[0][b-1] is not array1[1][0]. If that matters,
* then this won't work.
*/
double **array1 = malloc( sizeof *array1 * a );
if ( array1 )
{
size_t i;
for ( i = 0; i < a; i++ )
{
array1[i] = malloc( sizeof *array1[i] * b );
if ( !array1[i] )
break;
}
if ( i < a ) // memory allocation failure, clean up any previously allocated memory
{
while ( i-- )
free( array1[i] );
free( array1 );
// bail out with an error here
}
}
else
// memory allocation failed, bail with an error
#endif
На данный момент ваш код может ссылаться array1[i][j]
независимо от того, каким образом мы его распределили. Тем не менее, вам нужно будет добавить это в конце вашей функции:
#if !defined( __STDC_VERSION__ ) || __STDC_VERSION__ < 199901L || defined( __STDC_NO_VLA__ )
for ( size_t i = 0; i < a; i++ )
free( array1[i] );
free( array1 );
#endif
чтобы мы должным образом убирали за собой, если бы нам пришлось использовать malloc
,
VLA ведут себя так же, как и все остальные auto
(локальный) массив - память для них будет освобождена при выходе из функции, поэтому вы не можете вернуть указатель на них, и указатель будет действительным после выхода из функции.
Поскольку их размер не определяется до времени выполнения, вы не можете использовать их в области видимости файла (вне какой-либо функции), вы не можете объявить их как static
и они не могут быть членами struct
или же union
тип.
Вы можете комбинировать VLA с динамическим распределением памяти:
size_t a, b;
if ( scanf( "%zu %zu", &a, &b ) < 2 )
// bail out with an error
double (*array1)[b] = malloc( sizeof *array1 * a );
Вы получаете динамически распределяемый 2D-массив с заданными пользователем размерами, которые являются смежными, и вы ограничены только размером кучи, а не размером стека, поэтому вы можете размещать большие объекты таким образом.