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

Creative Commons License

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

Objectifs

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

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

#ifndef __OPERATORS_H__
#define __OPERATORS_H__

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

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

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

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 de couleurs

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

#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

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

Préprocesseur

$ gcc -E bonjour.c

La chaine de compilation

Langage intermédiaire

$ 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

$ gcc -march=i686 -m32 bonjour.c
$ gcc -S -march=i686 -m32 bonjour.c

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

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