-1

Why does this code below compiles and executes but doesn't print anything in output,

    int  i = 0;
    printf(i = 0);

but this gives a runtime error,

    int  i = 0;
    printf(i = 1);
haccks
  • 104,019
  • 25
  • 176
  • 264
Chaitanya
  • 105
  • 9
  • 5
    Read the documentation for printf. – Jonathon Reinhart Mar 04 '17 at 20:04
  • 1
    Did you get no compiler warnings? – Paul Hankin Mar 04 '17 at 20:05
  • 1
    It gives an error on GCC 4+ and Turbo C compiler but i=0 works fine on ideone. Why so ? – Chaitanya Mar 04 '17 at 20:12
  • You are very close to discover a term "undefined behavior". – haccks Mar 04 '17 at 20:14
  • haha. okay :) @haccks – Chaitanya Mar 04 '17 at 20:15
  • 2
    Assuming the required `#include `, this isn't just undefined behavior, it's a constraint violation, just like `sqrt("foobar")`. Passing incorrectly typed arguments to `printf` causes undefined behavior *for the variadic arguments* (the ones following the format string. The first argument is the format string, and it must be of type `char*`. Your compiler almost certainly diagnosed this error, possibly as a non-fatal warning. You chose to ignore the warning, and not to mention it in your question. **Do not ignore compiler warnings.** – Keith Thompson Mar 04 '17 at 21:13
  • 2
    Now that I pay closer attention to the comments, I see that you *did* get an error from your compiler. Include any error messages in your question. – Keith Thompson Mar 04 '17 at 21:24
  • Also Turbo C++ is an obsolete compiler. Do not use that. – haccks Mar 04 '17 at 21:28
  • 1
    The answer you accepted is incorrect. – Keith Thompson Mar 05 '17 at 09:30

5 Answers5

3
int  i = 0;
printf(i = 0);

The first argument to printf must be a char* value pointing to a format string. You gave it an int. That's the problem. The difference in behavior between printf(i = 0) and printf(i = 1) is largely irrelevant; both are equally wrong. (It's possible that the first passes a null pointer, and that printf detects and handles null pointers somehow, but that's a distraction from the real problem.)

If you wanted to print the value of i = 0, this is the correct way to do it:

printf("%d\n", i = 0);

You have a side effect in the argument (i = 0 is an assignment, not a comparison), which is legal but poor style.

If you have the required #include <stdio.h>, then your compiler must at least warn you about the type mismatch.

If you don't have #include <stdio.h>, then your compiler will almost certainly warn about calling printf without a declaration. (A C89/C90 compiler isn't strictly required to warn about this, but any decent compiler should, and a C99 or later compiler must.)

Your compiler probably gave you one or more warnings when you compiled your code. You failed to include those warnings in your question. You also failed to show us a complete self-contained program, so we can only guess whether you have the required #include <stdio.h> or not. And if your compiler didn't warn you about this error, you need to find out how to ask it for better diagnostics (we can't help with that without knowing which compiler you're using).

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
2

Expressions i = 0 and i = 1 in printf function will be evaluated to 0 and 1 (and i will be initialized to 0 and 1) respectively. So above printf statements after their expression evaluation will be equivalent to

printf(0);  // To be clear, the `0` here is not a integer constant expression.

and

printf(1);

respectively.

0 and 1 both will be treated as address in printf statements and it will try to fetch string from these addresses. But, both 0 and 1 are unallocated memory addresses and accessing them will result in undefined behavior.

haccks
  • 104,019
  • 25
  • 176
  • 264
  • There's no guaranteed equivalence between `printf(i=0)` and `printf(0)`. The C standard special-cases converting a constant integer expression that evaluates to 0 to a pointer (giving NULL). And `0` is a constant integer expression but `i=0` is not, so the results may be different (although not likely to be in practice of course). – Paul Hankin Mar 04 '17 at 20:53
  • 1
    @PaulHankin; `i=0` will result in expression value too and that will be `0`. – haccks Mar 04 '17 at 20:55
  • Sure that this is `0`, an `int` of value `0`. What it is not necessarily, is a null pointer, because first of all it might have a different width than pointer types, and null pointers might not be all bit `0` values. So `printf` *tries* to interpret the values it finds as pointers, and seemingly here it succeeds for `0`, perhaps because the OP has a 32 bit machine. – Jens Gustedt Mar 04 '17 at 20:58
  • @JensGustedt; I did't said that it will represent a null pointer. Null pointer constant may not be represented by `0`. – haccks Mar 04 '17 at 20:59
  • Yes, but `i=0` is not a constant integer expression. It sounds nuts, but it's not guaranteed that this prints "yes": `int x=0; if ((void*)x == (void*)0) printf("yes");` http://stackoverflow.com/questions/16563114/are-these-null-pointers-or-are-they-pointers-to-address-0?noredirect=1&lq=1 – Paul Hankin Mar 04 '17 at 21:00
  • @JensGustedt; *So printf tries to interpret the values it finds as pointers, and seemingly here it succeeds for 0, perhaps because the OP has a 32 bit machine.*: I did't get this part of your comment. – haccks Mar 04 '17 at 21:20
  • @PaulHankin; Agreed. But I never said the `int` value `0` (the result of `i=0`) is a null pointer. – haccks Mar 04 '17 at 21:26
  • You said `printf(i=0);` is equivalent to `printf(0);`. It's not -- the second passes NULL, the former doesn't necessarily. – Paul Hankin Mar 04 '17 at 21:31
