Warum ein Ausdruck anstelle einer Konstante in der Bedingung einer C-For-Schleife?

Lesezeit: 1 Minute

Benutzeravatar von Harrythomas
Harrythomas

Bei vielen Programmierwettbewerben habe ich gesehen, wie Leute diese Art von Texten geschrieben haben for-Schleife

for(i = 0; i < (1 << 7); i++)

Es sei denn, ich vermisse etwas, das ist dasselbe wie

for(i = 0; i < 128; i++)

Warum die verwenden (1 << 7) Ausführung?

Ist es nicht unnötig, die Bedingung jedes Mal zu berechnen?

  • C hat eine Sache namens “die Als-ob-Regel” (na ja – nicht genau; C++ hat das, und C hat äquivalenten Text, nennt ihn aber nicht so genau): Wichtig ist, dass das Programm die gleiche Ausgabe wie what erzeugt Ihre Codeausgaben. (“Ausgabe” umfasst Zugriffe auf flüchtige Variablen und Aufrufe von Bibliotheksfunktionen). Ansonsten kann er machen was er will. Wenn Sie ein Programm zum Generieren von Primzahlen geschrieben haben, könnte der Compiler dies erkennen und einfach eine Liste von Primzahlen in der ausführbaren Datei hartcodieren. In Ihrem Code würden alle Compiler 128 fest codieren, anstatt zur Laufzeit eine Verschiebung vorzunehmen.

    – MM

    30. August 2014 um 12:16 Uhr

  • Vielleicht kennen nicht alle Menschen die Zweierpotenzen zwischen 2^0 und 2^32 auswendig. (Obwohl ich zugegebenermaßen denke, dass ein Programmierer sie zumindest bis 2 ^ 16 kennen sollte, und einige “Interpolationspunkte” wie 2 ^ 20, 2 ^ 24, 2 ^ 30 und 2 ^ 32 selbst …)

    – Marco13

    30. August 2014 um 23:43 Uhr


  • @harrythomas: Schreiben 128 wird es wie eine “magische Konstante” unerklärlichen Ursprungs erscheinen lassen. Schreiben (1 << 7) macht sofort klar, wofür diese Konstante steht und woher sie kommt. Es ist vorzuziehen, es so zu machen.

    – AnT steht zu Russland

    31. August 2014 um 1:46 Uhr


  • Beide Versionen sind schlecht. Es sollte sein for(i = 0; i < NUMBER_OF_STEPS; i++). Verwenden Sie keine magischen Zahlen.

    – glampert

    31. August 2014 um 1:46 Uhr

  • @LukaHorvat: Dann schreib for(i = 0; i < ONE_HUNDRED_TWENTY_EIGHT; i++). Ich nenne das A magische Konstante!.

    – rodrigo

    31. August 2014 um 19:36 Uhr

Ja, sie sind im Verhalten gleichwertig.

Warum verwenden die Leute dann die (1 << 7)-Version?

Ich schätze, sie benutzen es, um zu dokumentieren, dass es eine Potenz von 2 ist.

Die Berechnung der Bedingung muss jedes Mal ein Overhead sein! Ich kann den Grund dafür nicht finden!

Nicht wirklich, jeder normale Compiler wird es ersetzen 1 << 7 durch 128 und so haben beide Schleifen die gleiche Leistung.

