it-swarm.com.de

Ist die nicht initialisierte lokale Variable der schnellste Zufallszahlengenerator?

Ich weiß, dass die nicht initialisierte lokale Variable ein undefiniertes Verhalten ist ( UB ), und der Wert enthält möglicherweise Trap-Darstellungen, die den weiteren Betrieb beeinträchtigen können, aber manchmal möchte ich sie verwenden Die Zufallszahl dient nur zur visuellen Darstellung und wird nicht weiter in anderen Programmteilen verwendet. Stellen Sie zum Beispiel etwas mit zufälliger Farbe in einem visuellen Effekt ein, zum Beispiel:

void updateEffect(){
    for(int i=0;i<1000;i++){
        int r;
        int g;
        int b;
        star[i].setColor(r%255,g%255,b%255);
        bool isVisible;
        star[i].setVisible(isVisible);
    }
}

ist es das schneller als

void updateEffect(){
    for(int i=0;i<1000;i++){
        star[i].setColor(Rand()%255,Rand()%255,Rand()%255);
        star[i].setVisible(Rand()%2==0?true:false);
    }
}

und auch schneller als andere zufallsgeneratoren?

316
ggrr

Wie andere angemerkt haben, ist dies undefiniertes Verhalten (Undefined Behaviour, UB).

In der Praxis wird es (wahrscheinlich) tatsächlich (irgendwie) funktionieren. Das Lesen von einem nicht initialisierten Register auf x86 [-64] -Architekturen führt in der Tat zu fehlerhaften Ergebnissen und wird wahrscheinlich nichts Schlechtes bewirken (im Gegensatz zu z. B. Itanium, wo Register können als ungültig markiert werden , so dass Liest Ausbreitungsfehler wie NaN).

Es gibt jedoch zwei Hauptprobleme:

  1. Es wird nicht besonders zufällig sein. In diesem Fall lesen Sie vom Stapel, sodass Sie alles bekommen, was vorher da war. Das kann praktisch zufällig sein, vollständig strukturiert, das Passwort, das Sie vor zehn Minuten eingegeben haben, oder das Keksrezept Ihrer Großmutter.

  2. Es ist eine schlechte Übung , solche Dinge in Ihren Code einfließen zu lassen. Technisch könnte der Compiler jedes Mal reformat_hdd(); einfügen, wenn Sie eine undefinierte Variable lesen. Es wird nicht, aber du solltest es trotzdem nicht tun. Mach keine unsicheren Dinge. Je weniger Ausnahmen Sie machen, desto sicherer sind Sie vor versehentlichen Fehlern alle der Zeit.

    Das dringlichere Problem bei UB ist, dass das Verhalten Ihres gesamten Programms dadurch undefiniert wird. Moderne Compiler können dies verwenden, um große Teile Ihres Codes zu entfernen oder sogar gehen Sie in der Zeit zurück . Mit UB zu spielen ist wie ein viktorianischer Ingenieur, der einen lebenden Kernreaktor demontiert. Es gibt zig Dinge, die schief gehen, und Sie werden wahrscheinlich nicht die Hälfte der zugrunde liegenden Prinzipien oder der implementierten Technologie kennen. Es ist könnte in Ordnung, aber du solltest es trotzdem nicht zulassen. Schauen Sie sich die anderen Nizza Antworten für Details an.

Außerdem würde ich dich feuern.

296
imallett

Lassen Sie mich dies klar sagen: Wir rufen in unseren Programmen kein undefiniertes Verhalten auf. Es ist nie eine gute Idee, Punkt. Es gibt seltene Ausnahmen von dieser Regel; Zum Beispiel, wenn Sie ein Bibliotheksimplementierer sind, der offsetof implementiert. Wenn Ihr Fall unter eine solche Ausnahme fällt, wissen Sie dies wahrscheinlich bereits. In diesem Fall wissen wir , dass die Verwendung nicht initialisierter automatischer Variablen undefiniertes Verhalten ist .

Compiler sind mit Optimierungen in Bezug auf undefiniertes Verhalten sehr aggressiv geworden, und es gibt viele Fälle, in denen undefiniertes Verhalten zu Sicherheitslücken geführt hat. Der berüchtigtste Fall ist wahrscheinlich das Entfernen des Null-Zeiger-Checks im Linux-Kernel , das ich in meiner Antwort auf den C++ - Kompilierungsfehler erwähne Die Compileroptimierung um undefiniertes Verhalten hat eine endliche Schleife in eine unendliche verwandelt.

