201 votes

Quels sont tous les comportements communs non définis qu'un programmeur C ++ devrait connaître?

Quels sont tous les comportements communs non définis qu'un programmeur C ++ devrait connaître?

Dire, comme:

 a[i] = i++;
 

233voto

Diomidis Spinellis Points 8417

Pointeur

  • Référence à un NULL pointeur
  • Un déréférencement d'un pointeur retourné par une "nouvelle" répartition de la taille zéro
  • À l'aide de pointeurs vers des objets dont la durée de vie a pris fin (par exemple, la pile des objets alloués ou des objets supprimés)
  • Un déréférencement d'un pointeur qui n'a pas encore été définitivement initialisé
  • L'exécution de l'arithmétique des pointeurs qui donne un résultat à l'extérieur des limites (au-dessus ou au-dessous) d'un tableau.
  • Un déréférencement du pointeur à un endroit au-delà de la fin d'un tableau.
  • La conversion des pointeurs vers des objets de types incompatibles
  • À l'aide de memcpy pour copier le chevauchement des tampons.

Les dépassements de mémoire tampon

  • En lecture ou en écriture à un objet ou un tableau à un offset est négatif, ou au-delà de la taille de l'objet (pile/tas de débordement)

Des Débordements D'Entiers

  • Signé débordement d'entier
  • L'évaluation d'une expression qui n'est pas définie mathématiquement
  • Gauche-valeurs de décalage par un montant négatif (à droite quarts de travail par les montants négatifs sont la mise en œuvre définies)
  • La mutation des valeurs d'un montant supérieur ou égal au nombre de bits du nombre (par exemple, int64_t i = 1; i <<= 72 n'est pas défini)

Types, la distribution et l'Const

  • Casting d'une valeur numérique en une valeur qui ne peuvent pas être représentés par le type de cible (soit directement ou par l'intermédiaire de static_cast)
  • À l'aide d'une variable automatique avant il a été définitivement attribué (par exemple, int i; i++; cout << i;)
  • À l'aide de la valeur d'un objet de type autre que volatile ou sig_atomic_t à la réception d'un signal
  • Essayez de modifier un littéral de chaîne ou de tout autre const objet au cours de sa durée de vie
  • La concaténation d'un étroit avec une grande chaîne littérale lors du pré-traitement

