3

I am new with C and I am trying to understand allocating strings.

I am trying to create a function called adding_string. It takes in an array of zero or more strings that has a null in the final location. Next, it makes a shallow copy of the array that is + 1 location bigger, then appends a copy of the string str onto the array. Finally, it deletes the original array and returns the new copy

This is what I have so far:

char **adding_string(char **array, const char *str)
{

    size_t num = strlen(str) + 1;
    char *final= (char *)malloc(num);
    strncpy(final, str, num);
    free(array);
    //The above code would create a copy of the string "str".
    //Then it puts that into the array.
    //Not sure if free(array); would be the right method
    //Having issues with returning final too

    return final;
}

In the main function, you would have something like:

char **array = NULL;
char **lines;

array = (char **)calloc(1, sizeof(char *));

array = adding_string(array, "help");
array = adding_string(array, "plz");
array = adding_string(array, "thanks");

for (lines = array; *lines; lines++)
{
    printf("%s\n", *lines);
}

I'm not sure if free(array) would be the right method to use to delete the original array, and I'm having issues with returning the new copy.

When I try returning the new copy, I get:

warning: return from incompatible pointer type

which is because of:

return final;
4
  • You aren't copying the array but just str. And final is of type char*, that's why it says "incompatible pointer type" - the functin is supposed to return char**. Commented Jan 28, 2018 at 23:10
  • .@rain city , that makes sense, thank you. Please correct me if I am wrong, but to fix this, I would have to do change the str to array, and then use strncat to add the two up. Next, I would return char**, which would be the array. Commented Jan 28, 2018 at 23:19
  • Why should *lines become null? Commented Jan 28, 2018 at 23:22
  • @QuasselKasper is a technique that emulates how cstrings are terminated by using a '\0'-terminating byte. Here you have instead a NULL pointer. argv in main works in that way, the last element is NULL. The execv for example demand that as well. Commented Jan 28, 2018 at 23:25

2 Answers 2

2

Your adding_string makes no sense, you make a copy of str, free the memory from array and return the new copy. The function should return a double pointer to char, you are passing a single-pointer to char. All other values are lost, you are leaking memory like crazy.

I'd rewrite your adding_string like this:

char **adding_string(char **array, const char *str)
{
    char **tmp;
    if(str == NULL)
        return NULL;

    // first make copy
    size_t len = strlen(str);
    char *strcopy = malloc(len+1);
    if(strcopy == NULL)
        return NULL;

    // you've allocated enough memory for the copy
    // no need of strncpy here
    strcpy(strcopy, str);

    // get the number of strings saved
    size_t size = 0; // number of strings saved
    if(array)
    {
        tmp = array;
        while(*(tmp++))
            size++;
    }

    // reallocate memory for array of strings
    tmp = realloc(array, (size+2) * sizeof *tmp);

    if(tmp == NULL)
    {
        // something went wrong, free the copy
        free(strcopy);
        return NULL;
    }

    tmp[size] = strcopy;
    tmp[size+1] = NULL;

    return tmp;
}

Note that in this version, if array is NULL, the function allocates the memory for the array of strings. That's only a design choice, you could as well check that array is not NULL and pass to adding_string a pre-allocated array of strings. I think (and that's only my opinion) that is more elegant that adding_string will create the first array. In this way, the code that allocates memory is in one place only.

Now in your main

char **array = NULL;
char **lines;

// adding_string will allocate the memory for array when it's NULL
array = adding_string(array, "help");
array = adding_string(array, "plz");
array = adding_string(array, "thanks");

for (lines = array; *lines; lines++)
{
    printf("%s\n", *lines);
}

Note that I do

tmp = realloc(array, (size+2) * sizeof *tmp);

size has the number of strings saved, that means that array holds size+1 spaces, because the last one points to NULL. You are appending one more strings, so you have to reallocate size+1+1 spaces, which is size+2.

Please don't forget to free the memory afterwards.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you so much! This made a lot of sense!
1

The program below strictly follows your needs and intentions.

The array array is resized every time a new string is added. At the end of the program the proper cleanup of all allocated memory is done.

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

char ** adding_string(char **array, const char *str)
{
    size_t num = strlen(str) + 1;
    char *final = (char *)malloc(num); // allocate memory for the string `str`
    strncpy(final, str, num);   // create the copy of the `str`  

    int i=0;
    for(i=0; array[i] !=NULL; i++) {}  // find how many elements do we have in the array

    array[i] = final; // add final to the first empty spot in the `array`
    i++;

    char ** new_array = calloc(1+i, sizeof(char *));  // allocate a new array 1 size bigger
    memcpy(new_array, array, sizeof(char*)*i);        // copy all the pointers

    free (array); // no need for the old array 

    return new_array; // return a pointer to the new bigger array
}

int main(void)
{
    char **array = NULL;
    char **lines;

    array = (char **)calloc(1, sizeof(char *)); // allocate array for 4 poiters if type (char *)

    array = adding_string(array, "help");
    array = adding_string(array, "plz");
    array = adding_string(array, "thanks");

    for (lines = array; *lines; lines++)
    {
       printf("%s\n", *lines);
       free(*lines);
    }

    free (array);

    return 0;
}

Output:

help
plz
thanks

This is different approach where

char *adding_string(const char *str)

returns a pointer (char *) to the copy of the string. The array has already preallocated memory to accommodate all string pointers.

A small program to demonstrate the concept:

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

char *adding_string(const char *str)
{

    size_t num = strlen(str) + 1;
    char *final= (char *)malloc(num); // allocate memory for the string str
    strncpy(final, str, num);   // crreate the copy 

    return final; // return a pointer to created copy
}


int main(void)
{
    char **array = NULL;

    array = (char **)calloc(4, sizeof(char *)); // allocate array for 4 pointers if type (char *)

    array[0] = adding_string("help");
    array[1] = adding_string("plz");
    array[2] = adding_string("thanks");

    for (int i=0; i<3; i++ )
    {
        printf("%s\n", array[i]);
        free(array[i]);
    }

    free (array);

    return 0;
}

Output:

help
plz
thanks

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.