🕒 : 3 h maximum


Prérequis:

  • utilisation de gcc et gdb
  • Monia base algorithme , organigrammes , pseudo langage


But:

  • apprendre à passer des paramètres a un programme écrit en c
  • Organigrammes (monia et PL)
  • traduire un code en Pseudo Langage en C et réciproquement


Répertoire du travail:

~/Works/TP2_C


Présentation:

En langage C, la fonction main constitue le point d’entrée principal d’un programme.Cours sur le passage de paramètres en c

argc et argv

dans un répertoire param0

param0.c

#include <stdio.h>
#include <stdlib.h>

int main(int argc,char ** argv)
{
    int i;

    printf("Nombre d'arguments : %d\n", argc);

    for (i = 0; i < argc; i++) {
        printf("Argument %d : %s\n", i, argv[i]);
    }

    return EXIT_SUCCESS;
}

compiler , tester ce code.

le for est introduit dans ce code , vous allez faire du pas à pas avec gdb, regarder comment fonctionne la boucle for .

etudiant@ordi:~/Works/TP2_C/param0$ gcc param0.c -o param0 -Wall -ansi -pedantic -g
etudiant@ordi:~/Works/TP2_C/param0$ 
etudiant@ordi:~/Works/TP2_C/param0$ ./param0
Nombre d'arguments : 1
Argument 0 : ./param0
etudiant@ordi:~/Works/TP2_C/param0$ ./param0 un deux trois                                     
Nombre d'arguments : 4
Argument 0 : ./param0
Argument 1 : un
Argument 2 : deux
Argument 3 : trois
etudiant@ordi:~/Works/TP2_C/param0$

Expliquer le rôle de argc et le rôle argv .

compiler le code de test param0.c, et le lancer dans gdb

Vous n’allez forcèment pas retrouver les mêmes adresses mais le fonctionnement sera identique .

etudiant@ordi:~/Works/TP2_C/param0$ ls
param0  param0.c
etudiant@ordi:~/Works/TP2_C/param0$ gdb param0 
GNU gdb (Ubuntu 15.0.50.20240403-0ubuntu1) 15.0.50.20240403-git
Copyright (C) 2024 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from param0...
(gdb) 

gdb possède le code param0

on va lister notre précieux code c !

(gdb) l
1       #include <stdio.h>
2       #include <stdlib.h>
3
4       int main(int argc,char *argv[])
5       {
6           int i;
7
8           printf("Nombre d'arguments : %d\n", argc);
9
10          for (i = 0; i < argc; i++) {
(gdb) l
11              printf("Argument %d : %s\n", i, argv[i]);
12          }
13
14          return EXIT_SUCCESS;
15      }
16
(gdb)

on va voir comment exécuter notre code en passant des paramètres

(gdb) run un deux trois
Starting program: /home/etudiant/Works/TP2_C/param0/param0 un deux trois                                                          

This GDB supports auto-downloading debuginfo from the following URLs:                                                             
  <https://debuginfod.ubuntu.com>
Enable debuginfod for this session? (y or [n]) y
Debuginfod has been enabled.
To make this setting permanent, add 'set debuginfod enabled on' to .gdbinit.                                                      
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".                                                        
Nombre d'arguments : 4
Argument 0 : /home/etudiant/Works/TP2_C/param0/param0
Argument 1 : un
Argument 2 : deux
Argument 3 : trois
[Inferior 1 (process 4007) exited normally]
(gdb) 

c’est parfait notre code fonctionne bien !

mais c’est la qu’il faut placer au moins un point d’arrêt !

on va utiliser br main ou br 8 , car ligne juste créer une variable i de type int .. il ne passe rien ! et 7 juste un passage à la ligne.

(gdb) br main
Breakpoint 1 at 0x55555555515c: file param0.c, line 8.
(gdb)

et on lance l’execution

(gdb) run un deux trois                                                                                                           
Starting program: /home/etudiant/Works/TP2_C/param0/param0 un deux trois                                                          
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".                                                        

Breakpoint 1, main (argc=4, argv=0x7fffffffe2f8) at param0.c:8
8           printf("Nombre d'arguments : %d\n", argc);
(gdb)

Donc ici nous sommes à la ligne 8 non exécutée encore!

on voit que argc contient l’entier 4 et on sait à partir de la que nous avons 4 arguments disponibles de argv[0] à argv[3] et uniquement !

on peut les voir :

(gdb) print argv[0]
$1 = 0x7fffffffe58b "/home/etudiant/Works/TP2_C/param0/param0"
(gdb) print argv[1]                                                                                                               
$2 = 0x7fffffffe5b4 "un"
(gdb) p argv[2]
$3 = 0x7fffffffe5b7 "deux"
(gdb) p argv[3]
$4 = 0x7fffffffe5bc "trois"
(gdb) p argv[4]
$5 = 0x0
(gdb) p argv[5]                                                                                                                   
$6 = 0x7fffffffe5c2 "SHELL=/bin/bash"
(gdb)

