2

I have a struct like this:

typedef struct TEXT {
    char *text;
    struct TEXT *next;
} TEXT;

and in some function I have something like:

TEXT *line = (TEXT *) malloc(sizeof(TEXT));
line->text = NULL; // was "\0" before
char current = getchar();
while (current != '\n') {
    AddChar(&(line->text), current); // Was AddChar(line->text, current);
    current = getchar();
}

and the AddChar function was something like this:

void AddChar(char **text, char c) { //was char *text
    *text = (char *) realloc(*text, strlen(*text)+2); //was text = ...
    char char_array[2] = {c, '\0'); 
    strcat(*text, char_array); //was strcat(text, char_array);
}

And unfortunately, program crashed.

As far as I understand, turns out that strlen can't figure out that if text == NULL, length should be 0...

Anyway, with this AddChar function, everything works:

void AddChar(char **text, char c) {
    if (*text == NULL) {
        *text = (char *) malloc(2);
        (*text)[0] = c;
        (*text)[1] = '\0';
    }
    else {
        *text= (char *) realloc(*text, sizeof(*text)+2);
        char char_array[2] = { c , '\0' };
        strcat(*text, char_array);
    }
}

.

.

I also had a problem with

void AddChar(char *text, char c) {
    text = "something";
}

not changing line->text, but changing *text to **text fixed that.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
Jecke
  • 239
  • 2
  • 13

4 Answers4

5

Only a NULL initialized pointer or a pointer returned by malloc family functions (malloc, calloc and realloc) can be passed to another malloc family functions. line->text is initialized with a string literal "\0" and therefore line->text can't be passed to realloc function.
Also note that you can't modify a string literal.

haccks
  • 104,019
  • 25
  • 176
  • 264
2

One of the problems is that you try to reallocate something you didn't allocate yourself.

The other problem is that you try to reassign a local variable inside the AddChar function and expect it to have any effect on the calling function, which it doesn't.

When passing an argument to a function it's passed by value which means that the value is copied into the local argument variable in the function, that variables local inside the function only, and changing that will not change the original variable used when calling the function. What you need is passing the argument by reference which C does not support, but you can emulate it by using a pointer. In your case by using a pointer to the pointer using the address-of operator &.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • I thought I'm only passing a pointer (char*), not copying the whole string – Jecke Jan 02 '16 at 20:22
  • 1
    @Jecke When you pass a pointer like you do, the *pointer* is copied. That leads you to have *two* pointer variables, initially pointing to the same memory, but once you reassign inside the function you have two different pointers each pointing to different locations. – Some programmer dude Jan 02 '16 at 20:23
  • I see. But why does pointer change after realloc? In this example: http://www.tutorialspoint.com/c_standard_library/c_function_realloc.htm the adress of str is same before and after realloc. – Jecke Jan 02 '16 at 20:30
  • 1
    @Jecke The `realloc` function *may* return the same pointer. Or it *may* return a totally different pointer. Or it *may* return `NULL` (which you do not handle, and I don't mean you should just check for it, don't assign the result directly to the pointer you're reallocating as that might mean you loose the original pointer if `realloc` fails). – Some programmer dude Jan 02 '16 at 20:31
  • Ok, I see. I used char **text and &(line->text) and it works better now (if I write explicitly *text = "something", it changes), but I still can't handle realloc. – Jecke Jan 02 '16 at 20:44
  • I have problem with strlen function. I have: `void AddChar(char **text, char c) { /*...*/ strlen(*text); /*...*/ }` and it crashes when text is (null) – Jecke Jan 02 '16 at 20:51
  • @Jecke Don't use `NULL` pointers when calling just about any function, using a null pointer leads to *undefined behavior* (and most likely crashes). Not all functions that accepts pointers are possible to call with a null pointer, only very few. – Some programmer dude Jan 02 '16 at 20:55
  • Oh, I'd think that the function returning length of null-terminated string will be able to tell the length of the null-terminated string. My bad; I broke it into two cases (text == NULL or not) and it works now. Thank you :) – Jecke Jan 02 '16 at 21:13
0

As far as I understand, turns out that strlen can't figure out that if text == NULL, length should be 0...

On the other hand, section 7.1.4, paragraph 1 the C standard states that NULL is an invalid value for strlen:

If an argument to a function has an invalid value (such as a value outside the domain of the function, or a pointer outside the address space of the program, or a null pointer, or a pointer to non-modifiable storage when the corresponding parameter is not const-qualified) or a type (after promotion) not expected by a function with variable number of arguments, the behavior is undefined.


While we're on the subject of invalid values, strcat will expect that both of it's arguments contain strings. What this means is they must both contain a sequence of characters that terminates at the first '\0' character. You've demonstrated this by adding a '\0' character to the array representing your second argument, but can you prove that there's a '\0' character in the array representing your first argument?


*text = (char *) realloc(*text, strlen(*text)+2);

Null pointer aside, anything of the pattern x = realloc(x, ...); is wrong. For more information on that, see Proper usage of realloc.


*text= (char *) realloc(*text, sizeof(*text)+2);

As for this, it may seem to work for you at first, but I can assure you that it's broken. It is important to realise that you intend to allocate characters, not char *s, so sizeof(*text) (which is sizeof (char *)) is invalid here.

Typically this would allocate between 6 and 10 characters. If you overflow that buffer, the behaviour is undefined. What does that mean? Well, it'd be a paradox to define the undefined behaviour, but typical consequences of undefined behaviour range from "nothing at all" and "it works fine" for some systems to "segfaults" and "heartbleed" for other systems; it's not portable nonetheless.


I also had a problem with

void AddChar(char *text, char c) {
    text = "something";
}

not changing line->text, but changing *text to **text fixed that.

I'm curious as to what you think AddChar(NULL, 42); would do, when AddChar is defined like this... Do you think it'd assign the null pointer constant that is NULL to the string literal that is "something"?

C doesn't support pass-by-reference. When you pass an argument (for example, a pointer, such as an array expressions implicitly converted to a pointer), what happens behind the scenes is a new variable is declared (in this case, the argument char *text is a variable) and a copy of that value (in this case, a pointer) is assigned to that variable. This is known as "pass-by-value".

When you pass a pointer, and modify the thing that the pointer is pointing at (e.g. by using *pointer = ... or pointer[x] = ...), you're emulating pass-by-reference; you're modifying an object that the variable refers to, rather than modifying the variable itself.


Ohh, and one more thing, despite the link to the "Proper usage of realloc", you should not cast the return value of malloc (or `realloc) in C.

Community
  • 1
  • 1
autistic
  • 1
  • 3
  • 35
  • 80
0

Disagree with OP's final "Anyway, with this AddChar function, everything works:" as that does not allocate sufficient memory.

Suggest

void AddChar(char **text, char c) {
    size_t length = strlen(*text); 
    *text = realloc(*text, length + 2);  // proper size calculation
    if (*text == NULL) {  // check if successful
      abort(-1);
    } 
    (*text)[length] = c;
    (*text)[length + 1] = '\0';
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256