232 votes

C ++ Accéder à un tableau hors limites ne donne pas d'erreur, pourquoi?

Je suis de l'attribution des valeurs dans un programme C++ sort des limites comme ceci:

#include <iostream>
using namespace std;
int main()
{
    int array[2];
    array[0] = 1;
    array[1] = 2;
    array[3] = 3;
    array[4] = 4;
    cout << array[3] << endl;
    cout << array[4] << endl;
    return 0;
}

Le programme imprime 3 et 4. Il ne devrait pas être possible. J'utilise g++ 4.3.3

Voici compiler et exécuter la commande

$ g++ -W -Wall errorRange.cpp -o errorRange
$ ./errorRange
3
4

Seulement lors de l'affectation d' array[3000]=3000 t-il me donner une erreur de segmentation.

Si gcc ne pas vérifier les limites du tableau, comment puis-je savoir si mon programme est correcte, car elle peut conduire à de graves problèmes plus tard?

J'ai remplacé le code ci-dessus avec

vector<int> vint(2);
vint[0] = 0;
vint[1] = 1;
vint[2] = 2;
vint[5] = 5;
cout << vint[2] << endl;
cout << vint[5] << endl;

et celui-ci est également produit pas d'erreur.

Solution: utiliser std conteneurs lorsque cela est possible, au lieu de raw tableaux, et d'utiliser .at() au lieu de [] pour les corriger, comportement bien déterminé.

462voto

jalf Points 142628

Bienvenue à tous les programmeurs C/C++ bestest ami: un Comportement Indéfini.

Il y a un lot qui n'est pas spécifié par la norme du langage, pour une variété de raisons. C'est l'un d'entre eux.

En général, chaque fois que vous rencontrez un comportement indéfini, tout ce qui pourrait arriver. L'application peut se bloquer, il peut geler, il peut éjecter votre lecteur de CD-ROM ou de faire des démons de sortir de votre nez. Il peut formater votre disque dur ou envoyer un courriel à tous vos porno à votre grand-mère.

Il peut même, si vous êtes vraiment malchanceux, semblent fonctionner correctement.

La langue dit simplement ce qui se passerait si vous accéder aux éléments à l' intérieur des limites d'un tableau. Il est pas défini ce qui se passe si vous allez en dehors des limites. Il pourrait sembler à l'œuvre aujourd'hui, votre compilateur, mais il n'est pas légal en C ou C++, et il n'ya aucune garantie que ça va encore travailler la prochaine fois que vous exécutez le programme. Ou qu'il n'a pas écrasé les données essentielles, même maintenant, et vous n'en avez pas rencontré les problèmes que va provoquer encore.

Comme pour pourquoi il n'y a pas de limites de la vérification, il ya un couple des aspects de la réponse:

  • Un tableau est un vestige de C. C des tableaux sont aussi primitive que vous pouvez obtenir. Juste une séquence d'éléments contigus adresses. Il n'y a pas de vérification des limites parce qu'il est simplement d'exposer la mémoire brute. L'application d'un solide vérification de limites mécanisme aurait été presque impossible, C.
  • En C++, une vérification de limites est possible sur les types de classe. Mais un tableau est toujours le bon vieux C-compatible. ce n'est pas une classe. De plus, C++, est lui aussi construit sur une autre règle qui rend la vérification de limites non-idéal. Le C++ principe est le suivant: "vous n'avez pas à payer pour ce que vous n'utilisez pas". Si votre code est correct, vous n'avez pas besoin d'une vérification de limites, et vous ne devriez pas être forcé de payer pour les frais généraux de l'exécution vérification de limites.
  • C++ offre la std::vector modèle de classe, ce qui permet à la fois. operator[] est conçu pour être efficace. La langue standard ne nécessite pas de qui il effectue la vérification des limites (même si elle ne l'interdit pas non plus). Un vecteur a aussi l' at() fonction membre qui est garanti pour effectuer une vérification de limites. Donc, en C++, vous obtenez le meilleur des deux mondes si vous utilisez un vecteur. Vous obtenez tableau des performances sans vérification de limites, et vous avez la possibilité d'utiliser les limites vérifié accéder quand vous le voulez.

39voto

Richard Corden Points 12292

En utilisant g ++, vous pouvez ajouter l’option de ligne de commande suivante: -fstack-protector-all .

Sur votre exemple, il en résulte:

 > g++ -o t -fstack-protector-all t.cc
> ./t
3
4
/bin/bash: line 1: 15450 Segmentation fault      ./t
 

Cela ne vous aide pas vraiment à trouver ou à résoudre le problème, mais au moins le segfault vous indiquera que quelque chose ne va pas.

15voto

Arkaitz Jimenez Points 10651

g++ ne pas vérifier les limites du tableau, et vous pouvez écraser quelque chose avec 3,4, mais rien de vraiment important, si vous essayez avec un plus grand nombre, vous aurez un crash.

Vous êtes juste à l'écrasement de parties de la pile qui ne sont pas utilisés, vous pouvez continuer jusqu'à ce que vous atteignez la fin de l'espace alloué pour la pile et ça crash finalement

EDIT: Vous n'avez aucun moyen de traiter avec cela, peut-être un analyseur de code statique pourrait révéler ces échecs, mais c'est trop simple, vous pouvez avoir similaire(mais plus complexe) de défaillances non détectées, même pour les analyseurs statiques

10voto

Hooked Points 1825

C'est un comportement indéfini pour autant que je sais. Exécuter un programme plus avec cela et il va se planter quelque part le long du chemin. La vérification des limites n'est pas une partie de raw tableaux (ou même std::vector).

Utiliser std::vector avec std::vector::iterator'à la place de sorte que vous n'avez pas à vous inquiéter à ce sujet.

Edit:

Juste pour le plaisir, courir ça et de voir combien de temps jusqu'à ce que vous crash:

int main()
{
   int array[1];

   for (int i = 0; i != 100000; i++)
   {
       array[i] = i;
   }

   return 0; //will be lucky to ever reach this
}

Edit2:

Ne cours pas.

Edit3:

OK, voici une rapide leçon sur les tableaux et leurs relations avec les pointeurs:

Lorsque vous utilisez le tableau d'indexation, vous êtes vraiment à l'aide d'un pointeur dans le déguisement (appelé une "référence"), qui est automatiquement déréférencé. C'est pourquoi, au lieu de *(tableau[1]), tableau[1] renvoie automatiquement la valeur à cette valeur.

Lorsque vous avez un pointeur vers un tableau, comme ceci:

int array[5];
int *ptr = array;

Ensuite, le "tableau" dans la deuxième déclaration est vraiment décomposition d'un pointeur vers le premier tableau. C'est l'équivalent de comportement à ceci:

int *ptr = &array[0];

Lorsque vous essayez d'accéder au-delà de ce que vous avez alloué, vous êtes vraiment juste à l'aide d'un pointeur vers la mémoire d'autres (qui C++ ne vais pas me plaindre). En prenant mon exemple de programme ci-dessus, qui est équivalent à ceci:

int main()
{
   int array[1];
   int *ptr = array;

   for (int i = 0; i != 100000; i++, ptr++)
   {
       *ptr = i;
   }

   return 0; //will be lucky to ever reach this
}

Le compilateur ne pas se plaindre parce que dans la programmation, vous avez souvent à communiquer avec d'autres programmes, en particulier le système d'exploitation. Ceci est fait avec des pointeurs tout à fait un peu.

3voto

Paul Dixon Points 122033

Vous écrasez certainement votre pile, mais le programme est assez simple pour que ses effets passent inaperçus.

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