2014-09-12 3 views
11
#include <stdio.h> 

int puts(const char* str) 
{ 
    return printf("Hiya!\n"); 
} 

int main() 
{ 
    printf("Hello world.\n"); 
    return 0; 
} 

Dieser Code gibt "Hiya!" wenn ausgeführt. Könnte jemand erklären warum?Kann printf durch Puts automatisch in einem C-Programm ersetzt werden?

Die Kompilierung Linie ist: gcc main.c

EDIT: es ist jetzt reine C und jede fremde Sachen von dem Kompilierung Linie entfernt.

+0

Es Optimierung, die nicht auf die Optimierung Option von GCC abhängt. – BLUEPIXY

+1

Die Frage wird mit "c" markiert, obwohl der Aufruf des Compilers darauf hinweist, dass der Code als C++ kompiliert wird. –

+2

Ja, gcc kann einen einfachen Konstantenstringdruck von 'printf()' nach 'puts()' optimieren, da letzteres bessere Ergebnisse liefert ... da Sie die Standardbibliothek puts() durch ein lokales Symbol ersetzt haben bekommen. Wenn Sie -OO anstelle von -O2 verwenden, sollten Sie eigentlich 'printf()' – JohnH

Antwort

12

Ja, ein Compiler einen Aufruf printf durch einen Äquivalent Aufruf puts ersetzen kann.

Da Sie Ihre eigene Funktion puts mit dem gleichen Namen wie eine Standardbibliotheksfunktion definiert haben, ist das Verhalten Ihres Programms nicht definiert.

Referenz: N1570 7.1.3:

Alle Bezeichner mit externer Bindung in einem der folgenden Subklauseln [diese puts beinhaltet] sind immer reserviert für die Verwendung als Bezeichner mit externer Bindung.
...
Wenn das Programm erklärt oder definiert einen Identifikator in einem Kontext, in dem es reserviert ist (außer wie durch 7.1.4 erlaubt), oder definiert einen reservierten Identifikator als Makroname ist das Verhalten undefiniert.

Wenn Sie Ihre eigene puts Funktion entfernen und eine Assembler-Liste prüfen, Sie könnten einen Aufruf an puts in dem generierten Code finden, wo Sie printf im Quellcode genannt. (Ich habe GCC diese besondere Optimierung durchführen.)

+2

(Haftungsausschluss: Nur in gehosteten Implementierungen) – Deduplicator

0

Vermutlich ruft printf() Ihrer Bibliothek puts() auf.

Ihre puts() ersetzt die Bibliotheksversion.

+6

Eigentlich ist das 'printf' * nicht * ruft' puts' bei * runtime * auf, aber der * compiler * hat recht, das 'printf' durch' puts' bei * kompilierzeit zu ersetzen * –

8

Es hängt vom Compiler und der Optimierungsstufe ab. Die neuesten Versionen von GCC, auf einige gemeinsame Systeme, mit einigen Optimierungen, sind in der Lage, eine solche Optimierung zu tun (eine einfache printf mit puts ersetzt, die AFAIU Rechts WRT Standards wie C99 ist)

Sie sollten Warnungen aktivieren beim Kompilieren (zB zuerst versuchen, mit gcc -Wall -g, dann debuggen mit gdb zu kompilieren, dann, wenn Sie mit Ihrem Code sicher sind, kompilieren Sie es mit gcc -Wall -O2)

BTW, neu zu definieren puts wirklich wirklich hässlich ist, wenn Sie es absichtlich tun (dh codieren Ihre eigene C-Bibliothek, und dann müssen Sie den Standards folgen). Sie erhalten einige undefined behavior (siehe auch this answer über mögliche Folgen von UB). Eigentlich sollten Sie es vermeiden, die im Standard erwähnten Namen neu zu definieren, es sei denn, Sie wissen wirklich genau, was Sie tun und was im Compiler passiert.

Auch, wenn Sie mit statischen Verknüpfung wie gcc -Wall -static -O main.c -o yourprog kompiliert habe, wette ich, dass der Linker sich beschwert hätte (über mehrere Definition von puts).

Aber IMNSHO ist Ihr Code einfach falsch, und Sie wissen das.

Sie könnten auch kompilieren, um den Assembler zu erhalten, z. mit gcc -fverbose-asm -O -S; und Sie könnten sogar fragen gcc, um eine Los von "Dump" -Dateien verschütten, mit gcc -fdump-tree-all -O, die Ihnen helfen könnte zu verstehen, was gcc tut.

Auch diese besondere Optimierung ist gültig und sehr nützlich: die printf Routine jeder libc muss zur Laufzeit der Druckformatstring (Umgang mit %s etc ... speziell) „interpretieren“; das ist in der Praxis ziemlich langsam. Ein guter Compiler ist richtig, wenn es darum geht, den Aufruf printf (und ersetzt durch puts) wenn möglich zu vermeiden.

BTW gcc ist nicht der einzige Compiler, der diese Optimierung durchführt. clang macht es auch.

Auch, wenn Sie mit

gcc -ffreestanding -O2 almo.c -o almo 

kompilieren das almo Programm zeigt Hello world.


Wenn Sie eine andere Phantasie und überraschende Optimierung wollen, versuchen

// 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; 
} 

mit gcc -O2 -fverbose-asm -S bas.c zu kompilieren dann schau in bas.s; Sie sehen keinen Anruf an malloc oder an free (tatsächlich wird keine call Maschinenanweisung ausgegeben) und wiederum gcc ist rechts zu optimieren (und tut dies auch clang)!

PS: Gnu/Linux/Debian/Sid/x86-64; gcc ist Version 4.9.1, clang ist Version 3.4.2

+0

Guter Punkt; gerade mit gcc versucht, so steht die frage, und danke für die antwort. Werde mehr Nachforschungen anstellen und später eine Antwort akzeptieren. :) – Almo

+0

Ich weiß, dass es falsch ist. Ich war neugierig, warum das so ist. – Almo

+0

Wow, das ist ein ziemlich cool (die Phantasie und Überraschung Optimierung) – Almo

1

Versuchen Sie ltrace auf Ihre ausführbare Datei. Sie werden sehen, dass printf durch puts Aufruf vom Compiler ersetzt wird. Dies hängt von der Art und Weise Sie printf

Eine interessante Lektüre zu diesem Thema genannt ist here

+0

@navigaid Danke für das Aufzeigen. Ich habe die Antwort bearbeitet. –

Verwandte Themen