carraysexecv

Dynamically adding elements to array C


I am trying to build a function that takes a string of chars and provides a list of those chars separated by a token.

This is what I have so far:

char * decode_args(char arguments[]){
  char* token = strtok(arguments, "00");
  while (token != NULL){
    printf("%s\n", token);
    token = strtok(NULL, "00");
  }
  return 0;
}

This function prints the desired values that I am looking for. For example:

>decode_args("77900289008764")
779
289
8764

The next step is to build an array that can be used in the execv command. The arguments need to be an array. An example here. I am a beginner so I don't even know if "array" is the right word. What data type should be built and how can I do that so I can call execv with the arguments that are currently being printed in a list?


Solution

  • For starters let me explain some stuff about storage and strings.

    There are 3 basic storage types. Automatic, dynamic, static. And static one usually split into two: read-only and read-write. Dynamic and static ones will be useful for you soon.

    Automatic variables are function parameters and local variables. When you call a function they pushed into stack and when function returns they got unwind.

    Dynamic one is the one you allocate in runtime with malloc family. This is how we create a dynamic array. And you need to return this source when you are done with free. If you don't, it is called memory leak and you can check for memory leaks with the tool valgrind beside other memory errors. It is quite useful for systems programming class.

    And static ones are the ones stay there for lifetime of the program. If you define a global variable or static int i = 42 it will create static read-write variable so you can change it. Now here is the trick.

    void foo() {
       char *string1 = "hello, world" //static read-only
       char string2[] = "hello, world" //automatic
    }
    

    So if you try to change string1 you will get a segmentation fault but it is OK to change string2. I don't know why you don't get segmentation fault by doing decode_args("77900289008764") but I get on my machine. :D

    Now C-strings. They are null terminated which means there is a (char) 0 character end of each string that says it is end of the string. strtok basically replace the pattern with NULL character so you have multiple substrings instead of one. So from your example it converts "77900289008764 NULL" to "779 NULL 289 NULL 8764 NULL"

    So if I were you I would count encounters of "00" in the string and allocate that much character pointer. Which is something like:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    char ** decode_args(char *arguments, char *delim) {
        int num_substrings = 1; // Note that C don't initialize with 0 like java etc.
        int len_delim = strlen(delim);
    
        for (int i = 0;
                arguments[i] != '\0' && arguments[i + 1] != '\0'; // Any of last 2 chars are terminator?
                ++i)
            if (strncmp(arguments + i /* same as &arguments[i] */, delim, len_delim) == 0)
                ++num_substrings;
    
        char **result = (char **) malloc(sizeof(char *) * (num_substrings + 1));
        int i = 0;
        char* token = strtok(arguments, delim);
    
        while (token != NULL){
            result[i] = token;
            ++i;
            token = strtok(NULL, delim);
        }
    
        result[i] = NULL; //End of results. execv wants this as I remember
    
        return result;
    }
    int main(int argc, char *argv[])
    {
        char str[] = "foo00bar00baz";
        char **results = decode_args(str, "00");
    
        for (int i = 0; results[i] != NULL; ++i) {
            char *result = results[i];
            puts(result);
        }
    
        free(results);
    
        return 0;
    }