Fehler im Buch "C von A bis Z"?



  • Hi!

    Ich lese zur Zeit das Buch "C von A bis Z" um meine C-Kenntnisse mal wieder aufzufrischen. Beim Durchlesen fiel mir allerdings ein Fehler auf, zumindest glaube ich das.
    Und zwar heißt es im im Kapitel 12.10.1 (Funktionen - Typ-Qualifizierer - volatile, hier der Text im Zusammenhang: http://www.pronix.de/pronix-703.html), dass in der Schleife

    [cpp]do {
    printf("Gerät X wird überprüft ...\n");
    } while(reg & (STATUS_A|STATUS_B) == 0);
    printf("Gerät X Status ... [OK]\n");[/cpp]
    

    "volatile" nötig wäre, damit der Compiler nicht die gesamte Schleife wegoptimiert.
    Ist es nicht vielmehr so, dass der Optimierer das Ganze so optimieren würde:

    [cpp]bool b = reg & (STATUS_A | STATUS_B);
    do {
    printf("Gerät X wird überprüft ...\n");
    } while(b == 0);
    printf("Gerät X Status ... [OK]\n");[/cpp]
    

    Natürlich ist die Behauptung, dass hier "volatile" erforderlich ist, richtig, imho allerdings nicht, dass die gesamte Schleife wegoptimiert werden würde.
    Vielmehr wird der boolsche Ausdruck soweit optimiert, dass er nur ein einziges Mal ausgewertet wird und nicht immer wieder neu berechnet wird.

    Sehe ich da was falsch, oder ist das ein kleiner Fehler im Buch?

    Viele Grüße,
    m3ph1570

    PS: Ansonsten ist das Buch zum Wiederauffrischen perfekt geeignet. 👍



  • wenn reg volatile sein soll, dann wird es wahrscheinlich von aussen veraendert und die schleife checkt, ob/wann/wie es veraendert wird und reagiert (miserable art, so ein tight loop).

    der compiler wuerde beim volatile nichts optimieren.
    ohne volatile kannst du nicht sicher sein. der compiler koennte ja erwarten, dass reg (wenns global ist) durch printf geaendert werden koennte...

    nach asm compilen und vergleichen

    edit: die schleife WUERDE wegfallen, jedoch bliebe das printf noch uebrig. nur der vergleich wuerde (insofern moeglich) schon beim compilen ausgewertet werden.



  • so, ich hab jetzt einfach mal ein kurzes Beispielprogramm geschrieben:

    #include <stdio.h>
    
    int main()
    {
    	int reg = 0;
    	int STATUS_A = 1;
    	int STATUS_B = 0;
    
    	do {
    		printf("Gerät X wird überprüft ...\n");
    	} while((reg & (STATUS_A|STATUS_B)) == 0);
    	printf("Gerät X Status ... [OK]\n");
    
    	return 0;	
    }
    

    Dann hab ich das ganze disassembliert (ganz simpel mit "$objdump -d <executable>" 🤡 ). Hier ein Ausschnitt:

    8048398:       c7 45 f4 00 00 00 00    movl   $0x0,0xfffffff4(%ebp)
     804839f:       c7 45 f8 01 00 00 00    movl   $0x1,0xfffffff8(%ebp)
     80483a6:       c7 45 fc 00 00 00 00    movl   $0x0,0xfffffffc(%ebp)
     [b]80483ad:[/b]       [b]83 ec 0c[/b]                [b]sub[/b]    [b]$0xc,%esp[/b]
     80483b0:       68 94 84 04 08          push   $0x8048494
     80483b5:       e8 ee fe ff ff          call   80482a8 <puts@plt>
     80483ba:       83 c4 10                add    $0x10,%esp
     80483bd:       8b 45 fc                mov    0xfffffffc(%ebp),%eax
     80483c0:       0b 45 f8                or     0xfffffff8(%ebp),%eax
     80483c3:       23 45 f4                and    0xfffffff4(%ebp),%eax
     80483c6:       85 c0                   test   %eax,%eax
     [b]80483c8:[/b]       [b]74 e3[/b]                   [b]je[/b]     [b]80483ad <main+0x31>[/b]
     80483ca:       83 ec 0c                sub    $0xc,%esp
     80483cd:       68 b2 84 04 08          push   $0x80484b2
     80483d2:       e8 d1 fe ff ff          call   80482a8 <puts@plt>
     80483d7:       83 c4 10                add    $0x10,%esp
     80483da:       b8 00 00 00 00          mov    $0x0,%eax
     80483df:       c9                      leave
    

    Der Bereich zwischen den fettgedruckten Zeilen stellt die Schleife dar.
    Diese wird also nicht, wie in dem Buch behauptet, wegoptimiert.

    Aber wenn ich das richtig sehe, dann hat der gcc da gar nix optimiert.
    Mit den Zeilen

    80483bd:       8b 45 fc                mov    0xfffffffc(%ebp),%eax
     80483c0:       0b 45 f8                or     0xfffffff8(%ebp),%eax
     80483c3:       23 45 f4                and    0xfffffff4(%ebp),%eax
    

    werden doch jedesmal in der Schleife die bool-Variablen aus dem Speicher geladen und neu verknüpft, oder?

    Viele Grüße,
    m3ph1570



  • C von A bis Z schrieb:

    Manche Compiler erkennen jetzt an der while-Schleife, dass hier immer auf die gleiche Adresse überprüft wird, und optimieren die do while-Schleife einfach weg.

    Offenbar gehört der gcc zu den Compilern, die das nicht optimiert haben (oder du hast ihm die Optimierung abgeschaltet).



  • Hm, abgeschaltet habe ich das nicht. Aber eigentlich erzeugt der gcc doch recht vernünftigen Code, wenn man sich son die Benchmarks ansieht... 😕



  • man probiere "-O2" oder "-03" oder schaue in die GCC doku nach den schaltern


Anmelden zum Antworten