(C11, 6.6p2) “Ein konstanter Ausdruck kann eher während der Übersetzung als zur Laufzeit ausgewertet werden und kann dementsprechend an jeder Stelle verwendet werden, an der sich eine Konstante befindet.”

  • Beachten Sie, dass die Sprache C erfordert der Compiler zu in der Lage sein konstante Ausdrücke zur Kompilierungszeit auszuwerten, da sie in allen möglichen Kontexten verwendet werden können, in denen der tatsächliche Wert bekannt sein muss, entweder um Beschränkungsverletzungen (z. B. negative Array-Größe oder ungültige Bitfeldbreiten) oder Ausdruckstypen (aufgrund der Tatsache dass es vom Wert abhängt, ob ein Ausdruck eine Null-Zeiger-Konstante ist, und das Ergebnis kann auf sehr mächtige Weise über die weitergegeben werden ?: Operator). Daher gibt es keinen guten Grund, nicht alle konstanten Ausdrücke zur Kompilierzeit vollständig auszuwerten.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    31. August 2014 um 5:37 Uhr


  • Eigentlich dachte ich, es sei der Präprozessor, der konstante Ausdrücke immer zur Kompilierzeit auswertet. Das ist also eine Garantie dafür, dass die Verwendung von Ausdrücken keinen Overhead verursacht – solange sie informativer sind als eine Konstante.

    – Staffel

    1. September 2014 um 11:37 Uhr

  • @Echelon: Der Präprozessor kann nur Präprozessor-Token erkennen und Zeichenfolgen ersetzen. Außerdem hat es keine Kenntnisse der C/C++-Syntax und -Semantik.

    – Yves Daust

    3. September 2014 um 6:31 Uhr

  • @YvesDaoust @Echelon Beachten Sie, dass der Präprozessor-Konstantenausdruck eine andere Klasse von Konstantenausdrücken ist. Präprozessor-Arithmetik wird unter Verwendung des größten ganzzahligen Typs im Ziel (intmax_t oder uintmax_t). Ein #if Steuern des konstanten Ausdrucks (z. B. in #if 1 << 7 == 128) ist nötig zur Vorprozessorzeit auszuwerten (genauer gesagt in Übersetzungsphase 4).

    – au

    3. September 2014 um 11:56 Uhr

  • @Echelon: Ich habe mir gerade eine Zwischenausgabe des Präprozessors (Visual Studio-Compiler) angesehen, und im Quellcode wird keine Konstante ersetzt. Könnte ein Präprozessor eine gültige wörtliche Konstantensubstitution durchführen, ohne die gesamte Sprachsyntax zu kennen?

    – Yves Daust

    4. September 2014 um 7:38 Uhr

Lassen Sie uns jede dieser Optionen in einfaches Englisch übersetzen:

for(i = 0; i < (1 << 7); i++) // For every possible combination of 7 bits
for(i = 0; i < 128; i++)      // For every number between 0 and 127

Das Laufzeitverhalten sollte in beiden Fällen identisch sein.

Wenn man einen anständigen Compiler annimmt, sollte sogar der Assembler-Code identisch sein.

Die erste Option wird also im Wesentlichen nur verwendet, um “eine Aussage zu treffen”.

Sie können genauso gut die zweite Option verwenden und oben einen Kommentar hinzufügen.

  • Eine noch bessere Aussage wäre ein passenderer Name für i. i bedeutet in der Regel so etwas wie “der Index, an dem ich arbeite”, aber der Programmierer versucht zu sagen “der Muster Ich arbeite an”. So etwas wie bitCombo würde es ohne den Kommentar klarstellen. Wettbewerbe neigen jedoch dazu, kurz und clever zu sein, anstatt klar zu sein, also sind sie vielleicht dabei geblieben i für die Mystik.

    – Mirinth

    31. August 2014 um 16:56 Uhr

1 << 7 ein konstanter Ausdruck ist, behandelt der Compiler ihn so 128gibt es keinen Overhead in der Laufzeit.

Ohne den Schleifenkörper ist es schwer zu sagen, warum der Autor ihn verwendet. Möglicherweise ist es eine Schleife, die etwas iteriert, das mit 7 Bits verbunden ist, aber das ist nur meine Vermutung.

  • Danke..yup die For-Schleife, die ich mir angesehen habe, arbeitet mit 7 Bits! Vielen Dank!

    – Harrythomas

    30. August 2014 um 12:10 Uhr

  • Ja. Ich mache das, wenn es beim Verständnis hilft. Ich nenne sie “Beißer” 🙂

    – Martin Jakob

    30. August 2014 um 12:53 Uhr

Benutzeravatar von Shafik Yaghmour
Shafik Yaghmur

Warum verwenden die Leute dann die (1 << 7)-Version?

Es ist eine Form der Dokumentation, es ist aber keine magische Zahl 2^7(zwei hoch siebte Potenz), was für denjenigen von Bedeutung ist, der den Code geschrieben hat. Ein moderner optimierender Compiler sollte für beide Beispiele genau denselben Code generieren, sodass die Verwendung dieses Formulars keine Kosten verursacht und der Kontext hinzugefügt wird.

Verwenden Gottriegel Wir können bestätigen, dass dies tatsächlich der Fall ist, zumindest für mehrere Versionen von gcc, clang und icc. Verwenden Sie ein einfaches Beispiel mit Nebenwirkungen, um sicherzustellen, dass der Code nicht vollständig wegoptimiert wird:

#include <stdio.h>

void forLoopShift()
{
  for(int i = 0; i < (1 << 7); i++)
  {
    printf("%d ", i ) ;
  }
}

void forLoopNoShift()
{
  for(int i = 0; i < 128; i++)
  {
        printf("%d ", i ) ;
  }
}

Für den relevanten Teil des Codes können wir sehen, dass beide Folgendes generieren live sehen:

cmpl    $128, %ebx

Was wir haben, ist ein ganzzahliger konstanter Ausdruck wie im Entwurf des C11-Standardabschnitts definiert 6.6 Konstante Ausdrücke was sagt:

Ein ganzzahliger konstanter Ausdruck117) muss einen ganzzahligen Typ haben und darf nur Operanden haben, die ganzzahlige Konstanten, Aufzählungskonstanten, Zeichenkonstanten, Größenausdrücke, deren Ergebnisse ganzzahlige Konstanten sind,[…]

und:

Konstante Ausdrücke dürfen keine Zuweisungs-, Inkrement-, Dekrement-, Funktionsaufruf- oder Kommaoperatoren enthalten, es sei denn, sie sind in einem Unterausdruck enthalten, der nicht ausgewertet wird.115)

und wir können sehen, dass ein konstanter Ausdruck während der Übersetzung ausgewertet werden darf:

Ein konstanter Ausdruck kann eher während der Übersetzung als zur Laufzeit ausgewertet werden und kann dementsprechend an jeder Stelle verwendet werden, an der sich eine Konstante befindet.

for(i = 0; i < (1 << 7); i++)

und

für(i = 0; i < 128; i++)

liefert die gleiche Leistung, aber der Entwickler kann einen großen Vorteil daraus ziehen, falls for(i = 0; i < (1 << 7); i++) in einer Schleife als verwendet wird

for(int k = 0; k < 8; k++)
{
  for(int i = 0; i < (1 << k); i++)
   {
    //your code
    }

}

Jetzt ist es in der inneren Schleife Obergrenze dh (1 << k) ändert sich mit Potenz von 2 Laufzeit. Aber es ist anwendbar, wenn Ihr Algorithmus diese Logik erfordert.

  • (1 << k) ist ein Schleifeninvariante Also der Compiler sollte berechnen Sie es nur einmal pro äußere Schleifeniteration. Tatsächlich kann es funktionieren Eliminierung der Induktionsvariablenumwandeln in for (int j = 1; j <= 128; j <<= 1) for (i = 0; i < j; i++) { ... }

    – zol

    30. August 2014 um 13:59 Uhr

Benutzeravatar von Shafik Yaghmour
Shafik Yaghmur

Der Compiler gibt für beide Fälle denselben Code aus. Sie möchten wahrscheinlich je nach Kontext unterschiedliche Formen verwenden.

  1. Sie können verwenden NUM_STEPS oder NUM_ELEMENTS_IN_NETWORK_PACKET wenn es sich um einen konstanten Teil oder eine Designentscheidung in Ihrem Algorithmus handelt, den Sie deutlich machen möchten.
  2. Oder du kannst schreiben 128um es klar zu machen 128eine Konstante.
  3. Oder schreiben 1 << 7 wenn du bei einem Wettbewerb bist und der Test so etwas gesagt hat “2^7 Mal ausführen”.

Oder Sie können angeben, dass Sie sich mit Bitoperationen auskennen!

Meiner bescheidenen Meinung nach ist Programmieren wie das Schreiben eines Briefes für zwei Personen, den Compiler und die Person, die ihn lesen muss. Was du meinst, sollte beiden klar sein.

  • (1 << k) ist ein Schleifeninvariante Also der Compiler sollte berechnen Sie es nur einmal pro äußere Schleifeniteration. Tatsächlich kann es funktionieren Eliminierung der Induktionsvariablenumwandeln in for (int j = 1; j <= 128; j <<= 1) for (i = 0; i < j; i++) { ... }

    – zol

    30. August 2014 um 13:59 Uhr

Benutzeravatar von Chris Fox
Chris Fox

Es wird vom Präprozessor ausgewertet, da beide Operanden konstant sind.

Aber wenn Sie die Zahl anstelle der Bitverschiebung verwenden, sollte es nicht 0x0100 sein?

1412440cookie-checkWarum ein Ausdruck anstelle einer Konstante in der Bedingung einer C-For-Schleife?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy