Pourquoi cela pour la boucle se termine-t-il sur certaines plates-formes et pas sur d'autres?


240

J'ai récemment commencé à apprendre le C et je prends un cours avec C comme sujet. Je joue actuellement avec des boucles et je rencontre un comportement étrange que je ne sais pas expliquer.

#include <stdio.h>

int main()
{
  int array[10],i;

  for (i = 0; i <=10 ; i++)
  {
    array[i]=0; /*code should never terminate*/
    printf("test \n");

  }
  printf("%d \n", sizeof(array)/sizeof(int));
  return 0;
}

Sur mon ordinateur portable exécutant Ubuntu 14.04, ce code ne se casse pas. Il se termine. Sur l'ordinateur de mon école exécutant CentOS 6.6, il fonctionne également très bien. Sous Windows 8.1, la boucle ne se termine jamais.

Ce qui est encore plus étrange, c'est que lorsque je modifie l'état de la forboucle en:, i <= 11le code se termine uniquement sur mon ordinateur portable exécutant Ubuntu. Il ne se termine jamais dans CentOS et Windows.

Quelqu'un peut-il expliquer ce qui se passe dans la mémoire et pourquoi les différents systèmes d'exploitation exécutant le même code donnent des résultats différents?

EDIT: Je sais que la boucle for sort des limites. Je le fais intentionnellement. Je n'arrive pas à comprendre comment le comportement peut être différent selon les différents systèmes d'exploitation et ordinateurs.


147
Puisque vous surchargez le tableau, un comportement indéfini se produit. Un comportement indéfini signifie que tout peut arriver, même s'il semble fonctionner. Ainsi, "le code ne doit jamais se terminer" n'est pas une attente valide.
kaylum

37
Exactement, bienvenue dans C. Votre tableau comporte 10 éléments - numérotés de 0 à 9.
Yetti99

14
@JonCav Vous avez cassé le code. Vous obtenez un comportement indéfini qui est du code cassé.
kaylum

50
Eh bien, le fait est que le comportement indéfini est exactement cela. Vous ne pouvez pas le tester de manière fiable et prouver que quelque chose de défini se produira. Ce qui se passe probablement sur votre machine Windows, c'est que la variable iest stockée juste après la fin de array, et vous la remplacez par array[10]=0;. Ce n'est peut-être pas le cas dans une version optimisée sur la même plate-forme, qui peut stocker idans un registre et ne jamais y faire référence en mémoire.
paddy

46
Parce que la non-prévisibilité est une propriété fondamentale du comportement indéfini. Vous devez comprendre cela ... Absolument tous les paris sont désactivés.
paddy

Réponses:


356

Sur mon ordinateur portable exécutant Ubuntu 14.04, ce code ne casse pas, il s'exécute complètement. Sur l'ordinateur de mon école exécutant CentOS 6.6, il fonctionne également très bien. Sous Windows 8.1, la boucle ne se termine jamais.

Ce qui est plus étrange, c'est que lorsque je modifie le conditionnel de la forboucle en:, i <= 11le code se termine uniquement sur mon ordinateur portable exécutant Ubuntu. CentOS et Windows ne s'arrêtent jamais.

Vous venez de découvrir un piétinement de mémoire. Vous pouvez en savoir plus à ce sujet ici: Qu'est-ce qu'un «coup de mémoire»?

Lorsque vous allouez int array[10],i;, ces variables entrent en mémoire (en particulier, elles sont allouées sur la pile, qui est un bloc de mémoire associé à la fonction). array[]et isont probablement adjacents les uns aux autres en mémoire. Il semble que sur Windows 8.1, ise trouve à array[10]. Sur CentOS, ise trouve à array[11]. Et sur Ubuntu, ce n'est à aucun endroit (peut-être que c'est array[-1]?).

Essayez d'ajouter ces instructions de débogage à votre code. Vous devriez remarquer qu'à l'itération 10 ou 11, array[i]pointez sur i.

#include <stdio.h>
 
int main() 
{ 
  int array[10],i; 
 
  printf ("array: %p, &i: %p\n", array, &i); 
  printf ("i is offset %d from array\n", &i - array);

  for (i = 0; i <=11 ; i++) 
  { 
    printf ("%d: Writing 0 to address %p\n", i, &array[i]); 
    array[i]=0; /*code should never terminate*/ 
  } 
  return 0; 
} 

