0
\$\begingroup\$

Array-List-int.h

#ifndef ARRAY_LIST_INT
#define ARRAY_LIST_INT

#include <stddef.h>
#include <stdbool.h>

////////////////////////////////////////////////////////////////////////////////

// AL_int x; creates an object handler called 'x' for an array list.
// The creation of an object handler for an array list must be followed by
// AL_int_create_empty() or AL_int_create_empty_with_initial_capacity() before
// the array list can be used.
typedef struct
{
    int* arr;
    size_t size;
    size_t capacity;
}
AL_int;

////////////////////////////////////////////////////////////////////////////////

extern const size_t AL_INT_MAX_CAPACITY;

// Allocates memory for the array list (which is handled by the object handler
// pointed to by 'ptr'), and sets the size and the capacity of the array list to
// 0 and 1, respectively.
// May cause the program to terminate due to 'out of memory'.
void AL_int_create_empty(AL_int* ptr);

// Allocates memory for the array list (which is handled by the object handler
// pointed to by 'ptr'), and sets the size and the capacity of the array list to
// 0 and 'initial_capacity', respectively.
// May cause the program to terminate due to 'out of memory'.
// 'initial_capacity' must be less than or equal to AL_INT_MAX_CAPACITY.
void AL_int_create_empty_with_initial_capacity(AL_int* ptr,
                                               size_t initial_capacity);

// Frees the memory taken up by the array list (which is handled by the object
// handler pointed to by 'ptr').
void AL_int_destroy(const AL_int* ptr);

////////////////////////////////////////////////////////////////////////////////

// Adds 'n' to the end of the array list (which is handled by the object handler
// pointed to by 'ptr').
// May cause the program to terminate due to 'capacity overflow' or 'out of
// memory'.
void AL_int_add(AL_int* ptr, int n);

// Adds 'n' at the index 'i' to the array list (which is handled by the object
// handler pointed to by 'ptr'), shifting elements as required.
// May cause the program to terminate due to 'index out of bounds', 'capacity
// overflow' or 'out of memory'.
void AL_int_add_at_index(AL_int* ptr, ptrdiff_t i, int n);

// Clears the array list (which is handled by the object handler pointed to by
// 'ptr'), and sets the size and the capacity of the array list to 0 and 1,
// respectively.
// May cause the program to terminate due to 'out of memory'.
void AL_int_clear(AL_int* ptr);

// Returns true if 'n' is present in the array list (which is handled by the
// object handler pointed to by 'ptr'), and returns false otherwise.
bool AL_int_contains(const AL_int* ptr, int n);

// Returns the element at the index 'i' in the array list (which is handled by
// the object handler pointed to by 'ptr').
// May cause the program to terminate due to 'index out of bounds'.
int AL_int_get(const AL_int* ptr, ptrdiff_t i);

// Returns the index of the first occurrence of 'n' at the range of indices
// ['i', 'j') in the array list (which is handled by the object handler pointed
// to by 'ptr'), and returns -1 if 'n' is not present at the range of indices in
// the array list.
ptrdiff_t AL_int_index_of(const AL_int* ptr, ptrdiff_t i, ptrdiff_t j, int n);

// Returns true if the array list (which is handled by the object handler
// pointed to by 'ptr') is empty (i.e. if the size of the array list is 0), and
// returns false otherwise.
bool AL_int_is_empty(const AL_int* ptr);

// Returns the index of the last occurrence of 'n' at the range of indices
// ['i', 'j') in the array list (which is handled by the object handler pointed
// to by 'ptr'), and returns -1 if 'n' is not present at the range of indices in
// the array list.
ptrdiff_t AL_int_last_index_of(const AL_int* ptr, ptrdiff_t i, ptrdiff_t j,
                               int n);

// Removes the element at the index 'i' from the array list (which is handled by
// the object handler pointed to by 'ptr'), shifting elements as required.
// May cause the program to terminate due to 'index out of bounds' or 'out of
// memory'.
void AL_int_remove(AL_int* ptr, ptrdiff_t i);

