1

I have created multiple arrays of strings during runtime and have knowledge of the number of arrays that need to be stored.

I want to store them or point to them. I am not sure how to do this or about the syntax and cannot find tutorials. I believe this can be done with struct or char** []?

I essentially want this behavior except not creating the string arrays till run time.

  struct arrayholder{
   const char **argv;
  };

  char *str_arr1[] = { "sdfddsf", "foo1", 0 };
  char *str_arr2[] = { "sdsosdfrt", "foo2", 0 };
  char *str_arr3[] = { "grsdsfdep", "foo3", 0 };


  struct arrayholder the_arrays [] = { {str_arr1},{str_arr2},{str_arr3} };

//      The contents should look like,
//         the_arrays[0] ==> {str_arr1}
//         the_arrays[0].argv[0] ==> "sdfddsf"
//         the_arrays[0].argv[1] ==> "foo1"
Marcus
  • 29
  • 3

3 Answers3

1

You could use a 3D array, like in this case: C 3d Array of char.

Or, you could use a simple linked list, where every node is a 2D array.

gsamaras
  • 71,951
  • 46
  • 188
  • 305
  • Thanks for the solution, sorry to get more complicated ...but for this example, how does a 3d array of char work with different lengths of string arrays ? What I mean is for the these dimensions --> ...`3darray [][3][2]` means an array with "three string arrays of length two" but what if we wanted different array lengths? (...this dimension --> `[][3][differentlengths]`would be different for each) is the only solution to choose a reasonable size that would suffice for any string array length determined during runtime? – Marcus Oct 15 '18 at 23:34
0

At the point where you know the number of arrays, you can create a 2D array of char*. Or more conveniently, a pointer to the first element of such an array. The first element will be an array of type char* [n] and so a pointer to it will be of type char* (*)[n]. Example:

char* (*str_table)[y] = malloc(sizeof (char*[x][y]));

Then populate this 2D array by calling a malloc per item. Example:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <assert.h>

static char* random_string (void)
{
  static char str[10+1];
  size_t size = rand()%10 + 1;
  size_t i;

  for(i=0; i<size; i++)
  {
    str[i] = rand() % ('Z' - 'A' + 1) + 'A';
  }
  str[i] = '\0';
  return str;
}

int main (void)
{
  srand(time(NULL));

  size_t x = 3;
  size_t y = 2;

  char* (*str_table)[y] = malloc(sizeof (char*[x][y]));
  assert(str_table);

  for(size_t i=0; i<x; i++)
  {
    for(size_t j=0; j<y; j++)
    {
      char* str = random_string();
      size_t str_size = strlen(str)+1;
      str_table[i][j] = malloc(str_size);
      assert(str_table[i][j]);
      strcpy(str_table[i][j], str);

      printf("%s ", str_table[i][j]);
    }
    printf("\n");
  }


  free(str_table);
}

This prints 3 rows with 2 random strings per row.

Lundin
  • 195,001
  • 40
  • 254
  • 396
0

You as the programmer need to decide how will you store the data. You have multiple options, bear in mind that:

  1. 'string' is an array characters stored terminated with a zero byte, ie. '\0'
  2. You need to store how many array elements there are in an array.
  3. In case of 'strings' the number of elements in an array / the number of characters in a string may be computed by counting the number of characters until the zero byte '\0' is encountered.
  4. For all other arrays you have two options.
    1. You explicitly set the last array member to NULL/zero, like you did in case of str_arr1[] = { "sdfddsf", "foo1", 0 };. When copying such array you need to remember to explicitly set the last member to zero. In this case you will often see a magic + 1 in code when calculating size, like malloc(sizeof(char) * (strlen(string) + 1)).
    2. You can store the array size in a separate variable, typically of size_t type.
  5. You have 3 arrays of strings, ie. three arrays terminated by NULL of array terminated by zero byte of characters.
  6. You want to have an array of arrays of strings. You need to decide how will you track the size of that array and the size of inside arrays.
  7. I don't like having an explicit array termination value. I like storing sizes of an array using separate variable of type size_t. Usually I wrap a pointer to the array and the variable that stores the size in one structure. That way I can write separate libraries for each level of array.
  8. Please don't decide for char ***var;. It makes code unreadable.

