0

The project I am currently working on requires four dynamically created arrays of strings (each string no longer than 50 chars). So, I am trying to write a function that takes a pointer to a pointer and dynamically allocates memory for that variable.

This is as far as I've gotten:

void makeArray(char*** arr[], int n) {
  int i;
  *arr = malloc(n*sizeof(char*));
  for (i = 0; i<n; i++) {
    *arr[i] = malloc(sizeof(char)*50);
  }
}

int main() {

  char** test;

  makeArray(&test,4);

  return 0;
}

When I compile and run, I get this error:

main.c:16:13: warning: passing argument 1 of ‘makeArray’ from incompatible pointer type [-Wincompatible-pointer-types]
   makeArray(&test,4);
             ^
main.c:4:6: note: expected ‘char ****’ but argument is of type ‘char ***’
 void makeArray(char*** arr[], int n) {

When I use C Tutor, the function appears to successfully take in my test array and allocate 4 pointer slots. Then it successfully allocates 50 chars to the 0th test slot. However, when the loop runs again, I get an error. enter image description here

I've been stuck on this for two days now, so I welcome any suggestions the kind users of Stack Overflow may have!

chadathin
  • 73
  • 1
  • 1
  • 8
  • OMG, a three star programmer! Question: why does the function return void? – wildplasser Feb 05 '20 at 23:14
  • When I see `char*** x[]` I see four stars. That's a ridiculous level of pointerism. Try and keep it to two at most, three if absolutely, unfortunately necessary. – tadman Feb 05 '20 at 23:16
  • Even worse, `char ***arr[][` is beyond my grasp. – wildplasser Feb 05 '20 at 23:18
  • The function just modifies the array in place, so it doesn't need to return anything. And I stole the *** from [here](https://stackoverflow.com/questions/4339201/c-how-to-pass-a-double-pointer-to-a-function). – chadathin Feb 05 '20 at 23:18
  • And, I have tried writing the function to take `char ***arr` but I get the same result. – chadathin Feb 05 '20 at 23:23
  • The fact of the matter is your argument `&test` is of type `char ***` and it should be of type `char ****` – anastaciu Feb 05 '20 at 23:23
  • 2
    @chadathin Read this question and the answers: [**Correctly allocating multi-dimensional arrays**](https://stackoverflow.com/questions/42094465/correctly-allocating-multi-dimensional-arrays) It will help explain what you're actually doing when you call nested loops around `malloc()` to create "arrays". You're not really creating multidimsional arrays when you do that. You are really creating arrays of pointers to arrays of pointers to one-dimensional arrays of values. Nested allocation can also be very slow for large n-dimensional "arrays". – Andrew Henle Feb 05 '20 at 23:28
  • @AndrewHenle Thank you for the reference! This is an excellent supplement to our course material. – chadathin Feb 05 '20 at 23:34

3 Answers3

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

void makeArray(char*** arr, int n) {
  int i;
  *arr = calloc(n, sizeof (char*));
  for (i = 0; i<n; i++) 
  {
    (*arr)[i] = calloc(50, sizeof(char));
  }
}

int main() {

  char **test;

  makeArray(&test,4);

  return 0;
}

Should be what you are looking for. How does it work:

  • First I changed your prototype to "char ***" as "char ***arr[]* as actally char****
  • Second *arr (allows writting to your variable test in main)
  • Third a little bit tricky (*arr) to write to test and than [] to use test as array.

I use calloc instead of malloc as it looks from my point of view better than malloc for a array and it ensurs your strings are initialized with 0 (zero-terminated)

Schafwolle
  • 501
  • 3
  • 15
  • That's the ticket! Thank you so much for helping me understand your modifications in the process! And actually, our instructor covered `malloc` heavily and just kind of 'glazed over' `calloc` so I'll have to check out the man pages for that. Thank you again! – chadathin Feb 05 '20 at 23:30
1

Here I took the solution from Schafwolle (a very soft and smooth nickname!) and mixed in my former solution and my idead of returning the allocated array. Do not forget to free the memory in the opposite order.

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

#define MAX_STRING_SIZE 50

char** makeArray(int n) {
  int i;
  char** arr = calloc(n, sizeof (char*));
  for (i = 0; i<n; i++) 
  {
    arr[i] = calloc(MAX_STRING_SIZE, sizeof(char));
  }
  return arr;
}

void recycleArray(char** arr, int n) {
  int i;
  for (i = 0; i<n; i++) 
  {
    free(arr[i]);
  }
  free(arr);
}

int main() {

  char** test = makeArray(4);
  strncpy(test[0],"Have ", MAX_STRING_SIZE-1);
  strncpy(test[1],"a ", MAX_STRING_SIZE-1);
  strncpy(test[2],"nice ", MAX_STRING_SIZE-1);
  strncpy(test[3],"evening.", MAX_STRING_SIZE-1);
  printf("%s%s%s%s\n", test[0], test[1], test[2], test[3]);
  recycleArray(test, 4);
  return 0;
}
MiCo
  • 399
  • 2
  • 6
  • 1
    Oh, wow. This is great! I never even thought of just returning the char** and assigning that to a pointer. Thank you! – chadathin Feb 06 '20 at 00:46
0

Just start with this:

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

#define MAX_STRING_SIZE 50

void makeArray(char* arr[], int n) {
  int i;
  for (i = 0; i<n; i++) {
    arr[i] = malloc(sizeof(char)*MAX_STRING_SIZE);
  }
}

int main() {

  char* test[4];
  makeArray(test,4);

  strncpy(test[0],"Have ", MAX_STRING_SIZE-1);
  strncpy(test[1],"a ", MAX_STRING_SIZE-1);
  strncpy(test[2],"nice ", MAX_STRING_SIZE-1);
  strncpy(test[3],"evening.", MAX_STRING_SIZE-1);
  printf("%s%s%s%s\n", test[0], test[1], test[2], test[3]);

  return 0;
}

If you also want to create the array of pointers dynamically, just return a pointer instead of void. This makes it more readable...

MiCo
  • 399
  • 2
  • 6