Wir können CERTs Gefährliche Optimierungen und den Verlust der Kausalität ( Video ) lesen, in dem unter anderem steht:

Zunehmend nutzen Compiler-Autoren undefinierte Verhaltensweisen in den Programmiersprachen C und C++, um die Optimierungen zu verbessern.

Häufig beeinträchtigen diese Optimierungen die Fähigkeit der Entwickler, eine Ursache-Wirkungs-Analyse für ihren Quellcode durchzuführen, dh die Abhängigkeit der nachgelagerten Ergebnisse von früheren Ergebnissen zu analysieren.

Folglich beseitigen diese Optimierungen die Kausalität in der Software und erhöhen die Wahrscheinlichkeit von Softwarefehlern, Fehlern und Schwachstellen.

Insbesondere in Bezug auf unbestimmte Werte sorgt der C-Standard-Fehlerbericht 451: Instabilität nicht initialisierter automatischer Variablen für eine interessante Ablesung. Es wurde noch nicht gelöst, führt aber das Konzept von wackeligen Werten ein, was bedeutet, dass sich die Unbestimmtheit eines Wertes durch das Programm ausbreiten kann und an verschiedenen Punkten im Programm unterschiedliche unbestimmt Werte haben kann.

Ich kenne keine Beispiele, wo dies passiert, aber an dieser Stelle können wir es nicht ausschließen.

Echte Beispiele, nicht das erwartete Ergebnis

Es ist unwahrscheinlich, dass Sie zufällige Werte erhalten. Ein Compiler könnte die Abwesenheitsschleife insgesamt optimieren. Zum Beispiel mit diesem vereinfachten Fall:

void updateEffect(int  arr[20]){
    for(int i=0;i<20;i++){
        int r ;    
        arr[i] = r ;
    }
}

clang optimiert es weg (sehe es live):

updateEffect(int*):                     # @updateEffect(int*)
    retq

oder vielleicht alle Nullen bekommen, wie bei diesem modifizierten Fall:

void updateEffect(int  arr[20]){
    for(int i=0;i<20;i++){
        int r ;    
        arr[i] = r%255 ;
    }
}

sehe es live :

updateEffect(int*):                     # @updateEffect(int*)
    xorps   %xmm0, %xmm0
    movups  %xmm0, 64(%rdi)
    movups  %xmm0, 48(%rdi)
    movups  %xmm0, 32(%rdi)
    movups  %xmm0, 16(%rdi)
    movups  %xmm0, (%rdi)
    retq

Beide Fälle sind vollkommen akzeptable Formen von undefiniertem Verhalten.

Beachten Sie, dass wir auf einem Itanium einen Trap-Wert erhalten könnten:

[...] Wenn das Register einen bestimmten Wert enthält, lesen Sie die Register-Traps mit Ausnahme einiger Anweisungen. [...]

Andere wichtige Hinweise

Es ist interessant, die Varianz zwischen gcc und clang zu bemerken, die im UB Canaries-Projekt festgestellt wurde, um festzustellen, wie bereit sie sind, undefiniertes Verhalten in Bezug auf nicht initialisiertes Gedächtnis auszunutzen. Der Artikel stellt fest ( Hervorhebung meiner):

Natürlich müssen wir uns darüber im Klaren sein, dass eine solche Erwartung nichts mit dem Sprachstandard zu tun hat und auch nicht mit dem, was ein bestimmter Compiler tut, weil die Anbieter dieses Compilers diese UB nicht ausnutzen wollen. oder nur, weil sie es noch nicht ausgenutzt haben. Wenn keine wirkliche Garantie vom Compiler-Anbieter vorliegt, wir möchten sagen, dass noch nicht ausgenutzte UBs Zeitbomben sind: Sie warten darauf, nächsten Monat oder nächstes Jahr loszulegen, wenn der Compiler ein bisschen mehr bekommt aggressiv.

Wie Matthieu M. hervorhebt , was jeder C-Programmierer über undefiniertes Verhalten # 2/3 wissen sollte, ist dies auch für diese Frage relevant. Es heißt unter anderem ( Hervorhebung meiner):

