Cours n°1 :
Introduction au C

Réseau et Prog. Bas Niveau
Victor Poupet

Généralités

Premier programme

#include <stdio.h>

/* la fonction main est appelée automatiquement à l'exécution du programme */
int main() {
  printf("Hello, world!\n");
  return 0; // valeur de retour
}
Quelques remarques :

Instructions

int a = 12; int b = 5;
// bloc d'instructions après if
if (a > 0) {
  a += 1;
  b = a + b;
}

Compilation

$ gcc -Wall code.c -o prog

$ ./prog
Hello, world!

Précompilation

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

#define PI 3.14159
#define ever ;1;

int main() {
  int n = 1;
  for(ever;1;) {
    if (PI3.14159 / n < 1) {
      break;
    }
    n++;
  }
}

Fonctions

#include <math.h>

float hyp(int, int);

float hyp(int a, int b) {
  float r;
  r = sqrt(a * a + b * b);
  return r;
}

Chaque fonction a un prototype (ou signature) :

Le prototype sert à définir le cadre d'utilisation de la fonction

Exemple: stdio.h

La fonction main

int main() {
  // code principal du programme
  return 0;
}


int main(int argc, char *argv[]) {
  // affiche tous les arguments :
  for (int i=0; i<argc; i++) {
    printf("%s\n", argv[i]);
  }
}

Tout programme doit comporter une fonction main

Types de base

int a = 12;
unsigned int b;
long int c;
long long int d;
unsigned long long int e;

float x = -2.72;
double y;

char s = 'A';

Les types de base en mémoire

Les différents types de base n'occupent pas le même espace mémoire.

En général :

Cependant, l'espace occupé par chaque type peut dépendre du système d'exploitation. La norme spécifie des tailles minimum et des relations entre les types (un long int doit être au moins aussi long qu'un int).

On peut utiliser l'instruction sizeof pour obtenir la taille en octets d'une variable

Structure de contrôle

int x = 12;

if (x > 0) {
  x--;
} else {
  x++;
}

while (x > 1) {
  x--;
}for (int i=0; i < 5; i++) {
  x *= 2;
}

// version équivalente avec un while
int i=0;
while (i < 5) {
  x *= 2;
  i++;
}switch (x % 3) {
  case 1:
    x -= 1;
    break;
  case 2:
    x += 1;
    break;
  default:
    x += 3;
    break;
}

On peut contrôler le déroulement du programme en fonction des valeurs des variables à l'aide de structures de contrôle :

Un exemple

Programme de calcul de racine d'un polynôme de degré 2

Conversion de type

char c = 'A'; // 'A' = 65
int a = 3;
int b = 2;
unsigned int u;
float x, y;

x = a / b;
y = (float) a / b;
// x = 1, y = 1.5

a = (int) 3.9;
b = -1;
u = (unsigned short) b;
b = a + c;
// a = 3, u = 65535, b = 68

Dans certains cas, il est possible de convertir une variable d'un type en un autre (cast) :

Tableaux

/* déclarations */
int tab[10];
// le programme réserve 40 octets
long int tab2[10];
// le programme réserve 80 octets
char s[20];
// le programme réserve 20 octets

/* utilisation */
tab[0] = 0;
tab[1] = 1;
for (int i = 2; i < 10; i++) {
  tab[i] = tab[i-1] + tab[i-2];
}

// initialisation à la création
int t[4] = {1, 2, 3, 4};

Tableaux

Suite à l'instruction
int tab[4];


tableaux

Pointeurs

int a, b, *p;

a = 10;
p = &a;

// *p vaut 10

b = *p;
a++;

// a = 11, *p = 11, b = 10

Arithmétique pointeur

int *p1;      // supposons p1 = 2100
int *p2;      // supposons p2 = 3300
sizeof(int);  // 4
p1 + 5;       // 2120
p1 - 3;       // 2088
p1++;         // p1 = 2104
p1--;         // p1 = 2100
p2 - p1;      // 300

void *p3 = p1;
char *p4 = p1;
p3 + 3;       // 2103
p4 - 5;       // 2095

On peut faire des opérations arithmétiques sur les pointeurs :

Les autres opérations ne sont pas valides


Attention : Pour toutes ces opérations, l'unité correspond à la taille en mémoire du type pointé

En mémoire

float a = 3.14;        (float) a : 1234
float *p;              (float*) p : 1842
p = &a;                (int*) tab : 834
int tab[5];            (int*) t : 2936
int *t = tab;
tab[2] = 10;
*(t+3) = 20;
t = p;
*p = 2.72;

pointeurspointeurspointeurspointeurspointeurspointeurspointeurspointeurspointeurs

Valeur ou référence

int a, b;
a = 1;
b = a;

a = 2; // a = 2, b = 1

int u[3] = {0, 10, 20};
int *v;
v = u;
u[1]++; // u[1] = 11, v[1] = 11
v++; // u[1] = 11, v[1] = 20

char *n, *m;
n = "abc";
m = n;
n = "def"; // n = "def", m = "abc"

Tableaux et pointeurs

Les opérations sur les tableaux reviennent à manipuler des pointeurs :


Chaînes de caractères

int longueur (char *s) {
  int i = 0;
  while (s[i] != '\0') {
    i++;
}
  return i;
}

int main() {
  char *s = "Youpi";
  int l = longueur(s); // l = 5
  char nom[4];
  nom[0] = 'B';
  nom[1] = 'o';
  nom[2] = 'b';
  nom[3] = '\0';
  l = longueur(nom); // l = 3
}

Structures

// définition du type
struct Voiture {
  int nb_portes;
  char *marque;
};

// initialisation
struct Voiture v1;
v1.marque = "Peugeot";
v1.nb_portes = 5;

struct Voiture v2 = {
  .nb_portes = 3,
  .marque = "Volvo",
};

struct Voiture v3 = {2, "Fiat"};