Reputation: 357
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
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.
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.
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
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 return
s, 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