2

EDIT*: I am aware there are similar questions, but since this is a practice assignment, the function signatures are given and i am only allowed to change the function bodies. Hence i cannot change the function parameters or the return types as say the answers on similar threads. Thanks in advance!

i am really confused about a program i am writing. I am basically writing a function that takes two pointers to structures, the structures have an int component and a character array component. The function appends the two character array components to return a struct (something like the strcat). The thing is, while the array part of the struct get updated by the function, the int part does not. Can anyone explain why? The function newText creates a new struct text and sets the array of the struct as the parameter of the function. It also sets the capacity = 24. If the length of the string parameter is larger than 24, it doubles the capacity until it fits. The append function appends the two strings, then calls newText with the new appended string and sets the t1 equal to the return value of newText. I have print statements at the end of the append function to show me that it works and the values are correct, however in main after i call the function, t->capacity becomes 24 again instead of 48 while t-> content is correct. Here is my code:

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


struct text { int capacity; char *content; };

typedef struct text text;


text *newText(char *s) {

  text *t = malloc(sizeof(text));
  t->capacity = 24;

  while (strlen(s) + 1 > t->capacity) {t->capacity = t->capacity * 2;} 
  t->content = malloc(t->capacity);
  strcpy(t->content, s);

  return t; 
}

void append(text *t1, text *t2) {
  int length1 = strlen(t1->content);
  int length2 = strlen(t2->content);
  int lengths = length1 + length2;


  for (int i = length1; i < length1 + length2; i++){
  t1->content[i] = t2->content[i - length1];
  t1->content[i + 1] = '\0';                }

  char text[100];
  strcpy(text, t1->content);


  t1 = newText(text);

  printf("%s \n", t1->content);
  printf("%d \n", t1->capacity);

}

int main () {
  text *t = malloc(sizeof(text));
  text *t1 = malloc(sizeof(text));

  t = newText("carpet");
  t1 = newText("789012345678901234");
  append(t, t1);
  printf("%d\n", t->capacity);
}
  • basically in short, my question is, why is t->capacity equal to 24 after the append function in main, since the print statement while append is running shows that t->capacity is 48. – Alexander Brown Nov 07 '18 at 15:36
  • Definitely a duplicate of that @UnholySheep – Chris Turner Nov 07 '18 at 15:38
  • In order to modify and pass back a pointer you have to pass the address of the pointer, so change your prototype to ** – SPlatten Nov 07 '18 at 15:44
  • 1
    Note also that there is some memory leak in your code: at the beginning of main(), you assign t and t1 with a malloc, then you overwrite their value with newText(). You should let them uninitialized, or set them to NULL, or call free() before newText(). – calandoa Nov 07 '18 at 15:46
  • 1
    thanks for the responses, i am aware that its a similar question but since i am unable to change the function signatures (as this is a practice assignment where the signatures are given and we are only allowed to modify the bodies) i cannot use the answers provided in the other question of changing the function so it takes a pointer to a pointer or so that it returns a pointer. – Alexander Brown Nov 07 '18 at 16:02
  • the `append` function should work without calling `newText`. You're meant to grow the existing text of `t1` . Also the `main` function has memory leaks since you call `malloc` unnecessarily. – M.M Nov 08 '18 at 07:57

2 Answers2

2

In order to modify a pointer in a function and pass it back you have to pass the address of the pointer for example:

void test(char** ppTest) {
    //Is anything valid passed?
    if ( ppTest == NULL ) {
        return;
    }
    *ppTest = "Hello World";
}

int main(void) {
    char* pTest = NULL;
    printf("Before function call pTest: %s\n", (pTest == NULL) ? "NULL" : pTest);
    test(&pTest); // Must pass the address of the pointer
    printf("After function call pTest: %s\n", (pTest == NULL) ? "NULL" : pTest);
    return 0;
}
Donal Fellows
  • 133,037
  • 18
  • 149
  • 215
SPlatten
  • 5,334
  • 11
  • 57
  • 128
1
 t1 = newText(text);

Here you're setting t1 to a new text. However, the original text that t1 was pointing to doesn't change here. It still points to the same place. The pointer that you passed to the function is a copy of the original pointer, so even though t1 points to the new text, the original doesn't.

So when you do the print here

  printf("%d\n", t->capacity);

You still get the old value for capacity. Or rather, the capacity of the old t1. Whereas here

printf("%s \n", t1->content);
printf("%d \n", t1->capacity);

You are printing the content and capacity of the new t1, to which you are losing the pointer after the function ends.

You could fix that, for instance, by returning that pointer. Change the signature to

text* append(text *t1, text *t2)

And at the end of the function:

return t1;

And then you update t1, so change this

append(t, t1);

To

t1 = append(t, t1);

Also, watch out for memory leaks. As it is now, in append, you're taking the pointers to two valid text and you're allocating a new text, and at the end of the function, you still only have two pointers to text and you didn't free any. This shows us that one was leaked.

Edit:

If you can't change the signature of the function, then you can't return a new text or pass one out using a double pointer. In this case, I would suggest not making a new text and just changing t1 instead. Using realloc you can change the size of the buffer (in reality, it might just allocate a new buffer, copy over the data and free the old buffer) so you can append the content of t2.

void append(text *t1, text *t2) {
    int length1 = strlen(t1->content);
    int length2 = strlen(t2->content);
    int lengths = length1 + length2;                // calculate the required length. You can change this to have some "reserve" such as in newText. In this example, it will be 24

    t1->content = realloc(t1->content, lengths);        // set the correct size for the buffer

    for (int i = length1; i < length1 + length2; i++) {
        t1->content[i] = t2->content[i - length1];
        t1->content[i + 1] = '\0';
    }   // append the second string

    t1->capacity = lengths;
    printf("%s \n", t1->content);
    printf("%d \n", t1->capacity);
}
Blaze
  • 16,736
  • 2
  • 25
  • 44
  • Oh great now i understand why this happens. Thanks for the response, however, this code is part of a larger assignment program where the function signatures are provided to us and we need to complete the bodies. Hence i am unable to change it so that it returns a text pointer. How would you suggest i fix it? – Alexander Brown Nov 07 '18 at 15:55
  • Alright, I added a solution that lets you append to `t1` without making a new `text`. – Blaze Nov 08 '18 at 07:46