Search code examples
cpointersmallocrealloc

Calling malloc and realloc from within a function gives unexpected results


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

char _getline(char *s)
{
    char c;
    s = (char *)malloc(sizeof(char));

    int i;
    for (i = 0; (s[i] = getchar()) != EOF && s[i] != '\n'; ++i)
    {
        s = (char *)realloc(s, (i + 1) * sizeof(char));
    }
    c = s[i];
    s = (char *)realloc(s, (i + 1) * sizeof(char));
    ++i;
    s[i] = '\0';
    return c; 
}

int main()
{
    char *s = "word";
    char c;
    _getline(s);
    printf("%s\n", s);
    free(s);
    return 0;
}

The output is :

input
word
munmap_chunk(): invalid pointer
Aborted (core dumped)

When I do the same thing in main I don't get the error, but when I try to print the string, I get a \0. Also when I tried to pass the pointer's address to _getline I got a segmentation fault. Here's that attempt:

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

char _getline(char **s)
{
    char c;
    *s = (char *)malloc(sizeof(char));

    int i;
    for (i = 0; (*s[i] = getchar()) != EOF && *s[i] != '\n'; ++i)
    {
        *s = (char *)realloc(*s, (i + 1) * sizeof(char));
    }
    c = *s[i];
    *s = (char *)realloc(*s, (i + 1) * sizeof(char));
    ++i;
    *s[i] = '\0';
    return c; 
}

int main()
{
    char *s = "word";
    char c;
    _getline(&s);
    printf("%s\n", s);
    free(s);
    return 0;
}

What am I doing wrong?


Solution

  • In your first attempt, you misunderstand how passing a pointer works. When you reassign s in _getline, that only affects its s, not main's s, so main prints word and then tries to free a string literal, which predictably ends badly. To fix it, change _getline(s) to _getline(&s), make _getline take a char ** instead of a char *, and change all of its uses of s to *s. Note that changing all of its uses isn't a simple text replacement; in some cases, you'll have to use (*s) instead. For example, s[i] needs to become (*s)[i]. If you just did *s[i], it would be incorrectly parsed as *(s[i]).

    Also, you have an off-by-one error: you need to use i + 2 when you realloc, not i + 1, since array indexing starts at 0 but the first element still takes up space.

    By the way, unrelated to the error you're currently seeing, but your test for EOF doesn't work the way you want it to, since it's being converted to char before you test it, and EOF is outside of char's range. (The exact problem depends on the signed-ness of char. If it's signed, then \xFF will incorrectly count as EOF, and if it's unsigned, then EOF won't be recognized and will incorrectly count as \xFF.)