web-dev-qa-db-fra.com

La variable locale non initialisée est-elle le générateur de nombres aléatoires le plus rapide?

Je sais que la variable locale non initialisée est un comportement indéfini (UB), et la valeur peut également avoir des représentations de déroutement pouvant affecter d'autres opérations, mais parfois, je souhaite utiliser le nombre aléatoire uniquement pour la représentation visuelle et ne les utiliserez plus dans d'autres parties du programme, par exemple, définir quelque chose avec une couleur aléatoire dans un effet visuel, par exemple:

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

est-ce plus rapide que

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

et aussi plus rapide que les autres générateurs de nombres aléatoires?

316
ggrr

Comme d'autres l'ont noté, il s'agit d'un comportement non défini (UB).

En pratique, cela fonctionnera (probablement) réellement. La lecture d'un registre non initialisé sur des architectures x86 [-64] produira en effet des résultats incohérents, et ne fera probablement rien de mal (par exemple, Itanium, où les registres peuvent être marqués comme non valides , de sorte que lit les erreurs de propagation telles que NaN).

Il y a cependant deux problèmes principaux:

  1. Ce ne sera pas particulièrement aléatoire. Dans ce cas, vous lisez à partir de la pile, vous obtiendrez ainsi tout ce qui se trouvait auparavant. Qui pourrait être effectivement aléatoire, complètement structuré, le mot de passe que vous avez entré il y a dix minutes, ou la recette de cookie de votre grand-mère.

  2. Il est mauvais (majuscule 'B') de laisser de telles choses se glisser dans votre code. Techniquement, le compilateur pourrait insérer reformat_hdd(); chaque fois que vous lisez une variable non définie. Cela ne le fera pas , mais vous ne devriez pas le faire de toute façon. Ne fais pas des choses dangereuses. Moins vous faites d'exceptions, plus vous êtes en sécurité contre les erreurs accidentelles tout le temps.

    Le problème le plus urgent avec UB est que cela rend le comportement de l'ensemble de votre programme non défini. Les compilateurs modernes peuvent l’utiliser pour éliminer d’énormes morceaux de votre code ou même remonter dans le temps . Jouer avec UB, c'est comme un ingénieur victorien qui démantèle un réacteur nucléaire en marche. Il y a des millions de choses à aller de travers, et vous ne saurez probablement pas la moitié des principes sous-jacents ou de la technologie mise en œuvre. Cela pourrait être correct, mais vous ne devriez toujours pas laisser les choses se produire. Regardez les autres réponses Nice pour plus de détails.

Aussi, je te virerais.

296
imallett

Permettez-moi de le dire clairement: nous n'invoquons pas de comportement indéfini dans nos programmes . Ce n'est jamais une bonne idée, point final. Il existe de rares exceptions à cette règle; Par exemple, si vous êtes un implémenteur de bibliothèque implémentant offsetof . Si votre cas relève d'une telle exception, vous le savez probablement déjà. Dans ce cas, nous savoir que l'utilisation de variables automatiques non initialisées est un comportement indéfini .

Les compilateurs sont devenus très agressifs avec des optimisations autour du comportement non défini et nous pouvons trouver de nombreux cas où un comportement non défini a conduit à des failles de sécurité. Le cas le plus infâme est probablement le suppression du contrôle du pointeur null dans le noyau Linux que je mentionne dans ma réponse au bogue de compilation C++? où une optimisation du compilateur autour du comportement non défini a transformé une boucle finie en un infini.

Nous pouvons lire le CERT Optimisations dangereuses et la perte de causalité ( vidéo ) qui dit, entre autres:

Les rédacteurs de compilateur tirent de plus en plus parti de comportements non définis dans les langages de programmation C et C++ pour améliorer les optimisations.

Fréquemment, ces optimisations interfèrent avec la capacité des développeurs à effectuer une analyse de cause à effet de leur code source, c'est-à-dire à analyser la dépendance des résultats en aval par rapport aux résultats antérieurs.

Par conséquent, ces optimisations éliminent la causalité dans les logiciels et augmentent la probabilité de défaillances, de défauts et de vulnérabilités du logiciel.