#define _GNU_SOURCE  1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void store_pointers_to_arrys_of_strings_null_delimeterd(
    const char * const * const strings_to_copy[], size_t strings_to_copy_len)
{
    const char * const **the_arrays;

    // alloc memory
    const size_t the_arrays_len = strings_to_copy_len + 1;
    the_arrays = malloc(sizeof(the_arrays) * the_arrays_len);
    if (the_arrays == NULL) {
        perror("malloc failed\n");
        abort();
    }

    // copy pointers
    for (size_t i = 0; i < strings_to_copy_len; ++i) {
        the_arrays[i] = strings_to_copy[i];
    }
    the_arrays[strings_to_copy_len] = NULL;

    for (size_t i = 0; the_arrays[i] != NULL; ++i) {
        for (size_t j = 0; the_arrays[i][j] != NULL; ++j) {
            printf("%s: %zu %zu %s\n", __func__, i, j, the_arrays[i][j]);
        }
    }

    free(the_arrays);
}

void store_strings_themselves_as_arrays_of_strings_null_delimeterd(
    const char * const * const strings_to_copy[], size_t strings_to_copy_len)
{
    const char ***the_arrays;

    // allocate for arrays
    const size_t the_arrays_len = strings_to_copy_len + 1;
    the_arrays = malloc(sizeof(the_arrays) * the_arrays_len);
    if (the_arrays == NULL) {
        perror("malloc failed\n");
        abort();
    }

    // copy strings
    for (size_t i = 0; i < strings_to_copy_len; ++i) {

        size_t cnt = 1;
        for (size_t j = 0; strings_to_copy[i][j] != NULL; ++j) {
            ++cnt;
        }

        the_arrays[i] = malloc(sizeof(the_arrays[i]) * cnt);
        memcpy(the_arrays[i], strings_to_copy[i], cnt * sizeof(the_arrays[i]));
    }
    the_arrays[strings_to_copy_len] = NULL;

    // print
    for (size_t i = 0; the_arrays[i] != NULL; ++i) {
        for (size_t j = 0; the_arrays[i][j] != NULL; ++j) {
            printf("%s: %zu %zu %s\n", __func__, i, j, the_arrays[i][j]);
        }
    }

    // free
    for (size_t i = 0; the_arrays[i] != NULL; ++i) {
        free(the_arrays[i]);
    }
    free(the_arrays);
}

void store_strings_in_array_of_strings_in_array_of_array_of_strings_null_delimeterd(
    const char * const * const strings_to_copy[], size_t strings_to_copy_len)
{
    char ***the_arrays;

    // allocate for arrays
    const size_t the_arrays_len = strings_to_copy_len + 1;
    the_arrays = malloc(sizeof(the_arrays) * the_arrays_len);
    if (the_arrays == NULL) {
        perror("malloc failed\n");
        abort();
    }

    // copy string pointers
    for (size_t i = 0; i < strings_to_copy_len; ++i) {

        size_t cnt = 0;
        for (size_t j = 0; strings_to_copy[i][j] != NULL; ++j) {
            ++cnt;
        }

        // allocate memory for string pointers
        const size_t the_arrays_i_len = cnt + 1;
        the_arrays[i] = malloc(sizeof(the_arrays[i]) * the_arrays_i_len);
        if (the_arrays[i] == NULL) { perror("AA"); abort(); }

        // copy the strings themselves
        for (size_t k = 0; k < cnt; ++k) {
            the_arrays[i][k] = strdup(strings_to_copy[i][k]);
            if (the_arrays[i][k] == NULL) { perror("AA"); abort(); }
        }
        the_arrays[i][cnt] = NULL;
    }
    the_arrays[strings_to_copy_len] = NULL;

    // print
    for (size_t i = 0; the_arrays[i] != NULL; ++i) {
        for (size_t j = 0; the_arrays[i][j] != NULL; ++j) {
            printf("%s: %zu %zu %s\n", __func__, i, j, the_arrays[i][j]);
        }
    }

    // free
    for (size_t i = 0; the_arrays[i] != NULL; ++i) {
        for (size_t j = 0; the_arrays[i][j] != NULL; ++j) {
            free(the_arrays[i][j]);
        }
        free(the_arrays[i]);
    }
    free(the_arrays);
}

