0
#include<stdio.h>
#include<stdlib.h>
int main()
{
    char* name;
    name=(char* )malloc(sizeof(char)); 
    printf("Enter a name: ");
    scanf("%s",name);
    printf("%s",name);
    return 0;
}

The above code perfectly stores a whole string when I am just allocating memory for a single character. How is this possible?

  • 2
    "*just allocating memory for a single character. How is this possible?*" `scanf()` just scans the rest beyond the end of the memory the code allocated. Doing so the infamous Undefined Behaviour is invoked. – alk Sep 30 '17 at 08:21

3 Answers3

2

You have undefined behavior. Be very scared. Read Lattner's blog on UB.

printf("%s",name);

This works when name is a genuine string (that is some licit and valid memory zone ending with a zero byte). In your program it is not, you are accessing at least one byte past the malloc-ed zone. You have a buffer overflow.

The only case for a single-char zone when it is a valid string is when that single char contains a zero byte so the string is empty.

The above code perfectly stores a whole string

You just have bad luck (BTW, it is not "perfectly"). It could be worse (e.g. the collapse of the universe) and it could be better (e.g. some segmentation violation, nasal demons, ....). This is why you should be afraid of UB.

(to explain what happened on your particular computer, you need to dive into implementation details, e.g. study your operating system, your compiler, your instruction set, the assembler code generated by your compiler, etc etc...; you don't want to spend years of work on that, and even if you did it stays UB)

You should compile with all warnings and debug info (gcc -Wall -Wextra -g) and use the debugger gdb, valgrind and be sure that every memory location your program is dealing with is valid.

BTW malloc could fail, and you should test against that and sizeof(char) is by definition 1. You should try

char* name = malloc(80);
if (!name) { perror("malloc"); exit(EXIT_FAILURE); };

Please also read the documentation of every function you are using. Start with printf and malloc and scanf. Notice that your use of scanf is dangerous. Better end your printf control strings with a \n or use fflush appropriately (since stdout is often line-buffered). Also download and study the specification n1570 of the C11 programming language.

On some systems, you have more than what the C11 standard guarantees. For example POSIX has getline and you could use it like here. Consider also fgets (in the C11 standard) if you don't have a POSIX system (then you would need complex tricks to read arbitrarily long lines, or else specify and document that your program can only handle lines of at most 79 bytes if using malloc(80)).

It is good manners to avoid arbitrary limits in your code; of course your computer still has limitations (you probably won't be able to handle a line of a hundred billion bytes).

Be aware of character encoding (see also this). Use today, in 2017, UTF-8 everywhere (e.g. with the help of libunistring or of glib) but then remember that a Unicode character can span several bytes (that is char-s).

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

You have allocated memory for just one character which is sufficient to store just the terminating null byte. You need to allocate more.

For example:

char *name = malloc(128); /* allocates 128 bytes */
printf("Enter a name: ");
if (fgets(name, 128, stdin) == NULL) {
    /* input failure */
}

Note that scanf() is notorious for reading input. Prefer fgets() instead. Also be aware that fgets() would read in the newline if there's space which you may want to remove.

Also see: Why does some code carefully cast the values returned by malloc to the pointer type being allocated?

P.P
  • 117,907
  • 20
  • 175
  • 238
  • `fgets()` will always read in the newline. If the buffer length is too short to read a complete line, the line will be read in parts, by consecutive calls of `fgets()`. The newline will be at the end of the last part read. – Peter Sep 30 '17 at 08:19
  • Not necessarily by "consecutive calls of fgets()" but by whatever the next input operation. – P.P Sep 30 '17 at 08:23
  • True, but your example showed a loop calling `fgets()`, not a loop calling `fgets()` mixed with another style of input. Mixing styles of input (e.g. `fgets()` followed by `scanf()` or vice versa) is an extremely bad idea anyway, because there can be unwanted interactions - such as one approach leaving a `'\n'` pending, and the other reading it and stopping before reading the intended input. – Peter Sep 30 '17 at 08:27
  • 1
    @Peter "but your example showed a loop calling fgets()" - There's no loop in my example. – P.P Sep 30 '17 at 08:28
  • Whatever - you're not mixing styles of input. – Peter Sep 30 '17 at 08:29
  • 1
    I am not mixing input styles - true - and I know it's bad. But I still don't understand what your point is. – P.P Sep 30 '17 at 08:43
1

You can only safely store one string into that array, and that string has a length of zero.

Perhaps, to your fortune, this code might appear to work for the rest of your life and you'll never hear any complaints about it... but that's really unlikely.

Type a larger number of characters; mash the keyboard with a few KB and I bet you'll start seeing segmentation faults. Be glad I showed you this, because I probably saved you many hours of debugging!

Having said that, I can't provide a 100% guarantee; it's just an educated guess. Perhaps your system doesn't segfault. Perhaps your system lets hackers bypass security, instead. Who knows? The behaviour is undefined.

I could have saved you a lawsuit due to insecure coding, instead, but we can't give a definition to undefined behaviour other than the definition which the standard programming language gives it, which is: non-portable and erratic.

The reason to avoid this should now be clear to you.

autistic
  • 1
  • 3
  • 35
  • 80