Spécifiquement en ce qui concerne les valeurs indéterminées, le standard C rapport de défaut 451: instabilité des variables automatiques non initialisées permet une lecture intéressante. Il n'a pas encore été résolu, mais introduit le concept de valeurs instables , ce qui signifie que l'indétermination d'une valeur peut se propager à travers le programme et peut avoir différentes valeurs indéterminées à différents points du programme.

Je ne connais aucun exemple où cela se produit, mais pour le moment, nous ne pouvons pas l'exclure.

Des exemples concrets, pas le résultat attendu

Il est peu probable que vous obteniez des valeurs aléatoires. Un compilateur pourrait optimiser complètement la boucle. Par exemple, avec ce cas simplifié:

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

clang l’optimise loin (le voir en direct):

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

ou peut-être obtenir tous les zéros, comme avec ce cas modifié:

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

voir en direct :

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

Ces deux cas sont des formes parfaitement acceptables de comportement indéfini.

Notez que si nous sommes sur un Itanium, nous pourrions finir avec une valeur de piège :

[...] si le registre contient une valeur spéciale, lisez les interruptions du registre à l'exception de quelques instructions [...]

Autres notes importantes

Il est intéressant de noter le écart entre gcc et clang noté dans le projet UB Canaries sur leur volonté de tirer parti d'un comportement non défini en ce qui concerne la mémoire non initialisée. L'article note ( l'emphase mine ):

Bien sûr, nous devons être parfaitement clairs avec nous-mêmes: toute attente de ce type n'a rien à voir avec le standard de langage et avec tout ce qui a à voir avec ce qu'un compilateur doit faire, soit parce que les fournisseurs de ce compilateur ne veulent pas exploiter ce UB ou simplement parce qu'ils ne sont pas encore prêts à l'exploiter . Quand aucune garantie réelle du compilateur n'existe, , nous aimons dire que les UB encore inexploitées sont des bombes à retardement : elles attendent de partir. mois ou l’année prochaine lorsque le compilateur devient un peu plus agressif.

Comme Matthieu M. le fait remarquer Ce que tout programmeur C devrait savoir sur le comportement indéfini n ° 2/ est également pertinent pour cette question. Il dit entre autres choses ( l'emphase mine ):

La chose importante et effrayante à réaliser est que à peu près toute optimisation basée sur un comportement indéfini peut commencer à être déclenchée à tout moment dans un code avenir . Les améliorations en ligne, le déroulement de boucle, la promotion de la mémoire et d’autres optimisations ne cessent de s’améliorer, et une partie importante de leur raison d’être est d’exposer les optimisations secondaires comme celles décrites ci-dessus.

Pour moi, c'est profondément insatisfaisant, en partie parce que le compilateur finit inévitablement par être blâmé, mais aussi parce que cela signifie que d'énormes corps de code C sont des mines terrestres qui ne demandent qu'à exploser.

Par souci d'exhaustivité, je devrais probablement mentionner que les implémentations peuvent choisir de rendre le comportement non défini bien défini, par exemple gcc autorise le typage via des unions tandis que en C++, cela ressemble à un comportement non défini . Si tel est le cas, l’implémentation doit la documenter et celle-ci ne sera généralement pas portable.

198
Shafik Yaghmour

Non, c'est terrible.

Le comportement d'utilisation d'une variable non initialisée n'est pas défini en C et en C++, et il est très peu probable qu'un tel schéma ait des propriétés statistiques souhaitables.

Si vous voulez un générateur de nombres aléatoires "rapide et sale", alors Rand() est votre meilleur choix. Dans sa mise en œuvre, tout ce qu'il fait est une multiplication, une addition et un module.

Le générateur le plus rapide que je connaisse vous oblige à utiliser un uint32_t comme type de la variable pseudo-aléatoire I, et à utiliser

I = 1664525 * I + 1013904223

générer des valeurs successives. Vous pouvez choisir n’importe quelle valeur initiale de I (appelée graine) à votre convenance. Évidemment, vous pouvez coder cela en ligne. L'enveloppement à garantie standard d'un type non signé fait office de module. (Les constantes numériques sont sélectionnées à la main par ce remarquable programmeur scientifique, Donald Knuth.)

163
Bathsheba

Bonne question!

Undefined ne signifie pas que c'est aléatoire. Pensez-y, les valeurs que vous obtiendriez dans les variables globales non initialisées ont été laissées là par le système ou par vos/autres applications en cours d'exécution. Selon ce que votre système fait avec de la mémoire non utilisée et/ou le type de valeurs générées par le système et les applications, vous pouvez obtenir:

  1. Toujours les mêmes.
  2. Faites partie d'un petit ensemble de valeurs.
  3. Obtenir des valeurs dans une ou plusieurs petites plages.
  4. Voir de nombreuses valeurs divisibles par 2/4/8 à partir de pointeurs sur un système 16/32/64 bits
  5. ...

Les valeurs que vous obtiendrez dépendent entièrement des valeurs non aléatoires laissées par le système et/ou les applications. Il y aura donc du bruit (à moins que votre système n’efface la mémoire qui n’est plus utilisée), mais le pool de valeurs dans lequel vous allez tirer ne sera en aucun cas aléatoire.

La situation des variables locales est bien pire, car elles proviennent directement de la pile de votre propre programme. Il y a de très bonnes chances que votre programme écrive réellement ces emplacements de pile lors de l'exécution d'un autre code. J’estime très peu les chances de chance dans cette situation, et un changement de code "aléatoire" que vous apportez tente cette chance.

Lisez à propos de randomness . Comme vous le verrez, le caractère aléatoire est une propriété très spécifique et difficile à obtenir. C'est une erreur courante de penser que si vous prenez quelque chose qui est difficile à suivre (comme votre suggestion), vous obtiendrez une valeur aléatoire.

42
meaning-matters

Beaucoup de bonnes réponses, mais permettez-moi d'en ajouter une autre et de souligner le fait que dans un ordinateur déterministe, rien n'est aléatoire. Cela vaut tant pour les nombres produits par un pseudo-RNG que pour les nombres apparemment "aléatoires" trouvés dans les zones de mémoire réservées aux variables locales C/C++ de la pile.

MAIS ... il y a une différence cruciale.

Les nombres générés par un bon générateur pseudo-aléatoire ont les propriétés qui les rendent statistiquement similaires aux tirages vraiment aléatoires. Par exemple, la distribution est uniforme. La durée du cycle est longue: vous pouvez obtenir des millions de nombres aléatoires avant que le cycle ne se répète. La séquence n'est pas autocorrélée: par exemple, vous ne verrez pas apparaître de motifs étranges si vous prenez tous les 2, 3 ou 27 numéros, ou si vous examinez des chiffres spécifiques dans les nombres générés.

En revanche, les nombres "aléatoires" laissés sur la pile n'ont aucune de ces propriétés. Leurs valeurs et leur caractère aléatoire apparent dépendent entièrement de la manière dont le programme est construit, comment il est compilé et comment il est optimisé par le compilateur. À titre d’exemple, voici une variante de votre idée en tant que programme autonome:

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

Lorsque je compile ce code avec GCC sur une machine Linux et que je l'exécute, il s'avère plutôt désagréablement déterministe:

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

Si vous examiniez le code compilé avec un désassembleur, vous pourriez reconstruire ce qui se passait en détail. Le premier appel à notrandom () a utilisé une zone de la pile qui n’était pas utilisée auparavant par ce programme; qui sait ce qu'il y avait dedans Mais après cet appel à notrandom (), il existe un appel à printf () (que le compilateur GCC optimise en fait en appel à putchar (), mais tant pis) et qui écrase la pile. Ainsi, la prochaine fois et les suivantes, lorsque notrandom () sera appelé, la pile contiendra des données obsolètes issues de l'exécution de putchar (), et puisque putchar () est toujours appelé avec les mêmes arguments, ces données obsolètes seront toujours les mêmes. aussi.

Il y a donc absolument rien aléatoire sur ce comportement, et les nombres obtenus de cette manière ne possèdent aucune des propriétés souhaitables d'un générateur de nombres pseudo-aléatoires bien écrit. En fait, dans la plupart des scénarios réels, leurs valeurs seront répétitives et hautement corrélées.

En effet, comme d’autres, j’envisagerais sérieusement de licencier quelqu'un qui aurait tenté de faire passer cette idée pour un "GNA de haute performance".

32
Viktor Toth

Un comportement indéfini signifie que les auteurs des compilateurs sont libres d'ignorer le problème, car les programmeurs n'auront jamais le droit de se plaindre quoi qu'il arrive.

En théorie, lorsque vous entrez sur le territoire de l'UB , tout peut arriver (y compris un démon qui vous échappe ), ce qui signifie normalement que le compilateur les auteurs s'en moquent et, pour les variables locales, la valeur sera celle qui se trouve dans la mémoire de la pile à ce stade.

Cela signifie également que souvent le contenu sera "étrange" mais fixe ou légèrement aléatoire ou variable mais avec un motif clair et évident (par exemple des valeurs croissantes à chaque itération).

Bien sûr, vous ne pouvez pas vous attendre à ce que ce soit un générateur aléatoire décent.

29
6502

Le comportement indéfini n'est pas défini. Cela ne signifie pas que vous obtenez une valeur indéfinie, cela signifie que le programme peut faire n'importe quoi tout en respectant la spécification de langue.

Un compilateur optimisant devrait prendre

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

et le compiler dans un noop. C'est certainement plus rapide que n'importe quelle alternative. Il a l'inconvénient de ne rien faire, mais tel est l'inconvénient d'un comportement indéfini.

28
Martijn

Pour des raisons de sécurité, la nouvelle mémoire affectée à un programme doit être nettoyée, sinon les informations pourraient être utilisées et les mots de passe pourraient être perdus d'une application à une autre. Lorsque vous réutilisez de la mémoire, vous obtenez des valeurs différentes de 0. Et il est très probable que, sur une pile, la valeur précédente soit simplement fixée, car l'utilisation précédente de cette mémoire est fixe.

18
Arne

Pas encore mentionné, mais les chemins de code qui invoquent un comportement indéfini sont autorisés à faire ce que veut le compilateur, par exemple.

void updateEffect(){}

Ce qui est certainement plus rapide que votre boucle correcte, et grâce à UB, est parfaitement conforme.

18
Caleth

Votre exemple de code particulier ne ferait probablement pas ce que vous attendez. Tandis que techniquement, chaque itération de la boucle recrée les variables locales pour les valeurs r, g et b, en pratique, il s'agit exactement du même espace mémoire sur la pile. Par conséquent, il ne sera pas re-randomisé à chaque itération et vous finirez par attribuer les 3 mêmes valeurs pour chacune des 1 000 couleurs, quel que soit le degré aléatoire des valeurs r, g et b individuellement et initialement.

En effet, si cela fonctionnait, je serais très curieux de savoir ce qui est en train de le re-randomiser. La seule chose à laquelle je peux penser serait une interruption imbriquée qui serait empilée au sommet de cette pile, ce qui est hautement improbable. Une optimisation interne conservant celles-ci en tant que variables de registre plutôt qu'en tant que véritables emplacements de mémoire, où les registres sont réutilisés plus loin dans la boucle, ferait également l'affaire, en particulier si la fonction de visibilité définie est particulièrement gourmande en registres. Pourtant, loin d'être aléatoire.

13
Jos

Comme la plupart des gens ici ont mentionné un comportement indéfini. Undefined signifie également que vous pouvez obtenir une valeur entière valide (par chance) et dans ce cas, cela sera plus rapide (car l'appel de la fonction Rand n'est pas effectué). Mais ne l'utilisez pratiquement pas. Je suis sûr que cela produira des résultats terribles, car la chance n’est pas avec vous tout le temps.

12
Ali Kazmi

Vraiment mauvais! Mauvaise habitude, mauvais résultat. Considérer:

A_Function_that_use_a_lot_the_Stack();
updateEffect();

Si la fonction A_Function_that_use_a_lot_the_Stack() effectue toujours la même initialisation, la pile contient les mêmes données. Ces données sont ce que nous appelons updateEffect(): toujours la même valeur!.

12
Frankie_C

J'ai effectué un test très simple, et ce n'était pas du tout aléatoire.

#include <stdio.h>

int main() {

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

Chaque fois que j'ai lancé le programme, il imprimait le même numéro (32767 dans mon cas) - vous ne pouvez pas obtenir beaucoup moins aléatoire que cela. Ceci est vraisemblablement quel que soit le code de démarrage de la bibliothèque d'exécution laissé sur la pile. Puisqu'il utilise le même code de démarrage à chaque exécution du programme et que rien d'autre ne varie dans le programme entre les exécutions, les résultats sont parfaitement cohérents.

11
Barmar

Vous devez avoir une définition de ce que vous entendez par "aléatoire". Une définition raisonnable implique que les valeurs que vous obtenez doivent avoir une faible corrélation. C'est quelque chose que vous pouvez mesurer. Il n’est pas non plus trivial d’obtenir de manière contrôlée et reproductible. Donc, un comportement non défini n'est certainement pas ce que vous recherchez.

10
Zsolt Szatmari

Il existe certaines situations dans lesquelles la mémoire non initialisée peut être lue en toute sécurité en utilisant le type "unsigned char *" [par ex. un tampon est retourné de malloc]. Le code peut lire une telle mémoire sans s’inquiéter du fait que le compilateur jette un lien de causalité par la fenêtre, et il peut parfois être plus efficace de préparer du code pour tout contenu en mémoire que de garantir que les données non initialisées ne seront pas lues ( un exemple courant consiste à utiliser memcpy sur un tampon partiellement initialisé plutôt que de copier discrètement tous les éléments contenant des données significatives).

Même dans de tels cas, cependant, il faut toujours supposer que si une combinaison d'octets est particulièrement vexatoire, la lecture donnera toujours ce modèle d'octets (et si un certain modèle serait vexatoire dans la production, mais pas dans le développement, une telle motif n'apparaîtra que lorsque le code sera en production).

Lire de la mémoire non initialisée peut s’avérer utile dans le cadre d’une stratégie de génération aléatoire dans un système embarqué dans lequel on peut être sûr que la mémoire n’a jamais été écrite avec un contenu substantiellement non aléatoire depuis la dernière mise sous tension du système, et si la fabrication a bien eu lieu. Le processus utilisé pour la mémoire provoque une variation semi-aléatoire de son état de mise sous tension. Le code devrait fonctionner même si tous les appareils produisent toujours les mêmes données, mais dans les cas où, par exemple, un groupe de nœuds doit chacun sélectionner des identifiants uniques arbitraires aussi rapidement que possible; disposer d'un générateur "pas très aléatoire" qui donne le même identifiant initial à la moitié des nœuds est peut-être préférable à l'absence de toute source initiale de caractère aléatoire.

7
supercat

Comme d'autres l'ont dit, ce sera rapide, mais pas aléatoire.

Ce que la plupart des compilateurs feront pour les variables locales est de leur laisser un peu d’espace sur la pile, mais ne leur donnerez pas la peine de le définir (la norme dit qu’ils n’ont pas besoin de le faire, alors pourquoi ralentir le code que vous générez?).

Dans ce cas, la valeur que vous obtiendrez dépendra de ce qui était précédemment sur la pile - si vous appelez une fonction avant celle-ci qui contient cent variables de caractères locales, toutes définies sur 'Q', puis appelez vous êtes fonction après qui revient, alors vous constaterez probablement que vos valeurs "aléatoires" se comportent comme si vous aviez memset() toutes les réponses à la question.

Ce qui est important pour votre fonction exemple essayant de l'utiliser, ces valeurs ne changeront pas à chaque fois que vous les lirez, elles seront identiques à chaque fois. Vous obtiendrez donc 100 étoiles avec la même couleur et visibilité.

En outre, rien ne dit que le compilateur ne devrait pas initialiser ces valeurs - un futur compilateur pourrait donc le faire.

En général: mauvaise idée, ne le faites pas. (comme beaucoup d'optimisations "intelligentes" au niveau du code ...)

5
Alun Thomas

Utilisez 7757 à chaque endroit où vous êtes tenté d’utiliser des variables non initialisées. Je l'ai choisi au hasard dans une liste de nombres premiers:

  1. c'est un comportement défini

  2. il est garanti de ne pas toujours être 0

  3. c'est premier

  4. il est probable qu'il soit aussi aléatoire sur le plan statistique que les variables non habituelles

  5. il est susceptible d'être plus rapide que les variables non initialisées car sa valeur est connue au moment de la compilation

3
Glenn Teitelbaum

Pas une bonne idée de faire appel à une logique quelconque sur le comportement indéfini du langage. En plus de tout ce qui est mentionné/discuté dans ce post, je voudrais mentionner qu'avec l'approche/style C++ moderne, un tel programme peut ne pas être compilé.

Cela a été mentionné dans mon post précédent qui contient l'avantage de la fonctionnalité auto et un lien utile pour la même chose.

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

Donc, si nous changeons le code ci-dessus et remplaçons les types actuels par auto , le programme ne serait même pas compilé.

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

Comme d'autres l'ont déjà mentionné, il s'agit d'un comportement non défini (UB), mais cela peut "fonctionner".

Hormis les problèmes déjà mentionnés par d’autres, je vois un autre problème (inconvénient): cela ne fonctionnera pas dans d’autres langages que C et C++. Je sais que cette question concerne le C++, mais si vous pouvez écrire du code qui sera un bon code C++ et Java et que ce n'est pas un problème, pourquoi pas? Peut-être qu'un jour, quelqu'un devra le porter dans une autre langue et rechercher les bugs causés par "tours de magie" UB comme celui-ci sera définitivement un cauchemar (surtout pour un développeur inexpérimenté en C/C++).

Ici il est question d'un autre UB similaire. Imaginez-vous que vous essayez de trouver un bogue comme celui-ci sans rien savoir de cet UB. Si vous voulez en savoir plus sur de telles choses étranges en C/C++, lisez les réponses aux questions de link et voyez thisGREAT diaporama. Cela vous aidera à comprendre ce qui se cache sous le capot et son fonctionnement. ce n'est pas juste un autre diaporama plein de "magie". Je suis à peu près sûr que même la plupart des programmeurs C/c ++ expérimentés peuvent apprendre beaucoup de cela.

3
cyriel

J'aime ta façon de penser. Vraiment hors des sentiers battus. Cependant, le compromis n'en vaut vraiment pas la peine. Le compromis entre la mémoire et l'exécution est une chose, y compris le comportement indéfini pour l'exécution qui est not.

Cela doit vous donner un sentiment très troublant de savoir que vous utilisez un tel "hasard" comme logique d’entreprise. Je ne le ferais pas.

3
DDan

Il y a une autre possibilité à considérer.

Les compilateurs modernes (ahem g ++) sont si intelligents qu'ils parcourent votre code pour voir quelles instructions affectent l'état, et ce qui ne change pas, et si une instruction a la garantie de ne PAS affecter l'état, g ++ supprimera simplement cette instruction.

Alors voici ce qui va arriver. g ++ verra certainement que vous lisez, exécutez une arithmétique, sauvegardez, ce qui est essentiellement une valeur de déchets, ce qui produit plus de déchets. Puisqu'il n'y a aucune garantie que la nouvelle poubelle soit plus utile que l'ancienne, elle supprimera simplement votre boucle. BLOOP!

Cette méthode est utile, mais voici ce que je ferais. Combinez le comportement non défini (UB) avec la vitesse Rand ().

Bien sûr, réduisez Rand()s exécuté, mais mélangez-le pour que le compilateur ne fasse rien que vous ne vouliez pas.

Et je ne vais pas te virer.

1
ps95