-5

I want to be able to have a user enter what they wish to do. There will be other options but for now I am working on "insert". The other two options will be "search" and "delete".

int main() {

    char *input = malloc(sizeof(char) * 6);
    printf("%s", "WELCOME TO USER'S SKIP LIST!\n\nWhat do you wish to do? ");
    scanf("%6s", input);
    if (strcmp(input, "insert") == 0) {
        printf("%s", "What is the item? ");
        input = realloc(input, (size_t)scanf("%s", input));
    }
}

I initially allocate enough memory for input to have 6 characters. This is because the other two options will also only have 6 characters. After they enter insert they will have to enter an item that will have any number of characters, so I want to reallocate memory for input based on what they enter for an item. So if they enter Nintendo Switch there will be a reallocation for 15 characters. Is my implementation of realloc() the correct way of doing this?

NobodyNada
  • 7,529
  • 6
  • 44
  • 51
jtetra13
  • 1
  • 7
  • 3
    You are not *implementing* `realloc`, you are using it (wrongly). – Basile Starynkevitch Dec 31 '18 at 13:29
  • 1
    :"Is my implementation of realloc() the correct way of doing this?" --> No. `scanf("%6s", input);` allows to read too big an input. `realloc()` uses wrong size and fails to check for out-of--memory. At least 5 issues with this code. – chux - Reinstate Monica Dec 31 '18 at 13:29
  • OT: regarding: `char *input = malloc(sizeof(char) * 6);` 1) the expression: `sizeof( char )` is defined in the C standard as 1. Multipling anything by 1 has absolutely no effect. Suggest removing that expression, it is just cluttering the code. 2) when calling any of the heap allocation functions: `malloc` `calloc` `realloc`, always check (!=NULL) the returned value to assure the operation was successful. 3) when calling `realloc()`, save the result into a temp variable, check it, and if good, then copy to the target variable. This avoids a memory leak if `realloc()` fails. – user3629249 Dec 31 '18 at 15:26
  • regarding: `scanf("%6s", input);` 1) when calling any of the `scanf()` family of functions, always check the returned value (not the parameter values) to assure the operation was successful. 2) the string: `insert` takes 7 characters due to the trailing NUL byte. SO the call to `malloc` should be allocating 7 characters, not 6 – user3629249 Dec 31 '18 at 15:29
  • regarding: `scanf("%s", input)` what are you expecting this to do? `scanf()` returns the number of successful input/conversions, not the length of the input – user3629249 Dec 31 '18 at 15:31

2 Answers2

5

I suppose you're primarily asking about this:

    input = realloc(input, (size_t)scanf("%s", input));

No, it is not a correct use of realloc(). There are at least four distinct problems with that line alone, including:

  • scanf() returns the number of input items successfully scanned and recorded, or EOF if an error occurs. "Input items" correspond to field directives, such as your %s, so that particular scanf() call will never return a value greater than 1. It might, however, return either 0 or EOF (usually -1).

  • Even if scanf() did return the number of characters read,

    • Any reallocation would be too late. The data are stored into the provided space by scanf(), and if the space is not large enough then its bounds will be overrun prior to any opportunity for reallocation.

    • No space is reserved for a string terminator.

    • realloc() can fail, in which case it returns NULL. Even if you check for that by examining input after the realloc(), you will no longer have a pointer to the original space, so it leaks.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
4

Read carefully the documentation of scanf, of malloc, of realloc, so read them several times. You could also refer to the C11 standard n1570 which mentions them (in §7.22.3).

Is this a correct implementation of realloc()?

You are not implementing realloc. Here is a joke-implementation of it (see also this joke-implementation of malloc):

void *realloc(void*ptr, size_t siz) {
  errno = ENOMEM;
  return NULL;
}

Of course you would actually use a serious implementation of realloc (not the joke above), and it is provided by your C standard library implementation (e.g. above operating system primitives or system calls like mmap(2) on Linux).

So, no, you don't have any implementation of realloc (and you would use the realloc already implemented in your C standard library). And (on Linux at least) you can study the implementation of realloc because it usually is implemented inside some free software (e.g. musl-libc or GNU glibc). Here (in its file src/malloc/malloc.c line 369) is musl-libc implementation of realloc.

you are wrongly using realloc

Then, you are using scanf as scanf("%s", input). But scanf returns EOF on failure, and the number of successfully input values on success. Usually EOF is -1. So in your case, that scanf("%s", input) can return -1 (on failure), 0 (if no input value has been processed) or 1 (if it has put something in input).

The size_t type is some unsigned integral type. On my Linux/x86-64 system, it is an unsigned 64 bits number, same as unsigned long. So (size_t)(-1) becomes a huge number, i.e. 264 - 1. Then realloc will surely fail (because my system don't have that much memory) if given that (size_t)(-1) so realloc(input, (size_t)-1) should give NULL.

If realloc is given (size_t)0, it is documented on Linux (see realloc(3)) to do what free does: release the given memory. But the C standard don't require that behavior.

If realloc is given (size_t)1, if would (or at least is permitted to) shrink the memory zone (to only hold one byte, which is not enough for your needs).

So your program is completely wrong

BTW, you need to handle failure of realloc, so coding input = realloc(input, newsize); is very naive.

Also, your scanf("%6s", input); is wrong (potential buffer overflow, for an input of exactly six bytes) since you need the space for the terminating NUL character.

So, throw your program to the garbage bin. Take some rest (or some fun). Read the documentation of standard functions (and the wikipage of C dynamic memory allocation). Think a little bit. And rewrite your program entirely.

Then compile your program with all warnings and debug info: gcc -Wall -Wextra -g with GCC. Improve your code to get no warnings. Ensure that your program handle the failure cases (of scanf, of malloc, of realloc, etc...). Read How to debug small programs. Use the gdb debugger and valgrind. Be scared of undefined behavior.

For your program (the one you rewrite from scratch), you could be interested in using fgets (or even, on Linux, getline(3) or readline(3)...) for input. You may sometimes want to clear the input buffer with memset before doing the actual input.

Be aware that stdio is buffered, and stdout is usually line-buffered. So take the habit of ending your printf format string with \n or use fflush appropriately.

PS. valgrind is available on Linux (but not on Windows), and that is one of the many reasons making Linux a very developer- and student- friendly operating system. So I do recommend using some Linux distribution to learn C programming.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547