6
Hey, merci! Cela expliquait vraiment pas mal. Dans Windows, il indique que si je suis décalé de 10 par rapport au tableau, alors que dans CentOS et Ubuntu, il est de -1. Ce qui est plus étrange, c'est que si je commente votre code de débogueur, CentOS ne peut pas exécuter le code (il se bloque), mais avec votre code de débogage, il s'exécute. C semble être jusqu'à présent un langage très X_x
JonCav

12
@JonCav "il se bloque" peut se produire si l'écriture pour array[10]détruire le cadre de la pile, par exemple. Comment peut-il y avoir une différence entre du code avec ou sans la sortie de débogage? Si l'adresse de in'est jamais nécessaire, le compilateur peut s'optimiser i. dans un registre, changeant ainsi la disposition de la mémoire sur la pile ...
Hagen von Eitzen

2
Je ne pense pas qu'il soit suspendu, je pense qu'il est dans une boucle infinie car il recharge le compteur de boucle à partir de la mémoire (qui vient d'être mis à zéro par array[10]=0. Si vous avez compilé votre code avec optimisation, cela ne se produirait probablement pas. (Parce que C a des règles d'alias qui limitent les types d'accès à la mémoire qui doivent être supposés chevaucher potentiellement d'autres mémoires. En tant que variable locale dont vous ne prenez jamais l'adresse, je pense qu'un compilateur devrait être capable de supposer que rien ne l'alias. Quoi qu'il en soit, en écrivant la fin d'un tableau est un comportement indéfini. Essayez toujours de ne pas dépendre de cela.
Peter Cordes

4
Une autre alternative est qu'un compilateur d'optimisation supprime complètement le tableau, car il n'a aucun effet observable (dans le code d'origine de la question). Par conséquent, le code résultant pourrait simplement imprimer cette chaîne constante onze fois, suivi de l'impression de la taille constante et rendre ainsi le débordement entièrement imperceptible.
Holger

9
@JonCav Je dirais qu'en général, vous n'avez pas besoin d'en savoir plus sur la gestion de la mémoire et que vous savez simplement de ne pas écrire de code non défini, en particulier, n'écrivez pas après la fin d'un tableau ...
T. Kiley

98

Le bogue se situe entre ces morceaux de code:

int array[10],i;

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

array[i]=0;

Puisqu'il arrayn'a que 10 éléments, dans la dernière itération array[10] = 0;est un débordement de tampon. Les débordements de tampon sont UN COMPORTEMENT NON DÉFINI , ce qui signifie qu'ils peuvent formater votre disque dur ou faire sortir les démons de votre nez.

Il est assez courant que toutes les variables de pile soient disposées les unes à côté des autres. Si iest situé là où les array[10]écritures à, l'UB réinitialisent ià 0, conduisant ainsi à la boucle non terminée.

Pour corriger, changez la condition de boucle en i < 10.


6
Nitpick: Vous ne pouvez pas réellement formater le disque dur sur un système d'exploitation sain sur le marché, sauf si vous exécutez en tant que root (ou l'équivalent).
Kevin

26
@Kevin lorsque vous invoquez UB, vous abandonnez toute prétention à la raison.
o11c

7
Peu importe que votre code soit sain d'esprit. Le système d'exploitation ne vous laissera pas faire cela.
Kevin

