简体   繁体   中英

Why can't I realloc this string?

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.

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.

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 & .

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 (eg 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 .

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';
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM