0

What I want to do here is, I want to read input from a user with scanf into a char pointer and dynamically allocate memory as more input is read.

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

int main()
{
    char *ptr, temp;
    int i, ind = 0;

    ptr = malloc(sizeof(char) * 2);

    while (scanf(" %[^\n]c", &temp) != EOF)
    {
        ptr[ind] = temp;
        ind++;
        ptr = realloc(ptr, sizeof(char) * (ind + 1));
    }

    for (i = 0; i < 5; i++)
        printf("%c", *(ptr + i));

    return 0;
}

My code is like this, however it either throws a segmentation error (when the number of character in one line is more than 8) or does not even print any characters. What am I missing? Thanks in advance.

10
  • 2 * sizeof(char) doesn't do exactly what you think, you only allocate memory for 2 characters. Commented May 30, 2021 at 0:42
  • @alex01011: That is what they think. They start with two characters and use realloc to get more as characters are read. Commented May 30, 2021 at 0:42
  • @EricPostpischil Well scanf() is the wrong function to use then:) Commented May 30, 2021 at 0:43
  • In that form at least Commented May 30, 2021 at 0:44
  • This is what the m flag to scanf is for: scanf("% m[^\n]", &ptr); Commented May 30, 2021 at 1:13

2 Answers 2

3

scanf(" %[^\n]c", &temp) does not read a character that is not a new-line ('\n'). The [ is a conversion specifier by itself, and c does not go with it.

%[^\n] says to read any number of characters until a new-line is seen. The c is not part of the conversion, and its presence causes a matching failure in scanf.

To read one character that is not a new-line character, use %1[^\n], as in scanf(" %1[^\n]", &temp);

Another solutions are:

  • Use %c in the scanf. Afterward, test the character and ignore it if it is a new-line character.
  • Change the code to use getchar instead of scanf.

Avoid testing with scanf(...) != EOF. scanf returns EOF only if an input failure occurs before the first conversion has completed. Otherwise, it returns the number of input items assigned. This may work in simple uses of scanf with only one item to assign. However, in general, you want to at least test whether scanf assigned the desired number of items, scanf(...) == 1. Even better would be to save the return value and handle the multiple possible returns: EOF, a number less than the desired number of items, or the desired number of items.

Sign up to request clarification or add additional context in comments.

Comments

0

Adding to Eric's answer, your code could be better without scanf(). Here is a getchar() solution:

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

int main(void)
{
    int c = 0; /* int so it can hold the EOF value */
    size_t ind = 0, i = 0;
    char *buff = NULL, *tmp = NULL;

    buff = malloc(2); /* sizeof char is 1 */

    if (buff == NULL) /* malloc failed */
    {
        exit(EXIT_FAILURE);
    }

    while (1)
    {
        c = getchar();

        if (c == EOF || c == '\n')
        {
            break;
        }

        buff[ind++] = (char)c; /* maybe check for overflow here, for non ascii characters */

        /* can use ctype functions or just manually compare with CHAR_MIN and CHAR_MAX */

        tmp = realloc(buff, ind + 2);

        if (tmp == NULL) /* reallloc failed */
        {
            fprintf(stderr,"Out of memory\n");
            exit(EXIT_FAILURE);
        }

        buff = tmp;
    }

    /* --------- NULL terminate if you are looking for a string  */

    /* buff[ind] = 0;                                            */

    /* --------------------------------------------------------- */

    for (; i < ind; i++)
    {
        putchar(*(buff + i));
    }

    free(buff);

    return 0;
}

I would like add that multiple realloc() calls, isn't really a sufficient practice. The better approach, would be initially allocate an X amount of memory with malloc() and if you need more, then use realloc() with an appropriate size.

4 Comments

Assigning the result of realloc to the same pointer as the input parameter is wrong. There are many people more knowledgeable than I, who would advise to not use realloc at all; instead call malloc, test whether it was successful, free the one you are using, and then overwrite it with the new value returned by the most recent malloc
buff = realloc(buff, ind + 2); when (not if) realloc() fails you overwrite the address held by buff with NULL losing your original pointer address creating a memory leak. Always realloc() using a temp pointer, e.g. void *tmp = realloc(buff, ind + 2); if (!tmp) { perror ("realloc-buff"); /* handle error */ } buff = tmp;
I used realloc because the size of the input was not known, and I was told to use it. But your solution works like charm. Thank you very much :)
@GeorgeMiddletown What they pointed is correct. I updated the answer. Thanks gents!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.