Wichtig und beängstigend ist, dass fast any eine Optimierung auf der Basis von undefiniertem Verhalten jederzeit in Zukunft auf Buggy-Code ausgelöst werden kann. Inlining, Loop-Unrolling, Memory-Promotion und andere Optimierungen werden immer besser, und ein wesentlicher Grund für das Vorhandensein besteht darin, sekundäre Optimierungen wie die oben genannten bereitzustellen.

Für mich ist dies zutiefst unbefriedigend, zum Teil, weil der Compiler unweigerlich beschuldigt wird, aber auch, weil riesige Teile des C-Codes Landminen warten nur darauf, explodiert zu werden.

Der Vollständigkeit halber sollte ich wahrscheinlich erwähnen, dass Implementierungen undefiniertes Verhalten klar definieren können, zum Beispiel . Gcc erlaubt das Punning von Typen durch Gewerkschaften , während in C++ dies scheint wie undefiniertes Verhalten . In diesem Fall sollte die Implementierung dies dokumentieren, und dies ist normalerweise nicht portierbar.

198
Shafik Yaghmour

Nein, es ist schrecklich.

Das Verhalten bei der Verwendung einer nicht initialisierten Variablen ist in C und C++ nicht definiert, und es ist sehr unwahrscheinlich, dass ein solches Schema wünschenswerte statistische Eigenschaften aufweist.

Wenn Sie einen "schnellen und schmutzigen" Zufallszahlengenerator wollen, dann ist Rand() Ihre beste Wahl. In seiner Implementierung ist alles, was es tut, eine Multiplikation, eine Addition und ein Modul.

Der schnellste Generator, den ich kenne, erfordert die Verwendung eines uint32_t als Typ der Pseudozufallsvariablen I und verwenden

I = 1664525 * I + 1013904223

aufeinanderfolgende Werte zu generieren. Sie können einen beliebigen Anfangswert von I (genannt seed) wählen, der Ihren Wünschen entspricht. Natürlich können Sie das inline codieren. Der standardmäßige garantierte Umlauf eines Typs ohne Vorzeichen fungiert als Modul. (Die numerischen Konstanten werden von dem bemerkenswerten wissenschaftlichen Programmierer Donald Knuth von Hand ausgewählt.)

163
Bathsheba

Gute Frage!

Undefiniert bedeutet nicht, dass es zufällig ist. Denken Sie darüber nach, die Werte, die Sie in globalen nicht initialisierten Variablen erhalten würden, wurden vom System oder von Ihren/anderen ausgeführten Anwendungen dort belassen. Abhängig davon, was Ihr System mit nicht mehr verwendetem Speicher macht und/oder welche Werte das System und die Anwendungen generieren, erhalten Sie möglicherweise:

  1. Immer gleich.
  2. Sei einer von wenigen Werten.
  3. Werte in einem oder mehreren kleinen Bereichen abrufen.
  4. Auf 16/32/64-Bit-Systemen können Sie viele Werte anhand von Zeigern durch 2/4/8 teilen
  5. ...

Die Werte, die Sie erhalten, hängen davon ab, welche nicht zufälligen Werte vom System und/oder von den Anwendungen zurückgelassen werden. Es wird also tatsächlich Rauschen geben (es sei denn, Ihr System löscht den nicht mehr verwendeten Speicher), aber der Wertepool, aus dem Sie zeichnen, wird keinesfalls zufällig sein.

Bei lokalen Variablen wird es noch schlimmer, da diese direkt vom Stapel Ihres eigenen Programms stammen. Es ist sehr wahrscheinlich, dass Ihr Programm diese Stapelspeicherorte während der Ausführung von anderem Code tatsächlich schreibt. Ich schätze die Chancen auf Glück in dieser Situation sehr niedrig, und eine zufällige Codeänderung, die Sie vornehmen, versucht dieses Glück.

Lesen Sie über Zufälligkeit . Wie Sie sehen werden, ist Zufälligkeit eine sehr spezifische und schwer zu beschaffende Eigenschaft. Es ist ein verbreiteter Fehler zu glauben, dass Sie einen zufälligen Wert erhalten, wenn Sie nur etwas nehmen, das schwer zu verfolgen ist (wie Ihr Vorschlag).

42
meaning-matters

