Skip to main content
3 of 5
added 18 characters in body
Jamal
  • 35.2k
  • 13
  • 134
  • 238

Automation of array allocation in C

I recently have been working on moving from projects written in Java and C++ to C and realized that the lack of std::vector in C makes it a bit more difficult to create multi-dimensional arrays. Also, without the std::string in C, it's harder to store arrays of strings and keep track of their sizes. To automate the process, I created a typedef'd struct: varray. varray attempts to emulate some basic functions of std::vector, e.g. storing sizes of the elements in an easily accessible location and allowing for allocation of multi-dimensional objects with "relative ease".

I've attached the header "varray.h" and a bit of sample code with comments to demonstrate its use. Please note that at this point, I haven't tried to dynamically expand the arrays, though I'll look into it as I practice more with the language.

If there is any feedback (syntax, memory allocation, anything), I'd greatly appreciate it - I'm still just getting started with C, C++, and Java.

varray.h

#ifndef VARRAY_H
#define VARRAY_H
#define VARRAY_DISPLAYDEBUG 0
#define VARRAY_STRSIZE(x) ((sizeof(char) * strlen(x)) + 1)
#include <stdlib.h>
#include <string.h>
#include <stdio.h> 

typedef struct {
    int* getInt;
    char* getChar;
    double* getDbl;
    char* str;
    int size;
} varray;

typedef enum {
    v_char = 0,
    v_int = 1,
    v_double = 2,
    v_varray = 3,
    v_tdvarray = 4
} varrayType;

void* createArray(varrayType arrayType, int arraySize);
varray varrayPush(const char* data);
varray allocateNumArray(varrayType type, const int size);
varray allocateCharArray(const int size);
varray* allocateVarray(const int size);
varray** allocateTDVarray(const int size);

inline void* createArray(varrayType arrayType, int arraySize) {
    varray* target;
    varray** vtarget;
    varray*** tdvtarget;
    if (arrayType == v_char) {
        target = malloc(sizeof(varray) * arraySize);
        *target = allocateCharArray(arraySize);
        return target;
    }
    else if (arrayType == v_int || arrayType == v_double) {
        target = malloc(sizeof(varray) * arraySize);
        *target = allocateNumArray(arrayType, arraySize);
        return target;
    }
    else if (arrayType == v_varray) {
        vtarget = malloc(sizeof(varray*) * arraySize);
        *vtarget = allocateVarray(arraySize);
        return *vtarget;
    }
    else if (arrayType == v_tdvarray) {
        tdvtarget = malloc(sizeof(varray**) * arraySize);
        *tdvtarget = allocateTDVarray(arraySize);
        return *tdvtarget;
    }
    else {
        return NULL;
    }
}   

inline varray varrayPush(const char* data) {
    varray target;
    if (VARRAY_DISPLAYDEBUG) {
        printf("%s%d%s%s\n", "Allocating array with size: ", (int)VARRAY_STRSIZE(data) - 1, " with contents: ", data);
    }
    target.str = malloc(VARRAY_STRSIZE(data));
    strcpy(target.str, data);
    if (VARRAY_DISPLAYDEBUG) {
        printf("%s%s\n", "String created successfully. Contents reads: ", target.str);
        printf("%s%d\n", "Memory address: ", (int)target.str);
    }
    target.size = VARRAY_STRSIZE(data);
    return target;
}

inline varray allocateNumArray(varrayType type, const int size) {
    int i;
    varray target;
    if (type == v_int) {
        if (VARRAY_DISPLAYDEBUG) {
            printf("%s%d\n", "Allocating array of type v_int with size ", size);
        }
        target.getInt = malloc(sizeof(int) * size);
        for (i = 0; i < size; i++) {
            target.getInt[i] = 0;
        }
    }
    else if (type == v_double) {
        if (VARRAY_DISPLAYDEBUG) {
            printf("%s%d\n", "Allocating array of type v_double with size ", size);
        }
        target.getDbl = malloc(sizeof(double) * size);
        for (i = 0; i < size; i++) {
            target.getDbl[i] = 0.0;
        }
    }
    target.size = size;
    return target;
}

