Manipulating structs with a void function in C











up vote
0
down vote

favorite












so I've been set a task of creating a faux string struct and implementing all the usual string functions on my faux string struct. I'm stuck on the tests of my strcat implementation called append, with the first test failing (segfault) being the 5th line. My function for creating new structs should be OK because it passed all the tests, but I've included it just incase.



I've already been able to successfully implement length, get, set and copy functions for my faux string structs.



The struct:



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

typedef struct text text;


My function for creating new structs:



text *newText(char *s) {
printf("new Text from %sn", s);
int sizeNeeded = (strlen(s)+1);
int sizeGot = 24;
while (sizeNeeded > sizeGot) {
sizeGot = sizeGot * 2;
}
text *out = malloc(sizeGot);
char *c = malloc(sizeGot);
strcpy(c, s);
out->content = c;
out->capacity = (sizeGot);
printf("the capacity is %dn", sizeGot);
return out;
free(c);
}


My append function:



void append(text *t1, text *t2) {
printf("t1 content is %s, t2 content is %dn", t1->content, *t2->content);
int sizeNeeded = (t1->capacity + t2->capacity);
int sizeGot = 24;
while (sizeNeeded > sizeGot) {
sizeGot = sizeGot * 2;
}
char *stringy = calloc(sizeGot, 32);
stringy = strcat(t1->content, t2->content);
free(t1);
t1 = newText(stringy);
}


and finally the tests:



void testAppend() {
text *t = newText("car");
text *t2 = newText("pet");
append(t, t2);
assert(like(t, "carpet"));
assert(t->capacity == 24);
text *t3 = newText("789012345678901234");
append(t, t3);
assert(like(t, "carpet789012345678901234"));
assert(t->capacity == 48);
freeText(t);
freeText(t2);
freeText(t3);
}









share|improve this question




















  • 1




    This is wrong: text *out = malloc(sizeGot);. out only needs to be big enough to fit 1 integer and 1 character pointer, that pointer will then point to the actual string.
    – Qubit
    Nov 19 at 9:23










  • the free(c) following return out; will nevel be called. Why 2 allocation in newText?
    – Mathieu Bunel
    Nov 19 at 9:24










  • @Qubit how would I malloc the size of the string it's pointing to then? char t->content->s = malloc(sizeGot);
    – Alex
    Nov 19 at 9:25






  • 1




    Honestly, I don't understand why newText has to be dynamic for the structure in the first place. The internal string I'll buy into, but what's the purpose for allocating that structure as well? I.e. how would returning a simple struct text by value not work ? Obviously you would pass it by address to other manip functions.
    – WhozCraig
    Nov 19 at 9:26






  • 2




    @Alex You do that fine, c is a pointer to a character array of (hopefully) sufficient size. Your new text only needs to store the integer and the pointer, the pointer points to your memory. And do think about what @WhozCraig suggested, there is no need to return out as a pointer, simply return it by value.
    – Qubit
    Nov 19 at 9:29















up vote
0
down vote

favorite












so I've been set a task of creating a faux string struct and implementing all the usual string functions on my faux string struct. I'm stuck on the tests of my strcat implementation called append, with the first test failing (segfault) being the 5th line. My function for creating new structs should be OK because it passed all the tests, but I've included it just incase.



I've already been able to successfully implement length, get, set and copy functions for my faux string structs.



The struct:



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

typedef struct text text;


My function for creating new structs:



text *newText(char *s) {
printf("new Text from %sn", s);
int sizeNeeded = (strlen(s)+1);
int sizeGot = 24;
while (sizeNeeded > sizeGot) {
sizeGot = sizeGot * 2;
}
text *out = malloc(sizeGot);
char *c = malloc(sizeGot);
strcpy(c, s);
out->content = c;
out->capacity = (sizeGot);
printf("the capacity is %dn", sizeGot);
return out;
free(c);
}


My append function:



void append(text *t1, text *t2) {
printf("t1 content is %s, t2 content is %dn", t1->content, *t2->content);
int sizeNeeded = (t1->capacity + t2->capacity);
int sizeGot = 24;
while (sizeNeeded > sizeGot) {
sizeGot = sizeGot * 2;
}
char *stringy = calloc(sizeGot, 32);
stringy = strcat(t1->content, t2->content);
free(t1);
t1 = newText(stringy);
}


and finally the tests:



void testAppend() {
text *t = newText("car");
text *t2 = newText("pet");
append(t, t2);
assert(like(t, "carpet"));
assert(t->capacity == 24);
text *t3 = newText("789012345678901234");
append(t, t3);
assert(like(t, "carpet789012345678901234"));
assert(t->capacity == 48);
freeText(t);
freeText(t2);
freeText(t3);
}









share|improve this question




















  • 1




    This is wrong: text *out = malloc(sizeGot);. out only needs to be big enough to fit 1 integer and 1 character pointer, that pointer will then point to the actual string.
    – Qubit
    Nov 19 at 9:23










  • the free(c) following return out; will nevel be called. Why 2 allocation in newText?
    – Mathieu Bunel
    Nov 19 at 9:24










  • @Qubit how would I malloc the size of the string it's pointing to then? char t->content->s = malloc(sizeGot);
    – Alex
    Nov 19 at 9:25






  • 1




    Honestly, I don't understand why newText has to be dynamic for the structure in the first place. The internal string I'll buy into, but what's the purpose for allocating that structure as well? I.e. how would returning a simple struct text by value not work ? Obviously you would pass it by address to other manip functions.
    – WhozCraig
    Nov 19 at 9:26






  • 2




    @Alex You do that fine, c is a pointer to a character array of (hopefully) sufficient size. Your new text only needs to store the integer and the pointer, the pointer points to your memory. And do think about what @WhozCraig suggested, there is no need to return out as a pointer, simply return it by value.
    – Qubit
    Nov 19 at 9:29













up vote
0
down vote

favorite









up vote
0
down vote

favorite











so I've been set a task of creating a faux string struct and implementing all the usual string functions on my faux string struct. I'm stuck on the tests of my strcat implementation called append, with the first test failing (segfault) being the 5th line. My function for creating new structs should be OK because it passed all the tests, but I've included it just incase.



I've already been able to successfully implement length, get, set and copy functions for my faux string structs.



The struct:



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

typedef struct text text;


My function for creating new structs:



text *newText(char *s) {
printf("new Text from %sn", s);
int sizeNeeded = (strlen(s)+1);
int sizeGot = 24;
while (sizeNeeded > sizeGot) {
sizeGot = sizeGot * 2;
}
text *out = malloc(sizeGot);
char *c = malloc(sizeGot);
strcpy(c, s);
out->content = c;
out->capacity = (sizeGot);
printf("the capacity is %dn", sizeGot);
return out;
free(c);
}


My append function:



void append(text *t1, text *t2) {
printf("t1 content is %s, t2 content is %dn", t1->content, *t2->content);
int sizeNeeded = (t1->capacity + t2->capacity);
int sizeGot = 24;
while (sizeNeeded > sizeGot) {
sizeGot = sizeGot * 2;
}
char *stringy = calloc(sizeGot, 32);
stringy = strcat(t1->content, t2->content);
free(t1);
t1 = newText(stringy);
}


