Yitzak Hernandez
Yitzak Hernandez

Reputation: 357

Malloc array of characters dynamic vs static C

So I'm basically trying to take an input of scanf of letters (no spacing between them), place each letter into an array and spit out the corresponding letter to the array by using dynamically allocated arrays (malloc).

Crashes

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

int main () {

    char *userInput = malloc(sizeof(char)*3); /* dynamic */
    scanf("%s", &userInput);
    printf("user inputed %c", userInput[1]);
    free(userInput);

    return 0;
}

Runs

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

int main () {

    char userInput [3];  /* array */
    scanf("%s", &userInput);
    printf("user inputed %c", userInput[1]);

    return 0;
}

Input: asd

Output: s

My understanding of dynamically allocated arrays was that char userInput [3]; is equivalent to char *userInput = malloc(sizeof(char)*3); but apparently from this case that isn't true? Anyone care to explain/help?

Upvotes: 1

Views: 1130

Answers (2)

Enzo Ferber
Enzo Ferber

Reputation: 3094

As pointed in Giorgi's answer, the main problem is the incorrect usage of the address-of operator &.


However, the reason why it worked on one case and why it didn't work on another is very interesting.

  1. char array[3]: When you declare that array, memory space will be allocated for it and array will be a label to that location(memory address). When you pass array to scanf (or use it anywhere else without subscripting []), you're passing an address to that function. Because of that, when you use the & operator on the label array, it returns the same address to you BUT with different type (T(*)[3]), which your compiler probably complained about. But, as the memory address is valid, it worked as expected.

  2. char *array = malloc(): When you declare that variable, memory is also reserve for it, but this time in a different place and the space reserved is sizeof T(*), so it can hold a memory address. This variable also has an address in memory, which you can also get using &array. Then you malloc some memory and malloc returns to you an address of a memory block which you can now use. You can get that memory address by simply doing array. So, when you call scanf with the &array you're passing the variable address instead of the block address. That's why it crashes (I'm guessing you were not entering only two characters).

Check this code:

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

int main(void)
{
    char *array[3];
    char *ptr = malloc(3 * sizeof(char));

    printf ("array : %p\n", array);
    printf ("&array: %p\n\n", &array);
    printf ("ptr   : %p\n", ptr);
    printf ("&ptr  : %p\n", &ptr);

    scanf("%s", &ptr);

    printf ("ptr   : %p\n", ptr);

    return 0;
}

Which outputs:

$ ./draft
array : 0x7ffe2ad05ca0
&array: 0x7ffe2ad05ca0

ptr   : 0x19a4010
&ptr  : 0x7ffe2ad05c98
ABCD
ptr   : 0x44434241

scanf got the address of the pointer, so when it saves the value it reads from stdin, it overwrites the address we had from malloc! The memory block we had is now gone, memory is leaking... Now, this is bad because we're overwriting stuff on our stack, memory is leaking, and it will crash.

Observe the last output: the value of ptr (which previously was the address of an allocated block) is now 0x44434241 (DCBA, ASCII Table)! How nice is that?

Upvotes: 1

type_outcast
type_outcast

Reputation: 635

Welcome to Stack Overflow! Coincidentally, the main problem with your code is that it is vulnerable to a stack overflow. scanf has no way of knowing how big userInput is, because you didn't tell it, and will happily continue filling memory long past the end of your very short array.

If you want to capture exactly three characters (with no nul terminator), use scanf("%3c", userInput) instead. Note that without the NUL, you must not expect to treat userInput as a string; printing it via printf for example will result in a random amount of gibberish owing to the fact that C does not know where the string ended.

Now, to answer your actual question on "what's the difference between malloc and the static array": the difference is of scope. If you only ever use userInput before its creating function returns, there is no practical difference, but you're in trouble the minute you try to do something like this:

int function1 {
     char my_string[3];

     scanf("%3c", my_string);

     return my_string; /* WRONG! DANGER! */
}

The return in the above example will happily return the pointer to my_string to the calling function. However, as soon as function1 returns, the stack is rolled back and the memory my_string occupied is essentially gone (and likely already re-used). The results are unpredictable but almost universally very bad for your program.

However, had you used malloc() instead, you could safely return the my_string pointer and the memory would persist until someone later called free(my_string) (where my_string is the pointer to the original my_string; it need not be named the same!).

This highlights another difference: with a stack variable such as char my_string[3];, you do not need to worry about (and indeed cannot) free() the memory, where as if the memory is malloc()'d instead, you must free() it if you wish to reclaim the memory.

There are some nuances to the above, such as file-scoped variables and static function variables, which I leave as topics for further reading.

Upvotes: 2

Related Questions