2
@Kevin L'exemple avec le formatage de votre disque dur a commencé bien avant que ce ne soit le cas. Même les unix de l'époque (d'où C est originaire) étaient très heureux de vous permettre de faire des choses comme ça - et même aujourd'hui, beaucoup de distributions vous permettront avec plaisir de commencer à tout supprimer rm -rf /même lorsque vous n'êtes pas root, pas "formater" le lecteur entier bien sûr, mais toujours détruire toutes vos données. Aie.
Luaan

5
@Kevin mais un comportement non défini peut exploiter une vulnérabilité de système d'exploitation, puis s'élever pour installer un nouveau pilote de disque dur, puis commencer à nettoyer le lecteur.
ratchet freak

38

Dans ce qui devrait être la dernière exécution de la boucle, vous écrivez array[10], mais il n'y a que 10 éléments dans le tableau, numérotés de 0 à 9. La spécification du langage C indique qu'il s'agit d'un «comportement non défini». En pratique, cela signifie que votre programme essaiera d'écrire dans la intmémoire de la taille qui se trouve immédiatement après arraydans la mémoire. Ce qui se passe alors dépend de ce qui se trouve en fait, et cela dépend non seulement du système d'exploitation, mais plus encore du compilateur, des options du compilateur (telles que les paramètres d'optimisation), de l'architecture du processeur, du code environnant , etc. Cela peut même varier d'une exécution à l'autre, par exemple en raison de la randomisation de l'espace d'adressage (probablement pas sur cet exemple de jouet, mais cela se produit dans la vie réelle). Certaines possibilités incluent:

  • L'emplacement n'a pas été utilisé. La boucle se termine normalement.
  • L'emplacement a été utilisé pour quelque chose qui avait la valeur 0. La boucle se termine normalement.
  • L'emplacement contenait l'adresse de retour de la fonction. La boucle se termine normalement, mais le programme se bloque car il essaie de passer à l'adresse 0.
  • L'emplacement contient la variable i. La boucle ne se termine jamais car iredémarre à 0.
  • L'emplacement contient une autre variable. La boucle se termine normalement, mais des choses «intéressantes» se produisent.
  • L'emplacement est une adresse mémoire non valide, par exemple parce qu'il se arraytrouve juste à la fin d'une page de mémoire virtuelle et que la page suivante n'est pas mappée.
  • Les démons volent hors de votre nez . Heureusement, la plupart des ordinateurs ne disposent pas du matériel requis.

Ce que vous avez observé sous Windows, c'est que le compilateur a décidé de placer la variable iimmédiatement après le tableau en mémoire, et a donc array[10] = 0fini par l'affecter à i. Sur Ubuntu et CentOS, le compilateur ne s'y itrouvait pas. Presque toutes les implémentations C regroupent des variables locales en mémoire, sur une pile mémoire , à une exception près: certaines variables locales peuvent être placées entièrement dans des registres . Même si la variable est sur la pile, l'ordre des variables est déterminé par le compilateur, et il peut dépendre non seulement de l'ordre dans le fichier source mais aussi de leurs types (pour éviter de gaspiller de la mémoire pour des contraintes d'alignement qui laisseraient des trous) , sur leurs noms, sur une valeur de hachage utilisée dans la structure de données interne d'un compilateur, etc.

Si vous voulez savoir ce que votre compilateur a décidé de faire, vous pouvez lui dire de vous montrer le code assembleur. Oh, et apprenez à déchiffrer l'assembleur (c'est plus facile que de l'écrire). Avec GCC (et certains autres compilateurs, en particulier dans le monde Unix), passez l'option -Spour produire du code assembleur au lieu d'un binaire. Par exemple, voici l'extrait d'assembleur pour la boucle de compilation avec GCC sur amd64 avec l'option d'optimisation -O0(pas d'optimisation), avec des commentaires ajoutés manuellement:

.L3:
    movl    -52(%rbp), %eax           ; load i to register eax
    cltq
    movl    $0, -48(%rbp,%rax,4)      ; set array[i] to 0
    movl    $.LC0, %edi
    call    puts                      ; printf of a constant string was optimized to puts
    addl    $1, -52(%rbp)             ; add 1 to i
.L2:
    cmpl    $10, -52(%rbp)            ; compare i to 10
    jle     .L3

Ici, la variable iest de 52 octets sous le haut de la pile, tandis que le tableau démarre 48 octets sous le haut de la pile. Il se trouve donc que ce compilateur s'est placé ijuste avant le tableau; vous écraseriez is'il vous arrivait d'écrire array[-1]. Si vous passez array[i]=0à array[9-i]=0, vous obtiendrez une boucle infinie sur cette plate-forme particulière avec ces options de compilation particulières.

Maintenant compilons votre programme avec gcc -O1.

    movl    $11, %ebx
.L3:
    movl    $.LC0, %edi
    call    puts
    subl    $1, %ebx
    jne     .L3

C'est plus court! Le compilateur a non seulement refusé d'allouer un emplacement de pile pour i- il n'est jamais stocké dans le registre ebx- mais il n'a pas pris la peine d'allouer de la mémoire arrayou de générer du code pour définir ses éléments, car il a remarqué qu'aucun des éléments sont jamais utilisés.

Pour rendre cet exemple plus révélateur, assurons-nous que les affectations de tableau sont effectuées en fournissant au compilateur quelque chose qu'il n'est pas en mesure d'optimiser. Un moyen facile de le faire est d'utiliser le tableau à partir d'un autre fichier - en raison de la compilation séparée, le compilateur ne sait pas ce qui se passe dans un autre fichier (à moins qu'il n'optimise au moment du lien, lequel gcc -O0ou gcc -O1non). Créez un fichier source use_array.ccontenant