Viele gute Antworten, aber lassen Sie mich noch eine hinzufügen und betonen, dass in einem deterministischen Computer nichts zufällig ist. Dies gilt sowohl für die von einem Pseudo-RNG erzeugten Zahlen als auch für die scheinbar "zufälligen" Zahlen in Speicherbereichen, die für lokale C/C++ - Variablen auf dem Stapel reserviert sind.

ABER ... da ist ein entscheidender Unterschied.

Die von einem guten Pseudozufallsgenerator generierten Zahlen haben die Eigenschaften, die sie statistisch zu wirklich zufälligen Ziehungen machen. Zum Beispiel ist die Verteilung gleichmäßig. Die Zykluslänge ist lang: Sie können Millionen von Zufallszahlen abrufen, bevor sich der Zyklus wiederholt. Die Sequenz ist nicht automatisch korreliert: Sie werden zum Beispiel keine seltsamen Muster sehen, wenn Sie jede 2., 3. oder 27. Zahl nehmen oder wenn Sie sich bestimmte Ziffern in den generierten Zahlen ansehen.

Im Gegensatz dazu haben die "zufälligen" Zahlen, die auf dem Stapel verbleiben, keine dieser Eigenschaften. Ihre Werte und ihre scheinbare Zufälligkeit hängen ganz davon ab, wie das Programm aufgebaut ist, wie es kompiliert und wie es vom Compiler optimiert wird. Beispielhaft ist hier eine Variation Ihrer Idee als eigenständiges Programm:

#include <stdio.h>

notrandom()
{
        int r, g, b;

        printf("R=%d, G=%d, B=%d", r&255, g&255, b&255);
}

int main(int argc, char *argv[])
{
        int i;
        for (i = 0; i < 10; i++)
        {
                notrandom();
                printf("\n");
        }

        return 0;
}

Wenn ich diesen Code mit GCC auf einem Linux-Computer kompiliere und ausführe, stellt sich heraus, dass er ziemlich unangenehm deterministisch ist:

R=0, G=19, B=0
R=130, G=16, B=255
R=130, G=16, B=255
R=130, G=16, B=255
R=130, G=16, B=255
R=130, G=16, B=255
R=130, G=16, B=255
R=130, G=16, B=255
R=130, G=16, B=255
R=130, G=16, B=255

Wenn Sie den kompilierten Code mit einem Disassembler betrachten, können Sie die Vorgänge im Detail rekonstruieren. Beim ersten Aufruf von notrandom () wurde ein Bereich des Stapels verwendet, der zuvor von diesem Programm nicht verwendet wurde. wer weiß was da drin war. Aber nach diesem Aufruf von notrandom () gibt es einen Aufruf von printf () (den der GCC-Compiler tatsächlich auf einen Aufruf von putchar () optimiert, aber egal) und der den Stack überschreibt. Wenn also das nächste und nachfolgende Mal notrandom () aufgerufen wird, enthält der Stapel veraltete Daten aus der Ausführung von putchar (). Da putchar () immer mit denselben Argumenten aufgerufen wird, sind diese veralteten Daten immer dieselben. auch.

Es ist also absolut nichts zufällig an diesem Verhalten, und die auf diese Weise erhaltenen Zahlen haben auch keine der wünschenswerten Eigenschaften eines gut geschriebenen Pseudozufallszahlengenerators. Tatsächlich sind ihre Werte in den meisten realen Szenarien repetitiv und stark korreliert.

In der Tat würde ich, wie andere auch, ernsthaft in Betracht ziehen, jemanden zu entlassen, der versucht hat, diese Idee als "Hochleistungs-RNG" auszugeben.

32
Viktor Toth

Undefiniertes Verhalten bedeutet, dass die Autoren von Compilern das Problem ignorieren können, da Programmierer niemals das Recht haben, sich zu beschweren, was auch immer passiert.

Während in der Theorie beim Betreten von UB Land alles passieren kann (einschließlich eines Daemon fliegt aus der Nase ), bedeutet das normalerweise, dass der Compiler Autoren kümmern sich einfach nicht darum, und für lokale Variablen ist der Wert der Wert, der sich zu diesem Zeitpunkt im Stapelspeicher befindet.

Dies bedeutet auch, dass der Inhalt häufig "seltsam", aber fest oder leicht zufällig oder variabel ist, aber ein klares offensichtliches Muster aufweist (z. B. ansteigende Werte bei jeder Iteration).

Sicher können Sie nicht erwarten, dass es ein anständiger Zufallsgenerator ist.

