2
\$\begingroup\$

I've been trying to remember old programming concepts like data structures.

My first challenge was about implementing my own array of positive integers:

#include<stdio.h>
#include<stdlib.h>

int* array_initialize(int size) {
    return calloc(size, size * sizeof(int));
}

void array_set(int *array, int index, int value) {
    array += index;
    *array = value;
}

int array_get(int *array, int index) {
    array += index;
    return *array;
}

int array_size(int *array) {
    int size = 0;
    while(*array != 0) {
        size++;
        array++;
    }
    return size;
}

int main() {
    int* array = array_initialize(5);

    array_set(array, 0, 2);
    array_set(array, 1, 4);
    array_set(array, 2, 6);
    array_set(array, 3, 8);
    array_set(array, 4, 10);

    for (int i = 0; i < array_size(array); i++) {
        printf("Index #%d -> %d\n", i, array_get(array, i));
    }
    printf("\nSize: %d\n", array_size(array));

    free(array);

    return 0;
}

Can I call it an array?

Is it a good starting point? What would you change?

Can I implement my custom array with [] to read/write values to my array?

\$\endgroup\$
0

3 Answers 3

3
\$\begingroup\$

Your array set could be written as *(array + index) = value; and your array get could be written as return *(array + index);.

array[index] could also be written as *(array + index). Those two functions don't do anything you can't already do with [].

\$\endgroup\$
2
  • \$\begingroup\$ Why do you suggest to write *(array + index) instead of the much more beautiful array[index]? \$\endgroup\$ Commented Dec 3, 2019 at 16:30
  • \$\begingroup\$ I don't. I was trying to illustrate the point that those two functions were the equivalent of writing array[index] \$\endgroup\$ Commented Dec 3, 2019 at 16:44
3
\$\begingroup\$

Is it a good starting point?

A starting point for what? The only obvious purpose for code like this is to provide error checking for accidentally indexing out of bounds, but this code doesn't do that.

And note that free(array) makes assumptions about the implementation. There should be an array_free(array) function that can be internally changed in future development without affecting any calling code.

Can I implement my custom array with [] to read/write values to my array?

Yes, but that's exactly what you don't want to do.

The most significant problem with this design is that it returns a value that the caller can use with […]. That allows it to bypass the error checking (which I assume will eventually be added).

The type returned should be something that is incompatible with accidental misuse. If someone calls array_set() with an argument that wasn't created by array_initialize(), you want that mistake caught at compile time, not during critical execution time.

Can I call it an array?

Yes, but it's not a generic array; it is an array of (int).

And, note that storing a zero value in the array will break array_size().


You're going in the right direction, but started on the wrong foot.

Define your basic data structure first, and then the rest of the design should flow naturally from that. e.g.

typedef struct {
    size_t size;
    size_t used;
    int *value;
} intArray;

Now it becomes obvious that the initialization function needs to allocate *value and should return a pointer to an intArray structure:

    intArray *
intArray_initialize(int size) {
    …
}

Then in intArray_set() and intArray_get() add code to verify that 0 <= index < size.

And intArray_size() becomes trivial: array->used.

Finally, intArray_free(intArray *array) will need to free both array->value and *array.

\$\endgroup\$
2
  • \$\begingroup\$ Just a small addition. This data structure is exactly what is called a slice in the Go programming language. Here's an article with more details. \$\endgroup\$ Commented Dec 3, 2019 at 16:33
  • \$\begingroup\$ @RolandIllig, interesting. A reading of that article suggests that the next thing the OP can add to the module is a function to grow an existing array. Then perhaps an addAnElementAndGrowIfNecessary() function. \$\endgroup\$ Commented Dec 4, 2019 at 1:35
1
\$\begingroup\$

Error Checking

When using malloc(), calloc() or realloc() in C make sure to check the return value before using the memory, if any of these functions fail they return NULL. Accessing memory through a NULL pointer causes unknown behavior, the best you can hope for is the program crashes, the worst in the point corrupts memory yielding incorrect results.

int* array_initialize(int size) {
    int *array = calloc(size, size * sizeof(int));
    if (array == NULL)
    {
        fprintf(stderr, "calloc failed in array_initialize\n");
        exit(EXIT_FAILURE);
    }
}

The function array_size() is not safe, if the array is completely full, the pointer will walk off the end of the array and into other memory. The code should pass the maximum size of the array into the function as well and limit the while loop.

A better implementation might be to use a struct that contains the size of the memory allocated as well as the memory allocated.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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