Функции динамического массива в C — настройка емкости и создание массива большего размера

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

/* Resizes the underlying array to be the size cap

param:  v       pointer to the dynamic array
param:  cap     the new desired capacity
pre:    v is not null
post:   v has capacity newCap
*/
void _dynArrSetCapacity(DynArr *v, int newCap)
{
    assert(v != NULL);
    struct DynArr largerArray; /*create a new dynamic array*/
    initDynArr(&largerArray, newCap); /*initialize the new array*/
    int i;
    /*copy the old array into the new array*/
    for(i = 0; i < v->size; i++) {
       largerArray->data[i] = v->data[i];
       largerArray->size++;
    }
freeDynArr(v); /*free memory of the old array*/
v = &largerArray; /*point to the new array*/
}

Во-первых, я получаю сообщение об ошибке «недопустимый аргумент типа '->' (есть структура DynArr)» для каждого из двух операторов в цикле for. Это новая ошибка, которая внезапно появилась, хотя я не вносил изменений в эту конкретную функцию, и изначально она не выдавала мне ошибку. Во-вторых, когда эта функция возвращается к функции, которая ее вызвала, размер и емкость массива возвращаются к своим прежним значениям. Мой оператор "v = ", похоже, не дает желаемого эффекта, а именно не присваивает все указатели и значения нового массива v. Полный код приведен ниже для контекста. Будем очень признательны за любые предложения по устранению этих двух проблем. Заранее спасибо.

#include <assert.h>
#include <stdlib.h>
#include "dynArray.h"

struct DynArr
{
   TYPE *data;      /* pointer to the data array */
   int size;        /* Number of elements in the array */
   int capacity;    /* capacity ofthe array */
};


/* ************************************************************************
Dynamic Array Functions
 ************************************************************************ */

/* Initialize (including allocation of data array) dynamic array.

param:  v       pointer to the dynamic array
param:  cap     capacity of the dynamic array
pre:    v is not null
post:   internal data array can hold cap elements
post:   v->data is not null
*/
void initDynArr(DynArr *v, int capacity)
{
    assert(capacity > 0);
    assert(v!= 0);
    v->data = (TYPE *) malloc(sizeof(TYPE) * capacity);
    assert(v->data != 0);
    v->size = 0;
    v->capacity = capacity;
}

    /* Allocate and initialize dynamic array.

param:  cap     desired capacity for the dyn array
pre:    none
post:   none
ret:    a non-null pointer to a dynArr of cap capacity
        and 0 elements in it.
    */
DynArr* newDynArr(int cap)
{
    assert(cap > 0);
    DynArr *r = (DynArr *)malloc(sizeof( DynArr));
    assert(r != 0);
    initDynArr(r,cap);
    return r;
}

/* Deallocate data array in dynamic array.

param:  v       pointer to the dynamic array
pre:    none
post:   d.data points to null
post:   size and capacity are 0
post:   the memory used by v->data is freed
*/
void freeDynArr(DynArr *v)
{
   if(v->data != 0)
   {
       free(v->data);   /* free the space on the heap */
       v->data = 0;     /* make it point to null */
   }
   v->size = 0;
   v->capacity = 0;
}

/* Deallocate data array and the dynamic array ure.

param:  v       pointer to the dynamic array
pre:    none
post:   the memory used by v->data is freed
post:   the memory used by d is freed
*/
void deleteDynArr(DynArr *v)
{
    freeDynArr(v);
    free(v);
}

/* Resizes the underlying array to be the size cap

param:  v       pointer to the dynamic array
param:  cap     the new desired capacity
pre:    v is not null
post:   v has capacity newCap
*/
void _dynArrSetCapacity(DynArr *v, int newCap)
 {
    assert(v != NULL);
        struct DynArr largerArray; /*create a new dynamic array*/
    initDynArr(&largerArray, newCap); /*initialize the new array*/
    int i;
    /*copy the old array into the new array*/
    for(i = 0; i < v->size; i++) {
       largerArray->data[i] = v->data[i];
       largerArray->size++;
}

     freeDynArr(v); /*free memory of the old array*/
     v = &largerArray; /*point to the new array*/
}

/* Get the size of the dynamic array

param:  v       pointer to the dynamic array
pre:    v is not null
post:   none
ret:    the size of the dynamic array
*/
int sizeDynArr(DynArr *v)
{
    int i;
     v->size = 0;
     for (i = 0; i < v->capacity; i++) {
         if (!(EQ(v->data[i], NULL))) {
            v->size++;
    }
}
return v->size;
}

 /*     Adds an element to the end of the dynamic array

param:  v       pointer to the dynamic array
param:  val     the value to add to the end of the dynamic array
pre:    the dynArry is not null
post:   size increases by 1
post:   if reached capacity, capacity is doubled
post:   val is in the last utilized position in the array*/
void addDynArr(DynArr *v, TYPE val)
{
    /* Check to see if a resize is necessary */
    assert(v != NULL);

   if(v->size >= v->capacity) {
           _dynArrSetCapacity(v, 2 * v->capacity);
   }
   v->data[v->size] = val;
   printf("Added %d to Array to position %d\n", v->data[v->size], v->size);
   v->size++;
}

/*  Get an element from the dynamic array from a specified position

param:  v       pointer to the dynamic array
param:  pos     integer index to get the element from
pre:    v is not null
pre:    v is not empty
pre:    pos < size of the dyn array and >= 0
post:   no changes to the dyn Array
ret:    value stored at index pos
*/

TYPE getDynArr(DynArr *v, int pos)
{
   assert(pos < v->size && pos >= 0);
   return v->data[pos];
}

/*  Put an item into the dynamic array at the specified location,
overwriting the element that was there

param:  v       pointer to the dynamic array
param:  pos     the index to put the value into
param:  val     the value to insert
pre:    v is not null
pre:    v is not empty
pre:    pos >= 0 and pos < size of the array
post:   index pos contains new value, val
*/
void putDynArr(DynArr *v, int pos, TYPE val)
{
    assert(pos >= 0 && pos < v->size);

    v->data[pos] = val;
    v->size++;
}

/*  Swap two specified elements in the dynamic array

param:  v       pointer to the dynamic array
param:  i,j     the elements to be swapped
pre:    v is not null
pre:    v is not empty
pre:    i, j >= 0 and i,j < size of the dynamic array
post:   index i now holds the value at j and index j now holds the value at i
 */
void swapDynArr(DynArr *v, int i, int  j)
{
    int temp = 0;
    assert(i >= 0 && i < v->size);
    assert(j >= 0 && j < v->size);
    temp = v->data[i];
    v->data[i] = v->data[j];
    v->data[j] = temp;

}

/*  Remove the element at the specified location from the array,
shifts other elements back one to fill the gap

param:  v       pointer to the dynamic array
param:  idx     location of element to remove
pre:    v is not null
pre:    v is not empty
pre:    idx < size and idx >= 0
post:   the element at idx is removed
post:   the elements past idx are moved back one
*/
 void removeAtDynArr(DynArr *v, int idx)
{
    int i;
    assert(idx < v->size && idx >= 0);

        printf("%d is being removed from array\n", v->data[idx]);
    v->data[idx] = 0;
    for (i = idx; i < v->size; i++) {
        v->data[i] = v->data[i + 1];
    }

    v->size--;
    v->data[v->size+1] = NULL;
}



/* ************************************************************************
    Stack Interface Functions
************************************************************************ */

/*  Returns boolean (encoded in an int) demonstrating whether or not the
dynamic array stack has an item on it.

param:  v       pointer to the dynamic array
pre:    the dynArr is not null
post:   none
ret:    1 if empty, otherwise 0
*/
int isEmptyDynArr(DynArr *v)
{
   if(v->size == 0) return 1;
       return 0;
}

/*  Push an element onto the top of the stack

param:  v       pointer to the dynamic array
param:  val     the value to push onto the stack
pre:    v is not null
post:   size increases by 1
        if reached capacity, capacity is doubled
        val is on the top of the stack
*/
void pushDynArr(DynArr *v, TYPE val)
{
    addDynArr(v, val);
    printf("Pushed %d on Stack\n",val);
}