29
6502

Undefiniertes Verhalten ist undefiniert. Dies bedeutet nicht, dass Sie einen undefinierten Wert erhalten, sondern, dass das Programm alles ausführen kann und dennoch die Sprachspezifikation erfüllt.

Ein guter Optimierungs-Compiler sollte dauern

void updateEffect(){
    for(int i=0;i<1000;i++){
        int r;
        int g;
        int b;
        star[i].setColor(r%255,g%255,b%255);
        bool isVisible;
        star[i].setVisible(isVisible);
    }
}

und kompiliere es zu einem noop. Dies ist sicherlich schneller als jede Alternative. Es hat den Nachteil, dass es nichts tut, aber das ist der Nachteil von undefiniertem Verhalten.

28
Martijn

Aus Sicherheitsgründen muss der einem Programm zugewiesene neue Speicher gesäubert werden, da sonst die Informationen verwendet werden und Kennwörter von einer Anwendung in eine andere gelangen können. Nur wenn Sie Speicher wiederverwenden, erhalten Sie andere Werte als 0. Und es ist sehr wahrscheinlich, dass auf einem Stapel der vorherige Wert nur feststeht, weil die vorherige Verwendung dieses Speichers feststeht.

18
Arne

Noch nicht erwähnt, aber Codepfade, die undefiniertes Verhalten aufrufen, können tun, was der Compiler will, z.

void updateEffect(){}

Welches ist sicherlich schneller als Ihre richtige Schleife, und wegen UB, ist perfekt konform.

18
Caleth

Ihr spezielles Codebeispiel würde wahrscheinlich nicht das tun, was Sie erwarten. Während technisch jede Iteration der Schleife die lokalen Variablen für die Werte r, g und b neu erstellt, ist es in der Praxis genau derselbe Speicherplatz auf dem Stapel. Daher wird es nicht bei jeder Iteration neu randomisiert, und Sie werden am Ende die gleichen 3 Werte für jede der 1000 Farben zuweisen, unabhängig davon, wie zufällig die r, g und b einzeln und anfangs sind.

In der Tat, wenn es funktionieren würde, wäre ich sehr neugierig, was es re-randomisiert. Das einzige, was ich mir vorstellen kann, wäre ein Interleaved-Interrupt, der höchst unwahrscheinlich auf diesem Stapel liegt. Möglicherweise reicht auch eine interne Optimierung aus, bei der diese Variablen als Registervariablen und nicht als echte Speicherorte beibehalten werden, an denen die Register weiter unten in der Schleife wiederverwendet werden, insbesondere wenn die eingestellte Sichtbarkeitsfunktion besonders registerhungrig ist. Trotzdem alles andere als zufällig.

13
Jos

Wie die meisten Leute hier undefiniertes Verhalten erwähnt haben. Undefiniert bedeutet auch, dass Sie möglicherweise einen gültigen Integer-Wert erhalten (zum Glück), der in diesem Fall schneller ist (da kein Rand-Funktionsaufruf erfolgt). Aber benutze es nicht praktisch. Ich bin mir sicher, dass dies schreckliche Folgen haben wird, da Sie nicht immer Glück haben.

12
Ali Kazmi

Wirklich schlecht! Schlechte Angewohnheit, schlechtes Ergebnis. Erwägen:

A_Function_that_use_a_lot_the_Stack();
updateEffect();

Wenn die Funktion A_Function_that_use_a_lot_the_Stack() immer dieselbe Initialisierung vornimmt, verlässt sie den Stapel mit denselben Daten. Diese Daten werden als updateEffect(): immer derselbe Wert! bezeichnet.

12
Frankie_C

Ich habe einen sehr einfachen Test durchgeführt, der überhaupt nicht zufällig war.

#include <stdio.h>

int main() {

    int a;
    printf("%d\n", a);
    return 0;
}

Jedes Mal, wenn ich das Programm ausführte, druckte es die gleiche Nummer (32767 in meinem Fall) - Sie können nicht viel weniger zufällig als das bekommen. Dies ist vermutlich unabhängig von dem Startcode in der Laufzeitbibliothek, der auf dem Stapel verblieben ist. Da bei jeder Programmausführung derselbe Startcode verwendet wird und sich zwischen den einzelnen Programmläufen nichts anderes ändert, sind die Ergebnisse absolut konsistent.

11
Barmar

