2010-12-03 3 views
2

Je suis tombé sur ce morceau de code aujourd'hui tout en tutorat certains étudiants dans un cours de langage de programmation C. L'exercice demandé pour implémenter deux fonctions. Le premier scanne l'entrée d'un utilisateur et le second affiche ce qui a été précédemment analysé. Le code que je suis tombé est le suivant:comportement étrange dans un code C sous gcc 4.4.3


#include <stdio.h> 

void myInput(int i,int n) 
{ 
    int cpt; 
    int tab[n]; 

    for (cpt=0; cpt<n; cpt++) 
    { 
    printf("Enter a number :"); 
    scanf("%d",&i); 
    tab[cpt]=i; 
    } 
} 



void myDisp (int n) 
{ 
    int tab[n];  
    int cpt; 

    for (cpt=0; cpt <n; cpt++) 
    { 
    printf("%d ", tab[cpt]); 
    } 
} 

int main() 
{ 
    int n; int i; 
    printf(" Entrer the numbers of elements you want: \n"); 
    scanf("%d \n",&n); 
    int tab[n]; 
    myInput(i,n);   
    myDisp(n); 
} 

Bien que ce code est plein d'incohérences, il ne fonctionne effectivement sous gcc 4.4.3: il affiche les chiffres qui ont été entrés! !!!!! Est-ce que quelqu'un comprend comment ce code fonctionne?

Merci beaucoup

+0

Pour référence future: mettez en surbrillance votre code et appuyez sur Ctrl + K ou cliquez sur le bouton '0 1' et ainsi de suite pour formater votre code. Merci. – birryree

+0

Ceci est parfaitement valide C99, d'après ce que je peux voir. – Electro

+0

@Electro, @birryree: Il regarde probablement l'utilisation de la variable 'tab' non initialisée dans 'myDisp'. – Thanatos

Répondre

8

Si cela fonctionne, il est par chance stupide pure. Ce qui est imprimé dans myDisp est une pile non initialisée, qui peut contenir ou non les données qui ont été placées dans des variables portant le même nom dans myInput. Related reading

est ici un moyen facile de le casser avec le code do-rien:

void myInput(int i,int n) 
{ 
    // Add some variables to mess up the stack positioning. 
    int breaker; 
    int cpt; 
    int stomper; 
    int tab[n]; 
    int smasher; 

    for (cpt=0; cpt<n; cpt++) 
    { 
    printf("Enter a number :"); 
    scanf("%d",&i); 
    tab[cpt]=i; 
    } 

    // Trick the compiler into thinking these variables do something. 
    breaker = 1; 
    smasher = 3 * breaker; 
    stomper = smasher + breaker; 
    breaker = stomper * smasher; 
} 

Une autre façon de le casser serait de mettre un appel de fonction (par exemple, à printf) entre les appels à myInput et myDisp .

+3

Pour développer: Vous pourriez être chanceux que 'myDisp' alloue' tab' dans le même segment de mémoire comme dans les autres fonctions. Cela semble fonctionner, mais le code n'est pas correct et doit être corrigé. – Thanatos

+0

Salut Nathon! Je viens juste de recompiler sous gcc et de l'exécuter. Ça marche. Il affiche les nombres que j'ai saisis !!! – strangeLoop

+0

@strangeLoop: Bien sûr, mais essayez de compiler avec -O3. Est-ce que ça marche encore? Est-ce qu'il affiche tous les numéros que vous entrez dans le bon ordre? – nmichaels

0

Étant donné que les deux matrices sont complètement séparées, cela ne devrait pas fonctionner. Si c'est le cas, c'est simplement parce qu'ils se sont retrouvés au même endroit en mémoire.

1

Cela ne fonctionne pas, du moins pas toujours. Certes je ne 4.4.4 gcc 4.4.3

$ ./a.out 
Entrer the numbers of elements you want: 
5 
2 
Enter a number :Enter a number :4 
Enter a number :1 
Enter a number :2 
Enter a number :3 
2 4 1 134514562 3 

Morale de l'histoire est lorsque vous accédez à la mémoire non initialisée, tout peut arriver, y compris l'apparition de travail.

+0

Ok, merci SiegeX, je vais essayer avec gcc 4.4.4. – strangeLoop

0

Probablement que cela fonctionne parce que l'emplacement de la mémoire de tab local à myInput et myDisp se trouve être (presque?) La même chose.

Il ne semble pas si étrange pour moi: myInput et myDisp ont presque la même signature (ils diffèrent pour un seul paramètre int); même dans le pire des cas, les emplacements de la pile par tab visé dans les deux fonctions seraient encore correctement alignées et décalées au maximum par deux ints (i et cpt à myInput).

0

Il semble que le programme accède au même emplacement de mémoire pour chaque tableau int tab[n] que vous avez déclaré mais, comme mentionné précédemment, cela ne devrait pas fonctionner.

Mais je pense que ce qui se passe ici est quelque chose comme: vous allouez tab [] dans main(), disons, sous l'adresse 0x00000001 (prenons comme exemple seulement). Le tableau a n entiers mais pas de valeurs du tout.

Ensuite, vous allez dans myInput(), déclarez le tableau à nouveau (même taille), dans une autre adresse, comme 0x001F0000, puis définissez les valeurs, un par un.Ainsi, lorsque la fonction se termine, elle libère la mémoire allouée de ses variables, de sorte que votre tableau n'existe plus.

Mais attendez, c'est C, donc quand vous libérez la mémoire, vous dites seulement au tas (ou à l'allocateur de mémoire, en général) que les adresses peuvent être réutilisées. Vous ne retirez PAS exactement les valeurs de la mémoire.

Ensuite, vous appelez myDisp() et déclarez votre tableau à nouveau. Il semble que la mémoire que vous venez de demander ait une priorité plus élevée et qu'elle soit ensuite redonnée à votre programme. Donc votre tableau est de nouveau instancié et sur la même adresse. Donc, même si vous ne l'avez pas rempli avec des valeurs, la mémoire est lue (car elle est toujours valide en C) et les valeurs sont toujours là.

Oh, et le tableau déclaré à l'intérieur de main()? Rien n'arrive à ça. Essayez d'imprimer ses valeurs et je parie que vous n'en aurez pas les bonnes.

C'est ma conjecture. EDIT: Juste pour voir les choses se produire: essayer de déclarer un autre tableau après tabulation (ne pas renommer tab), disons tab2, même longueur, et l'utiliser pour mettre vos valeurs au lieu de tabulation, puis laisser le programme s'exécuter:

+0

Oui, bien le tableau dans la portée main() est complètement inutile. J'ai juste réécrit le code comme on me l'avait donné. Si cette déclaration de tableau est commentée, le code fonctionne toujours et donne les bonnes valeurs! – strangeLoop

+0

Bien sûr. Je blâme le tas de mémoire pour cette situation. À mon avis, comme je l'ai dit, vous déclarez que le tableau et le tas vous donne une adresse X parce qu'elle était au sommet du tas. Ensuite, vous libérez X et il va de nouveau sur le tas (sons bizarres), puis le tas vous donnera la même adresse lorsque vous déclarez un nouvel onglet. – Giuliano