Algorithmes et C appliqués aux Systèmes Numériques
Les appels systèmes, les erreurs et la chaine de compilation
John Samuel
CPE Lyon
Année : 2021-2022
Courriel : john(dot)samuel(at)cpe(dot)fr
Objectifs
La portée des variables
Préprocesseur
Les appels systèmes
Les erreurs
La chaine de compilation
Une variable constante
/* Une variable constante
*/ #include <stdio.h>
int main ()
{
const int year = 2017 ; // une variable constante
year = 2019 ; // une variable constante
printf ("C'est l'annee %d" , year ); //affiche 2017
return 0; }
Erreur pendant la compilation
$ gcc bonjour.c
const.c: In function ‘main’:
const.c:5:8: error: assignment of read-only variable ‘year’
year = 2019;
Une variable globale
/* affiche un message à l'écran en utilisant une variable globale
*/ #include <stdio.h>
int year = 2017 // une variable globale;
int main ()
{
printf ("C'est l'annee %d" , year ); //affiche 2017 return 0; }
Une variable locale
/* affiche un message à l'écran en utilisant une variable locale
*/ #include <stdio.h>
int year = 2017 // une variable globale;
int main ()
{
int year = 2018 // une variable locale;
printf ("C'est l'annee %d" , year ); //affiche 2018 return 0; }
Une variable locale
/* affiche un message à l'écran en utilisant une variable locale
*/ #include <stdio.h>
int year = 2017 // une variable globale;
int main ()
{
int year = 2018 // une variable locale;
{
int year = 2019 // une variable locale;
printf ("C'est l'annee %d" , year ); //affiche 2019
}
return 0; }
Passage par valeur
void
echange(
int
a ,
int
b
) {
int
temp
= a ;
a
= b ;
b
= temp ;
}
int main ()
{
int a = 10;
int b = 20;
echange (a , b );
printf ("a: %d, b: %d" , a , b ); //affiche 10, 20 return 0; }
Passage par référence
void
echange(
int
*a ,
int
*b
) {
int
temp
= *a ;
*a
= *b ;
*b
= temp ;
}
int main ()
{
int a = 10;
int b = 20;
echange (&a , &b );
printf ("a: %d, b: %d" , a , b ); //affiche 20, 10 return 0; }
cercle.c
#define PI 3.14159
float area (
float radius ) {
return (PI * radius
* radius );
}
Prototype (defs.h)
#define PI 3.14159
cercle.c
#include "defs.h"
#ifndef PI // Si PI n'est pas défini
#define PI 3.14159
#endif
float area (
float radius ) {
return (PI * radius
* radius );
}
Remarque 1: PI n'est pas une variable
Remarque 2: Utilisez l'option gcc -E pour comprendre l'objectif d'un préprocesseur
Prototype (defs.h)
Erreur: PI n'est pas une variable
#include "defs.h"
#ifndef PI
#define PI 3.14159
#endif
float area (
float radius ) {
PI = 3.14;
return (PI * radius
* radius );
}
Erreur (compilation)
Error: lvalue required as left operand of assignment
PI = 3.15;
^
operators.h
int
add(
int ,
int );
L'utilisation
#include
"operators.h" // en-têtes(headers)
#include
"operators.h" // pas d'erreurs
operators.h
int
num = 20;
L'utilisation
#include
"operators.h" // en-têtes(headers)
#include
"operators.h" // erreur
$ gcc operator.c
note: previous definition of ‘num’ was here
int num=20;
operators.h
#ifndef __OPERATORS_H__
#define __OPERATORS_H__
int
num = 20;
int
add(
int ,
int );
#endif //__OPERATORS_H__
L'utilisation
#include
"operators.h" // en-têtes(headers)
#include
"operators.h" // 2eme fois, mais pas d'erreurs
#ifndef PI
#define PI 3.14159
#endif
#ifndef square
#define square(value) value * value
#endif
float area (
float radius ) {
return (PI * square(radius) );
}
Remarque: Il'n y a pas d'espace entre square et (
struct couleur1{
unsigned char rouge;
unsigned char vert;
unsigned char bleu;
unsigned int count;
};
struct couleur2{
unsigned char rouge;
unsigned int count;
unsigned char vert;
unsigned char bleu;
};
printf (
"size- couleur1: %lu, couleur2: %lu\n" ,
sizeof (
struct
couleur1 ),
sizeof (
struct
couleur2 ));
Question: Quel est l'affichage de ce programme?
Alignement d'un octet
couleur1
couleur2
Alignement de 4 octets
couleur1
couleur2
En utilisant gcc
#pragma pack(push)
#pragma pack(1) //alignement d'un octet
struct couleur3{
unsigned char rouge;
unsigned char vert;
unsigned char bleu;
unsigned int count;
};
#pragma pack(pop)
printf (
"%lu\n" ,
sizeof (struct couleur3 ));
Remarque: L'affichage de ce programme: 7
struct couleur4{ //alignement de 4 octets
unsigned char rouge;
unsigned char vert;
unsigned char bleu;
unsigned char _pad;
unsigned int count;
};
struct couleur5{ //alignement de 4 octets
unsigned char rouge;
unsigned char _pad1[3];
unsigned int count;
unsigned char vert;
unsigned char bleu;
unsigned char _pad2[2];
};
Remarque: Utilisez l'option gcc -Wpadded pour savoir si une structure nécessite du padding our être alignée
struct couleur{
unsigned char rouge;
unsigned char vert;
unsigned char bleu;
unsigned int count;
};
int main () {
struct couleur c1 ;
struct couleur *scptr = &c1 ;
c1.bleu = 0xff ;
scptr->bleu = 0x01 ;
printf (
"c1.bleu: %hhx\n" , c1.bleu ); //0x01
}
struct couleur{
unsigned char rouge;
unsigned char vert;
unsigned char bleu;
unsigned int count;
};
void change (
struct couleur *c) {
c->bleu = 0x01 ;
}
int main () {
struct couleur c1 ;
c1.bleu = 0xff ;
change(&c1 );
printf (
"c1.bleu: %hhx\n" , c1.bleu ); //0x01
}
Une liste de couleurs simplement chaînée
struct couleur{
unsigned char rouge;
unsigned char vert;
unsigned char bleu;
unsigned int count;
};
void nochange (
struct couleur c) {
c.bleu = 0x03 ;
}
int main () {
struct couleur c1 ;
c1.bleu = 0xff ;
nochange(c1 );
printf (
"c1.bleu: %hhx\n" , c1.bleu ); //0xff
}
Une liste de couleurs simplement chaînée
struct couleur{
unsigned char rouge;
unsigned char vert;
unsigned char bleu;
unsigned int count;
struct couleur *next;
};
Une liste de couleurs simplement chaînée
int main () {
struct couleur first , c1 , c2 ;
struct couleur *cptr ;
first.next = &c1 ;
c1.next = &c2 ;
c2.next = NULL ;
cptr = &first ;
while (cptr != NULL ) { //navigation
printf (
"cptr->bleu: %hhx\n" , cptr->bleu );
cptr = cptr->next ; //couleur suivante
}
}
Une liste d'entiers simplement chaînée
Une liste d'entiers simplement chaînée
struct element{
unsigned int numero;
struct element *suivant;
};
// insertion d'un élement dans une liste
void insertion (struct element *, int );
// parcours de la liste
void parcours (struct element *);
La saisie d'entiers par l'utilisateur
int main () {
struct element premier ;
premier.suivant = NULL ;
while (1) {
int num ;
char strnum [50];
fgets (strnum , sizeof(strnum ), stdin );
if (strcmp (strnum , "FIN\n" ) == 0) {
break ;
}
sscanf (strnum , "%d\n" , &num );
insertion (&premier , num );
}
parcours (&premier );
}
Insertion d'un élément et parcours d'une liste simplement chaînée
void insertion (struct element *premier , int num ) {
struct element *nouveau ;
nouveau = malloc (sizeof (*nouveau ));
nouveau->num = num;
nouveau->suivant = premier->suivant ;
premier->suivant = nouveau ;
}
void parcours (struct element *premier ) {
struct element *elem = premier ;
while (elem != NULL ) {
printf("%d\n" , elem->num );
elem = elem->suivant ;
}
}
Une liste de couleurs doublement chaînée
Une liste de couleurs doublement chaînée
struct couleur{
unsigned char rouge;
unsigned char vert;
unsigned char bleu;
unsigned int count;
struct couleur *next;
struct couleur *prev;
};
int main () {
struct couleur first , c1 , c2 , last ;
struct couleur *cptr = &last ;
first.next = &c1 ;
first.prev = NULL ;
last.next = NULL ;
last.prev = &c2 ;
c1.next = &c2 ;
c1.prev = &first ;
c2.next = &last ;
c2.prev = &c1 ;
while (cptr != &first ) { //navigation
printf (
"cptr->bleu: %hhx\n" , cptr->bleu );
cptr = cptr->prev ; //couleur précédente
}
}
Une liste d'entiers simplement chaînée
Insertion d'un élément et parcours d une liste doublement chaînée
struct element {
int num ;
struct element *suivant ;
struct element *precedent ;
};
struct liste {
struct element premier ;
struct element dernier ;
};
void insertion_debut (struct liste *, struct element *);
void insertion_fin (struct liste *, struct element *);
void parcourir_debut (struct liste *);
void parcourir_fin (struct liste *);
Une liste d'entiers doublement chaînée
void insertion_debut (struct liste *liste , struct element *nouveau ) {
nouveau->suivant = liste->premier.suivan t;
nouveau->precedent = &liste->premier ;
liste->premier.suivant->precedent = nouveau ;
liste->premier.suivant = nouveau ;
}
void insertion_fin (struct liste *liste , struct element *nouveau ) {
nouveau->suivant = &liste->dernier ;
nouveau->precedent = liste->dernier.precedent ;
liste->dernier.precedent->suivant = nouveau ;
liste->dernier.precedent = nouveau ;
}
Une liste d'entiers doublement chaînée
void parcourir_debut (struct liste *liste ) {
struct element *elem = liste->premier.suivant ;
while (elem != &liste->dernier ) {
printf ("%d\n" , elem->num );
elem = elem->suivant ;
}
}
void parcourir_fin (struct liste *liste ) {
struct element *elem = liste->dernier.precedent ;
while (elem != &liste->premier ) {
printf ("%d\n" , elem->num );
elem = elem->precedent ;
}
}
La saisie d'entiers par l'utilisateur
int main () {
struct liste liste ;
liste.premier.suivant = &liste.dernier ;
liste.dernier.precedent = &liste.premier ;
liste.premier.precedent = NULL ;
liste.dernier.suivant = NULL ;
La saisie d'entiers par l'utilisateur
while (1) {
char strnum [50];
struct element *elem = malloc (sizeof (*elem ));
fgets (strnum , sizeof (strnum ), stdin );
if (strcmp(strnum, "FIN\n" ) == 0) {
break ;
}
sscanf (strnum , "%d\n" , &elem->num );
insertion_fin (&liste, elem );
}
parcourir_debut (&liste );
parcourir_fin (&liste );
}
int
add(
int
a ,
int
b
) {
return
a
+ b ;
}
int
subtract(
int
a ,
int
b
) {
return
a
- b ;
}
int main () {
int (*func )(int , int ); //pointeur de function
char op = '-' ;
int num1 = 20 , num2 = 30 ;
if (op == '+' ) {
func = add ;
}
else {
func = subtract ;
}
printf ("value: %d\n" ,func (20, 30));
}
/* Fichier: stats.c * la taille d'une fichier
* auteur: John Samuel */ #include
<stdio.h> // en-têtes(headers)
#include
<sys/types.h>
#include
<sys/stat.h>
#include
<unistd.h>
int main (int argc , char ** argv)
{
struct stat sf ;
stat ("./stats.c" , &sf);
printf ("Taille: %ld octets" , sf.st_size );
return 0; }
#include
<stdio.h> // en-têtes(headers)
#include
<sys/types.h>
#include
<sys/stat.h>
#include
<unistd.h>
#include
<stdlib.h>
int main (int argc , char ** argv)
{
struct stat sf ;
int status ;
status = stat (argv[1] , &sf);
if (status == -1) {
perror ("Stats" );
return (EXIT_FAILURE);
}
printf ("Taille: %ld octets" , sf.st_size );
return 0; }
La compilation
$ gcc -o stats stats.c
L'exécution
$./stats stats.c
Taille: ... octets
$ echo $?
0
La compilation
$ gcc -o stats stats.c
L'exécution
$ ./stats nostats
Stats: No such file or directory
$ echo $?
1
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main (int argc , char **argv ) {
if (argc < 2) {
printf ("Usage: readdir path\n" );
return (EXIT_FAILURE);
}
DIR *dirp = opendir (argv [1]);
if (dirp == NULL ) {
perror ("opendir" );
return (EXIT_FAILURE);
}
struct dirent * ent;
while (1) {
ent = readdir (dirp );
if (ent == NULL ) {
break ;
}
printf ("%s\n" , ent->d_name );
}
closedir (dirp );
return (0);
}
#define PORT 8089
int main() {
int socketfd;
int bind_status;
struct sockaddr_in server_addr, client_addr;
/*
* Creation of a socket
* AF_INET: IPv4 Internet protocols
* SOCK_STREAM: two-way, connection-based byte streams
*/
socketfd = socket(AF_INET, SOCK_STREAM, 0);
if ( socketfd < 0 ) {
perror("Unable to open a socket" );
return -1;
}
//server address details
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = INADDR_ANY;
//Connect to the server
int connect_status = connect(socketfd, (struct sockaddr *)
&server_addr, sizeof(server_addr));
if ( connect_status < 0 ) {
perror("Unable to connect to server" );
return -1;
}
int main() {
int socketfd;
int bind_status;
struct sockaddr_in server_addr, client_addr;
/*
* Creation of a socket
* AF_INET: IPv4 Internet protocols
* SOCK_STREAM: two-way, connection-based byte streams
*/
socketfd = socket(AF_INET, SOCK_STREAM, 0);
if ( socketfd < 0 ) {
perror("Unable to open a socket" );
return -1;
}
int option = 1;
setsockopt(socketfd, SOL_SOCKET, SO_REUSEADDR,
&option, sizeof(option));
//server address details
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = INADDR_ANY;
bind_status = bind(socketfd, (struct sockaddr *)
&server_addr, sizeof(server_addr));
if (bind_status < 0 ) {
perror("Unable to bind to socket");
return -1;
}
// Start listening to the socket
listen(socketfd, 10);
char data[1024];
int client_addr_len = sizeof(client_addr);
int client_socket_fd = accept(socketfd,
(struct sockaddr *) &client_addr, &client_addr_len);
if (client_socket_fd < 0 ) {
perror("Unable to accept client requests");
return -1;
}
6 étapes de la chaine de compilation
Préprocesseur
$ gcc -E bonjour.c
Langage intermédiaire
$ gcc -v bonjour.c # les étapes importantes
$ gcc -save-temps -v bonjour.c # les fichiers *.i, *.s
$ gcc -fdump-tree-all bonjour.c
$ gcc -fdump-rtl-all bonjour.c
Optimisation de code
$ gcc -O2 bonjour.c
$ gcc -O3 bonjour.c
Remarque: Autres optimisations: -O1 -O2 -O3 -Os -Ofast -Og
Génération de code natif
$ gcc -S bonjour.c
$ cat bonjour.s
Optimisation de code
int main () {
int num = 2 + 3;
return (0);
}
Compilation (Pas d'optimisations)
$ gcc -O0 -S add.c
add.s
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $5, -4(%rbp)
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
Optimisation de code
int main () {
int num = 2 + 3;
return (0);
}
Compilation (optimisation: 2)
$ gcc -O2 -S add.c
add.s
main:
.LFB0:
.cfi_startproc
xorl %eax, %eax
ret
.cfi_endproc
Génération de code
sur une machine d'architecture 64 bits
$ gcc bonjour.c
$ file a.out
a.out: ELF 64-bit LSB shared object, x86-64
Génération de code
sur une machine d'architecture 64 bits
$ gcc -march=i686 -m32 bonjour.c
$ gcc -S -march=i686 -m32 bonjour.c
$ file ./a.out
./a.out: ELF 32-bit LSB shared object, Intel 80386
Code objet
$ gcc -c client.c
$ gcc -c color.c
$ gcc -o color color.o client.o
Code objet (Modifications et recompilation)
$ gcc -c client.c
$ gcc -c color.c
$ gcc -o client client.o color.o
$ vim client.c
$ gcc -c client.c
$ gcc -c client.o color.o
$ vim client.c
$ gcc -c client.c
$ gcc -o client client.o color.o
Makefile
CC ?= gcc
CFLAGS ?= -Wall -Wextra -g
COBJS ?= client.o color.o
SOBJS ?= server.o color.o
.SUFFIXES : .c .o
SERVER = server
CLIENT = client
Makefile
all : $(SERVER) $(CLIENT)
$(SERVER ): $(SOBJS)
$(CC) -o $(SERVER) $(SOBJS)
$(CLIENT ): $(COBJS)
$(CC) -o $(CLIENT) $(COBJS)
.c.o:
$(CC) -c $*.c
Exécution d'un Makefile
$ vim server.c
$ vim client.c
$ vim color.c
$ make
$ vim color.c
$ make
Références
Crédits d'images