void store_pointers_to_arrays_of_strings_in_a_struct(
    const char * const * const strings_to_copy[], size_t strings_to_copy_len)
{
    struct arrays_of_array_of_strings_s {
        const char * const **strs;
        size_t strscnt;
    };

    const size_t the_arrays_cnt = strings_to_copy_len;
    struct arrays_of_array_of_strings_s the_arrays;
    the_arrays.strs = malloc(sizeof(the_arrays.strs[0]) * the_arrays_cnt);
    if (the_arrays.strs == NULL) {
        perror("malloc failed\n");
        abort();
    }

    // fill array
    for (size_t i = 0; i < strings_to_copy_len; ++i) {
        the_arrays.strs[i] = strings_to_copy[i];
    }
    the_arrays.strscnt = the_arrays_cnt;

    // print
    for (size_t i = 0; i < the_arrays.strscnt; ++i) {
        for (size_t j = 0; the_arrays.strs[i][j] != NULL; ++j) {
            printf("%s: %zu %zu %s\n", __func__, i, j, the_arrays.strs[i][j]);
        }
    }

    // free
    free(the_arrays.strs);
    the_arrays.strscnt = 0;
}


void store_pointers_to_strings_in_array_of_structs(
    const char * const * const strings_to_copy[], size_t strings_to_copy_len)
{
    struct array_of_strings_s {
        const char * const *strs;
    };

    const size_t the_arrays_cnt = strings_to_copy_len;
    struct array_of_strings_s * const the_arrays = malloc(sizeof(the_arrays[0]) * the_arrays_cnt);
    if (the_arrays == NULL) {
        perror("malloc failed\n");
        abort();
    }

    // fill array
    for (size_t i = 0; i < strings_to_copy_len; ++i) {
        the_arrays[i].strs = strings_to_copy[i];
    }

    // print
    for (size_t i = 0; i < the_arrays_cnt; ++i) {
        for (size_t j = 0; the_arrays[i].strs[j] != NULL; ++j) {
            printf("%s: %zu %zu %s\n", __func__, i, j, the_arrays[i].strs[j]);
        }
    }

    // free
    free(the_arrays);
}

void store_strings_in_an_array_of_structs_delimeterd_by_null_of_arrays_of_strings_delimeterd_by_null(
    const char * const * const strings_to_copy[], size_t strings_to_copy_len)
{
    struct array_of_strings_s {
        const char * *arrstrs;
    };

    const size_t the_arrays_cnt = strings_to_copy_len + 1;
    struct array_of_strings_s * const the_arrays = malloc(sizeof(the_arrays[0]) * the_arrays_cnt);
    if (the_arrays == NULL) {
        perror("malloc failed\n");
        abort();
    }

    // fill array
    for (size_t i = 0; i < strings_to_copy_len; ++i) {

        size_t cnt = 0;
        for (size_t j = 0; strings_to_copy[i][j] != NULL; ++j) {
            ++cnt;
        }

        const size_t the_arrays_i_strs_cnt = cnt + 1;
        the_arrays[i].arrstrs = malloc(sizeof(the_arrays[i].arrstrs[0]) * cnt);
        if (the_arrays[i].arrstrs == NULL) { perror("AA"); abort(); }

        for (size_t k = 0; k < cnt; ++k) {
            the_arrays[i].arrstrs[k] = strings_to_copy[i][k];
        }
        the_arrays[i].arrstrs[cnt] = NULL;
    }
    the_arrays[strings_to_copy_len].arrstrs = NULL;

    // print
    for (size_t i = 0; the_arrays[i].arrstrs != NULL; ++i) {
        for (size_t j = 0; the_arrays[i].arrstrs[j] != NULL; ++j) {
            printf("%s: %zu %zu %s\n", __func__, i, j, the_arrays[i].arrstrs[j]);
        }
    }

    // free
    for (size_t i = 0; the_arrays[i].arrstrs != NULL; ++i) {
        free(the_arrays[i].arrstrs);
    }
    free(the_arrays);
}