// Removes the elements at the range of indices ['i', 'j') from the array list
// (which is handled by the object handler pointed to by 'ptr'), shifting
// elements as required.
// May cause the program to terminate due to 'index out of bounds' or 'out of
// memory'.
void AL_int_remove_range(AL_int* ptr, ptrdiff_t i, ptrdiff_t j);

// Assigns 'n' to the element at the index 'i' in the array list (which is
// handled by the object handler pointed to by 'ptr').
// May cause the program to terminate due to 'index out of bounds'.
void AL_int_set(AL_int* ptr, ptrdiff_t i, int n);

// Returns the size of the array list (which is handled by the object handler
// pointed to by 'ptr').
size_t AL_int_size(const AL_int* ptr);

// Copies the elements of the array list (which is handled by the object handler
// pointed to by 'ptr') to a malloc'ed array, and returns a pointer to its first
// element.
// May cause the program to terminate due to 'out of memory'.
int* AL_int_to_array(const AL_int* ptr);

////////////////////////////////////////////////////////////////////////////////

#endif

Array-List-int.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include "Array-List-int.h"

////////////////////////////////////////////////////////////////////////////////

#define AL_INT_INDEX_OUT_OF_BOUNDS "Array List (int) - Index out of Bounds"
#define AL_INT_CAPACITY_OVERFLOW "Array List (int) Capacity Overflow"
#define AL_INT_OUT_OF_MEMORY "Array List (int) - Out of Memory"

// Terminates the program after sending a diagnostic message to stderr.
// 'cause' points to the first character of a null-terminated string describing
// the cause of termination.
// 'caller' points to the first character of a null-terminated string
// representing the name of the calling function.
static void AL_int_exit(const char* cause, const char* caller)
{
    fprintf(stderr, "%s\n\tat %s()\n", cause, caller);
    fprintf(stderr, "\nExiting due to failure\n");

    exit(EXIT_FAILURE);
}

////////////////////////////////////////////////////////////////////////////////

const size_t AL_INT_MAX_CAPACITY = ((unsigned long long) PTRDIFF_MAX <
    (unsigned long long) SIZE_MAX / (unsigned long long) sizeof (int)) ?
        (size_t) PTRDIFF_MAX : SIZE_MAX / sizeof (int);

// Increases the capacity of the array list (which is handled by the object
// handler pointed to by 'ptr').
// May cause the program to terminate due to 'capacity overflow' or 'out of
// memory'.
static void AL_int_expand(AL_int* ptr)
{
    if (ptr->capacity > AL_INT_MAX_CAPACITY / (size_t) 2)
    {
        if (ptr->capacity < AL_INT_MAX_CAPACITY)
            ptr->capacity = AL_INT_MAX_CAPACITY;
        else
            AL_int_exit(AL_INT_CAPACITY_OVERFLOW, __func__);
    }

    else
    {
        ptr->capacity = ptr->capacity * (size_t) 2;
    }

    ptr->arr = (int*) realloc((void*) ptr->arr, ptr->capacity * sizeof (int));
    if ((void*) ptr->arr == NULL)
        AL_int_exit(AL_INT_OUT_OF_MEMORY, __func__);
}

// Reduces the capacity of the array list (which is handled by the object
// handler pointed to by 'ptr').
// May cause the program to terminate due to 'out of memory'.
static void AL_int_shrink(AL_int* ptr)
{
    if (ptr->size == (size_t) 0)
        ptr->capacity = (size_t) 1;
    else
        ptr->capacity = ptr->size;

    ptr->arr = (int*) realloc((void*) ptr->arr, ptr->capacity * sizeof (int));
    if ((void*) ptr->arr == NULL)
        AL_int_exit(AL_INT_OUT_OF_MEMORY, __func__);
}