void use_array(int *array) {}

et changez votre code source en

#include <stdio.h>
void use_array(int *array);

int main()
{
  int array[10],i;

  for (i = 0; i <=10 ; i++)
  {
    array[i]=0; /*code should never terminate*/
    printf("test \n");

  }
  printf("%zd \n", sizeof(array)/sizeof(int));
  use_array(array);
  return 0;
}

Compiler avec

gcc -c use_array.c
gcc -O1 -S -o with_use_array1.c with_use_array.c use_array.o

Cette fois, le code assembleur ressemble à ceci:

    movq    %rsp, %rbx
    leaq    44(%rsp), %rbp
.L3:
    movl    $0, (%rbx)
    movl    $.LC0, %edi
    call    puts
    addq    $4, %rbx
    cmpq    %rbp, %rbx
    jne     .L3

Maintenant, le tableau est sur la pile, à 44 octets du haut. Et alors i? Il n'apparaît nulle part! Mais le compteur de boucles est conservé dans le registre rbx. Ce n'est pas exactement i, mais l'adresse du array[i]. Le compilateur a décidé que puisque la valeur de in'a jamais été utilisée directement, il était inutile d'effectuer une arithmétique pour calculer où stocker 0 lors de chaque exécution de la boucle. Au lieu de cela, cette adresse est la variable de boucle, et l'arithmétique pour déterminer les limites a été effectuée en partie au moment de la compilation (multiplier 11 itérations par 4 octets par élément de tableau pour obtenir 44) et en partie au moment de l'exécution mais une fois pour toutes avant le début de la boucle ( effectuer une soustraction pour obtenir la valeur initiale).

Même sur cet exemple très simple, nous avons vu comment changer les options du compilateur (activer l'optimisation) ou changer quelque chose de mineur ( array[i]en array[9-i]) ou même changer quelque chose d'apparemment sans rapport (ajouter l'appel à use_array) peut faire une différence significative dans ce que le programme exécutable a généré par le compilateur. Les optimisations du compilateur peuvent faire beaucoup de choses qui peuvent sembler peu intuitives sur les programmes qui invoquent un comportement non défini . C'est pourquoi le comportement indéfini est laissé complètement indéfini. Lorsque vous vous écartez très légèrement des pistes, dans les programmes du monde réel, il peut être très difficile de comprendre la relation entre ce que fait le code et ce qu'il aurait dû faire, même pour les programmeurs expérimentés.


25

Contrairement à Java, C ne fait pas de vérification des limites du tableau, c'est-à-dire qu'il n'y en a pas ArrayIndexOutOfBoundsException, la tâche de s'assurer que l'index du tableau est valide est laissée au programmeur. Faire cela exprès conduit à un comportement indéfini, tout peut arriver.


Pour un tableau:

int array[10]

les index ne sont valides que dans la plage 0de 9. Cependant, vous essayez de:

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

accédez array[10]ici, changez la condition eni < 10


6
Ne pas le faire exprès conduit également à un comportement indéfini - le compilateur ne peut pas le dire! ;-)
Toby Speight

1
Utilisez simplement une macro pour convertir vos erreurs en avertissements: #define UNINTENDED_MISTAKE (EXP) printf ("Avertissement:" #EXP "erreur \ n");
lkraider

1
Je veux dire, si vous faites une erreur exprès, vous pourriez aussi bien l'identifier en tant que tel et le rendre sûr pour éviter le comportement indéfini; D
lkraider

19

Vous avez une violation de limites, et sur les plates-formes non terminales, je pense que vous définissez par inadvertance la valeur izéro à la fin de la boucle, afin qu'elle recommence.

array[10]est invalide; il contient 10 éléments, à array[0]travers array[9], et array[10]est le 11e. Votre boucle doit être écrite pour s'arrêter avant 10 , comme suit:

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

Là où les array[10]terrains sont définis par l'implémentation, et de manière amusante, sur deux de vos plates-formes, ils atterrissent sur ilesquels ces plates-formes semblent apparemment directement après array. iest mis à zéro et la boucle continue indéfiniment. Pour vos autres plates-formes, ipeut être situé avant arrayou arraypeut avoir un rembourrage après.


Je ne pense pas que valgrind puisse attraper cela car c'est toujours un emplacement valide, mais ASAN le peut.
o11c

13

Vous déclarer int array[10]moyen arrayest d' indice 0à 9(total des 10éléments entiers qu'il peut contenir). Mais la boucle suivante,

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