La fonction et le Modèle

  • Ne pas retourner une valeur à partir d'une valeur de retour de la fonction (directement ou par le courant d'arrêt à partir d'un essayez-bloc)
  • Plusieurs définitions différentes pour la même entité (catégorie, modèle, de l'énumération, de la fonction inline, fonction membre statique, etc.)
  • Récursion infinie dans l'instanciation de modèles
  • L'appel d'une fonction à l'aide de différents paramètres ou des liens vers les paramètres de liaison et que la fonction est définie comme l'aide.

La programmation orientée objet

  • Une cascade de destructions d'objets dont la durée de stockage statique
  • Le résultat de l'attribution à des objets qui se chevauchent partiellement
  • De manière récursive re-saisie d'une fonction lors de l'initialisation de ses objets statiques
  • Prise de fonction virtuelle des appels à des fonctions virtuelles pures d'un objet à partir de son constructeur ou le destructeur
  • Se référant aux non membres des objets qui n'ont pas été construits ou ont déjà été détruits

Fichier Source et de Prétraitement

  • Un non-vide fichier source qui ne se termine pas par un saut de ligne, ou se termine par une barre oblique inverse (avant C++11)
  • Un antislash suivi d'un caractère qui ne fait pas partie de l'codes d'échappement dans un caractère ou d'une chaîne constante (ce qui est définis par l'implémentation en C++11).
  • Dépassant les limites de la mise en œuvre (nombre de blocs imbriqués, le nombre de fonctions dans un programme, la disposition d'espace de pile ...)
  • Préprocesseur valeurs numériques qui ne peuvent pas être représentés par un long int
  • Prétraitement de la directive sur le côté gauche d'une fonction-comme la définition de macro
  • Génération dynamique de l'défini jeton en #if expression

Pour être classé

  • L'appel de la sortie lors de la destruction d'un programme avec la durée de stockage statique

31voto

Loki Astari Points 116129

L'ordre que les paramètres de la fonction sont évalués est quelconque comportement. (Cela ne fera pas votre plantage du programme, exploser, ou commander une pizza... à la différence de undefined comportement.)

La seule exigence est que tous les paramètres doivent être évaluées avant que la fonction est appelée.


Ce:

// The simple obvious one.
callFunc(getA(),getB());

Peut être équivalent à ceci:

int a = getA();
int b = getB();
callFunc(a,b);

Ou ceci:

int b = getB();
int a = getA();
callFunc(a,b);

Il peut être soit; c'est le compilateur. Le résultat peut, selon les effets secondaires.

27voto

Loki Astari Points 116129

Le compilateur est libre de réorganiser les parties d'évaluation d'une expression (en supposant que la signification est inchangée).

De la question originale:

 a[i] = i++;

// This expression has three parts:
(a) a[i]
(b) i++
(c) Assign (b) to (a)

// (c) is guaranteed to happen after (a) and (b)
// But (a) and (b) can be done in either order.
// See n2521 Section 5.17
// (b) increments i but returns the original value.
// See n2521 Section 5.2.6
// Thus this expression can be written as:

int rhs  = i++;
int lhs& = a[i];
lhs = rhs;

// or
int lhs& = a[i];
int rhs  = i++;
lhs = rhs;
 

Double verrouillage à carreaux. Et une erreur facile à faire.

 A* a = new A("plop");

// Looks simple enough.
// But this can be split into three parts.
(a) allocate Memory
(b) Call constructor
(c) Assign value to 'a'

// No problem here:
// The compiler is allowed to do this:
(a) allocate Memory
(c) Assign value to 'a'
(b) Call constructor.
// This is because the whole thing is between two sequence points.

// So what is the big deal.
// Simple Double checked lock. (I know there are many other problems with this).
if (a == null) // (Point B)
{
    Lock   lock(mutex);
    if (a == null)
    {
        a = new A("Plop");  // (Point A).
    }
}
a->doStuff();

// Think of this situation.
// Thread 1: Reaches point A. Executes (a)(c)
// Thread 1: Is about to do (b) and gets unscheduled.
// Thread 2: Reaches point B. It can now skip the if block
//           Remember (c) has been done thus 'a' is not NULL.
//           But the memory has not been initialized.
//           Thread 2 now executes doStuff() on an uninitialized variable.

// The solution to this problem is to move the assignment of 'a'
// To the other side of the sequence point.
if (a == null) // (Point B)
{
    Lock   lock(mutex);
    if (a == null)
    {
        A* tmp = new A("Plop");  // (Point A).
        a = tmp;
    }
}
a->doStuff();

// Of course there are still other problems because of C++ support for
// threads. But hopefully these are addresses in the next standard.
 

5voto

Daniel Earwicker Points 63298

Mon préféré est "récursion infinie dans l'instanciation des modèles" parce que je crois que c'est le seul où le comportement indéfini se produit au moment de la compilation.

5voto

Constantin Points 12185

En plus d' un comportement indéterminé, il est également tout aussi méchant de mise en œuvre définies par le comportement.

Comportement indéfini se produit lorsqu'un programme fait quelque chose dont le résultat n'est pas spécifiée par la norme.

La mise en œuvre définies par le comportement est une action par un programme dont le résultat n'est pas défini par la norme, mais dont la mise en œuvre du document. Un exemple est le "multi-octets de caractères littéraux", de Dépassement de Pile question Est-il un compilateur C qui ne parvient pas à le compiler?.

Mise en œuvre-comportement défini uniquement les morsures de vous lorsque vous commencez à portage (mais la mise à niveau vers la nouvelle version de compilateur est aussi le portage!)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X