////////////////////////////////////////////////////////////////////////////////

// Shifts (by copying) the elements of the array list (which is handled by the
// object handler pointed to by 'ptr') at the range of indices [l, ptr->size)
// left by 'offset' positions.
// It must be ensured that 'l' and 'offset' are valid.
static void AL_int_shift_left(const AL_int* ptr, ptrdiff_t l, ptrdiff_t offset)
{
    // for (ptrdiff_t k = l; k < (ptrdiff_t) ptr->size; ++k)
    //     (ptr->arr)[k - offset] = (ptr->arr)[k];

    memmove((void*) (ptr->arr + l - offset), (void*) (ptr->arr + l),
            (ptr->size - (size_t) l) * sizeof (int));
}

// Shifts (by copying) the elements of the array list (which is handled by the
// object handler pointed to by 'ptr') at the range of indices [l, ptr->size)
// right by 'offset' positions.
// It must be ensured that 'l' and 'offset' are valid, and that the capacity of
// the array list is large enough.
static void AL_int_shift_right(const AL_int* ptr, ptrdiff_t l, ptrdiff_t offset)
{
    // for (ptrdiff_t k = (ptrdiff_t) ptr->size - (ptrdiff_t) 1; k >= l; --k)
    //     (ptr->arr)[k + offset] = (ptr->arr)[k];

    memmove((void*) (ptr->arr + l + offset), (void*) (ptr->arr + l),
            (ptr->size - (size_t) l) * sizeof (int));
}

////////////////////////////////////////////////////////////////////////////////

void AL_int_create_empty(AL_int* ptr)
{
    ptr->arr = (int*) malloc(sizeof (int));
    if ((void*) ptr->arr == NULL)
        AL_int_exit(AL_INT_OUT_OF_MEMORY, __func__);

    ptr->size = (size_t) 0;
    ptr->capacity = (size_t) 1;
}

void AL_int_create_empty_with_initial_capacity(AL_int* ptr,
                                               size_t initial_capacity)
{
    ptr->arr = (int*) malloc(initial_capacity * sizeof (int));
    if ((void*) ptr->arr == NULL)
        AL_int_exit(AL_INT_OUT_OF_MEMORY, __func__);

    ptr->size = (size_t) 0;
    ptr->capacity = initial_capacity;
}

void AL_int_destroy(const AL_int* ptr)
{
    free((void*) ptr->arr);
}

////////////////////////////////////////////////////////////////////////////////

void AL_int_add(AL_int* ptr, int n)
{
    if (ptr->size == ptr->capacity)
        AL_int_expand(ptr);

    (ptr->arr)[(ptrdiff_t) ptr->size] = n;
    ++(ptr->size);
}

void AL_int_add_at_index(AL_int* ptr, ptrdiff_t i, int n)
{
    if ((i < (ptrdiff_t) 0) || (i > (ptrdiff_t) ptr->size))
        AL_int_exit(AL_INT_INDEX_OUT_OF_BOUNDS, __func__);

    if (ptr->size == ptr->capacity)
        AL_int_expand(ptr);

    AL_int_shift_right(ptr, i, (ptrdiff_t) 1);
    (ptr->arr)[i] = n;
    ++(ptr->size);
}

void AL_int_clear(AL_int* ptr)
{
    ptr->size = (size_t) 0;
    AL_int_shrink(ptr);
}

bool AL_int_contains(const AL_int* ptr, int n)
{
    for (ptrdiff_t i = (ptrdiff_t) 0; i < (ptrdiff_t) ptr->size; ++i)
        if ((ptr->arr)[i] == n)
            return true;

    return false;
}

int AL_int_get(const AL_int* ptr, ptrdiff_t i)
{
    if ((i < (ptrdiff_t) 0) || (i >= (ptrdiff_t) ptr->size))
        AL_int_exit(AL_INT_INDEX_OUT_OF_BOUNDS, __func__);

    return (ptr->arr)[i];
}