en boucle 0à des 10moyens 11temps. Par conséquent, quand i = 10il va déborder le tampon et provoquer un comportement indéfini .

Essayez donc ceci:

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

ou,

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

7

Il n'est pas défini à array[10]et donne un comportement non défini comme décrit précédemment. Pensez-y comme ça:

J'ai 10 articles dans mon panier d'épicerie. Elles sont:

0: une boîte de céréales
1: pain
2: lait
3: tarte
4: œufs
5: gâteau
6: 2 litres de soda
7: salade
8: burgers
9: crème glacée

cart[10]n'est pas défini et peut donner une exception hors limites dans certains compilateurs. Mais beaucoup ne le font apparemment pas. Le 11ème article apparent n'est pas réellement dans le panier. Le 11ème élément pointe vers ce que je vais appeler un "élément poltergeist". Elle n'a jamais existé, mais elle était là.

La raison pour laquelle certains compilateurs donnent iun index de array[10]ou array[11]ou même array[-1]est à cause de votre instruction d'initialisation / déclaration. Certains compilateurs interprètent cela comme:

  • "Allouez 10 blocs de ints pour array[10]et un autre intbloc. Pour le rendre plus facile, placez-les les uns à côté des autres."
  • Comme avant, mais éloignez-le d'un espace ou deux pour que array[10]cela ne pointe pas i.
  • Faites la même chose qu'avant, mais allouez ià array[-1](car un index d'un tableau ne peut pas, ou ne devrait pas être négatif), ou allouez-le à un endroit complètement différent parce que le système d'exploitation peut le gérer, et c'est plus sûr.

Certains compilateurs veulent que les choses aillent plus vite, et certains compilateurs préfèrent la sécurité. Tout dépend du contexte. Si je développais une application pour l'ancien système d'exploitation BREW (le système d'exploitation d'un téléphone de base), par exemple, il ne se soucierait pas de la sécurité. Si je développais pour un iPhone 6, il pourrait fonctionner rapidement quoi qu'il arrive, donc j'aurais besoin de mettre l'accent sur la sécurité. (Sérieusement, avez-vous lu les directives de l'App Store d'Apple ou lu des informations sur le développement de Swift et Swift 2.0?)


Remarque: j'ai tapé la liste pour qu'elle devienne "0, 1, 2, 3, 4, 5, 6, 7, 8, 9", mais le langage de balisage de SO a corrigé les positions de ma liste ordonnée.
DDPWNAGE

6

Puisque vous avez créé un tableau de taille 10, la condition de boucle doit être la suivante:

int array[10],i;

for (i = 0; i <10 ; i++)
{

Actuellement, vous essayez d'accéder à l'emplacement non attribué à partir de la mémoire à l'aide array[10]et cela provoque le comportement non défini . Un comportement indéfini signifie que votre programme se comportera de façon indéterminée, il peut donc donner des sorties différentes à chaque exécution.


5

Eh bien, le compilateur C ne vérifie généralement pas les limites. Vous pouvez obtenir une erreur de segmentation au cas où vous vous référez à un emplacement qui n'appartient pas à votre processus. Cependant, les variables locales sont allouées sur la pile et selon la façon dont la mémoire est allouée, la zone juste au-delà du tableau ( array[10]) peut appartenir au segment de mémoire du processus. Ainsi, aucun piège de faute de segmentation n'est lancé et c'est ce que vous semblez expérimenter. Comme d'autres l'ont souligné, il s'agit d'un comportement non défini en C et votre code peut être considéré comme erratique. Puisque vous apprenez le C, vous feriez mieux de prendre l'habitude de vérifier les limites de votre code.


4

Au-delà de la possibilité que la mémoire soit disposée de manière à ce qu'une tentative d'écriture a[10]écrase réellement i, il serait également possible qu'un compilateur d'optimisation détermine que le test de boucle ne peut pas être atteint avec une valeur isupérieure à dix sans que le code n'ait d'abord accédé au élément de tableau inexistant a[10].

Étant donné qu'une tentative d'accès à cet élément serait un comportement indéfini, le compilateur n'aurait aucune obligation quant à ce que le programme pourrait faire après ce point. Plus précisément, puisque le compilateur n'aurait aucune obligation de générer du code pour vérifier l'index de boucle dans tous les cas où il pourrait être supérieur à dix, il n'aurait aucune obligation de générer du code pour le vérifier du tout; il pourrait plutôt supposer que le <=10test donnera toujours la valeur true. Notez que cela serait vrai même si le code le lisait a[10]plutôt que de l'écrire.


3

Lorsque vous parcourez le passé, i==9vous attribuez zéro aux «éléments du tableau» qui se trouvent réellement au- delà du tableau , de sorte que vous écrasez d'autres données. Vous remplacez très probablement la ivariable, qui se trouve après a[]. De cette façon, vous remettezi simplement la variable à zéro et redémarrez ainsi la boucle.

Vous pourriez le découvrir vous-même si vous imprimiez idans la boucle:

      printf("test i=%d\n", i);

au lieu de juste

      printf("test \n");

Bien sûr, ce résultat dépend fortement de l'allocation de mémoire pour vos variables, qui à son tour dépend d'un compilateur et de ses paramètres, il s'agit donc généralement d'un comportement indéfini - c'est pourquoi les résultats sur différentes machines ou différents systèmes d'exploitation ou sur différents compilateurs peuvent différer.


0

l'erreur est dans le tableau de portions [10] w / c est également l'adresse de i (tableau int [10], i;). lorsque le tableau [10] est défini sur 0, alors le i serait 0 w / c réinitialise la boucle entière et provoque la boucle infinie. il y aura une boucle infinie si le tableau [10] est compris entre 0 et 10. la boucle correcte devrait être pour (i = 0; i <10; i ++) {...} int tableau [10], i; pour (i = 0; i <= 10; i ++) tableau [i] = 0;


0

Je vais suggérer quelque chose que je ne trouverai pas ci-dessus:

Essayez d'affecter un tableau [i] = 20;

Je suppose que cela devrait terminer le code partout .. (étant donné que vous gardez i <= 10 ou ll)

Si cela fonctionne, vous pouvez fermement décider que les réponses spécifiées ici sont déjà correctes [la réponse liée à la mémoire en écrasant une par exemple.]


-9

Il y a deux choses qui ne vont pas ici. L'int i est en fait un élément de tableau, tableau [10], comme on le voit sur la pile. Parce que vous avez permis à l'indexation de faire réellement tableau [10] = 0, l'index de boucle, i, ne dépassera jamais 10. Faites-le for(i=0; i<10; i+=1).

i ++ est, comme K&R l'appelle, un «mauvais style». Il incrémente i de la taille de i, pas 1. i ++ est pour les mathématiques du pointeur et i + = 1 pour l'algèbre. Bien que cela dépende du compilateur, ce n'est pas une bonne convention pour la portabilité.


5
-1 Complètement faux. La variable iest un élément de tableau NOTan a[10], il n'y a aucune obligation ni même suggestion pour un compilateur de le mettre sur la pile immédiatement après a[] - il peut aussi bien être localisé avant le tableau, ou séparé avec un espace supplémentaire. Il pourrait même être alloué en dehors de la mémoire principale, par exemple dans un registre CPU. C'est également faux ++pour les pointeurs et non pour les entiers. Tout à fait faux est «i ++ incrémente i de la taille de i» - lisez la description de l'opérateur dans la définition du langage!
CiaPan

c'est pourquoi cela fonctionne sur certaines plateformes et pas sur d'autres. c'est la seule explication logique de la raison pour laquelle il boucle en permanence sur Windows. en ce qui concerne I ++, il s'agit d'un pointeur mathématique et non d'un entier. lire les Écritures ... le «langage de programmation C». par Kernigan et Ritche, si vous voulez, j'ai une copie dédicacée et je programme en c depuis 1981.
SkipBerne

1
Lisez le code source par OP et trouvez la déclaration de variable i- elle est de inttype. C'est un entier , pas un pointeur; un nombre entier, utilisée comme un indice à la array,.
CiaPan

1
Je l'ai fait et c'est pourquoi j'ai commenté comme je l'ai fait. vous devriez peut-être réaliser que, sauf si le compilateur inclut des vérifications de pile et dans ce cas, cela n'aurait pas d'importance en tant que référence de pile lorsque I = 10 référencerait réellement, dans certaines compilations, l'index du tableau et qui se situe dans les limites de la région de pile. les compilateurs ne peuvent pas fixer stupide. Les compilations pourraient faire une correction comme il semble que celle-ci le fasse, mais une interprétation pure du langage de programmation c ne prendrait pas en charge cette convention et entraînerait, comme le dit l'OP, des résultats non portables.
SkipBerne

@SkipBerne: pensez à supprimer votre réponse avant de vous voir "attribuer" plus de points négatifs.
Peter VARGA
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.