and finally the tests:



void testAppend() {
text *t = newText("car");
text *t2 = newText("pet");
append(t, t2);
assert(like(t, "carpet"));
assert(t->capacity == 24);
text *t3 = newText("789012345678901234");
append(t, t3);
assert(like(t, "carpet789012345678901234"));
assert(t->capacity == 48);
freeText(t);
freeText(t2);
freeText(t3);
}









share|improve this question















so I've been set a task of creating a faux string struct and implementing all the usual string functions on my faux string struct. I'm stuck on the tests of my strcat implementation called append, with the first test failing (segfault) being the 5th line. My function for creating new structs should be OK because it passed all the tests, but I've included it just incase.



I've already been able to successfully implement length, get, set and copy functions for my faux string structs.



The struct:



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

typedef struct text text;


My function for creating new structs:



text *newText(char *s) {
printf("new Text from %sn", s);
int sizeNeeded = (strlen(s)+1);
int sizeGot = 24;
while (sizeNeeded > sizeGot) {
sizeGot = sizeGot * 2;
}
text *out = malloc(sizeGot);
char *c = malloc(sizeGot);
strcpy(c, s);
out->content = c;
out->capacity = (sizeGot);
printf("the capacity is %dn", sizeGot);
return out;
free(c);
}


My append function:



void append(text *t1, text *t2) {
printf("t1 content is %s, t2 content is %dn", t1->content, *t2->content);
int sizeNeeded = (t1->capacity + t2->capacity);
int sizeGot = 24;
while (sizeNeeded > sizeGot) {
sizeGot = sizeGot * 2;
}
char *stringy = calloc(sizeGot, 32);
stringy = strcat(t1->content, t2->content);
free(t1);
t1 = newText(stringy);
}


and finally the tests:



void testAppend() {
text *t = newText("car");
text *t2 = newText("pet");
append(t, t2);
assert(like(t, "carpet"));
assert(t->capacity == 24);
text *t3 = newText("789012345678901234");
append(t, t3);
assert(like(t, "carpet789012345678901234"));
assert(t->capacity == 48);
freeText(t);
freeText(t2);
freeText(t3);
}






c memory memory-management struct






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 19 at 9:43

























asked Nov 19 at 9:18









Alex

83




83








  • 1




    This is wrong: text *out = malloc(sizeGot);. out only needs to be big enough to fit 1 integer and 1 character pointer, that pointer will then point to the actual string.
    – Qubit
    Nov 19 at 9:23










  • the free(c) following return out; will nevel be called. Why 2 allocation in newText?
    – Mathieu Bunel
    Nov 19 at 9:24










  • @Qubit how would I malloc the size of the string it's pointing to then? char t->content->s = malloc(sizeGot);
    – Alex
    Nov 19 at 9:25






  • 1




    Honestly, I don't understand why newText has to be dynamic for the structure in the first place. The internal string I'll buy into, but what's the purpose for allocating that structure as well? I.e. how would returning a simple struct text by value not work ? Obviously you would pass it by address to other manip functions.
    – WhozCraig
    Nov 19 at 9:26






  • 2




    @Alex You do that fine, c is a pointer to a character array of (hopefully) sufficient size. Your new text only needs to store the integer and the pointer, the pointer points to your memory. And do think about what @WhozCraig suggested, there is no need to return out as a pointer, simply return it by value.
    – Qubit
    Nov 19 at 9:29














  • 1




    This is wrong: text *out = malloc(sizeGot);. out only needs to be big enough to fit 1 integer and 1 character pointer, that pointer will then point to the actual string.
    – Qubit
    Nov 19 at 9:23










  • the free(c) following return out; will nevel be called. Why 2 allocation in newText?
    – Mathieu Bunel
    Nov 19 at 9:24










  • @Qubit how would I malloc the size of the string it's pointing to then? char t->content->s = malloc(sizeGot);
    – Alex
    Nov 19 at 9:25






  • 1




    Honestly, I don't understand why newText has to be dynamic for the structure in the first place. The internal string I'll buy into, but what's the purpose for allocating that structure as well? I.e. how would returning a simple struct text by value not work ? Obviously you would pass it by address to other manip functions.
    – WhozCraig
    Nov 19 at 9:26






  • 2




    @Alex You do that fine, c is a pointer to a character array of (hopefully) sufficient size. Your new text only needs to store the integer and the pointer, the pointer points to your memory. And do think about what @WhozCraig suggested, there is no need to return out as a pointer, simply return it by value.
    – Qubit
    Nov 19 at 9:29








1




1




This is wrong: text *out = malloc(sizeGot);. out only needs to be big enough to fit 1 integer and 1 character pointer, that pointer will then point to the actual string.
– Qubit
Nov 19 at 9:23




This is wrong: text *out = malloc(sizeGot);. out only needs to be big enough to fit 1 integer and 1 character pointer, that pointer will then point to the actual string.
– Qubit
Nov 19 at 9:23












the free(c) following return out; will nevel be called. Why 2 allocation in newText?
– Mathieu Bunel
Nov 19 at 9:24




the free(c) following return out; will nevel be called. Why 2 allocation in newText?
– Mathieu Bunel
Nov 19 at 9:24












@Qubit how would I malloc the size of the string it's pointing to then? char t->content->s = malloc(sizeGot);
– Alex
Nov 19 at 9:25




@Qubit how would I malloc the size of the string it's pointing to then? char t->content->s = malloc(sizeGot);
– Alex
Nov 19 at 9:25




1




1




Honestly, I don't understand why newText has to be dynamic for the structure in the first place. The internal string I'll buy into, but what's the purpose for allocating that structure as well? I.e. how would returning a simple struct text by value not work ? Obviously you would pass it by address to other manip functions.
– WhozCraig
Nov 19 at 9:26




Honestly, I don't understand why newText has to be dynamic for the structure in the first place. The internal string I'll buy into, but what's the purpose for allocating that structure as well? I.e. how would returning a simple struct text by value not work ? Obviously you would pass it by address to other manip functions.
– WhozCraig
Nov 19 at 9:26




2




2




@Alex You do that fine, c is a pointer to a character array of (hopefully) sufficient size. Your new text only needs to store the integer and the pointer, the pointer points to your memory. And do think about what @WhozCraig suggested, there is no need to return out as a pointer, simply return it by value.
– Qubit
Nov 19 at 9:29




@Alex You do that fine, c is a pointer to a character array of (hopefully) sufficient size. Your new text only needs to store the integer and the pointer, the pointer points to your memory. And do think about what @WhozCraig suggested, there is no need to return out as a pointer, simply return it by value.
– Qubit
Nov 19 at 9:29












2 Answers
2






active

oldest

votes

















up vote
0
down vote













You are allocating memory in the wrong way. You could fix this by using a flexible array member like this:



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

text *out = malloc(sizeof(text) + sizeof(something));
strcpy(out->content, str);
...


And obviously code such as this is nonsense:



  return out;