0

printf awaits a format string as its first argument. As strings (char*) are nothing else than pointers, you provide an address as the first parameter.

After an address and an integer are implicitly converted into each other,

printf(i = 0) 

is (from the perspective of printf) the same as:

printf( 0 )

which is the same as

printf( NULL ) // no string given at all

If you provide 0 as parameter, printf will gracefully handle that case and don't do anything at all, because 0 is a reserved address meaning nothing at all.

But printf( 1 ) is different: printf now searches for a string at address 1, which is not a valid address for your program and so your program throws a segmentation fault.

[update] The main reason why this works is a combination of several facts you need to know about how char arrays, pointers, assignments and printf itself work:

  • Pointers are implicitly convertible to int and vice versa, so the int value 17 for example gets converted to the memory address 0x00000011 (17) without any further notice. This is due to the fact that C is very close to the hardware and allows you to calculate with memory addresses. In fact, internally an int (or more specific: one special type of an integer) and a pointer are the same, just with a different syntax. This leads to a lot of problems when porting code from 32bit to 64bit-architecture, but this is another topic.

  • Assignments are different from comparations: i = 0 and i == 0 are totally different in meaning. i == 0 returns true when i contains the value 0 while i = 0 returns... 0. Why is that? You can concatenate for example the following variable assignments:

    a = b = 3;

At first, b = 3 is executed, returning 3 again, which is then assigned to a. This comes in handy for cases like testing if there is more items in an array:

while( (data = get_Item()) )

This now leads to the next point:

  • NULL is the same as 0 (which also is the same as false). For the fact that integers and pointers can be converted into each other (or better: are physically the same thing, see my remarks above), there is a reserved value called NULL which means: pointing nowhere. This value is defined to be the virtual address 0x00000000 (32bit). So providing a 0 to a function that expects a pointer, you provide an empty pointer, if you like.

  • char* is a pointer. Same as char[] (there is a slight logical difference in the behaviour and in the allocation, but internally they are basically the same). printf expects a char*, but you provide an int. This is not a problem, as described above, int will get interpreted as an address. Nearly every (well written) function taking pointers as parameters will do a sanity check on these parameters. printf is no exclusion: If it detects an empty pointer, it will handle that case and return silently. However, when providing something different, there is no way to know if it is not really a valid address, so printf needs to do its job, getting interrupted by the kernel when trying to address the memory at 0x00000001 with a segmentation fault.

This was the long explanation.