/*  Returns the element at the top of the stack

param:  v       pointer to the dynamic array
pre:    v is not null
pre:    v is not empty
post:   no changes to the stack
*/
TYPE topDynArr(DynArr *v)
{
    assert(sizeDynArr(v) != 0);
    return getDynArr(v, sizeDynArr(v) - 1);
}

/* Removes the element on top of the stack

param:  v       pointer to the dynamic array
pre:    v is not null
pre:    v is not empty
post:   size is decremented by 1
        the top has been removed
*/
void popDynArr(DynArr *v)
{
    assert(sizeDynArr(v) != 0);
    removeAtDynArr(v, sizeDynArr(v) - 1);
    v->size--;
}

/* ************************************************************************
Bag Interface Functions
 ************************************************************************ */

/*  Returns boolean (encoded as an int) demonstrating whether or not
the specified value is in the collection
true = 1
false = 0

param:  v       pointer to the dynamic array
param:  val     the value to look for in the bag
pre:    v is not null
pre:    v is not empty
post:   no changes to the bag
*/
int containsDynArr(DynArr *v, TYPE val)
{

int i;
    for (i = 0; i < v->size; i++) {
        if(EQ(v->data[i], val)) {
            return 1;
        }
     }
    return 0;
}

/*  Removes the first occurrence of the specified value from the collection
if it occurs

param:  v       pointer to the dynamic array
param:  val     the value to remove from the array
pre:    v is not null
pre:    v is not empty
post:   val has been removed
post:   size of the bag is reduced by 1
 */
 void removeDynArr(DynArr *v, TYPE val)
 {
    int i;
    for (i = 0; i < v->size; i++) {
            if (EQ(val, v->data[i])) {
                 removeAtDynArr(v, i);
                 return;
            }
    }
}

int main(int argc, char** argv){
    printf("Program: Dynamically-allocated Array\n");
    int cap = 10;
    int i;

DynArr *r;
r = newDynArr(cap);


    for (i = 0; i < cap; i++) {
        pushDynArr(r, i);
    }

    r->size = sizeDynArr(r);
    if(isEmptyDynArr(r) == 1) {
        printf("Array is empty\n");
    }

    else if(isEmptyDynArr(r) == 0) {
        printf("Array is not empty\n");
    }
    removeDynArr(r, 5);

    printf("Before adding to array, size is %d and capacity is %d\n", r->size, r->capacity);
    printf("The top of the array before push is %d\n", topDynArr(r));

    for (i = r->size; i < cap + 4; i++) {
       pushDynArr(r,i);
    }

    printf("After adding to array, size is %d and capacity is %d\n", r->size, r->capacity);
    printf("The top of the array after push is %d\n", topDynArr(r));

    printf("The top of the array before remove is %d\n", topDynArr(r));
    printf("Before removing from array, size is %d and capacity is %d\n", r->size, r->capacity);

    for (i = r->size; i < cap; i++) {
        pushDynArr(r,i);
    }

    printf("After removing from array, size is %d and capacity is %d\n", r->size, r->capacity);
    printf("The top of the array after remove is %d\n", topDynArr(r));

    if (containsDynArr(r, 3) == 1) {
        printf("Value 30 is in the array.\n");
    }

    printf("The top of the array before pop is %d\n", topDynArr(r));
    popDynArr(r);
    printf("The top of the array after pop is %d\n", topDynArr(r));

deleteDynArr(r);

   return 0;
}

person user1852050    schedule 19.01.2013    source источник
comment
Это много кода. Не могли бы вы сузить круг вопросов.   -  person ChiefTwoPencils    schedule 19.01.2013
comment
Проблемы в пятой функции сверху, _dynArraySetCapacity. Я включил код для контекста. Некоторые люди предпочитают именно так.   -  person user1852050    schedule 19.01.2013
comment
Я отредактировал исходный пост и скопировал конкретный код, вызывающий проблемы, в мое описание проблемы. Я оставил код внизу своего поста для контекста.   -  person user1852050    schedule 19.01.2013


Ответы (3)