free(c);
}


Enable compiler warnings and listen to them.






share|improve this answer




























    up vote
    0
    down vote













    Och, some errors you have:




    1. Inside text_new you allocate memory for text *out using text *out = malloc(sizeGot); when sizeGot = 24 is a constant value. You should allocate sizeof(*out) or sizeof(text) bytes of memory for it.

    2. I don't know what for int sizeGot = 24; while (sizeNeeded > sizeGot) the loop inside text_new and append is for. I guess the intention is to do allocations in power of 24. Also it mostly looks like the same code is in both functions, it does look like code duplication, which is a bad thing.

    3. Inside append You pass a pointer to t1, not a double pointer, so if you modify the t1 pointer itself the modification will not be visible outside of function scope. t1 = newText(stringy); is just pointless and leaks memory. You could void append(text **t1, text *t2) and then *t1 = newText(stringy). But you can use a way better approach using realloc - I would expect append to "append" the string, not to create a new object. So first resize the buffer using realloc then strcat(&t1->content[oldcapacity - 1], string_to_copy_into_t1).


    4. int sizeNeeded = (t1->capacity + t2->capacity); is off. You allocate capacity in power of 24, which does not really interact with string length. You need to have strlen(t1->content) + strlen(t2->content) + 1 bytes for both strings and the null terminator.


    Try this:



    size_t text_newsize(size_t sizeNeeded)
    {
    // I think this is just `return 24 << (sizeNeeded / 24);`, but not sure
    int sizeGot = 24;
    while (sizeNeeded > sizeGot) {
    sizeGot *= 2;
    }
    return sizeGot;
    }

    text *newText(char *s) {
    printf("new Text from %sn", s);
    if (s == NULL) return NULL;
    int sizeNeeded = strlen(s) + 1;

    int sizeGot = text_newsize(sizeNeeded);

    text *out = malloc(sizeof(*out));
    if (out == NULL) {
    return NULL;
    }

    out->content = malloc(sizeGot);
    if (out->content == NULL) {
    free(out);
    return NULL;
    }

    strcpy(out->content, s);
    out->capacity = sizeGot;

    printf("the capacity is %dn", sizeGot);

    return out;
    }


    and this:



    int append(text *t1, text *t2) {
    printf("t1 content is %s, t2 content is %sn", t1->content, t2->content);
    int sizeNeeded = strlen(t1->content) + strlen(t2->content) + 1;

    if (t1->capacity < sizeNeeded) {

    // this could a text_resize(text*, size_t) function
    int sizeGot = text_newsize(sizeNeeded);
    void *tmp = realloc(t1->content, sizeGot);
    if (tmp == NULL) return -ENOMEM;
    t1->content = tmp;
    t1->capacity = sizeGot;

    }

    strcat(t1->content, t2->content);

    return 0;
    }


    Some remarks:




    1. Try to handle errors in your library. If you have a function like void append(text *t1, text *t2) let it be int append(text *t1, text *t2) and return 0 on success and negative number on *alloc errors.

    2. Store the size of everything using size_t type. It's defined in stddef.h and should be used to represent a size of an object. strlen returns size_t and sizeof also returns size_t.

    3. I like to put everything inside a single "namespace", I do that by prepending the functions with a string like text_.

    4. I got some free time and decided to implement your library. Below is the code with a simple text object storing strings, I use 24 magic number as allocation chunk size.




    // text.h file

    #ifndef TEXT_H_
    #define TEXT_H_

    #include <stddef.h>
    #include <stdbool.h>

    struct text;

    typedef struct text text;

    text *text_new(const char content);
    void text_free(text *t);
    int text_resize(text *t, size_t newsize);
    int text_append(text *to, const text *from);
    int text_append_mem(text *to, const void *from, size_t from_len);
    const char *text_get(const text *t);
    int text_append_str(text *to, const char *from);
    char *text_get_nonconst(text *t);
    size_t text_getCapacity(const text *t);
    bool text_equal(const text *t1, const text *t2);

    #endif // TEXT_H_

    // text.c file

    //#include "text.h"
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <string.h>
    #include <assert.h>

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

    text *text_new(const char content)
    {
    text * const t = malloc(sizeof(*t));
    if (t == NULL) goto MALLOC_ERR;
    const struct text zero = {
    .capacity = 0,
    .content = NULL,
    };
    *t = zero;
    if (content != NULL) {
    const int ret = text_append_str(t, content);
    if (ret) {
    goto TEXT_APPEND_ERR;
    }
    }
    return t;
    TEXT_APPEND_ERR:
    free(t);
    MALLOC_ERR:
    return NULL;
    }

    void text_free(text *t)
    {
    assert(t != NULL);
    free(t->content);
    free(t);
    }

    int text_resize(text *t, size_t newcapacity)
    {
    // printf("%s %d -> %dn", __func__, t->capacity, newcapacity);
    // we resize in chunks
    const size_t chunksize = 24;
    // clap the capacity into multiple of 24
    newcapacity = (newcapacity + chunksize - 1) / chunksize * chunksize;
    void * const tmp = realloc(t->content, newcapacity);
    if (tmp == NULL) return -ENOMEM;
    t->content = tmp;
    t->capacity = newcapacity;
    return 0;
    }

    int text_append_mem(text *to, const void *from, size_t from_len)
    {
    if (to == NULL || from == NULL) return -EINVAL;
    if (from_len == 0) return 0;
    const size_t oldcapacity = to->capacity == 0 ? 0 : strlen(to->content);
    const size_t newcapacity = oldcapacity + from_len + 1;
    int ret = text_resize(to, newcapacity);
    if (ret) return ret;
    memcpy(&to->content[newcapacity - from_len - 1], from, from_len);
    to->content[newcapacity - 1] = '';
    return 0;
    }

    int text_append_str(text *to, const char *from)
    {
    if (to == NULL || from == NULL) return -EINVAL;
    return text_append_mem(to, from, strlen(from));
    }

    int text_append(text *to, const text *from)
    {
    if (to == NULL || from == NULL) return -EINVAL;
    if (text_getCapacity(from) == 0) return 0;
    return text_append_str(to, text_get(from));
    }

    const char *text_get(const text *t)
    {
    return t->content;
    }

    const size_t text_strlen(const text *t)
    {
    return t->capacity == 0 ? 0 : strlen(t->content);
    }

    size_t text_getCapacity(const text *t)
    {
    return t->capacity;
    }

    bool text_equal_str(const text *t, const char *str)
    {
    assert(t != NULL);
    if (str == NULL && t->capacity == 0) return true;
    const size_t strlength = strlen(str);
    const size_t t_strlen = text_strlen(t);
    if (t_strlen != strlength) return false;
    if (memcmp(text_get(t), str, strlength) != 0) return false;
    return true;
    }

    // main.c file

    #include <stdio.h>

    int text_testAppend(void) {
    text *t = text_new("car");
    if (t == NULL) return -1;
    text *t2 = text_new("pet");
    if (t2 == NULL) return -1;
    if (text_append(t, t2)) return -1;
    assert(text_equal_str(t, "carpet"));
    assert(text_getCapacity(t) == 24);
    text *t3 = text_new("789012345678901234");
    if (t3 == NULL) return -1;
    if (text_append(t, t3)) return -1;
    assert(text_equal_str(t, "carpet789012345678901234"));
    assert(text_getCapacity(t) == 48);
    text_free(t);
    text_free(t2);
    text_free(t3);
    return 0;
    }

    int main()
    {
    text *t1 = text_new("abc");
    text_append_str(t1, "def");
    printf("%sn", text_get(t1));
    text_free(t1);

    printf("text_testAppend = %dn", text_testAppend());

    return 0;
    }





    share|improve this answer





















      Your Answer






      StackExchange.ifUsing("editor", function () {
      StackExchange.using("externalEditor", function () {
      StackExchange.using("snippets", function () {
      StackExchange.snippets.init();
      });
      });
      }, "code-snippets");

      StackExchange.ready(function() {
      var channelOptions = {
      tags: "".split(" "),
      id: "1"
      };
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function() {
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled) {
      StackExchange.using("snippets", function() {
      createEditor();
      });
      }
      else {
      createEditor();
      }
      });

      function createEditor() {
      StackExchange.prepareEditor({
      heartbeatType: 'answer',
      convertImagesToLinks: true,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: 10,
      bindNavPrevention: true,
      postfix: "",
      imageUploader: {
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      },
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      });


      }
      });














      draft saved

      draft discarded


















      StackExchange.ready(
      function () {
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53371504%2fmanipulating-structs-with-a-void-function-in-c%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      2 Answers
      2






      active

      oldest

      votes








      2 Answers
      2






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes








      up vote
      0
      down vote













      You are allocating memory in the wrong way. You could fix this by using a flexible array member like this:



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

      text *out = malloc(sizeof(text) + sizeof(something));
      strcpy(out->content, str);
      ...


      And obviously code such as this is nonsense:



        return out;
      free(c);
      }


      Enable compiler warnings and listen to them.






      share|improve this answer

























        up vote
        0
        down vote













        You are allocating memory in the wrong way. You could fix this by using a flexible array member like this:



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

        text *out = malloc(sizeof(text) + sizeof(something));
        strcpy(out->content, str);
        ...


        And obviously code such as this is nonsense:



          return out;
        free(c);
        }


        Enable compiler warnings and listen to them.






        share|improve this answer























          up vote
          0
          down vote










          up vote
          0
          down vote









          You are allocating memory in the wrong way. You could fix this by using a flexible array member like this:



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

          text *out = malloc(sizeof(text) + sizeof(something));
          strcpy(out->content, str);
          ...


          And obviously code such as this is nonsense:



            return out;
          free(c);
          }


          Enable compiler warnings and listen to them.






          share|improve this answer












          You are allocating memory in the wrong way. You could fix this by using a flexible array member like this:



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

          text *out = malloc(sizeof(text) + sizeof(something));
          strcpy(out->content, str);
          ...


          And obviously code such as this is nonsense:



            return out;
          free(c);
          }


          Enable compiler warnings and listen to them.







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Nov 19 at 10:01









          Lundin

          105k17154258




          105k17154258
























              up vote
              0
              down vote













              Och, some errors you have:




              1. Inside text_new you allocate memory for text *out using text *out = malloc(sizeGot); when sizeGot = 24 is a constant value. You should allocate sizeof(*out) or sizeof(text) bytes of memory for it.

              2. I don't know what for int sizeGot = 24; while (sizeNeeded > sizeGot) the loop inside text_new and append is for. I guess the intention is to do allocations in power of 24. Also it mostly looks like the same code is in both functions, it does look like code duplication, which is a bad thing.

              3. Inside append You pass a pointer to t1, not a double pointer, so if you modify the t1 pointer itself the modification will not be visible outside of function scope. t1 = newText(stringy); is just pointless and leaks memory. You could void append(text **t1, text *t2) and then *t1 = newText(stringy). But you can use a way better approach using realloc - I would expect append to "append" the string, not to create a new object. So first resize the buffer using realloc then strcat(&t1->content[oldcapacity - 1], string_to_copy_into_t1).


              4. int sizeNeeded = (t1->capacity + t2->capacity); is off. You allocate capacity in power of 24, which does not really interact with string length. You need to have strlen(t1->content) + strlen(t2->content) + 1 bytes for both strings and the null terminator.


              Try this:



              size_t text_newsize(size_t sizeNeeded)
              {
              // I think this is just `return 24 << (sizeNeeded / 24);`, but not sure
              int sizeGot = 24;
              while (sizeNeeded > sizeGot) {
              sizeGot *= 2;
              }
              return sizeGot;
              }

              text *newText(char *s) {
              printf("new Text from %sn", s);
              if (s == NULL) return NULL;
              int sizeNeeded = strlen(s) + 1;

              int sizeGot = text_newsize(sizeNeeded);

              text *out = malloc(sizeof(*out));
              if (out == NULL) {
              return NULL;
              }

              out->content = malloc(sizeGot);
              if (out->content == NULL) {
              free(out);
              return NULL;
              }

              strcpy(out->content, s);
              out->capacity = sizeGot;

              printf("the capacity is %dn", sizeGot);

              return out;
              }


              and this:



              int append(text *t1, text *t2) {
              printf("t1 content is %s, t2 content is %sn", t1->content, t2->content);
              int sizeNeeded = strlen(t1->content) + strlen(t2->content) + 1;

              if (t1->capacity < sizeNeeded) {

              // this could a text_resize(text*, size_t) function
              int sizeGot = text_newsize(sizeNeeded);
              void *tmp = realloc(t1->content, sizeGot);
              if (tmp == NULL) return -ENOMEM;
              t1->content = tmp;
              t1->capacity = sizeGot;

              }

              strcat(t1->content, t2->content);

              return 0;
              }


              Some remarks:




              1. Try to handle errors in your library. If you have a function like void append(text *t1, text *t2) let it be int append(text *t1, text *t2) and return 0 on success and negative number on *alloc errors.

              2. Store the size of everything using size_t type. It's defined in stddef.h and should be used to represent a size of an object. strlen returns size_t and sizeof also returns size_t.

              3. I like to put everything inside a single "namespace", I do that by prepending the functions with a string like text_.

              4. I got some free time and decided to implement your library. Below is the code with a simple text object storing strings, I use 24 magic number as allocation chunk size.




              // text.h file

              #ifndef TEXT_H_
              #define TEXT_H_

              #include <stddef.h>
              #include <stdbool.h>

              struct text;

              typedef struct text text;

              text *text_new(const char content);
              void text_free(text *t);
              int text_resize(text *t, size_t newsize);
              int text_append(text *to, const text *from);
              int text_append_mem(text *to, const void *from, size_t from_len);
              const char *text_get(const text *t);
              int text_append_str(text *to, const char *from);
              char *text_get_nonconst(text *t);
              size_t text_getCapacity(const text *t);
              bool text_equal(const text *t1, const text *t2);

              #endif // TEXT_H_

              // text.c file

              //#include "text.h"
              #include <stdio.h>
              #include <stdlib.h>
              #include <errno.h>
              #include <string.h>
              #include <assert.h>

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

              text *text_new(const char content)
              {
              text * const t = malloc(sizeof(*t));
              if (t == NULL) goto MALLOC_ERR;
              const struct text zero = {
              .capacity = 0,
              .content = NULL,
              };
              *t = zero;
              if (content != NULL) {
              const int ret = text_append_str(t, content);
              if (ret) {
              goto TEXT_APPEND_ERR;
              }
              }
              return t;
              TEXT_APPEND_ERR:
              free(t);
              MALLOC_ERR:
              return NULL;
              }

              void text_free(text *t)
              {
              assert(t != NULL);
              free(t->content);
              free(t);
              }

              int text_resize(text *t, size_t newcapacity)
              {
              // printf("%s %d -> %dn", __func__, t->capacity, newcapacity);
              // we resize in chunks
              const size_t chunksize = 24;
              // clap the capacity into multiple of 24
              newcapacity = (newcapacity + chunksize - 1) / chunksize * chunksize;
              void * const tmp = realloc(t->content, newcapacity);
              if (tmp == NULL) return -ENOMEM;
              t->content = tmp;
              t->capacity = newcapacity;
              return 0;
              }

              int text_append_mem(text *to, const void *from, size_t from_len)
              {
              if (to == NULL || from == NULL) return -EINVAL;
              if (from_len == 0) return 0;
              const size_t oldcapacity = to->capacity == 0 ? 0 : strlen(to->content);
              const size_t newcapacity = oldcapacity + from_len + 1;
              int ret = text_resize(to, newcapacity);
              if (ret) return ret;
              memcpy(&to->content[newcapacity - from_len - 1], from, from_len);
              to->content[newcapacity - 1] = '';
              return 0;
              }

              int text_append_str(text *to, const char *from)
              {
              if (to == NULL || from == NULL) return -EINVAL;
              return text_append_mem(to, from, strlen(from));
              }

              int text_append(text *to, const text *from)
              {
              if (to == NULL || from == NULL) return -EINVAL;
              if (text_getCapacity(from) == 0) return 0;
              return text_append_str(to, text_get(from));
              }

              const char *text_get(const text *t)
              {
              return t->content;
              }

              const size_t text_strlen(const text *t)
              {
              return t->capacity == 0 ? 0 : strlen(t->content);
              }

              size_t text_getCapacity(const text *t)
              {
              return t->capacity;
              }

              bool text_equal_str(const text *t, const char *str)
              {
              assert(t != NULL);
              if (str == NULL && t->capacity == 0) return true;
              const size_t strlength = strlen(str);
              const size_t t_strlen = text_strlen(t);
              if (t_strlen != strlength) return false;
              if (memcmp(text_get(t), str, strlength) != 0) return false;
              return true;
              }

              // main.c file

              #include <stdio.h>

              int text_testAppend(void) {
              text *t = text_new("car");
              if (t == NULL) return -1;
              text *t2 = text_new("pet");
              if (t2 == NULL) return -1;
              if (text_append(t, t2)) return -1;
              assert(text_equal_str(t, "carpet"));
              assert(text_getCapacity(t) == 24);
              text *t3 = text_new("789012345678901234");
              if (t3 == NULL) return -1;
              if (text_append(t, t3)) return -1;
              assert(text_equal_str(t, "carpet789012345678901234"));
              assert(text_getCapacity(t) == 48);
              text_free(t);
              text_free(t2);
              text_free(t3);
              return 0;
              }

              int main()
              {
              text *t1 = text_new("abc");
              text_append_str(t1, "def");
              printf("%sn", text_get(t1));
              text_free(t1);

              printf("text_testAppend = %dn", text_testAppend());

              return 0;
              }





              share|improve this answer

























                up vote
                0
                down vote













                Och, some errors you have:




                1. Inside text_new you allocate memory for text *out using text *out = malloc(sizeGot); when sizeGot = 24 is a constant value. You should allocate sizeof(*out) or sizeof(text) bytes of memory for it.

                2. I don't know what for int sizeGot = 24; while (sizeNeeded > sizeGot) the loop inside text_new and append is for. I guess the intention is to do allocations in power of 24. Also it mostly looks like the same code is in both functions, it does look like code duplication, which is a bad thing.

                3. Inside append You pass a pointer to t1, not a double pointer, so if you modify the t1 pointer itself the modification will not be visible outside of function scope. t1 = newText(stringy); is just pointless and leaks memory. You could void append(text **t1, text *t2) and then *t1 = newText(stringy). But you can use a way better approach using realloc - I would expect append to "append" the string, not to create a new object. So first resize the buffer using realloc then strcat(&t1->content[oldcapacity - 1], string_to_copy_into_t1).


                4. int sizeNeeded = (t1->capacity + t2->capacity); is off. You allocate capacity in power of 24, which does not really interact with string length. You need to have strlen(t1->content) + strlen(t2->content) + 1 bytes for both strings and the null terminator.


                Try this:



                size_t text_newsize(size_t sizeNeeded)
                {
                // I think this is just `return 24 << (sizeNeeded / 24);`, but not sure
                int sizeGot = 24;
                while (sizeNeeded > sizeGot) {
                sizeGot *= 2;
                }
                return sizeGot;
                }

                text *newText(char *s) {
                printf("new Text from %sn", s);
                if (s == NULL) return NULL;
                int sizeNeeded = strlen(s) + 1;

                int sizeGot = text_newsize(sizeNeeded);

                text *out = malloc(sizeof(*out));
                if (out == NULL) {
                return NULL;
                }

                out->content = malloc(sizeGot);
                if (out->content == NULL) {
                free(out);
                return NULL;
                }

                strcpy(out->content, s);
                out->capacity = sizeGot;

                printf("the capacity is %dn", sizeGot);

                return out;
                }


                and this:



                int append(text *t1, text *t2) {
                printf("t1 content is %s, t2 content is %sn", t1->content, t2->content);
                int sizeNeeded = strlen(t1->content) + strlen(t2->content) + 1;

                if (t1->capacity < sizeNeeded) {

                // this could a text_resize(text*, size_t) function
                int sizeGot = text_newsize(sizeNeeded);
                void *tmp = realloc(t1->content, sizeGot);
                if (tmp == NULL) return -ENOMEM;
                t1->content = tmp;
                t1->capacity = sizeGot;

                }

                strcat(t1->content, t2->content);

                return 0;
                }


                Some remarks:




                1. Try to handle errors in your library. If you have a function like void append(text *t1, text *t2) let it be int append(text *t1, text *t2) and return 0 on success and negative number on *alloc errors.

                2. Store the size of everything using size_t type. It's defined in stddef.h and should be used to represent a size of an object. strlen returns size_t and sizeof also returns size_t.

                3. I like to put everything inside a single "namespace", I do that by prepending the functions with a string like text_.

                4. I got some free time and decided to implement your library. Below is the code with a simple text object storing strings, I use 24 magic number as allocation chunk size.




                // text.h file

                #ifndef TEXT_H_
                #define TEXT_H_

                #include <stddef.h>
                #include <stdbool.h>

                struct text;

                typedef struct text text;

                text *text_new(const char content);
                void text_free(text *t);
                int text_resize(text *t, size_t newsize);
                int text_append(text *to, const text *from);
                int text_append_mem(text *to, const void *from, size_t from_len);
                const char *text_get(const text *t);
                int text_append_str(text *to, const char *from);
                char *text_get_nonconst(text *t);
                size_t text_getCapacity(const text *t);
                bool text_equal(const text *t1, const text *t2);

                #endif // TEXT_H_

                // text.c file

                //#include "text.h"
                #include <stdio.h>
                #include <stdlib.h>
                #include <errno.h>
                #include <string.h>
                #include <assert.h>

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

                text *text_new(const char content)
                {
                text * const t = malloc(sizeof(*t));
                if (t == NULL) goto MALLOC_ERR;
                const struct text zero = {
                .capacity = 0,
                .content = NULL,
                };
                *t = zero;
                if (content != NULL) {
                const int ret = text_append_str(t, content);
                if (ret) {
                goto TEXT_APPEND_ERR;
                }
                }
                return t;
                TEXT_APPEND_ERR:
                free(t);
                MALLOC_ERR:
                return NULL;
                }

                void text_free(text *t)
                {
                assert(t != NULL);
                free(t->content);
                free(t);
                }

                int text_resize(text *t, size_t newcapacity)
                {
                // printf("%s %d -> %dn", __func__, t->capacity, newcapacity);
                // we resize in chunks
                const size_t chunksize = 24;
                // clap the capacity into multiple of 24
                newcapacity = (newcapacity + chunksize - 1) / chunksize * chunksize;
                void * const tmp = realloc(t->content, newcapacity);
                if (tmp == NULL) return -ENOMEM;
                t->content = tmp;
                t->capacity = newcapacity;
                return 0;
                }

                int text_append_mem(text *to, const void *from, size_t from_len)
                {
                if (to == NULL || from == NULL) return -EINVAL;
                if (from_len == 0) return 0;
                const size_t oldcapacity = to->capacity == 0 ? 0 : strlen(to->content);
                const size_t newcapacity = oldcapacity + from_len + 1;
                int ret = text_resize(to, newcapacity);
                if (ret) return ret;
                memcpy(&to->content[newcapacity - from_len - 1], from, from_len);
                to->content[newcapacity - 1] = '';
                return 0;
                }

                int text_append_str(text *to, const char *from)
                {
                if (to == NULL || from == NULL) return -EINVAL;
                return text_append_mem(to, from, strlen(from));
                }

                int text_append(text *to, const text *from)
                {
                if (to == NULL || from == NULL) return -EINVAL;
                if (text_getCapacity(from) == 0) return 0;
                return text_append_str(to, text_get(from));
                }

                const char *text_get(const text *t)
                {
                return t->content;
                }

                const size_t text_strlen(const text *t)
                {
                return t->capacity == 0 ? 0 : strlen(t->content);
                }

                size_t text_getCapacity(const text *t)
                {
                return t->capacity;
                }

                bool text_equal_str(const text *t, const char *str)
                {
                assert(t != NULL);
                if (str == NULL && t->capacity == 0) return true;
                const size_t strlength = strlen(str);
                const size_t t_strlen = text_strlen(t);
                if (t_strlen != strlength) return false;
                if (memcmp(text_get(t), str, strlength) != 0) return false;
                return true;
                }

                // main.c file

                #include <stdio.h>

                int text_testAppend(void) {
                text *t = text_new("car");
                if (t == NULL) return -1;
                text *t2 = text_new("pet");
                if (t2 == NULL) return -1;
                if (text_append(t, t2)) return -1;
                assert(text_equal_str(t, "carpet"));
                assert(text_getCapacity(t) == 24);
                text *t3 = text_new("789012345678901234");
                if (t3 == NULL) return -1;
                if (text_append(t, t3)) return -1;
                assert(text_equal_str(t, "carpet789012345678901234"));
                assert(text_getCapacity(t) == 48);
                text_free(t);
                text_free(t2);
                text_free(t3);
                return 0;
                }

                int main()
                {
                text *t1 = text_new("abc");
                text_append_str(t1, "def");
                printf("%sn", text_get(t1));
                text_free(t1);

                printf("text_testAppend = %dn", text_testAppend());

                return 0;
                }





                share|improve this answer























                  up vote
                  0
                  down vote










                  up vote
                  0
                  down vote









                  Och, some errors you have:




                  1. Inside text_new you allocate memory for text *out using text *out = malloc(sizeGot); when sizeGot = 24 is a constant value. You should allocate sizeof(*out) or sizeof(text) bytes of memory for it.

                  2. I don't know what for int sizeGot = 24; while (sizeNeeded > sizeGot) the loop inside text_new and append is for. I guess the intention is to do allocations in power of 24. Also it mostly looks like the same code is in both functions, it does look like code duplication, which is a bad thing.

                  3. Inside append You pass a pointer to t1, not a double pointer, so if you modify the t1 pointer itself the modification will not be visible outside of function scope. t1 = newText(stringy); is just pointless and leaks memory. You could void append(text **t1, text *t2) and then *t1 = newText(stringy). But you can use a way better approach using realloc - I would expect append to "append" the string, not to create a new object. So first resize the buffer using realloc then strcat(&t1->content[oldcapacity - 1], string_to_copy_into_t1).


                  4. int sizeNeeded = (t1->capacity + t2->capacity); is off. You allocate capacity in power of 24, which does not really interact with string length. You need to have strlen(t1->content) + strlen(t2->content) + 1 bytes for both strings and the null terminator.


                  Try this:



                  size_t text_newsize(size_t sizeNeeded)
                  {
                  // I think this is just `return 24 << (sizeNeeded / 24);`, but not sure
                  int sizeGot = 24;
                  while (sizeNeeded > sizeGot) {
                  sizeGot *= 2;
                  }
                  return sizeGot;
                  }

                  text *newText(char *s) {
                  printf("new Text from %sn", s);
                  if (s == NULL) return NULL;
                  int sizeNeeded = strlen(s) + 1;

                  int sizeGot = text_newsize(sizeNeeded);

                  text *out = malloc(sizeof(*out));
                  if (out == NULL) {
                  return NULL;
                  }

                  out->content = malloc(sizeGot);
                  if (out->content == NULL) {
                  free(out);
                  return NULL;
                  }

                  strcpy(out->content, s);
                  out->capacity = sizeGot;

                  printf("the capacity is %dn", sizeGot);

                  return out;
                  }


                  and this:



                  int append(text *t1, text *t2) {
                  printf("t1 content is %s, t2 content is %sn", t1->content, t2->content);
                  int sizeNeeded = strlen(t1->content) + strlen(t2->content) + 1;

                  if (t1->capacity < sizeNeeded) {

                  // this could a text_resize(text*, size_t) function
                  int sizeGot = text_newsize(sizeNeeded);
                  void *tmp = realloc(t1->content, sizeGot);
                  if (tmp == NULL) return -ENOMEM;
                  t1->content = tmp;
                  t1->capacity = sizeGot;

                  }

                  strcat(t1->content, t2->content);

                  return 0;
                  }


                  Some remarks:




                  1. Try to handle errors in your library. If you have a function like void append(text *t1, text *t2) let it be int append(text *t1, text *t2) and return 0 on success and negative number on *alloc errors.

                  2. Store the size of everything using size_t type. It's defined in stddef.h and should be used to represent a size of an object. strlen returns size_t and sizeof also returns size_t.

                  3. I like to put everything inside a single "namespace", I do that by prepending the functions with a string like text_.

                  4. I got some free time and decided to implement your library. Below is the code with a simple text object storing strings, I use 24 magic number as allocation chunk size.




                  // text.h file

                  #ifndef TEXT_H_
                  #define TEXT_H_

                  #include <stddef.h>
                  #include <stdbool.h>

                  struct text;

                  typedef struct text text;

                  text *text_new(const char content);
                  void text_free(text *t);
                  int text_resize(text *t, size_t newsize);
                  int text_append(text *to, const text *from);
                  int text_append_mem(text *to, const void *from, size_t from_len);
                  const char *text_get(const text *t);
                  int text_append_str(text *to, const char *from);
                  char *text_get_nonconst(text *t);
                  size_t text_getCapacity(const text *t);
                  bool text_equal(const text *t1, const text *t2);

                  #endif // TEXT_H_

                  // text.c file

                  //#include "text.h"
                  #include <stdio.h>
                  #include <stdlib.h>
                  #include <errno.h>
                  #include <string.h>
                  #include <assert.h>

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

                  text *text_new(const char content)
                  {
                  text * const t = malloc(sizeof(*t));
                  if (t == NULL) goto MALLOC_ERR;
                  const struct text zero = {
                  .capacity = 0,
                  .content = NULL,
                  };
                  *t = zero;
                  if (content != NULL) {
                  const int ret = text_append_str(t, content);
                  if (ret) {
                  goto TEXT_APPEND_ERR;
                  }
                  }
                  return t;
                  TEXT_APPEND_ERR:
                  free(t);
                  MALLOC_ERR:
                  return NULL;
                  }

                  void text_free(text *t)
                  {
                  assert(t != NULL);
                  free(t->content);
                  free(t);
                  }

                  int text_resize(text *t, size_t newcapacity)
                  {
                  // printf("%s %d -> %dn", __func__, t->capacity, newcapacity);
                  // we resize in chunks
                  const size_t chunksize = 24;
                  // clap the capacity into multiple of 24
                  newcapacity = (newcapacity + chunksize - 1) / chunksize * chunksize;
                  void * const tmp = realloc(t->content, newcapacity);
                  if (tmp == NULL) return -ENOMEM;
                  t->content = tmp;
                  t->capacity = newcapacity;
                  return 0;
                  }

                  int text_append_mem(text *to, const void *from, size_t from_len)
                  {
                  if (to == NULL || from == NULL) return -EINVAL;
                  if (from_len == 0) return 0;
                  const size_t oldcapacity = to->capacity == 0 ? 0 : strlen(to->content);
                  const size_t newcapacity = oldcapacity + from_len + 1;
                  int ret = text_resize(to, newcapacity);
                  if (ret) return ret;
                  memcpy(&to->content[newcapacity - from_len - 1], from, from_len);
                  to->content[newcapacity - 1] = '';
                  return 0;
                  }

                  int text_append_str(text *to, const char *from)
                  {
                  if (to == NULL || from == NULL) return -EINVAL;
                  return text_append_mem(to, from, strlen(from));
                  }

                  int text_append(text *to, const text *from)
                  {
                  if (to == NULL || from == NULL) return -EINVAL;
                  if (text_getCapacity(from) == 0) return 0;
                  return text_append_str(to, text_get(from));
                  }

                  const char *text_get(const text *t)
                  {
                  return t->content;
                  }

                  const size_t text_strlen(const text *t)
                  {
                  return t->capacity == 0 ? 0 : strlen(t->content);
                  }

                  size_t text_getCapacity(const text *t)
                  {
                  return t->capacity;
                  }

                  bool text_equal_str(const text *t, const char *str)
                  {
                  assert(t != NULL);
                  if (str == NULL && t->capacity == 0) return true;
                  const size_t strlength = strlen(str);
                  const size_t t_strlen = text_strlen(t);
                  if (t_strlen != strlength) return false;
                  if (memcmp(text_get(t), str, strlength) != 0) return false;
                  return true;
                  }

                  // main.c file

                  #include <stdio.h>

                  int text_testAppend(void) {
                  text *t = text_new("car");
                  if (t == NULL) return -1;
                  text *t2 = text_new("pet");
                  if (t2 == NULL) return -1;
                  if (text_append(t, t2)) return -1;
                  assert(text_equal_str(t, "carpet"));
                  assert(text_getCapacity(t) == 24);
                  text *t3 = text_new("789012345678901234");
                  if (t3 == NULL) return -1;
                  if (text_append(t, t3)) return -1;
                  assert(text_equal_str(t, "carpet789012345678901234"));
                  assert(text_getCapacity(t) == 48);
                  text_free(t);
                  text_free(t2);
                  text_free(t3);
                  return 0;
                  }

                  int main()
                  {
                  text *t1 = text_new("abc");
                  text_append_str(t1, "def");
                  printf("%sn", text_get(t1));
                  text_free(t1);

                  printf("text_testAppend = %dn", text_testAppend());

                  return 0;
                  }





                  share|improve this answer












                  Och, some errors you have:




                  1. Inside text_new you allocate memory for text *out using text *out = malloc(sizeGot); when sizeGot = 24 is a constant value. You should allocate sizeof(*out) or sizeof(text) bytes of memory for it.

                  2. I don't know what for int sizeGot = 24; while (sizeNeeded > sizeGot) the loop inside text_new and append is for. I guess the intention is to do allocations in power of 24. Also it mostly looks like the same code is in both functions, it does look like code duplication, which is a bad thing.

                  3. Inside append You pass a pointer to t1, not a double pointer, so if you modify the t1 pointer itself the modification will not be visible outside of function scope. t1 = newText(stringy); is just pointless and leaks memory. You could void append(text **t1, text *t2) and then *t1 = newText(stringy). But you can use a way better approach using realloc - I would expect append to "append" the string, not to create a new object. So first resize the buffer using realloc then strcat(&t1->content[oldcapacity - 1], string_to_copy_into_t1).


                  4. int sizeNeeded = (t1->capacity + t2->capacity); is off. You allocate capacity in power of 24, which does not really interact with string length. You need to have strlen(t1->content) + strlen(t2->content) + 1 bytes for both strings and the null terminator.


                  Try this:



                  size_t text_newsize(size_t sizeNeeded)
                  {
                  // I think this is just `return 24 << (sizeNeeded / 24);`, but not sure
                  int sizeGot = 24;
                  while (sizeNeeded > sizeGot) {
                  sizeGot *= 2;
                  }
                  return sizeGot;
                  }

                  text *newText(char *s) {
                  printf("new Text from %sn", s);
                  if (s == NULL) return NULL;
                  int sizeNeeded = strlen(s) + 1;

                  int sizeGot = text_newsize(sizeNeeded);

                  text *out = malloc(sizeof(*out));
                  if (out == NULL) {
                  return NULL;
                  }

                  out->content = malloc(sizeGot);
                  if (out->content == NULL) {
                  free(out);
                  return NULL;
                  }

                  strcpy(out->content, s);
                  out->capacity = sizeGot;

                  printf("the capacity is %dn", sizeGot);

                  return out;
                  }


                  and this:



                  int append(text *t1, text *t2) {
                  printf("t1 content is %s, t2 content is %sn", t1->content, t2->content);
                  int sizeNeeded = strlen(t1->content) + strlen(t2->content) + 1;

                  if (t1->capacity < sizeNeeded) {

                  // this could a text_resize(text*, size_t) function
                  int sizeGot = text_newsize(sizeNeeded);
                  void *tmp = realloc(t1->content, sizeGot);
                  if (tmp == NULL) return -ENOMEM;
                  t1->content = tmp;
                  t1->capacity = sizeGot;

                  }

                  strcat(t1->content, t2->content);

                  return 0;
                  }


                  Some remarks:




                  1. Try to handle errors in your library. If you have a function like void append(text *t1, text *t2) let it be int append(text *t1, text *t2) and return 0 on success and negative number on *alloc errors.

                  2. Store the size of everything using size_t type. It's defined in stddef.h and should be used to represent a size of an object. strlen returns size_t and sizeof also returns size_t.

                  3. I like to put everything inside a single "namespace", I do that by prepending the functions with a string like text_.

                  4. I got some free time and decided to implement your library. Below is the code with a simple text object storing strings, I use 24 magic number as allocation chunk size.




                  // text.h file

                  #ifndef TEXT_H_
                  #define TEXT_H_

                  #include <stddef.h>
                  #include <stdbool.h>

                  struct text;

                  typedef struct text text;

                  text *text_new(const char content);
                  void text_free(text *t);
                  int text_resize(text *t, size_t newsize);
                  int text_append(text *to, const text *from);
                  int text_append_mem(text *to, const void *from, size_t from_len);
                  const char *text_get(const text *t);
                  int text_append_str(text *to, const char *from);
                  char *text_get_nonconst(text *t);
                  size_t text_getCapacity(const text *t);
                  bool text_equal(const text *t1, const text *t2);

                  #endif // TEXT_H_

                  // text.c file

                  //#include "text.h"
                  #include <stdio.h>
                  #include <stdlib.h>
                  #include <errno.h>
                  #include <string.h>
                  #include <assert.h>

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

                  text *text_new(const char content)
                  {
                  text * const t = malloc(sizeof(*t));
                  if (t == NULL) goto MALLOC_ERR;
                  const struct text zero = {
                  .capacity = 0,
                  .content = NULL,
                  };
                  *t = zero;
                  if (content != NULL) {
                  const int ret = text_append_str(t, content);
                  if (ret) {
                  goto TEXT_APPEND_ERR;
                  }
                  }
                  return t;
                  TEXT_APPEND_ERR:
                  free(t);
                  MALLOC_ERR:
                  return NULL;
                  }

                  void text_free(text *t)
                  {
                  assert(t != NULL);
                  free(t->content);
                  free(t);
                  }

                  int text_resize(text *t, size_t newcapacity)
                  {
                  // printf("%s %d -> %dn", __func__, t->capacity, newcapacity);
                  // we resize in chunks
                  const size_t chunksize = 24;
                  // clap the capacity into multiple of 24
                  newcapacity = (newcapacity + chunksize - 1) / chunksize * chunksize;
                  void * const tmp = realloc(t->content, newcapacity);
                  if (tmp == NULL) return -ENOMEM;
                  t->content = tmp;
                  t->capacity = newcapacity;
                  return 0;
                  }

                  int text_append_mem(text *to, const void *from, size_t from_len)
                  {
                  if (to == NULL || from == NULL) return -EINVAL;
                  if (from_len == 0) return 0;
                  const size_t oldcapacity = to->capacity == 0 ? 0 : strlen(to->content);
                  const size_t newcapacity = oldcapacity + from_len + 1;
                  int ret = text_resize(to, newcapacity);
                  if (ret) return ret;
                  memcpy(&to->content[newcapacity - from_len - 1], from, from_len);
                  to->content[newcapacity - 1] = '';
                  return 0;
                  }

                  int text_append_str(text *to, const char *from)
                  {
                  if (to == NULL || from == NULL) return -EINVAL;
                  return text_append_mem(to, from, strlen(from));
                  }

                  int text_append(text *to, const text *from)
                  {
                  if (to == NULL || from == NULL) return -EINVAL;
                  if (text_getCapacity(from) == 0) return 0;
                  return text_append_str(to, text_get(from));
                  }

                  const char *text_get(const text *t)
                  {
                  return t->content;
                  }

                  const size_t text_strlen(const text *t)
                  {
                  return t->capacity == 0 ? 0 : strlen(t->content);
                  }

                  size_t text_getCapacity(const text *t)
                  {
                  return t->capacity;
                  }

                  bool text_equal_str(const text *t, const char *str)
                  {
                  assert(t != NULL);
                  if (str == NULL && t->capacity == 0) return true;
                  const size_t strlength = strlen(str);
                  const size_t t_strlen = text_strlen(t);
                  if (t_strlen != strlength) return false;
                  if (memcmp(text_get(t), str, strlength) != 0) return false;
                  return true;
                  }

                  // main.c file

                  #include <stdio.h>

                  int text_testAppend(void) {
                  text *t = text_new("car");
                  if (t == NULL) return -1;
                  text *t2 = text_new("pet");
                  if (t2 == NULL) return -1;
                  if (text_append(t, t2)) return -1;
                  assert(text_equal_str(t, "carpet"));
                  assert(text_getCapacity(t) == 24);
                  text *t3 = text_new("789012345678901234");
                  if (t3 == NULL) return -1;
                  if (text_append(t, t3)) return -1;
                  assert(text_equal_str(t, "carpet789012345678901234"));
                  assert(text_getCapacity(t) == 48);
                  text_free(t);
                  text_free(t2);
                  text_free(t3);
                  return 0;
                  }

                  int main()
                  {
                  text *t1 = text_new("abc");
                  text_append_str(t1, "def");
                  printf("%sn", text_get(t1));
                  text_free(t1);

                  printf("text_testAppend = %dn", text_testAppend());

                  return 0;
                  }






                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Nov 19 at 13:31









                  Kamil Cuk

                  7,9871222




                  7,9871222






























                      draft saved

                      draft discarded




















































                      Thanks for contributing an answer to Stack Overflow!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.





                      Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                      Please pay close attention to the following guidance:


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function () {
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53371504%2fmanipulating-structs-with-a-void-function-in-c%23new-answer', 'question_page');
                      }
                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown