34

I had gone to an interview in which I was asked the question:

What do you think about the following?

int i;
scanf ("%d", i);
printf ("i: %d\n", i);

I responded:

  • The program will compile successfully.
  • It will print the number incorrectly but it will run till the end without crashing

The response that I made was wrong. I was overwhelmed.

After that they dismissed me:

The program would crash in some cases and lead to an core dump.

I could not understand why the program would crash? Could anyone explain me the reason? Any help appreciated.

Lundin
  • 195,001
  • 40
  • 254
  • 396
Box Box Box Box
  • 5,094
  • 10
  • 49
  • 67

6 Answers6

49

When a variable is defined, the compiler allocates memory for that variable.

int i;  // The compiler will allocate sizeof(int) bytes for i

i defined above is not initialized and have indeterminate value.

To write data to that memory location allocated for i, you need to specify the address of the variable. The statement

scanf("%d", &i);

will write an int data by the user to the memory location allocated for i.

If & is not placed before i, then scanf will try to write the input data to the memory location i instead of &i. Since i contains indeterminate value, there are some possibilities that it may contain a value equivalent to the value of a memory address or it may contain a value which is out of range of memory address.

In either case, the program may behave erratically and will lead to undefined behavior. In that case anything could happen.

haccks
  • 104,019
  • 25
  • 176
  • 264
  • 5
    It's almost guaranteed you'll crash, as you'll get a memory protection fault or the equivalent on whatever system is running it. Of course, other things *could* happen, depending on the size of the program, allocated memory, etc, but my money's on a system/application crash. – sfdcfox Jan 01 '16 at 23:54
  • 1
    @sfdcfox my money's on the compiler noticing undefined behavior and treating the respective code block as unreachable (= removing it from the code and redirecting any code path leading to it). – John Dvorak Jan 02 '16 at 08:17
  • @JanDvorak I've seen this idea that the compiler can effectively 'delete' UB mentioned before, but do you know of an example where we can see it in action? It sounds hard to believe - which is why I'm pretty sure it'll be true, as this is C++ after all. ;-) Specifically here, do you mean that the ill-advised `scanf` might not be reached, so the user could not be asked for input? I didn't know that 'as-if' could produce observable side-effects, but I guess that rule doesn't apply to UB, like all the rest. – underscore_d Jan 02 '16 at 12:37
  • @underscore_d http://blog.llvm.org/2011/05/what-every-c-programmer-should-know_14.html – John Dvorak Jan 02 '16 at 12:44
  • @underscore_d even better: http://i1.blogs.msdn.com/b/oldnewthing/archive/2014/06/27/10537746.aspx – John Dvorak Jan 02 '16 at 12:55
  • 1
    @JanDvorak: If compilers would "disable" **all** UB code that way, a huge number of quite common applications would suddenly cease to function... – DevSolar Jan 18 '16 at 13:10
  • 1
    @sfdcfox: You may **not** assume the presence of memory protection. Outside the desktop niche, there are quite some systems running without -- where UB means "undefined behaviour of this application *and all others currently in memory, including the OS*". Those are the same systems that react rather poorly to memory not `free()`d before returning from `main()`... – DevSolar Jan 18 '16 at 13:12
18

Beacuse it invokes undefined behavior. The scanf() family of functions expect a pointer to an integer when the "%d" specifier is found. You are passing an integer which can be interpreted as the address of some integer but it's not. This doesn't have a defined behavior in the standard. It will compile indeed (will issue some warning however) but it will certainly work in an unexpected way.

In the code as is, there is yet another problem. The i variable is never initialized so it will have an indeterminate value, yet another reason for Undefined Behavior.

Note that the standard doesn't say anything about what happens when you pass a given type when some other type was expected, it's simply undefined behavior no matter what types you swap. But this particular situation falls under a special consideration because pointers can be converted to integers, though the behavior is only defined if you convert back to a pointer and if the integer type is capable of storing the value correctly. This is why it compiles, but it surely does not work correctly.

haccks
  • 104,019
  • 25
  • 176
  • 264
Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
  • But in my college days, when I was studying from _Spirit of C_, there was a statement like this: `scanf ("%s", name);` – Box Box Box Box Jan 01 '16 at 14:57
  • 10
    ^ that works becauce `name` is probably a char array, and the name of an array is equal to the address of the first element in that array. – kfx Jan 01 '16 at 14:58
  • That is very different. Surely `name` was an array and arrays are autmatically converted to pointers to their first element. Also, it would be safer `char name[100]; scanf("%99s", name);`. – Iharob Al Asimi Jan 01 '16 at 14:58
  • So, basically you mean to say, that `scanf` will try to store the value at location with variable i. – Box Box Box Box Jan 01 '16 at 15:02
  • 1
    Although your comment is not clear I think that you did understand so Yes, that's how it works. – Iharob Al Asimi Jan 01 '16 at 15:03
  • Thanks for the information. +1. Although, I feel, @haccks answer is better explained. – Box Box Box Box Jan 01 '16 at 15:14
  • I don't think it's guaranteed to compile at all. Since behavior is undefined, if the function and the statement that contains the printf-statement will be executed, the compiler may aswell reject it upfront. Please correct me if I'm wrong, but I think that's the common understanding for C++ at least. I would be surprised if in C it would be different. – Johannes Schaub - litb Jan 01 '16 at 15:14
  • @JohannesSchaub-litb No, it would compile actually. I don't know how it works in c++. But in c it does compile. You can test if you like. – Iharob Al Asimi Jan 01 '16 at 15:15
  • @JohannesSchaub-litb, it compiles for me using gcc. The compiler gives a warning. – Box Box Box Box Jan 01 '16 at 15:16
  • @AshishAhuja Yes Haccks answer is very good indeed I upvoted it actually. And you should pick that one indeed. – Iharob Al Asimi Jan 01 '16 at 15:16
  • @AshishAhuja with that particular flags you are using. For `-Werror`, it fails. Now the question is, is it standards-conformant when it fails to compile? Does the C language make a difference between translating and executing a program? The C language makes a difference between "conforming" programs and "strictly conforming" programs. A program of the latter kind is accepted by *any* C implementation (i.e no diagnostic/"error" is given). In other words, is a program that misuses scanf like that a *strictly conforming* program? – Johannes Schaub - litb Jan 01 '16 at 15:18
  • Warnings are from compilers not the standard, and of course `-Werror` would make it fail, because it tells the compiler to consider the compilation warnings as errors. To make it clear, *clang* has some warnings that *gcc* doesn't yet both fully support c99 standard. – Iharob Al Asimi Jan 01 '16 at 15:19
  • @JohannesSchaub-litb. When I tried to compile the program using gcc with `-Werr` it gave the following error: `gcc: error: unrecognized command line option ‘--Werr’ ` I work in Ubuntu 14.04 LTS – Box Box Box Box Jan 01 '16 at 15:20
  • @AshishAhuja use a single `-`. And do use `-Werror` it's a good way to avoid silly mistakes. – Iharob Al Asimi Jan 01 '16 at 15:21
  • @JohannesSchaub-litb, by the way when I ran it without any flags, this was the warning: `a.c:5:5: warning: format ‘%d’ expects argument of type ‘int *’, but argument 2 has type ‘int’ [-Wformat=] ` – Box Box Box Box Jan 01 '16 at 15:23
  • @JohannesSchaub-litb, then I got: `gcc: error: unrecognized command line option ‘-Werr’` – Box Box Box Box Jan 01 '16 at 15:23
  • @AshishAhuja It's `-Werror`, and with no warnings at all it silently compiles. For me `gcc -o example example.c` compiles without any warning. – Iharob Al Asimi Jan 01 '16 at 15:25
  • @iharob, oh now it compiles successfully. Although, it gives a warning. – Box Box Box Box Jan 02 '16 at 02:34
  • @AshishAhuja What warning is it? – Iharob Al Asimi Jan 02 '16 at 02:39
10

You passed data having the wrong type (int* is expected, but int is passed) to scanf(). This will lead to undefined behavior.

Anything can happen for undefined behavior. The program may crash and may not crash.

In a typical environment, I guess the program will crash when some "address" which points to a location which isn't allowed to write into by the operating system is passed to scanf(), and writing to there will have the OS terminate the application program, and it will be observed as a crash.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
MikeCAT
  • 73,922
  • 11
  • 45
  • 70
10

One thing that the other answers haven't mentioned yet is that on some platforms, sizeof (int) != sizeof (int*). If the arguments are passed in a certain way*, scanf could gobble up part of another variable, or of the return address. Changing the return address could very well lead to a security vulnerability.

* I'm no assembly language expert, so take this with a grain of salt.

Functino
  • 1,939
  • 17
  • 25
5

I could not understand why the program would crash? Could anyone explain me the reason. Any help appreciated.

Maybe a little more applied:

int i = 123;
scanf ("%d", &i);

With the first command you allocate memory for one integer value and write 123 in this memory block. For this example let's say this memory block has the address 0x0000ffff. With the second command you read your input and scanf writes the input to memory block 0x0000ffff - because you are not accessing (dereferencing) the value of this variable i but it's address.

If you use the command scanf ("%d", i); instead you are writing the input to the memory address 123 (because that's the value stored inside this variable). Obviously that can go terribly wrong and cause a crash.

MarkWatney
  • 144
  • 6
0

Since there is no &(ampersand) in scanf(as required by the standard), so as soon as we enter the value the program will terminate abruptly, no matter how many lines of code are written further in the program.

-->> I executed and found that in code blocks.

Same program if we run in turbo c compiler then it will run perfectly all the lines even which are after scanf, but the only thing, as we know the value of i printed would be garbage.

Conclusion:- Since at some compiler it will run and at some it would not, so this is not a valid program.

  • 1
    As some of the other answers explain, while it's true that the program is incorrect and will probably terminate right away, we *can't* say this for sure. There's a chance it will successfully write the scanned integer into an indeterminate location, and continue. – Steve Summit Jul 25 '21 at 15:59