inline varray allocateCharArray(const int size) {
    varray target;
    int i;
    if (VARRAY_DISPLAYDEBUG) {
         printf("%s%d\n", "Allocating array of type v_char with size ", size);
    }
    target.getChar = malloc(sizeof(char) * size);
    for (i = 0; i < size; i++) {
        target.getChar[i] = 0;
    }
    target.size = size;
    return target;
}

inline varray* allocateVarray(const int size) {
    varray* target = malloc(sizeof(varray) * (size + 1));
    if (VARRAY_DISPLAYDEBUG) {
        printf("%s%d\n", "Allocated array of type v_varray with size ", size);
    }
    target[0].size = size;
    return target;
}

inline varray** allocateTDVarray(const int size) {
    varray** target = malloc(sizeof(varray*) * (size + 1));
    if (VARRAY_DISPLAYDEBUG) {
        printf("%s%d\n", "Allocated array of type v_tdvarray with size ", size);
    }
    target[0] = malloc(sizeof(varray));
    target[0][0].size = size;
    return target;
}

#endif

Example implementation

#include "varray.h"

void varrayDemo() {
    varray sampleInt;
    /*
     * Functions as a normal array but also stores
     * the size of the array at sampleInt.size.
     * Accessing data elements used the syntax
     * VARRAY.getInt[POSITION]
     */
    varray* sampleString;
    /*
     * Since a C string is an array of char, creating
     * an array of varray objects can easily store multiple
     * strings. The size of the varray array can be accessed
     * via VARRAY[0].size Individual elements can be 
     * accessed at VARRAY[POSITION].str with position >= 1
     */
    varray** sampleContainer;
    /*
     * The container of varrays can store multiple data types
     * at each element's position. For example, the syntax
     * VARRAY[1][1].str will return the string, if stored.
     * while VARRAY[2][2].getDbl[POSITION] returns the double
     */
    sampleInt = *(varray*)createArray(v_int, 5);
    /*
     * The function createArray(TYPE, SIZE) initializes the array
     * NOTE: I am not sure if there is a more "aesthetically pleasing"
     * way to initialize the array, since createArray returns a void
     * pointer. When initialized, all values are set to 0
     */
    printf("The size of sampleInt is: %d\n", sampleInt.size);
    printf("The data at position 0 is: %d\n", sampleInt.getInt[0]);

    sampleString = createArray(v_varray, 2);
    /*
     * Each varray can contain one string. Initialize each element
     * with the createArray command.
     */
    printf("The size of sampleString is: %d\n", sampleString[0].size);
    /*
     * As noted above, the size is stored in a varray at position 0
     * in the container
     */
    sampleString[1] = varrayPush("This is a sample string");
    sampleString[2] = varrayPush("This is also a sample string!"); 
    /*
     * To store a string within a varray, the function varrayPush is used
     * with the desired string as the argument. The function initializes
     * another varray object within the container.
     */
    printf("The string at position 1 is: %s\n", sampleString[1].str);
    printf("The size of the string stored at position 1 is: %d\n", sampleString[1].size); 
    printf("The char at position 5 in sampleString[1] is: %c\n", sampleString[1].str[5]);

    sampleContainer = createArray(v_tdvarray, 2);
    sampleContainer[1] = createArray(v_varray, 1);
    sampleContainer[1][1] = *(varray*)createArray(v_double, 5);
    sampleContainer[1][1].getDbl[4] = 3.14;
    sampleContainer[2] = createArray(v_varray, 1);
    sampleContainer[2][1] = varrayPush("yet another sample string");
    /*
     * As noted with the original sampleInt example, the *(varray*)
     * syntax is used again.
     */
    printf("sampleContainer[1][1] has size: %d with data %lf at position 4\n", sampleContainer[1][1].size, sampleContainer[1][1].getDbl[4]);
    printf("sampleContainer[2][1] has size %d with string \"%s\"\n", sampleContainer[2][1].size, sampleContainer[2][1].str); 
}