0

I am trying to work with dynamically allocated string inputs in C, such that it will reallocate memory realloc() once the inputted string exceeds the previously allocated memory instead of going into an undefined behavior

This is my code:


#include <stdio.h>

int main() {
    char *text, *retext;
    text = malloc(10 * sizeof(*text));
    scanf("%s", text);
    if(text == NULL) {
        printf("\nTry Again: ");
        retext = realloc(text, 20 * sizeof(*retext));
        scanf("%s", retext);
        printf("\n%s", retext);
        free(retext);
        retext = NULL;
    }
    printf("\n%s", text);
    free(text);
    text = NULL;
}

I basically want to run realloc() if the text's length exceeds 10 (including \0). I later learnt from many Stackoverflow answers that malloc() doesn't really return NULL and instead goes into "Undefined Behavior" and everything will seem to work even after exceeding 10chars. However, I want somehow to be able to "detect" when malloc() is starting this Undefined behavior, and jump to the realloc() block instead of proceeding any further. How do I achieve that?

  • You need to read one character at a time with `getchar`. – user3386109 Dec 31 '22 at 22:32
  • 1
    The `scanf` function will not, and can not, change the pointer passed as an argument. The `text == NULL` check after calling `scanf` will not detect problems with `scanf` or the input. To check if `scanf` worked you need to check what `scanf` [*returns*](https://en.cppreference.com/w/c/io/fscanf#Return_value). But please note that it can't detect buffer overflows, the result of `scanf` only indicates if the format could be matched. – Some programmer dude Dec 31 '22 at 22:35
  • 1
    Also note that `scanf` (and all other input functions) extracts data from the input. Unless the input stream can be rewound (like for an actual file) then the characters will be lost. – Some programmer dude Dec 31 '22 at 22:38
  • 1
    I used to work with this QA guy who was a programmer's worst nightmare, but ultimately his best friend. If I told him to enter no more than 9 characters into my program, the first thing he would do is enter 200. What do you think would happen when that QA guy entered 200 characters into your program? – Carey Gregory Dec 31 '22 at 23:12
  • Use `getline` instead of `scanf` or `fgets`. It will automatically adjust the buffer size to fit (via internal calls to `malloc/realloc` as needed), even if the line has 1,000,000 chars in it. (e.g.) `char *text = NULL; size_t maxlen = 0; ssize_t rlen = getline(&text,&maxlen,stdin); /* do stuff */ free(text);` – Craig Estey Dec 31 '22 at 23:17
  • 2
    `scanf("%s",` You cannot safely use this, ever. – n. m. could be an AI Dec 31 '22 at 23:17
  • You cannot do `free(text)` after `free(retext)` because if `realloc` succeeded, then `text` points to invalid memory and `realloc` took care of free the original memory if needed. You need to do `retext = realloc(text, ...); if(!retext) { text = retext; } ...` – Pablo Jan 01 '23 at 01:07
  • And `scanf("%s"` is an buffer overflow waiting to happen. Use `fgets` instead. – Pablo Jan 01 '23 at 01:08
  • If you have a POSIX-compliant version of [`scanf()`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/scanf.html), you can use `char *data; if (scanf("%ms", &data) == 1) { … }` — the `m` modifier in `%ms` means that `scanf()` should allocate enough memory for the string it reads. Note that macOS is not sufficiently compliant; modern Linux (GLibC) is. – Jonathan Leffler Jan 01 '23 at 03:13
  • Thanks for all the comments, looks like I need to study more C to get all these edgecases right. This will hopefully help me to start somewhere – Abdullah Omar Nasseef Jan 01 '23 at 18:35

1 Answers1

1

scanf reads the string until it reaches \0 whitespace or end of the input (thanks for the comment!), so you need to implement your function to read a string of unknown size. Byte by byte. Unlike Java, you can't read from stdin and check what it is without advancing past the input. Once the byte was read, you are responsible for remembering it if you need it later.

Take a look at that: How can I read an input string of unknown length?

FYI: with scanf you can specify the length of the buffer you are writing to: https://stackoverflow.com/a/12306624/7095554

The return value of scanf:

RETURN VALUE         
       On success, these functions return the number of input items
       successfully matched and assigned; this can be fewer than
       provided for, or even zero, in the event of an early matching
       failure.

       The value EOF is returned if the end of input is reached before
       either the first successful conversion or a matching failure
       occurs.  EOF is also returned if a read error occurs, in which
       case the error indicator for the stream (see ferror(3)) is set,
       and errno is set to indicate the error.

source: https://man7.org/linux/man-pages/man3/scanf.3.html#RETURN_VALUE

Tudny
  • 96
  • 4
  • I kinda understood all the resources you gave, still trying to study some of the stuff so that I can get all the edgecases right naturally. Thanks a lot for the help – Abdullah Omar Nasseef Jan 01 '23 at 18:37