Забудьте о стеке. largerArray управляется автоматически. Этот объект будет уничтожен, когда _dynArrSetCapacity вернется.

Когда вы переходите к функциям в C, вы передаете по значению. Это означает, что функция получает копию данных в новом объекте, а не в старом. Если вы измените новый объект, старый объект останется неизменным. v = &largerArray; неэффективен, потому что v является копией, а не оригиналом. Обратите внимание, что в приведенном ниже примере int x в parse_by_value_example не является тем же объектом, что и int x в main.

#include <stdio.h>
int parse_by_value_example(int x);
int main(void) {
    int x = 0, y;
    y = parse_by_value_example(x);
    printf("Value of x: %d. Value of y: %d\n", x, y);
    return 0;
}
int parse_by_value_example(int x) {
    x = 42;
    return x;
}

Я думаю, вы путаете определение «массива» в C. В C массив таков, что sizeof (array) будет произведением количества элементов и размера элемента, а весь массив представляет собой одно непрерывное выделение . Например, следующий код не вызовет никаких ошибок утверждений:

size_t array_capacity = 42;
int array[array_capacity];
assert(sizeof (array) == sizeof (array_capacity) * sizeof (array[0]));

Оператор нижнего индекса array[] на самом деле является оператором нижнего индекса указателя; массивы распадаются на указатели, если только они не находятся в таких выражениях, как &array, sizeof (array), или они не используются при инициализации.

Какую книгу ты читаешь? Похоже, у тебя с этим проблемы. Я уверен, что если вы будете выполнять упражнения по мере их появления (не пропуская их), вы получите пользу от K&R 2E.

person autistic    schedule 19.01.2013
comment
Язык программирования C Кернигана - person user1852050; 19.01.2013
comment
Вы делаете упражнения? - person autistic; 19.01.2013
comment
Пока нет из-за нехватки времени. Я пытаюсь выучить себя C для класса Data Structures. Я думал, что довольно хорошо разобрался в управлении памятью благодаря изучению языка ассемблера, но C — медведь. - person user1852050; 19.01.2013
comment
Нет. C стандартизирован не только для x86. Помните, что существует много форм ассемблера... Если вы пишете стандартный совместимый C, он будет вести себя одинаково на любом процессоре, на который может ориентироваться стандартный совместимый компилятор C. Изучайте C так, как будто это что-то, чего вы никогда раньше не видели, вместо того, чтобы предполагать, что у него будет что-то общее с ассемблером. Выполняйте упражнения, потому что они являются механизмом подтверждения того, что вы поняли содержание. - person autistic; 19.01.2013

struct DynArr largerArray — это переменная стека, она существует только в текущем стеке. v = &largerArray заставляет v указывать на адрес памяти в стеке. Затем ваша функция возвращается, и стек уничтожается, поэтому v указывает на несуществующую память, которая в дальнейшем будет переопределена, когда другие функции переработают это пространство стека.

Вы также никогда не создаете динамический массив.

DynArr *r;
initDynArr(r, cap);

r — это указатель на DynArray, это его тип, но вы не инициализируете его так, чтобы он фактически указывал на такой массив, а initDynArr() предполагает, что r уже указывает на DynArray, что не так, он не инициализирован. Правильно будет либо

DynArr r;
initDynArr(&r, cap);

в этом случае DynArray существует в стеке или

DynArr *r = malloc(sizeof(*r));
initDynArr(r, cap);

в этом случае DynArray существует в куче.

В вашем коде так много ошибок в отношении управления памятью, что я даже не знаю, с чего начать.

Возможно, вам следует посмотреть эту страницу. Этот парень реализовал в значительной степени то, что вы пытаетесь реализовать здесь.

person Mecki    schedule 19.01.2013
comment
Хм. Я инициализирую и заполняю второй массив так же, как и первый, поэтому я предположил (это звучит неправильно), что будет просто создать новый массив и указать на него указатель. Любые предложения о том, как создать второй массив, чтобы его не было в стеке? - person user1852050; 19.01.2013

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

*v = largerArray;

скопировать из largerArray.

person tia    schedule 19.01.2013