#include <stdio.h>
int puts(const char* str)
{
return printf("Hiya!\n");
}
int main()
{
printf("Hello world.\n");
return 0;
}
This code outputs "Hiya!" when run. Could someone explain why?
The compile line is:
gcc main.c
EDIT: it's now pure C, and any extraneous stuff has been removed from the compile line.
In brief, both printf and puts helps to display a string or a set of characters on to the standard output which is usually the computer screen. The main difference between printf and puts is that printf does not move the cursor to the new line by default while puts moves the cursor to the new line by default.
puts() vs printf() for printing a string puts() can be preferred for printing a string because it is generally less expensive (implementation of puts() is generally simpler than printf()), and if the string has formatting characters like '%s', then printf() would give unexpected results.
the printf() function is used to print both strings and variables to the screen while the puts() function only permits you to print a string only to your screen.
puts() The function puts() is used to print the string on the output stream with the additional new line character '\n'. It moves the cursor to the next line. Implementation of puts() is easier than printf().
Yes, a compiler may replace a call to printf
by an equivalent call to puts
.
Because you defined your own function puts
with the same name as a standard library function, your program's behavior is undefined.
Reference: N1570 7.1.3:
All identifiers with external linkage in any of the following subclauses [this includes
puts
] are always reserved for use as identifiers with external linkage....
If the program declares or defines an identifier in a context in which it is reserved (other than as allowed by 7.1.4), or defines a reserved identifier as a macro name, the behavior is undefined.
If you remove your own puts
function and examine an assembly listing, you might find a call to puts
in the generated code where you called printf
in the source code. (I've seen gcc perform this particular optimization.)
It depends upon the compiler and the optimization level. Most recent versions of GCC, on some common systems, with some optimizations, are able to do such an optimization (replacing a simple printf
with puts
, which AFAIU is legal w.r.t. standards like C99)
You should enable warnings when compiling (e.g. try first to compile with gcc -Wall -g
, then debug with gdb
, then when you are confident with your code compile it with gcc -Wall -O2
)
BTW, redefining puts
is really really ugly, unless you do it on purpose (i.e. are coding your own C library, and then you have to obey to the standards). You are getting some undefined behavior (see also this answer about possible consequences of UB). Actually you should avoid redefining names mentioned in the standard, unless you really really know well what you are doing and what is happening inside the compiler.
Also, if you compiled with static linking like gcc -Wall -static -O main.c -o yourprog
I'll bet that the linker would have complained (about multiple definition of puts
).
But IMNSHO your code is plain wrong, and you know that.
Also, you could compile to get the assembler, e.g. with gcc -fverbose-asm -O -S
; and you could even ask gcc
to spill a lot of "dump" files, with gcc -fdump-tree-all -O
which might help you understanding what gcc
is doing.
Again, this particular optimization is valid and very useful : the printf
routine of any libc has to "interpret" at runtime the print format string (handling %s
etc ... specially); this is in practice quite slow. A good compiler is right in avoiding calling printf
(and replacing with puts
) when possible.
BTW gcc
is not the only compiler doing that optimization. clang
also does it.
Also, if you compile with
gcc -ffreestanding -O2 almo.c -o almo
the almo
program shows Hello world.
If you want another fancy and surprizing optimization, try to compile
// file bas.c
#include <stdlib.h>
int f (int x, int y) {
int r;
int* p = malloc(2*sizeof(int));
p[0] = x;
p[1] = y;
r = p[0]+p[1];
free (p);
return r;
}
with gcc -O2 -fverbose-asm -S bas.c
then look into bas.s
; you won't see any call to malloc
or to free
(actually, no call
machine instruction is emitted) and again, gcc
is right to optimize (and so does clang
)!
PS: Gnu/Linux/Debian/Sid/x86-64; gcc
is version 4.9.1, clang
is version 3.4.2
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With