on remarque argv[4] contient 0x0 , la valeur NULL , zéro ! (et ce n’est pas un argument passé)

ici nous allons nous intéresser au pointeur char ** à l’adresse 0x7fffffffe5b4 qui pointe la chaine argv[1]

syntaxe dans gdb pour lister par byte (octet)

x/<nombre><format> adresse
(gdb) x/10xb 0x7fffffffe5b4                                                                                                       
0x7fffffffe5b4: 0x75    0x6e    0x00    0x64    0x65    0x75    0x78    0x00                                                      
0x7fffffffe5bc: 0x74    0x72
(gdb)

0x75: ‘u’ , 0x6e:’n’ , 0x00 : fin de chaîne

0x64:’d » , 0x65:’e’ , 0x75:’u’ , 0x78:’x’ , 0x00 : NUL

.. etc

on voit bien ici que les données (les paramètres) sont stockés sous forme d’octets contenant les codes ASCII

ainsi on comprends ce qu’est un pointeur sur char .

on va regarder

(gdb) print argv
$16 = (char **) 0x7fffffffe2f8

on voit que argv est encore une autre adresse

g = giant word = 8 octets = 64 bits , ici les pointeurs sont codés sur 64 bits (8octets)

(gdb) p sizeof(char *)
$1 = 8

pour visualiser 5 nombres de 64 bits

x/5gx <adresse>
(gdb) print argv
$16 = (char **) 0x7fffffffe2f8
(gdb)  x/5gx 0x7fffffffe2f8
0x7fffffffe2f8: 0x00007fffffffe58b      0x00007fffffffe5b4
0x7fffffffe308: 0x00007fffffffe5b7      0x00007fffffffe5bc
0x7fffffffe318: 0x0000000000000000

on retrouve les adresses de chaque pointeur sur char , argv[0] , argv[1] , argv[2] , argv[3]; argv [4] qui est égal a NULL le dernier de la liste !

on va s’intéresser comment sont mis les 64 bits

(gdb) p argv
$18 = (char **) 0x7fffffffe2f8
(gdb) x/8bx 0x7fffffffe2f8
0x7fffffffe2f8: 0x8b    0xe5    0xff    0xff    0xff    0x7f    0x00    0x00                                                      
(gdb)

on remarque ici que c’est bien l’adresse de argv[0] , contenu mais c’est l’envers , en fait c’est a l’endroit mais tout est relatif !

C’est ce qu’on appel du little ebdian

Les octets les moins significatifs sont stockés en premier (à l’adresse la plus basse).

Par exemple, un pointeur 0x00007fffffffe58b est en mémoire :

Octet #AdresseContenu
00x7fffffffe2f80x8bLSB
10x7fffffffe2f90xe5
20x7fffffffe2fa0xff
30x7fffffffe2fb0xff
40x7fffffffe2fc0xff
50x7fffffffe2fd0x7f
60x7fffffffe2fe0x00
70x7fffffffe2ff0x00MSB

Et pour argc

(gdb) p &argc
$19 = (int *) 0x7fffffffe1bc
(gdb) p sizeof(int)
$20 = 4
(gdb) x/1hx 0x7fffffffe1bc                                                                                                        
0x7fffffffe1bc: 0x0004
(gdb) 

donner les explications chez vous !

(gdb) x/4bx 0x7fffffffe1bc                                                                                                        
0x7fffffffe1bc: 0x04    0x00    0x00    0x00
(gdb)

donner les explications chez vous !

Code c ,add , qui va additionner 2 entier.

Utilisation des passages de paramètres et algorithmie

on va utiliser monia

on va faire un répertoire monia_add

on y retrouvera

  • le fichier monia: add.xmo
  • le fichier add.pl
  • add.c
  • add (exécutable final)

il faut noter ici que le pl est donné ! et il faut transformer le pseudo langage en langage C

on veut un code qui va donner cela:

./add 5 6
la somme donne 11
./add 5
il faut 2 arguments. ex .:add 2 3

L’organigramme

Il faut refaire avec Monia ce code en organigramme

En extraire le fichier .pl (Pseudo langage)

# ----------------------------------------------------------------------------
# Nom         : add.pl
# Sujet       : Programme d'addition de 2 nombres
# Version     : 0.1
#
# Auteur      : Bogt
# Création    : 16/07/2025
# Mise à jour : 16/07/2025
# ----------------------------------------------------------------------------
# source généré par MoniaOrg version 0.38

Programme nom_du_programme ;

##FORWARDCOMMENT##

