1

One of the greatest things about Python is the flexibility of dynamic arrays (lists).

It's so useful I find it hard to believe there isn't a library for this already included in C. This question indicates that there is no standard library for this. Apparently, glibc includes functionality for queues, but as I'm on a windows machine this is not helpful. As the question is 10 years old, I'm wondering if there's been an update.

Is implementing the functionality yourself still the only way to get something like a Python list or is there a standard C library that allows you to use dynamic arrays?

Connor
  • 867
  • 7
  • 18
  • 1
    I don't think so. https://en.cppreference.com/w/c/header – kiner_shah Mar 15 '23 at 11:15
  • Not sure what "standard" means in this context. There's definitely nothing like this in the Cxx standards. I'm sure there are external libraries ( [example](https://docs.gtk.org/glib/struct.Array.html) ) that do something similar. – teapot418 Mar 15 '23 at 11:19
  • 2
    C's standard library has always been minimal. C has never had anything like the [STL](https://en.wikipedia.org/wiki/Standard_Template_Library) of C++. C does have Variable-Length Arrays (VLA's), and [`realloc`](https://linux.die.net/man/3/realloc). Beyond that, you're looking at third-party libraries, and their attendant portability / dependency issues. – Steve Summit Mar 15 '23 at 11:32
  • @SteveSummit Okay, I presume it's kept minimal for a reason. But I can't find that reason either! In your experience, why is the C standard library so small? – Connor Mar 15 '23 at 12:40
  • 1
    @Connor - Those who want a big standard library use C++ instead. :-) – BoP Mar 15 '23 at 12:46
  • 1
    @Connor Mostly it's because *everything* about C is small and minimal! Partly because there are too many tradeoffs in choosing One Standard Way of doing things. – Steve Summit Mar 15 '23 at 14:29
  • 1
    And partly because of this quote by Ken Thompson (who is talking about Unix, but this is applicable, in a way, to C as well): "The kernel is the only Unix code that cannot be substituted by a user to his own liking. For this reason, the kernel should make as few real decisions as possible. This does not mean to allow the user a million options to do the same thing. Rather, it means to allow only one way to do one thing, but have that way be the least-common divisor of all the options that might have been provided." (But, it's true, the C library *can* be substituted by the user.) – Steve Summit Mar 15 '23 at 14:29
  • @SteveSummit So the C language follows that same principle? "Do as little as is necessary to allow the user to achieve anything they want."? I've noticed that C makes you think about data structures far more, which prevents you from adding functionality that's not necessary/ could be harmful. – Connor Mar 16 '23 at 10:19

3 Answers3

2

There is currently no standard library for dynamic arrays, but it's not hard to implement the functionality with realloc() and family.

Here's something you can start with:

struct dynamic_array {
    void *array;          
    size_t occupied_size;
    size_t allocated_size; 
};

See: Dynamic arrays in C.

Harith
  • 4,663
  • 1
  • 5
  • 20
1

C language has something which is called flexible array members:

typedef struct 
{
    size_t size;
    size_t used; 
    int data[];
}int_arr;


int_arr *addsize(int_arr *arr, size_t inc)
{
    size_t newsize = 0;
    size_t newused = 0;

    if(arr)
    {
        newsize = arr -> size + inc;
        newused = arr -> used;
    }
    arr = realloc(arr, sizeof(*arr) + newsize * sizeof(arr -> data[0]));
    if(arr)
    {
        arr -> size = newsize;
        arr -> used = newused;
    }
    return arr;
}
0___________
  • 60,014
  • 4
  • 34
  • 74
  • Okay, so does that mean for every new project you work on you're basically copying and pasting the same code to give you this functionality? I realise there are sometimes nice tricks you can do to get around these data structures, but in general they're so helpful it's hard to avoid them! – Connor Mar 15 '23 at 12:41
  • 1
    Write your library/module and reuse it. You do not have to copy/paste. Same as other libraries/modules – 0___________ Mar 15 '23 at 12:43
1

Here is the implementation I normally use on my code:

#define DYNARRAY_GROW(_cell_type, _array, _need, _to_add) do {    \
               if (_array##_len + (_need) >= (_to_add)) {         \
                    _array##_cap += (_to_add);                    \
                    _cell_type *_aux = realloc(_array,            \
                              _array##_cap * sizeof _array[0]);   \
                    if (!_aux) {                                  \
                        /* error checking code ... */             \
                        ERR("Error growing array " #_array "\n"); \
                    } else {                                      \
                        _array = _aux;                            \
                    }                                             \
               }                                                  \
           } while (0)

later, I define an array by using the following variables:

    MyCellType *A     = NULL;  /* pointer to cell type */
    size_t      A_len = 0,     /* no elements, empty array */
                A_cap = 0;
#define ARRAY_A_GROW_INCREMENT        (20) /* NUMBER OF ELEMENTS \
                                            * TO GROW IN CASE OF NEED */
    ...
    /* ASSUME I NEED TO GROW THE ARRAY FOR 3 ELEMENTS */
    DYNARRAY_GROW(MyType, A, 3, ARRAY_A_GROW_INCREMENT);
    A[A_len++] = first_element;
    A[A_len++] = second_element;
    A[A_len++] = third_element;
    /* if A has need to grow, the number of new cells given to the array
     * is not 3, but the value of ARRAY_A_GROW_INCREMENT (in this case 20)
     * so you save calls to realloc() */

There's a drawback, although (but this also applies to any kind of array you can have in this manner):

  • in the reallocation, it is possible that a new memory segment is completely allocated, and all the array contents to be moved to another place in memory. This makes that any other pointer reference that points into the array to be invalid from the reallocation time onwards... and it has been source of some errors I have learned the bad way. Always think that the elements can move, so be careful with pointers that happen to point to elements inside the array (this includes the pointers that are themselves inside the array)
  • If you test your code with a C++ compiler (let's say you use GTest or similar) then you need to cast the realloc() call return value, as the void * is not assignment compatible with any other type in that language, as C is. So, in that case, substitute the call to realloc to show as:
_cell_type *_aux = (_cell_type *) realloc(...
Luis Colorado
  • 10,974
  • 1
  • 16
  • 31