Sie müssen eine Definition dessen haben, was Sie mit "zufällig" meinen. Eine vernünftige Definition beinhaltet, dass die Werte, die Sie erhalten, wenig korrelieren sollten. Das können Sie messen. Es ist auch nicht trivial, auf kontrollierte, reproduzierbare Weise zu erreichen. Undefiniertes Verhalten ist also nicht das, wonach Sie suchen.

10
Zsolt Szatmari

Es gibt bestimmte Situationen, in denen nicht initialisierter Speicher unter Verwendung des Typs "unsigned char *" [z. Ein Puffer, der von malloc] zurückgegeben wurde. Code kann diesen Speicher lesen, ohne sich Sorgen machen zu müssen, dass der Compiler die Kausalität aus dem Fenster wirft. Manchmal ist es möglicherweise effizienter, Code für Speicherinhalte vorzubereiten, als sicherzustellen, dass nicht initialisierte Daten nicht gelesen werden ( Ein gängiges Beispiel hierfür wäre die Verwendung von memcpy für einen teilweise initialisierten Puffer, anstatt alle Elemente, die aussagekräftige Daten enthalten, diskret zu kopieren.

Selbst in solchen Fällen sollte man jedoch immer davon ausgehen, dass das Lesen eines Bytes, wenn eine Kombination von Bytes besonders ärgerlich ist, immer dieses Muster von Bytes ergibt (und wenn ein bestimmtes Muster in der Produktion ärgerlich wäre, aber nicht in der Entwicklung, wie z Muster erscheint erst, wenn Code in Produktion ist).

Das Lesen von nicht initialisiertem Speicher kann als Teil einer Zufallsgenerierungsstrategie in einem eingebetteten System nützlich sein, bei der sichergestellt werden kann, dass der Speicher seit dem letzten Einschalten des Systems und bei der Herstellung nie mit im Wesentlichen nicht zufälligen Inhalten beschrieben wurde Der für den Speicher verwendete Prozess bewirkt, dass sich sein Einschaltzustand halbzufällig ändert. Code sollte auch dann funktionieren, wenn alle Geräte immer die gleichen Daten liefern, aber in Fällen, in denen z. Eine Gruppe von Knoten muss beliebige eindeutige IDs so schnell wie möglich auswählen. Ein "nicht sehr zufälliger" Generator, der der Hälfte der Knoten dieselbe anfängliche ID verleiht, ist möglicherweise besser, als überhaupt keine anfängliche Zufallsquelle zu haben.

7
supercat

Wie andere gesagt haben, wird es schnell sein, aber nicht zufällig.

Was die meisten Compiler für lokale Variablen tun, ist, etwas Speicherplatz für sie auf dem Stapel zu beschaffen, aber sich nicht darum zu kümmern, ihn auf irgendetwas zu setzen (der Standard sagt, dass sie das nicht müssen, warum also den Code, den Sie generieren, verlangsamen?).

In diesem Fall hängt der Wert, den Sie erhalten, von dem ab, was zuvor auf dem Stapel war - wenn Sie eine Funktion vor dieser aufrufen, bei der hundert lokale Zeichenvariablen alle auf 'Q' gesetzt sind, und dann Ihre Funktion danach aufrufen das gibt, dann werden Sie wahrscheinlich feststellen, dass sich Ihre "zufälligen" Werte so verhalten, als hätten Sie memset() sie alle zu 'Q'.

Wichtig für Ihre Beispielfunktion ist, dass sich diese Werte nicht jedes Mal ändern, wenn Sie sie lesen. Sie sind jedes Mal gleich. So erhalten Sie 100 Sterne, die alle auf die gleiche Farbe und Sichtbarkeit eingestellt sind.

Außerdem sagt nichts, dass der Compiler diesen Wert nicht initialisieren sollte - so könnte es ein zukünftiger Compiler tun.

Im Allgemeinen: schlechte Idee, tu es nicht. (Wie viele "clevere" Optimierungen auf Codeebene ...)

5
Alun Thomas

Verwenden 7757 Jeder Ort, an dem Sie versucht sind, nicht initialisierte Variablen zu verwenden. Ich habe es zufällig aus einer Liste von Primzahlen ausgewählt:

  1. es ist definiertes Verhalten

  2. es ist garantiert, dass es nicht immer 0 ist

  3. es ist prime

  4. es ist wahrscheinlich so statistisch zufällig wie nicht-institutionalisierte Variablen

  5. es ist wahrscheinlich schneller als nicht initialisierte Variablen, da sein Wert zum Zeitpunkt der Kompilierung bekannt ist

3

Nicht eine gute Idee, unsere Logik auf Sprache undefiniertes Verhalten zu verlassen. Zusätzlich zu dem, was in diesem Beitrag erwähnt/diskutiert wurde, möchte ich erwähnen, dass ein solches Programm mit einem modernen C++ - Ansatz/-Stil möglicherweise nicht kompiliert werden kann.

Dies wurde in meinem vorherigen Beitrag erwähnt, der den Vorteil der automatischen Funktion und einen nützlichen Link dafür enthält.

https://stackoverflow.com/a/26170069/27247

Wenn wir also den obigen Code ändern und die tatsächlichen Typen durch auto ersetzen, würde das Programm nicht einmal kompilieren.

void updateEffect(){
    for(int i=0;i<1000;i++){
        auto r;
        auto g;
        auto b;
        star[i].setColor(r%255,g%255,b%255);
        auto isVisible;
        star[i].setVisible(isVisible);
    }
}
3
Mantosh Kumar

Wie andere bereits erwähnt haben, ist dies undefiniertes Verhalten ( [~ # ~] ub [~ # ~]), aber es kann "funktionieren".

Abgesehen von Problemen, die bereits von anderen erwähnt wurden, sehe ich ein weiteres Problem (Nachteil) - es wird in keiner anderen Sprache als C und C++ funktionieren. Ich weiß, dass es bei dieser Frage um C++ geht, aber wenn Sie Code schreiben können, der gut für C++ und Java Code ist und kein Problem ist, warum dann nicht? Vielleicht muss es eines Tages jemand portieren zu einer anderen Sprache und auf der Suche nach Fehlern, die durch "Zaubertricks" UB wie dieses wird definitiv ein Albtraum sein (insbesondere für einen unerfahrenen C/C++ - Entwickler).

hier es gibt eine Frage zu einem anderen ähnlichen UB. Stellen Sie sich vor, Sie versuchen, einen solchen Fehler zu finden, ohne etwas über diese UB zu wissen. Wenn Sie mehr über solche seltsamen Dinge in C/C++ lesen möchten, lesen Sie die Antworten auf Fragen unter dem Link und sehen Sie sich this [~ # ~] great [~ # ~ an ] Diashow. Es wird Ihnen helfen zu verstehen, was sich unter der Haube befindet und wie es funktioniert. es ist nicht nur eine weitere Diashow voller "Magie". Ich bin mir ziemlich sicher, dass selbst die meisten erfahrenen C/C++ - Programmierer viel daraus lernen können.

3
cyriel

Ich mag deine Denkweise. Wirklich über den Tellerrand hinaus. Der Kompromiss ist es jedoch nicht wert. Speicher-Laufzeit-Kompromiss ist eine Sache, einschließlich undefiniertem Verhalten für die Laufzeit nicht.

Es muss ein sehr beunruhigendes Gefühl für Sie sein, zu wissen, dass Sie so "zufällig" als Geschäftslogik verwenden. Ich würde es nicht tun.

3
DDan

Es gibt noch eine Möglichkeit zu prüfen.

Moderne Compiler (ahem g ++) sind so intelligent, dass sie Ihren Code durchgehen, um zu sehen, welche Anweisungen den Status beeinflussen und welche nicht. Wenn garantiert wird, dass eine Anweisung den Status NICHT beeinflusst, entfernt g ++ diese Anweisung einfach.

Also hier ist was passieren wird. g ++ wird definitiv sehen, dass Sie lesen, rechnen, speichern, was im Wesentlichen ein Müllwert ist, der mehr Müll produziert. Da es keine Garantie dafür gibt, dass der neue Müll nützlicher ist als der alte, wird Ihre Schleife einfach beseitigt. BLOOP!

Diese Methode ist nützlich, aber hier ist was ich tun würde. Kombiniere UB (Undefiniertes Verhalten) mit Rand () Geschwindigkeit.

Reduzieren Sie natürlich die Anzahl der ausgeführten Rand(), aber mischen Sie sie ein, damit der Compiler nichts tut, was Sie nicht möchten.

Und ich werde dich nicht feuern.

1
ps95