void store_strings_in_array_of_strings_in_array_of_array_of_strings_in_a_struct(
    const char * const * const strings_to_copy[], size_t strings_to_copy_len)
{
    struct string_s {
        char *str;
    };
    struct array_of_strings_s {
        struct string_s *strs;
        size_t strscnt;
    };
    struct array_of_arrays_of_strings_s {
        struct array_of_strings_s *arrstrs;
        size_t arrstrscnt;
    };

    struct array_of_arrays_of_strings_s the_arrays;

    // fill array
    the_arrays.arrstrscnt = strings_to_copy_len;
    the_arrays.arrstrs = malloc(sizeof(the_arrays.arrstrs[0]) * the_arrays.arrstrscnt);
    if (the_arrays.arrstrs == NULL) { perror("malloc failed\n"); abort(); }

    for (size_t i = 0; i < the_arrays.arrstrscnt; ++i) {
        struct array_of_strings_s * const array_of_strings = &the_arrays.arrstrs[i];

        size_t cnt = 0;
        for (size_t j = 0; strings_to_copy[i][j] != NULL; ++j) {
            ++cnt;
        }

        array_of_strings->strscnt = cnt;
        array_of_strings->strs = malloc(sizeof(array_of_strings->strs[0]) * array_of_strings->strscnt);
        if (array_of_strings->strs == NULL) { perror("AA"); abort(); }


        for (size_t k = 0; k < array_of_strings->strscnt; ++k) {
            struct string_s * const string = &array_of_strings->strs[k];

            string->str = strdup(strings_to_copy[i][k]);
            if (string->str == NULL) { perror("AA"); abort(); }
        }

    }

    // print
    for (size_t i = 0; i < the_arrays.arrstrscnt; ++i) {
        for (size_t j = 0; j < the_arrays.arrstrs[i].strscnt; ++j) {
            printf("%s: %zu %zu %s\n", __func__, i, j, the_arrays.arrstrs[i].strs[j].str);
        }
    }

    // free
    for (size_t i = 0; i < the_arrays.arrstrscnt; ++i) {
        struct array_of_strings_s * const array_of_strings = &the_arrays.arrstrs[i];

        for (size_t j = 0; j < array_of_strings->strscnt; ++j) {
            struct string_s * const string = &array_of_strings->strs[i];

            free(string->str);
        }

        free(array_of_strings->strs);
    }
    free(the_arrays.arrstrs);
}

int main()
{
    // "blabla" is a string literal - it's a immutable sting
    const char * const str_arr1[] = { "sdfddsf", "foo1", NULL };
    const char * const str_arr2[] = { "sdsosdfrt", "foo2", NULL };
    const char * const str_arr3[] = { "grsdsfdep", "foo3", NULL };
    // or char `str_arr1[][] = { .... };`
    // string literal are immutable, read-only
    // so `const char *var = "abc";` or string initialization like `char var[] = "abc";`

    // just an array of strings to copy
    const char * const * const strings_to_copy[] = { str_arr1, str_arr2, str_arr3 };
    const size_t strings_to_copy_len = sizeof(strings_to_copy)/sizeof(strings_to_copy[0]);

    store_pointers_to_arrys_of_strings_null_delimeterd(
        strings_to_copy, strings_to_copy_len);

    store_strings_themselves_as_arrays_of_strings_null_delimeterd(
        strings_to_copy, strings_to_copy_len);

    store_strings_in_array_of_strings_in_array_of_array_of_strings_null_delimeterd(
        strings_to_copy, strings_to_copy_len);

    store_pointers_to_arrays_of_strings_in_a_struct(
        strings_to_copy, strings_to_copy_len);

    store_strings_in_an_array_of_structs_delimeterd_by_null_of_arrays_of_strings_delimeterd_by_null(
        strings_to_copy, strings_to_copy_len);

    store_pointers_to_strings_in_array_of_structs(
        strings_to_copy, strings_to_copy_len);

    return 0;
}

Will output:

store_pointers_to_arrys_of_strings_null_delimeterd: 0 0 sdfddsf                                                                                                                    
store_pointers_to_arrys_of_strings_null_delimeterd: 0 1 foo1                                                                                                                       
store_pointers_to_arrys_of_strings_null_delimeterd: 1 0 sdsosdfrt                                                                                                                  
store_pointers_to_arrys_of_strings_null_delimeterd: 1 1 foo2                                                                                                                       
store_pointers_to_arrys_of_strings_null_delimeterd: 2 0 grsdsfdep                                                                                                                  
store_pointers_to_arrys_of_strings_null_delimeterd: 2 1 foo3                                                                                                                       
store_strings_themselves_as_arrays_of_strings_null_delimeterd: 0 0 sdfddsf                                                                                                         
store_strings_themselves_as_arrays_of_strings_null_delimeterd: 0 1 foo1                                                                                                            
store_strings_themselves_as_arrays_of_strings_null_delimeterd: 1 0 sdsosdfrt                                                                                                       
store_strings_themselves_as_arrays_of_strings_null_delimeterd: 1 1 foo2                                                                                                            
store_strings_themselves_as_arrays_of_strings_null_delimeterd: 2 0 grsdsfdep                                                                                                       
store_strings_themselves_as_arrays_of_strings_null_delimeterd: 2 1 foo3                                                                                                            
store_strings_in_array_of_strings_in_array_of_array_of_strings_null_delimeterd: 0 0 sdfddsf                                                                                        
store_strings_in_array_of_strings_in_array_of_array_of_strings_null_delimeterd: 0 1 foo1                                                                                           
store_strings_in_array_of_strings_in_array_of_array_of_strings_null_delimeterd: 1 0 sdsosdfrt                                                                                      
store_strings_in_array_of_strings_in_array_of_array_of_strings_null_delimeterd: 1 1 foo2                                                                                           
store_strings_in_array_of_strings_in_array_of_array_of_strings_null_delimeterd: 2 0 grsdsfdep                                                                                      
store_strings_in_array_of_strings_in_array_of_array_of_strings_null_delimeterd: 2 1 foo3                                                                                           
store_pointers_to_arrays_of_strings_in_a_struct: 0 0 sdfddsf                                                                                                                       
store_pointers_to_arrays_of_strings_in_a_struct: 0 1 foo1                                                                                                                          
store_pointers_to_arrays_of_strings_in_a_struct: 1 0 sdsosdfrt                                                                                                                     
store_pointers_to_arrays_of_strings_in_a_struct: 1 1 foo2                                                                                                                          
store_pointers_to_arrays_of_strings_in_a_struct: 2 0 grsdsfdep                                                                                                                     
store_pointers_to_arrays_of_strings_in_a_struct: 2 1 foo3                                                                                                                          
store_strings_in_an_array_of_structs_delimeterd_by_null_of_arrays_of_strings_delimeterd_by_null: 0 0 sdfddsf                                                                       
store_strings_in_an_array_of_structs_delimeterd_by_null_of_arrays_of_strings_delimeterd_by_null: 0 1 foo1                                                                          
store_strings_in_an_array_of_structs_delimeterd_by_null_of_arrays_of_strings_delimeterd_by_null: 1 0 sdsosdfrt                                                                     
store_strings_in_an_array_of_structs_delimeterd_by_null_of_arrays_of_strings_delimeterd_by_null: 1 1 foo2                                                                          
store_strings_in_an_array_of_structs_delimeterd_by_null_of_arrays_of_strings_delimeterd_by_null: 2 0 grsdsfdep                                                                     
store_strings_in_an_array_of_structs_delimeterd_by_null_of_arrays_of_strings_delimeterd_by_null: 2 1 foo3                                                                          
store_pointers_to_strings_in_array_of_structs: 0 0 sdfddsf                                                                                                                         
store_pointers_to_strings_in_array_of_structs: 0 1 foo1                                                                                                                            
store_pointers_to_strings_in_array_of_structs: 1 0 sdsosdfrt                                                                                                                       
store_pointers_to_strings_in_array_of_structs: 1 1 foo2                                                                                                                            
store_pointers_to_strings_in_array_of_structs: 2 0 grsdsfdep                                                                                                                       
store_pointers_to_strings_in_array_of_structs: 2 1 foo3                       

Live version available at onlinegdb.

KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • Each time we use 3 levels of indirection, an adorable little kitten dies. Please check [Correctly allocating multi-dimensional arrays](https://stackoverflow.com/questions/42094465/correctly-allocating-multi-dimensional-arrays) for better alternatives. – Lundin Oct 15 '18 at 09:58