VAR             nb1 : entier ;
                nb2 : entier ;
                somme  : entier ;

DEBUTPROG
        SI (  argc=3 ) ALORS
                nb1 est la valeur entiere contenu dans argv[1] ;
                nb2 est la valeur entière contenu dans argv[2] ;
                somme= nb1+nb2 ;
                Aficher "la somme est : (somme) " ;
        SINON
                Afficher " Il faut 2 arguments , ex: ./add 2 3" ;
        FINSI
FINPROG
~                                                                                                                                              
~                                                                                                                                              
~                                                                                                                                              
~                                                                                                                                              
~                                                                                                                                              
                                                                                                                             4,19         Tout

Traduire PL en c (travail final !)

A vous de jouer , vous allez avoir besoin de transformer une chaîne de caractère en entier

pseudo langage :

nb1 est la valeur entiere contenu dans argv[1]

traduction en langage C

nb1=atoi(argv[1]);  /* a vous de déduire la suite */

la fonction int atoi(const char *nptr) voir le man 3 atoi

man atoi
atoi(3)                                             Library Functions Manual                                            atoi(3)   

NAME
       atoi, atol, atoll - convert a string to an integer

LIBRARY
       Standard C library (libc, -lc)

SYNOPSIS
       #include <stdlib.h>

       int atoi(const char *nptr);
       long atol(const char *nptr);
       long long atoll(const char *nptr);

   Feature Test Macro Requirements for glibc (see feature_test_macros(7)):                                                        

       atoll():
           _ISOC99_SOURCE
               || /* glibc <= 2.19: */ _BSD_SOURCE || _SVID_SOURCE                                                                

DESCRIPTION
       The atoi() function converts the initial portion of the string pointed to by nptr to int.  The behavior is the same as     

...

atoi , lire a to i

transforme une chaîne de caractère ASCII en type entier

test_atoi.c dans le repertoire test_atoi

#include <stdlib.h>
#include <stdio.h>

int main(int argc,char **argv)
{
int nombre;
if (argc==2) 
   {
     nombre=atoi(argv[1]);
     printf ("la chaine %s donne en entier : %d \n",argv[1],nombre);
     return (EXIT_SUCCESS);
   } 
printf("Il le faut une chaine avec un nombre entier dedans! \n");
return(EXIT_FAILURE);
}

test le code test_atoi pour comprendre l’usage de la fonction atoi()

code en c (et organigramme et pl) de soustract

répertoire soustrac, soustrac.c, soustrac.xmo soustrac.pl

avec Monia réaliser un fichier organigramme

de monia en extraire le fichier soustrac.pl

traduire le pl en c

compiler et tester le code

code en c (et organigramme et pl) de multi

répertoire multi , multi.c, multi.xmo, multi.pl

avec Monia réaliser un fichier organigramme

de monia en extraire le fichier multi.pl

traduire le pl en c

compiler et tester le code

code en c (et organigramme et pl) de div

répertoire div , div.c, div.xmo, div.pl

avec Monia réaliser un fichier organigramme

de monia en extraire le fichier div.pl

traduire le pl en c

compiler et tester le code

Erreur à ne jamais faire !

L’étudiant tôt ou tard fera cette erreur , vous allez analyser et conclure!

répertoire nogood ; nogood.c

/* crash test */
#include <stdio.h>

int main(int argc,char **argv)
{
        printf("argv[1][0]=%c, argv[1][1]=%c \n",argv[1][0],argv[1][1]);
return 0;
}
~                                                                                                                            
~                                                                                                                            
"nogood.c" 8L, 148B                                                                                        6,37-44      Tout
[albert] 0:bash  1:bash  2:bash- 3:ssh*                                                          "workboot" 08:33 17-juil.-25

nous avons appris que argv est pointeur sur un pointeur sur char , en d’autres mots un pointeur sur une chaîne de caractères

tout va bien si argv[1] existe , normal non?

etudiant@ordi:~/Works/TP2_C/nogood$ ./nogood un
argv[1][0]=u, argv[1][1]=n 
etudiant@ordi:~/Works/TP2_C/nogood$ 

la tout est pour le mieux dans le meilleur des mondes !

au passage justifier le résultat obtenu

maintenant le crash !

etudiant@ordi:~/Works/TP2_C/nogood$ ./nogood 
Erreur de segmentation (core dumped)
etudiant@ordi:~/Works/TP2_C/nogood$ 

cette erreur de segmentation est courante chez les débutants !

Et ou argv[1] n’existe pas ici argc=1 non pas 2 !

et on demande au système d’afficher des caractères n’existant pas !

Corriger le code en testant argc

proposer un code good.c

Avec gdb

Avec gdb tester et voir l’erreur de segmentation .. de nogood

Avec gdb teste et voir que l’erreur ne sa fait plus avec good