ptrdiff_t AL_int_index_of(const AL_int* ptr, ptrdiff_t i, ptrdiff_t j, int n)
{
    for (ptrdiff_t k = i; k < j; ++k)
        if ((ptr->arr)[k] == n)
            return k;

    return (ptrdiff_t) -1;
}

bool AL_int_is_empty(const AL_int* ptr)
{
    return ptr->size == (size_t) 0;
}

ptrdiff_t AL_int_last_index_of(const AL_int* ptr, ptrdiff_t i, ptrdiff_t j,
                               int n)
{
    for (ptrdiff_t k = j - (ptrdiff_t) 1; k >= i; --k)
        if ((ptr->arr)[k] == n)
            return k;

    return (ptrdiff_t) -1;
}

void AL_int_remove(AL_int* ptr, ptrdiff_t i)
{
    AL_int_remove_range(ptr, i, i + (ptrdiff_t) 1);
}

void AL_int_remove_range(AL_int* ptr, ptrdiff_t i, ptrdiff_t j)
{
    if ((i >= j) || (i < (ptrdiff_t) 0) || (j > (ptrdiff_t) ptr->size))
        AL_int_exit(AL_INT_INDEX_OUT_OF_BOUNDS, __func__);

    ptrdiff_t offset = j - i;

    AL_int_shift_left(ptr, j, offset);
    ptr->size -= (size_t) offset;

    if (ptr->size < ptr->capacity / 2)
        AL_int_shrink(ptr);
}

void AL_int_set(AL_int* ptr, ptrdiff_t i, int n)
{
    if ((i < (ptrdiff_t) 0) || (i >= (ptrdiff_t) ptr->size))
        AL_int_exit(AL_INT_INDEX_OUT_OF_BOUNDS, __func__);

    (ptr->arr)[i] = n;
}

size_t AL_int_size(const AL_int* ptr)
{
    return ptr->size;
}

int* AL_int_to_array(const AL_int* ptr)
{
    int* array = (int*) malloc(ptr->size * sizeof (int));
    if ((void*) ptr->arr == NULL)
        AL_int_exit(AL_INT_OUT_OF_MEMORY, __func__);

    for (ptrdiff_t i = (ptrdiff_t) 0; i < (ptrdiff_t) ptr->size; ++i)
        array[i] = (ptr->arr)[i];

    return array;
}

Test.c

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include "Array-List-int.h"