And by the way: This only works for 32-bit pointers (so compiled as 32-bit binary). The same would be true for long int and pointers on 64-bit machines. However, this is a matter of the compiler, how it converts the expression you provide (normally, an int value of 0 is implicitly cast to a long int value of 0 and then used as a pointer address when assigned, but vice versa won't work without an explicit cast).

Psi
  • 6,387
  • 3
  • 16
  • 26
  • Thanks. I got it :) – Chaitanya Mar 04 '17 at 20:13
  • @Chaitanya; What did you just got? Someone telling you `2 apples + 3 bananas = 10 elephant` and you just agreed!! – haccks Mar 04 '17 at 20:16
  • I guess _you_ did not get me – Psi Mar 04 '17 at 20:17
  • 1
    Can you provide justification from the C standard that `i=0` cast to a pointer results in NULL? Or that `printf(0)` is handled gracefully? I believe that `i=0` cast to a pointer is not guaranteed to result in NULL (only constant integer expressions that evaluate to zero are), and that passing NULL to functions in the standard library is undefined behavior unless specifically noted not to be. – Paul Hankin Mar 04 '17 at 20:18
  • @Psi; Enlighten me? – haccks Mar 04 '17 at 20:20
  • I thought it was a valid answer but ill look into docs now and confirm it myself. @haccks . I would be happy if you explain it :) – Chaitanya Mar 04 '17 at 20:21
  • In fact, what I said is true. printf internally calls vfprintf which does a sanity check on `s` (its first argument). NULL is defined as `#define NULL ( (void *) 0)` and the implicit cast between int and *void is defined in the c standard. There is no comparison like `i == 0` but just an assignment which outputs its input again. I just tried to explain what is happening here – Psi Mar 04 '17 at 20:23
  • 1
    An implementation is allowed to do anything with undefined behavior -- and apparently in the specific implementation C you're looking at, passing 0 to printf does nothing. But even then, if you turn optimizations on, the compiler might start assuming that this printf is never called with an invalid pointer. For example in code like this: `if (x == 0) printf(x); else printf("hello");` it's quite possible that this will be optimized to `printf("hello");` because the compiler can assume that `x` is never 0. http://blog.regehr.org/archives/213 – Paul Hankin Mar 04 '17 at 20:32
  • 1
    That may or may not be true, but I just explained in that particular case why the first call did _not_ result in a runtime error while the second one did (and that was the question) – Psi Mar 04 '17 at 20:34
  • I think your answer would be fine if you edited it to say that the compiler is converting 0 to a pointer, giving address 0 (which is likely to be the same value as you'd get from NULL), and that on some implementations printf handles 0 gracefully (although in general that's undefined behavior). Right now, the answer explains using factually incorrect statements, which makes your answer less helpful than it might be. – Paul Hankin Mar 04 '17 at 20:43
  • 2
    "As strings (char*) are nothing else than pointers". No, strings are not pointers. Arrays are not pointers. A `char*` value may be a pointer to a string; it cannot be a string. Read section 6 of the [comp.lang.c FAQ](http://www.c-faq.com/). – Keith Thompson Mar 04 '17 at 21:15
  • True, this is made more clear in the update. I just wanted to use terminology the user tends to know. Arrays in fact _are_ pointers. And `char*` is not a pointer to a string, but a pointer to a memory location whose values get interpreted as `char` to be precise. But this is hairsplitting. – Psi Mar 04 '17 at 21:18
  • 1
    @Psi; *Arrays in fact are pointers.*: No. It isn't. ***Arrays are not pointers and pointers are not arrays***. – haccks Mar 04 '17 at 21:35
  • I knew this would come... Internally, they are exactly the same, the only difference is the definition. Arrays tend to keep their values on the stack thus only pointing there while pointers are simple addresses pointing anywhere, but the overall handling is the same. When it comes to parameter passing, type conversion or similar things, they are 1:1 exchangeable. And yes, their memory management is performed differently. – Psi Mar 04 '17 at 21:38
  • @Psi; What do you mean by *Internally, they are exactly the same,*? They aren't same at all. The only similarity between pointer and arrays comes because of the fact that array indexing and pointer arithmetic are same. That's all. – haccks Mar 04 '17 at 21:44
  • Internally: An array variable also is not more than a memory address pointing somewhere. The only difference is that the compiler handles the allocation and deallocation. This may be vital to know in situations where you need to know about memory management, but it makes no difference if you pass a char[] or a char* to sprintf. And when passing a char[] variable to a char* parameter, there is even no internal conversion necessary, because _internally_ they are the same (one is managed, the other one is not... and please don't confuse my managed with managed code like in .NET) – Psi Mar 04 '17 at 21:46
  • I like your _a little over-simplified_ answer. C is a messy and stupid language, patched over time to make it (perhaps) more reasonable. Some people take these patches as a bible and try to go even further, leaving the reality behind. It is not wrong, but for an OP posing a really naive question, a naive answer (with warnings) is the best. There will be time in the future to analyze while `(void *) 0` is not the same as `(void *) (i=0)`. – linuxfan says Reinstate Monica Mar 05 '17 at 07:29
  • @Psi: You are completely and utterly wrong, and I ask you to stop spreading this misunderstanding. Arrays **are not** pointers. Please read section 6 of the [comp.lang.c FAQ](http://www.c-faq.com/) before commenting again. If arrays were just pointers, then `int arr[10]; printf("%zu\n", sizeof arr);` would print the value of `sizeof (int)`; instead, it prints the value of `10*sizeof(int)`. – Keith Thompson Mar 05 '17 at 09:28
  • Also, you say "*After an address and an integer are implicitly converted into each other*". No, there is no such implicit conversion. If the compiler accepts the call, what will most likely happen is that the `int` value will be treated as if it were a pointer value. That's not the same as a conversion. – Keith Thompson Mar 05 '17 at 09:29
0

printf requires a const char * for input, whereas you're giving it an int

sdMarth
  • 521
  • 1
  • 8
  • 15
  • You need [this](http://stackoverflow.com/questions/42600733/why-printf-gives-a-runtime-error#comment72332191_42600733). – haccks Mar 04 '17 at 20:19
0

Why does this code below compiles and executes but doesn't print anything in output?

printf(i = 0);

The question embodies a false premise. On any modern C compiler, this clearly-erroneous code generates at least one error.

Steve Summit
  • 45,437
  • 7
  • 70
  • 103