Can printf get replaced by puts automatically in a C program?
Asked Answered
D

4

16
#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.

Dramaturgy answered 12/9, 2014 at 20:54 Comment(6)
It optimization that does not depend on the optimization option of GCC.Eleanore
The question is tagged as c, although the compiler invocation suggests that the code is compiled as C++.Metabolize
Yes, gcc can optimize a simple constant string print from printf() to puts(), as the latter performs better... since you replaced the standard library puts() with a local symbol, that is what you get. If you use -O0 instead of -O2, you should actually call printf()Poet
OK, retagging and editing title.Dramaturgy
BTW, your code looks like C code but you compile it as C++11; why do you do that? If you want to code in C++11 use std::cout<<"hello world" << std::endl;Louvre
Good point; was just a copypasta compile line. Still get same result without -std=c++11 and -O2. Sorry about that.Dramaturgy
C
17

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.)

Cymose answered 12/9, 2014 at 20:59 Comment(1)
(Disclaimer: Only in hosted implementations)Scrimpy
L
12

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

Louvre answered 12/9, 2014 at 20:57 Comment(3)
Good point; just tried with gcc, so the question stands, and thanks for the answer. Will do some more investigation and accept an answer later. :)Dramaturgy
I do know it's wrong. I was curious about why it does this.Dramaturgy
Wow that's a pretty cool (the fancy and surprizing optimization)Dramaturgy
C
1

Try ltrace on your executable. You will see that printf gets replaced by puts call by the compiler. This depends on the way you called printf

An interesting reading on this is here

Crossman answered 18/4, 2016 at 17:20 Comment(0)
S
-1

Presumably, your library's printf() calls puts ().

Your puts() is replacing the library version.

Shrieve answered 12/9, 2014 at 20:56 Comment(2)
Actually, the printf is not calling puts at runtime, but the compiler is right to replace the printf with puts at compile-timeLouvre
If that were the case then OPs definition of puts would be infinite recursion and never output anything.Nobe

© 2022 - 2024 — McMap. All rights reserved.