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: 2020-2021
Courriel: john(dot)samuel(at)cpe(dot)fr

Creative Commons License

Algorithmes et C appliqués aux Systèmes Numériques

Objectifs

Les variables constantes

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;
}

Les variables constantes

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;

La portée des variables

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;
}

La portée des variables

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;
}

La portée des variables

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;
}

Le passage de paramètres

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;
}

Le passage de paramètres

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;
}

Préprocesseur

cercle.c

#define PI 3.14159

float area( float radius) {
 return(PI * radius * radius);
}

Préprocesseur

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

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;
   ^

Préprocesseur

operators.h

int add( int, int);

L'utilisation

#include "operators.h" // en-têtes(headers)

#include "operators.h" // pas d'erreurs

Préprocesseur

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;

Préprocesseur

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

Préprocesseur

#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 (

Alignement en mémoire

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 en mémoire

Alignement d'un octet

couleur1
couleur2

Alignement en mémoire

Alignement de 4 octets

couleur1
couleur2

Alignement en mémoire

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

Alignement en mémoire

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

Les structures et les pointeurs

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
}

Les structures et les pointeurs

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
}

Les structures et les pointeurs

Une liste de couleurs simplement chaînée

Les structures et les pointeurs

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
}

Les structures et les pointeurs

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;
};

Les structures et les pointeurs

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
 }
}

Les structures et les pointeurs

Une liste d'entiers simplement chaînée

Les structures et les pointeurs

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 *);

Les structures et les pointeurs

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);
}

Les structures et les pointeurs

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;
  }
 }

Les structures et les pointeurs

Une liste de couleurs doublement chaînée

Les structures et les pointeurs

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;
};

Les structures et les pointeurs

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
 }
}

Les structures et les pointeurs

Une liste d'entiers simplement chaînée

Les structures et les pointeurs

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 *);

Les structures et les pointeurs

Une liste d'entiers doublement chaînée

 void insertion_debut(struct liste *liste, struct element *nouveau) {
  nouveau->suivant = liste->premier.suivant;
  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;
 }

Les structures et les pointeurs

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;
  }
 }

Les structures et les pointeurs

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;
 

Les structures et les pointeurs

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);
 }

Les fonctions et les pointeurs

int add( int a, int b ) {
 return a + b;
}

int subtract( int a, int b ) {
 return a - b;
}

Les fonctions et les pointeurs

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));
}

stats

/* 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;
}

Les erreurs: stats

#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;
}

Les erreurs: stats

La compilation

$ gcc -o stats stats.c

L'exécution

$./stats stats.c
Taille: ... octets
$ echo $?
0

Les erreurs: stats

La compilation

$ gcc -o stats stats.c

L'exécution

$ ./stats nostats
Stats: No such file or directory
$ echo $?
1

Répertoire (dossier)

#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);
 }

Répertoire (dossier)

 struct dirent * ent;
 while(1) {
  ent = readdir(dirp);
  if (ent == NULL) {
   break;
  }
  printf("%s\n", ent->d_name);
 }

 closedir(dirp);

 return(0);
}

Réseau: Architecture client-serveur

Réseau: Client

Réseau: Client

#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;
 }

Réseau: Client

 //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;
 }

Réseau: serveur

Réseau: serveur

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;
 }

Réseau: Architecture cliente-serveur

 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);

Réseau: serveur

 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;
 }

La chaine de compilation

6 étapes de la chaine de compilation

La chaine de compilation

Préprocesseur

$ gcc -E bonjour.c

La chaine de compilation

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

La chaine de compilation

Optimisation de code

$ gcc -O2 bonjour.c
$ gcc -O3 bonjour.c

Remarque: Autres optimisations: -O1 -O2 -O3 -Os -Ofast -Og

La chaine de compilation

Génération de code natif

$ gcc -S bonjour.c
$ cat bonjour.s

La chaine de compilation

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

La chaine de compilation

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

La chaine de compilation

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

La chaine de compilation

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

La chaine de compilation

Code objet

$ gcc -c client.c
$ gcc -c color.c
$ gcc -o color color.o client.o

La chaine de compilation

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

La chaine de compilation

Makefile

CC ?= gcc

CFLAGS ?= -Wall -Wextra -g

COBJS ?= client.o color.o
SOBJS ?= server.o color.o

.SUFFIXES: .c .o

SERVER = server
CLIENT = client

La chaine de compilation

Makefile

all: $(SERVER) $(CLIENT)

$(SERVER): $(SOBJS)
    $(CC) -o $(SERVER) $(SOBJS)

$(CLIENT): $(COBJS)
    $(CC) -o $(CLIENT) $(COBJS)

.c.o:
    $(CC) -c $*.c

La chaine de compilation

Exécution d'un Makefile

$ vim server.c
$ vim client.c
$ vim color.c
$ make
$ vim color.c
$ make

Références

Références

Crédits d'images