C dynamically growing array

前端 未结 7 1994
闹比i
闹比i 2020-11-22 07:57

I have a program that reads a \"raw\" list of in-game entities, and I intend to make an array holding an index number (int) of an indeterminate number of entities, for proce

相关标签:
7条回答
  • 2020-11-22 08:18

    Well, I guess if you need to remove an element you will make a copy of the array despising the element to be excluded.

    // inserting some items
    void* element_2_remove = getElement2BRemove();
    
    for (int i = 0; i < vector->size; i++){
           if(vector[i]!=element_2_remove) copy2TempVector(vector[i]);
           }
    
    free(vector->items);
    free(vector);
    fillFromTempVector(vector);
    //
    

    Assume that getElement2BRemove(), copy2TempVector( void* ...) and fillFromTempVector(...) are auxiliary methods to handle the temp vector.

    0 讨论(0)
  • When you're saying

    make an array holding an index number (int) of an indeterminate number of entities

    you're basically saying you're using "pointers", but one which is a array-wide local pointer instead of memory-wide pointer. Since you're conceptually already using "pointers" (i.e. id numbers that refers to an element in an array), why don't you just use regular pointers (i.e. id numbers that refers to an element in the biggest array: the whole memory).

    Instead of your objects storing a resource id numbers, you can make them store a pointer instead. Basically the same thing, but much more efficient since we avoid turning "array + index" into a "pointer".

    Pointers are not scary if you think of them as array index for the whole memory (which is what they actually are)

    0 讨论(0)
  • 2020-11-22 08:27

    To create an array of unlimited items of any sort of type:

    typedef struct STRUCT_SS_VECTOR {
        size_t size;
        void** items;
    } ss_vector;
    
    
    ss_vector* ss_init_vector(size_t item_size) {
        ss_vector* vector;
        vector = malloc(sizeof(ss_vector));
        vector->size = 0;
        vector->items = calloc(0, item_size);
    
        return vector;
    }
    
    void ss_vector_append(ss_vector* vec, void* item) {
        vec->size++;
        vec->items = realloc(vec->items, vec->size * sizeof(item));
        vec->items[vec->size - 1] = item;
    };
    
    void ss_vector_free(ss_vector* vec) {
        for (int i = 0; i < vec->size; i++)
            free(vec->items[i]);
    
        free(vec->items);
        free(vec);
    }
    

    and how to use it:

    // defining some sort of struct, can be anything really
    typedef struct APPLE_STRUCT {
        int id;
    } apple;
    
    apple* init_apple(int id) {
        apple* a;
        a = malloc(sizeof(apple));
        a-> id = id;
        return a;
    };
    
    
    int main(int argc, char* argv[]) {
        ss_vector* vector = ss_init_vector(sizeof(apple));
    
        // inserting some items
        for (int i = 0; i < 10; i++)
            ss_vector_append(vector, init_apple(i));
    
    
        // dont forget to free it
        ss_vector_free(vector);
    
        return 0;
    }
    

    This vector/array can hold any type of item and it is completely dynamic in size.

    0 讨论(0)
  • 2020-11-22 08:38

    I can use pointers, but I am a bit afraid of using them.

    If you need a dynamic array, you can't escape pointers. Why are you afraid though? They won't bite (as long as you're careful, that is). There's no built-in dynamic array in C, you'll just have to write one yourself. In C++, you can use the built-in std::vector class. C# and just about every other high-level language also have some similar class that manages dynamic arrays for you.

    If you do plan to write your own, here's something to get you started: most dynamic array implementations work by starting off with an array of some (small) default size, then whenever you run out of space when adding a new element, double the size of the array. As you can see in the example below, it's not very difficult at all: (I've omitted safety checks for brevity)

    typedef struct {
      int *array;
      size_t used;
      size_t size;
    } Array;
    
    void initArray(Array *a, size_t initialSize) {
      a->array = malloc(initialSize * sizeof(int));
      a->used = 0;
      a->size = initialSize;
    }
    
    void insertArray(Array *a, int element) {
      // a->used is the number of used entries, because a->array[a->used++] updates a->used only *after* the array has been accessed.
      // Therefore a->used can go up to a->size 
      if (a->used == a->size) {
        a->size *= 2;
        a->array = realloc(a->array, a->size * sizeof(int));
      }
      a->array[a->used++] = element;
    }
    
    void freeArray(Array *a) {
      free(a->array);
      a->array = NULL;
      a->used = a->size = 0;
    }
    

    Using it is just as simple:

    Array a;
    int i;
    
    initArray(&a, 5);  // initially 5 elements
    for (i = 0; i < 100; i++)
      insertArray(&a, i);  // automatically resizes as necessary
    printf("%d\n", a.array[9]);  // print 10th element
    printf("%d\n", a.used);  // print number of elements
    freeArray(&a);
    
    0 讨论(0)
  • 2020-11-22 08:38

    As with everything that seems scarier at first than it was later, the best way to get over the initial fear is to immerse yourself into the discomfort of the unknown! It is at times like that which we learn the most, after all.

    Unfortunately, there are limitations. While you're still learning to use a function, you shouldn't assume the role of a teacher, for example. I often read answers from those who seemingly don't know how to use realloc (i.e. the currently accepted answer!) telling others how to use it incorrectly, occasionally under the guise that they've omitted error handling, even though this is a common pitfall which needs mention. Here's an answer explaining how to use realloc correctly. Take note that the answer is storing the return value into a different variable in order to perform error checking.

    Every time you call a function, and every time you use an array, you are using a pointer. The conversions are occurring implicitly, which if anything should be even scarier, as it's the things we don't see which often cause the most problems. For example, memory leaks...

    Array operators are pointer operators. array[x] is really a shortcut for *(array + x), which can be broken down into: * and (array + x). It's most likely that the * is what confuses you. We can further eliminate the addition from the problem by assuming x to be 0, thus, array[0] becomes *array because adding 0 won't change the value...

    ... and thus we can see that *array is equivalent to array[0]. You can use one where you want to use the other, and vice versa. Array operators are pointer operators.

    malloc, realloc and friends don't invent the concept of a pointer which you've been using all along; they merely use this to implement some other feature, which is a different form of storage duration, most suitable when you desire drastic, dynamic changes in size.

    It is a shame that the currently accepted answer also goes against the grain of some other very well-founded advice on StackOverflow, and at the same time, misses an opportunity to introduce a little-known feature which shines for exactly this usecase: flexible array members! That's actually a pretty broken answer... :(

    When you define your struct, declare your array at the end of the structure, without any upper bound. For example:

    struct int_list {
        size_t size;
        int value[];
    };
    

    This will allow you to unite your array of int into the same allocation as your count, and having them bound like this can be very handy!

    sizeof (struct int_list) will act as though value has a size of 0, so it'll tell you the size of the structure with an empty list. You still need to add to the size passed to realloc to specify the size of your list.

    Another handy tip is to remember that realloc(NULL, x) is equivalent to malloc(x), and we can use this to simplify our code. For example:

    int push_back(struct int_list **fubar, int value) {
        size_t x = *fubar ? fubar[0]->size : 0
             , y = x + 1;
    
        if ((x & y) == 0) {
            void *temp = realloc(*fubar, sizeof **fubar
                                       + (x + y) * sizeof fubar[0]->value[0]);
            if (!temp) { return 1; }
            *fubar = temp; // or, if you like, `fubar[0] = temp;`
        }
    
        fubar[0]->value[x] = value;
        fubar[0]->size = y;
        return 0;
    }
    
    struct int_list *array = NULL;
    

    The reason I chose to use struct int_list ** as the first argument may not seem immediately obvious, but if you think about the second argument, any changes made to value from within push_back would not be visible to the function we're calling from, right? The same goes for the first argument, and we need to be able to modify our array, not just here but possibly also in any other function/s we pass it to...

    array starts off pointing at nothing; it is an empty list. Initialising it is the same as adding to it. For example:

    struct int_list *array = NULL;
    if (!push_back(&array, 42)) {
        // success!
    }
    

    P.S. Remember to free(array); when you're done with it!

    0 讨论(0)
  • 2020-11-22 08:41

    Building on Matteo Furlans design, when he said "most dynamic array implementations work by starting off with an array of some (small) default size, then whenever you run out of space when adding a new element, double the size of the array". The difference in the "work in progress" below is that it doesn't double in size, it aims at using only what is required. I have also omitted safety checks for simplicity...Also building on brimboriums idea, I have tried to add a delete function to the code...

    The storage.h file looks like this...

    #ifndef STORAGE_H
    #define STORAGE_H
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
        typedef struct 
        {
            int *array;
            size_t size;
        } Array;
    
        void Array_Init(Array *array);
        void Array_Add(Array *array, int item);
        void Array_Delete(Array *array, int index);
        void Array_Free(Array *array);
    
    #ifdef __cplusplus
    }
    #endif
    
    #endif /* STORAGE_H */
    

    The storage.c file looks like this...

    #include <stdio.h>
    #include <stdlib.h>
    #include "storage.h"
    
    /* Initialise an empty array */
    void Array_Init(Array *array) 
    {
        int *int_pointer;
    
        int_pointer = (int *)malloc(sizeof(int));
    
        if (int_pointer == NULL)
        {       
            printf("Unable to allocate memory, exiting.\n");
            free(int_pointer);
            exit(0);
        }
        else
        {
            array->array = int_pointer; 
            array->size = 0;
        }
    }
    
    /* Dynamically add to end of an array */
    void Array_Add(Array *array, int item) 
    {
        int *int_pointer;
    
        array->size += 1;
    
        int_pointer = (int *)realloc(array->array, array->size * sizeof(int));
    
        if (int_pointer == NULL)
        {       
            printf("Unable to reallocate memory, exiting.\n");
            free(int_pointer);
            exit(0);
        }
        else
        {
            array->array = int_pointer;
            array->array[array->size-1] = item;
        }
    }
    
    /* Delete from a dynamic array */
    void Array_Delete(Array *array, int index) 
    {
        int i;
        Array temp;
        int *int_pointer;
    
        Array_Init(&temp);
    
        for(i=index; i<array->size; i++)
        {
            array->array[i] = array->array[i + 1];
        }
    
        array->size -= 1;
    
        for (i = 0; i < array->size; i++)
        {
            Array_Add(&temp, array->array[i]);
        }
    
        int_pointer = (int *)realloc(temp.array, temp.size * sizeof(int));
    
        if (int_pointer == NULL)
        {       
            printf("Unable to reallocate memory, exiting.\n");
            free(int_pointer);
            exit(0);
        }
        else
        {
            array->array = int_pointer; 
        } 
    }
    
    /* Free an array */
    void Array_Free(Array *array) 
    {
      free(array->array);
      array->array = NULL;
      array->size = 0;  
    }
    

    The main.c looks like this...

    #include <stdio.h>
    #include <stdlib.h>
    #include "storage.h"
    
    int main(int argc, char** argv) 
    {
        Array pointers;
        int i;
    
        Array_Init(&pointers);
    
        for (i = 0; i < 60; i++)
        {
            Array_Add(&pointers, i);        
        }
    
        Array_Delete(&pointers, 3);
    
        Array_Delete(&pointers, 6);
    
        Array_Delete(&pointers, 30);
    
        for (i = 0; i < pointers.size; i++)
        {        
            printf("Value: %d Size:%d \n", pointers.array[i], pointers.size);
        }
    
        Array_Free(&pointers);
    
        return (EXIT_SUCCESS);
    }
    

    Look forward to the constructive criticism to follow...

    0 讨论(0)
提交回复
热议问题