carraysexecv

Create Char* array dynamically in C


I am trying to create a char* array dynamically for execv. The number of parameters is known only at runtime. This is my code:

char *id_array= malloc((size_array) * sizeof(char));
int length = 4 + groupId_list.nr; //groupId_list.nr know only at runtime
id_array= realloc(id_array,sizeof(char)*length);

id_array[0] = "/usr/bin/java";
id_array[1] = "-jar";
id_array[2] = "pathToFile/jar";

for (int j = 0; j < groupId_list.nr; j++) {
    id_array[j+3] = groupId_list.items[j].string;
}
id_array[size_array-1] = (char*)0; //terminator need for execv

execv("/usr/bin/java",&id_array);

When I compile the code i am having different warnings:

warning: incompatible pointer to integer conversion assigning to 'char' from 'char [14]'
 id_array[0] = "/usr/bin/java";
warning: incompatible pointer to integer conversion assigning to 'char' from 'char [5]'
 id_array[1] = "-jar";
....

I tried to follow different tutorials but i didn't understand how i can solve it. Thanks.


Solution

  • You want an array of strings, an array of arrays of characters, a pointer to a pointer to a char.

    int length = 4 + groupId_list.nr;
    char **id_array= malloc(sizeof(char*) * length);
    
    const char *str1 = "/usr/bin/java";
    id_array[0] =  malloc(sizeof(char) * (strlen(str1) + 1));
    strcpy(id_array[0], str1);
    
    const char *str2 = "-jar";
    id_array[1] =  malloc(sizeof(char) * (strlen(str2) + 1));
    strcpy(id_array[1], str2);
    
    const char *str3 = "pathToFile/jar";
    id_array[2] =  malloc(sizeof(char) * (strlen(str3) + 1));
    strcpy(id_array[2], str3);
    
    for (size_t j = 0; j < groupId_list.nr; ++j) {
        id_array[j + 3] = malloc(sizeof(char) * (strlen(groupId_list.items[j].string) + 1));
        strcpy(id_array[j + 3], groupId_list.items[j].string);
    }
    
    id_array[length - 1] = NULL;
    

    It get's clearer when you use sizeof(*variable), which is way more readable and less error prone, and more clearer. Also the parts const char *string = "some string"; malloc(strlen(string) + 1); strcpy can be simplified with just a call to strdup.

    char **id_array= malloc(sizeof(*id_array) * 4);
    id_array[0] = strdup("/usr/bin/java");
    id_array[1] = strdup("-jar");
    id_array[2] = strdup("pathToFile/jar");
    for (int j = 0; j < groupId_list.nr; j++) {
        id_array[j + 3] = strdup(groupId_list.items[j].string);
    }
    id_array[length - 1] = NULL;
    

    Remember to free the memory.

    Or depending on your memory ownership model, maybe you don't want to duplicate the memory and just assign pointers. Then just allocate the memory for array of pointers to char and assign pointers. You could allocate memory for string literals on stack.

    char **id_array = malloc(sizeof(*id_array) * length);
    id_array[0] = (char[]){"/usr/bin/java"};
    id_array[1] = (char[]){"-jar"};
    id_array[2] = (char[]){"pathToFile/jar"};
    for (....) { 
       id_array[j + 3] = groupId_list.items[j].string;
    }
    id_array[length - 1] = NULL;
    

    Or you could even carefully read execv specification (see this post) and pass pointers to string literals to execv and ignore the const qualifier, as execv will not modify the array anyway.

    # string literals are constant
    const char **id_array = malloc(sizeof(*id_array) * length);
    id_array[0] = "/usr/bin/java";
    id_array[1] = "-jar";
    id_array[2] = "pathToFile/jar";
    for (....) { 
       id_array[j + 3] = groupId_list.items[j].string;
    }
    id_array[length - 1] = NULL;
    
    # explicitly remove the const qualifier, which is safe, because we 
    # know from the documentation that execv will not modify the array contents
    execv(...., (void*)id_array);