Search code examples
cstringdynamicmallocallocation

string dynamic allocation in C


I'm writing a function that needs to:

  1. Get a full sentence from the user

  2. Dynamically assign for each word a memory block that can contain it

  3. Copy it to it and save the address to the * words pointer array

I wrote a loop that in each iteration gets a word from the sentence the user inserted, but I can't Dynamically allocate it properly. This is what I have:

char str[50];
while(scanf("%s", str) != EOF)
{
    int len = strlen(str);
    char *words = (char*) malloc((len+1)*sizeof(char));
}

I also need to make sure that:

  • If the function succeeds, it returns the number of words entered.

  • If memory allocation fails the function returns -1.


Solution

  • The condition while(scanf("%s", str) != EOF) is a potencial infinite loop, this will only stop if you feed the program with EOF signal through the command line.

    For a single string that hodls all the words you are going to need to reallocate memory at every read to increase the memory space where the string is saved.

    You can do something like this:

    Live demo

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int ret_words(char **words) { //double pointer argument
        char str[50];
        char *temp = malloc(1); //first allocation required
        int count = 0;
    
        if(temp == NULL){       //allocation error
            return -1;
        }
        do{
            temp = realloc(temp, strlen(str) + strlen(temp) + 2); //reallocate space on
                                                                  //each iteration
            if(temp == NULL){                                      
                return -1;
            }
            count++; //count words
        } while (scanf("%49s", str) == 1 && strcat(temp, str) //scan str and append to temp
          && getchar() != '\n' && strcat(temp, " "));         //with spaces between
    
        *words = temp; //assign temp to words
        return count; //return word count
    }
    
    int main() {
    
        int ret;
        char *words; //pointer to the array of strings
        puts("Enter the string");
        putchar('>');
        ret = ret_words(&words); //returned number of words on success, -1 on error
    
        printf("Inputed string: %s\nNumber of words: %d", words, ret); //test print
    }
    

    Constraints: There cannot be any spaces after the last inputed word for the cycle to conclude.

    Now, if you want bi-dimensional array like structure, dynamically allocated not only for the words themselves, but also for the pointers that point to those strings you will need something a bit more special, a triple pointer.

    Think of it as a pointer to an array of strings, this is what you need as an argument of your function in order to make the changes permanent in the caller function, in this case, the main function.

    Live demo

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int ret_words(char ***words) { //triple pointer argument
        char str[50];
        int count = 0;
        char **temp = malloc(sizeof *temp); //allocate a pointer to pointer to char
    
        if(temp == NULL){       //allocation error
            return -1;
        }
        do {
            temp = realloc(temp, (count + 1) * sizeof *temp); //reallocate space at every scan
            if(temp == NULL){
                return -1;
            } 
            temp[count] = malloc(strlen(str) + 1);
            count++;
        } while (scanf("%49s",temp[count - 1]) == 1 //scan str and append to temp
          && getchar() != '\n');                    //with spaces between
    
        *words = temp; //assing the allocated array of pointer to the argument
        return count; //return word count
    }
    
    int main()
    { 
        char **words; // pointer to pointer to navigate the array of strings
    
        int ret;
        puts("Enter the string");
        putchar('>');
        ret = ret_words(&words); //returned number of words on success, -1 on error
    
        for(int i = 0; i < ret; i++) //print each stored one word string
            printf("Word %d: %s\n",i + 1, words[i]);
    
        printf("Word count: %d", ret); //print the number of words
    }
    

    Constraints: There cannot be any spaces after the last inputed word for the cycle to conclude.

    Note that there is an extra level of indirection due to the fact that these assignments are made through the arguments of the function, if they were to be used in place or returned by the function you would only need a simple pointer, in the first sample, and a double pointer in the second sample.