int main(void)
{
    AL_int a1;
    AL_int_create_empty(&a1);

    printf("---------------------------------------------------------------\n");

    printf(AL_int_is_empty(&a1) ? "true\n" : "false\n");

    printf("---------------------------------------------------------------\n");

    AL_int_add(&a1, 10);
    AL_int_add(&a1, 20);
    AL_int_add(&a1, 30);

    printf("%d\n", AL_int_get(&a1, 1));

    printf("---------------------------------------------------------------\n");

    printf(AL_int_is_empty(&a1) ? "true\n" : "false\n");

    printf("%zu\n", AL_int_size(&a1));

    printf("---------------------------------------------------------------\n");

    for (size_t i = 0; i < AL_int_size(&a1); ++i)
    {
        printf("%d ", AL_int_get(&a1, i));
    }

    printf("\n");

    printf("---------------------------------------------------------------\n");

    AL_int_set(&a1, (ptrdiff_t) 2, 40);

    for (size_t i = 0; i < AL_int_size(&a1); ++i)
    {
        printf("%d ", AL_int_get(&a1, i));
    }

    printf("\n");

    printf("---------------------------------------------------------------\n");

    AL_int_add_at_index(&a1, (ptrdiff_t) 1, 50);

    for (size_t i = 0; i < AL_int_size(&a1); ++i)
    {
        printf("%d ", AL_int_get(&a1, i));
    }

    printf("\n");

    printf("---------------------------------------------------------------\n");

    AL_int_clear(&a1);

    printf("%zu\n", AL_int_size(&a1));

    printf("---------------------------------------------------------------\n");

    AL_int_add(&a1, 10);
    AL_int_add(&a1, 20);
    AL_int_add(&a1, 30);
    AL_int_add(&a1, 40);
    AL_int_add(&a1, 10);
    AL_int_add(&a1, 20);

    for (size_t i = 0; i < AL_int_size(&a1); ++i)
    {
        printf("%d ", AL_int_get(&a1, i));
    }

    printf("\n");

    printf(AL_int_contains(&a1, 30) ? "true\n" : "false\n");

    printf(AL_int_contains(&a1, 50) ? "true\n" : "false\n");

    printf("---------------------------------------------------------------\n");

    for (size_t i = 0; i < AL_int_size(&a1); ++i)
    {
        printf("%d ", AL_int_get(&a1, i));
    }

    printf("\n");

    printf("%td\n", AL_int_index_of(&a1, (ptrdiff_t) 0,
                                    (ptrdiff_t) AL_int_size(&a1), 10));

    printf("%td\n", AL_int_index_of(&a1, (ptrdiff_t) 0,
                                    (ptrdiff_t) AL_int_size(&a1), 30));

    printf("%td\n", AL_int_index_of(&a1, (ptrdiff_t) 0,
                                    (ptrdiff_t) AL_int_size(&a1), 50));

    printf("---------------------------------------------------------------\n");

    for (size_t i = 0; i < AL_int_size(&a1); ++i)
    {
        printf("%d ", AL_int_get(&a1, i));
    }

    printf("\n");

    printf("%td\n", AL_int_last_index_of(&a1, (ptrdiff_t) 0,
                                         (ptrdiff_t) AL_int_size(&a1), 10));

    printf("%td\n", AL_int_last_index_of(&a1, (ptrdiff_t) 0,
                                         (ptrdiff_t) AL_int_size(&a1), 30));

    printf("%td\n", AL_int_last_index_of(&a1, (ptrdiff_t) 0,
                                         (ptrdiff_t) AL_int_size(&a1), 50));

    printf("---------------------------------------------------------------\n");

    AL_int_remove(&a1, (ptrdiff_t) 3);

    for (size_t i = 0; i < AL_int_size(&a1); ++i)
    {
        printf("%d ", AL_int_get(&a1, i));
    }

    printf("\n");

    printf("---------------------------------------------------------------\n");

    AL_int_remove_range(&a1, (ptrdiff_t) 1, (ptrdiff_t) 3);

    int* array = AL_int_to_array(&a1);

    for (size_t i = 0; i < AL_int_size(&a1); ++i)
    {
        printf("%d ", array[i]);
    }

    printf("\n");

    printf("---------------------------------------------------------------\n");

    free((void*) array);

    AL_int_destroy(&a1);

    return 0;
}

  • Later, I will add more functions such as AL_int_sort(), AL_int_index_of_binary_search(), etc.

  • I thought about freeing all allocated memory before exiting in AL_int_exit(), but then I couldn't figure out a way to free the memory pertaining to other array lists, or even other data structures, used within the same program. So, in the end, I decided to simply let exit() handle the freeing up of memory.

  • I made the initial capacity and the capacity after clearing equal to 1 in order to avoid dealing with malloc(0) and realloc(foo, 0).

  • I thought about not casting the return values of malloc() and realloc(), but I always use a compiler which is good enough to warn me about a missing #include <stdlib.h>. Also, with explicit casts, my program also works as a C++ program.

  • Instead of making a generic array list, I've made a basic array list of int's, as I'm still learning and will later delve into generic programming in C.

  • I decided to keep the object handlers on the stack (similar to the object handlers of std::vector's from C++). So, I was forced to define AL_int in the header file, instead of only declaring it.


Finally, I want to ask a question.

If we have a pointer to const struct, then we cannot modify the members of the struct using that pointer. But, if a member happens to be a pointer, then we can modify the pointed-to item.

For eg., AL_int_set() will work even if ptr is declared to be a pointer-to-const.

How are such situations dealt with?

\$\endgroup\$
3
  • \$\begingroup\$ "How are such situations dealt with?" --> Yes, even with void AL_int_set(const AL_int* ptr, ptrdiff_t i, int n) { ... (ptr->arr)[i] = n; }, (ptr->arr)[i] = n; is allowed. Please explain why that is a situation to deal with? Do you want member int* arr; to somehow become const int* arr; because ptr is now const AL_int* ptr? \$\endgroup\$ Commented Oct 31, 2023 at 10:20
  • \$\begingroup\$ @chux-ReinstateMonica, It does sound weird when you put it that way. Well, I just wanted to have some technique to allow a function to ONLY read from an array list, without having ANY write access to it whatsoever. \$\endgroup\$ Commented Oct 31, 2023 at 10:52
  • 2
    \$\begingroup\$ With AL_int_get(), you already have "function to ONLY read from an array list, without having ANY write access to it" by not writing to (ptr->arr)[i]. If you want the compiler to fail-to-compile on code like (ptr->arr)[i] = n;, then member .array needs to be const int *, which of course messes up functions that you do want to (ptr->arr)[i] = n;. As you have noticed, the const in const AL_int* ptr protects the members of AL_int, but not their "children". To get deep insight, search Stackoverflow for such a question. \$\endgroup\$ Commented Oct 31, 2023 at 11:07

2 Answers 2

4
\$\begingroup\$

A small review:

Only for the pedantic: PTRDIFF_MAX

OP's assignment of AL_INT_MAX_CAPACITY is interesting in a subtle way.

const size_t AL_INT_MAX_CAPACITY = ((unsigned long long) PTRDIFF_MAX <
    (unsigned long long) SIZE_MAX / (unsigned long long) sizeof (int)) ?
        (size_t) PTRDIFF_MAX : SIZE_MAX / sizeof (int);

Apparently OP wants to avoid signed integer overflow of code that stems from using ptrdiff_t. Whereas, in extreme cases, the size of an allocation can exceed PTRDIFF_MAX, as PTRDIFF_MAX may be less than SIZE_MAX. This is admirable, if pedantic.

Yet the right-hand side can be simplified.

  • Unnecessary cast:
const size_t AL_INT_MAX_CAPACITY = (/* (unsigned long long) */ PTRDIFF_MAX <
    /* (unsigned long long) */ SIZE_MAX / /* (unsigned long long) */ sizeof (int)) ?
        (size_t) PTRDIFF_MAX : SIZE_MAX / sizeof (int);
  • The first cast is not needed when sizeof(prtdiff_t) == sizeof(size_t) as in that case, as PTRDIFF_MAX is converted to size_t and then compared.

  • The first cast is not needed when sizeof(prtdiff_t) > sizeof(size_t) as in that case, as SIZE_MAX is converted to ptrdiff_t and then compared. In these 2 cases, the result is as desired without the cast.

  • Yet a cast is useful to quiet compiler warnings about comparing types of mixed, sign-ness, so OK, let us leave it in.

  • Leaving in the first cast, then there is no need for the 2nd cast, as SIZE_MAX is expected to have an unsigned type and comparing 2 types of the same sign-ness (after the usual promotions), yet different size is not an issue.

  • Like-wise for the 3rd cast. There is no need to cast as the quotient does not need to be type unsigned long long, size_t is sufficient.

Now we have:

const size_t AL_INT_MAX_CAPACITY = ((unsigned long long) PTRDIFF_MAX <
    SIZE_MAX / sizeof (int)) ?
        (size_t) PTRDIFF_MAX : SIZE_MAX / sizeof (int);

And perhaps even consider:

const size_t AL_INT_MAX_CAPACITY = ((uintmax_t) PTRDIFF_MAX <
    SIZE_MAX / sizeof (int)) ?
        (size_t) PTRDIFF_MAX : SIZE_MAX / sizeof (int);

FWIW, For ((unsigned long long) PTRDIFF_MAX < (unsigned long long) SIZE_MAX / (unsigned long long) sizeof (int)) to be true, would be a most unusual machine. As I see it, sizeof (int) would have to be 1. Possible, yet perhaps a unicorn?

So we could use:

const size_t AL_INT_MAX_CAPACITY = SIZE_MAX / sizeof (int);
\$\endgroup\$
4
\$\begingroup\$

Typically, library functions should not initiate program termination upon failure. None of your functions should be calling exit() (exit() never fails and never returns to its caller). Instead, consider modifying the library's design to return error codes when failures occur, allowing the caller to decide whether to exit or handle errors gracefully. This way you won't have to worry about freeing all allocated memory before exiting in AL_int_exit().

The documentation should include that the caller is responsible for ensuring that a valid pointer is passed in, and that failure to comply would likely result in undefined behavior (unexpected program termination and potential loss of data).


#define AL_INT_INDEX_OUT_OF_BOUNDS "Array List (int) - Index out of Bounds"
#define AL_INT_CAPACITY_OVERFLOW "Array List (int) Capacity Overflow"
#define AL_INT_OUT_OF_MEMORY "Array List (int) - Out of Memory"

These macros are better defined as:

static const char *const errors[] = {
    "Array List (int) - Index out of Bounds",
#define AL_INT_INDEX_OUT_OF_BOUNDS 0
    "Array List (int) Capacity Overflow",
#define AL_INT_CAPACITY_OVERFLOW   1
    "Array List (int) - Out of Memory"
#define AL_INT_OUT_OF_MEMORY       2
};

Now access them using:

errors[AL_INT_INDEX_OUT_OF_BOUNDS];
...

Or you could use an enum for the error codes.


There are a myriad of redundant casts in your code, which not only hamper readability but also add unnecessary complexity to the code. For instance, here:

if (ptr->size == (size_t) 0)
        ptr->capacity = (size_t) 1;
    else
        ptr->capacity = ptr->size;

there is no need to cast the values to size_t. Consider:

ptr->capacity = !ptr->size ? 1u : ptr->size;

// Or, a more straightforward version:
ptr->capacity = ptr->size ? ptr->size : 1u;

In the calls to memmove() and realloc(), the casts to void * are not needed, as there is an implicit conversion to and from a void * in C. Casting the return of malloc() and family is redundant too. In C++, you'd use something like a static_cast. Why, you won't even use realloc() in C++. C and C++ are different; don't mix them, and don't mix them up. Don't use a C++ compiler to compile C code. This is neither good C code, nor good C++ code.

Why are we casting ptr->sizeto ptrdiff_t everywhere in the codebase? If you want a ptrdiff_t, change the type of ptr->size from size_t to ptrdiff_t.


Code snippets like printf(AL_int_is_empty(&a1) ? "true\n" : "false\n") are used for informational purposes and do not actively verify correctness. Consider using assertions.


Simplify:

#if 0
     fprintf(stderr, "%s\n\tat %s()\n", cause, caller);
     fprintf(stderr, "\nExiting due to failure\n");
#else 
     fprintf(stderr, "%s\n\tat %s()\n"
                     "\nExiting due to failure\n",
                     cause, caller);
#endif

We don't require AL_int_create_empty() when AL_int_create_empty_with_capacity() provides the same functionality. Consider:

void AL_int_create_empty_with_capacity(AL_int* ptr, size_t initial_capacity)
{
    if (initial_capacity < 1) {
        initial_capacity = 1;  // Ensure a minimum capacity of 1.
    }
    
    ...

Also consider naming it to something better.

\$\endgroup\$
0

Not the answer you're looking for? Browse other questions tagged or ask your own question.