🐍 Il n'y a pas que Python dans la vie !
Langage C · Débogage · Assertions · Tests unitaires en Python
Jusqu'à présent, nous avons surtout utilisé le langage de programmation Python. Il existe beaucoup d'autres langages : Java, C++, Ruby, PHP, JavaScript… Tous partagent plus de points communs que de différences. Nous allons nous intéresser au langage C, qui tient une place à part dans l'histoire de l'informatique.
Le langage C a été créé par Dennis Ritchie (1941–2011) et Ken Thompson (1943– ) en 1972 (les mêmes qu'UNIX). C est encore dans le top 10 des langages les plus utilisés — le noyau de Linux est écrit en C.
Voici le premier programme classique en C :
#include <stdio.h>
int main(void) {
printf("Hello World\n");
return 0;
}
| Ligne | Explication |
|---|---|
#include <stdio.h> | Directive de préprocesseur. Charge les prototypes des fonctions d'entrée/sortie (dont printf). Obligatoire. |
int main(void) { } | Fonction principale obligatoire. int = type de retour entier ; void = pas de paramètre. Les accolades { } délimitent le bloc (à la place de l'indentation Python). |
printf("…\n"); | Affiche un message. \n = retour à la ligne. Chaque instruction se termine par un point-virgule obligatoire. |
return 0; | Renvoie 0 = exécution réussie. Toute autre valeur signale une erreur. |
Si l'on oublie un point-virgule, le compilateur produit une erreur explicite :
main.c:4:24: error: expected ';' after expression
printf("Hello World\n")
^
;
1 error generated.
print("Hello World")Pour utiliser des variables, C exige de les déclarer avec leur type avant utilisation :
i = 15
print(f"La valeur de i est {i}")
int main(void) {
int i;
i = 15;
printf("La valeur de i est %d\n", i);
return 0;
}
Les boucles :
i = 0
while i < 10:
print(f"i vaut {i}")
i = i + 1
int i = 0;
while (i < 10) {
printf("i vaut %d\n", i);
i = i + 1;
}
Les conditions :
i = 19
if i < 18:
print("Vous êtes mineur")
else:
print("Vous êtes majeur")
int i = 19;
if (i < 18) {
printf("Vous êtes mineur");
}
else {
printf("Vous êtes majeur");
}
Les fonctions : chaque paramètre et la valeur de retour doivent être typés :
def somme(x, y):
s = x + y
return s
a, b = 5, 4
res = somme(a, b)
print(f"{a} + {b} = {res}")
int somme(int x, int y) {
int s;
s = x + y;
return s;
}
int main(void) {
int a=5, b=4, res;
res = somme(a, b);
printf("%d + %d = %d\n",a,b,res);
return 0;
}
En C, une fonction doit être définie avant d'être appelée. Si ce n'est pas le cas, le compilateur ne reconnaît pas l'appel. La solution : déclarer un prototype en début de fichier.
#include <stdio.h>
int somme(int x, int y); /* prototype — annonce la fonction */
int main(void) {
int a=5, b=4, res;
res = somme(a, b);
printf("La somme de %d et %d vaut %d\n", a, b, res);
return 0;
}
int somme(int x, int y) { /* définition — peut être après main() */
int s = x + y;
return s;
}
#include <stdio.h> : il charge le prototype de printf depuis le fichier système stdio.h.Les contraintes C (typage des variables, paramètres, valeur de retour) obligent à plus de rigueur et permettent de détecter des erreurs silencieuses. Voici un exemple :
def somme(x, y):
s = x + y
return s
| Appel | Résultat | Problème ? |
|---|---|---|
somme(2, 5) | 7 | ✅ Correct |
somme("2", "5") | "25" (concaténation !) | ⚠️ Aucun avertissement Python — bogue silencieux ! |
En C, le compilateur avertit immédiatement :
warning: incompatible pointer to integer conversion
passing 'char *' to parameter of type 'int'
res = somme("2", "5");
^~~
Pour pallier le laxisme du typage Python, on peut utiliser assert combiné à isinstance() pour vérifier le type des paramètres :
def somme(x, y):
assert isinstance(x, int), "x doit être un entier"
assert isinstance(y, int), "y doit être un entier"
s = x + y
return s
| Appel | Résultat |
|---|---|
somme(2, 5) | Renvoie 7 — aucun problème ✅ |
somme("2", "5") | AssertionError levée — le programme s'arrête et signale le problème ✅ |
isinstance(x, int) renvoie True si x est entier, False sinon. Si assert reçoit False, une exception est levée et l'exécution s'arrête immédiatement.Les assertions permettent aussi de mettre en place des tests unitaires : vérifier qu'une fonction renvoie bien la valeur attendue pour différents cas d'entrée.
def somme(x, y):
s = x + y
return s
# Tests unitaires
assert somme( 5, 2) == 7, "Erreur : somme(5, 2)"
assert somme( 0, 0) == 0, "Erreur : somme(0, 0)"
assert somme( 5, -2) == 3, "Erreur : somme(5, -2)"
assert somme(-5, 2) == -3, "Erreur : somme(-5, 2)"
assert somme(-5, -2) == -7, "Erreur : somme(-5, -2)"
| Test | Valeur attendue | Ce que l'on vérifie |
|---|---|---|
somme(5, 2) | 7 | Cas standard positif |
somme(0, 0) | 0 | Cas neutre (zéro) |
somme(5, -2) | 3 | Un entier négatif |
somme(-5, 2) | -3 | Un entier négatif |
somme(-5, -2) | -7 | Deux entiers négatifs |
- Impossible de tout tester Il faut choisir judicieusement les cas à tester, notamment les cas limites (zéro, valeurs négatives, chaînes vides…).
- Pas une preuve formelle Les tests unitaires ne prouvent pas qu'une fonction est correcte. Pour une preuve formelle, il faut des outils mathématiques dédiés.
- Étape fondamentale La mise en place de tests unitaires est incontournable dans tout développement logiciel sérieux. Ne jamais la négliger.
🐍 Python vs C — Exercices progressifs
NSI · Classe de Première · De débutant à intermédiaire
Pour écrire du C sans installer de compilateur, utilise :
Teste le programme C ci-dessous et compare-le à son équivalent Python :
print('Bonjour NSI !')
#include <stdio.h>
int main(void) {
printf("Bonjour NSI !\n");
return 0;
}
Questions :
a) Quelles différences remarques-tu ?
b) À quoi sert le #include <stdio.h> en C ?
c) Pourquoi y a-t-il un point-virgule à la fin de chaque instruction en C ?
Observe le programme Python, puis écris son équivalent en C dans la zone prévue.
print("Je m'appelle Ada Lovelace")
print("J'ai 15 ans")
print("J'apprends la NSI")
printf("texte\n"); — le \n représente un retour à la ligne.Programme C équivalent :
En Python, on crée une variable sans préciser son type. En C, on doit déclarer le type avant utilisation.
age = 17
taille = 1.65
print(f"age : {age} ans")
print(f"Taille : {taille} m")
int age = 17;
float taille = 1.65;
printf("age : %d ans\n", age);
printf("Taille : %.2f m\n", taille);
| Type C | Équivalent Python | Exemple | Formatage printf |
|---|---|---|---|
int | int (entier) | int age = 17; | %d |
float | float (décimal) | float pi = 3.14; | %f |
char | str (1 caractère) | char l = 'A'; | %c |
Déclare en C les variables : un entier score valant 42, un flottant moyenne valant 13.5, puis affiche-les.
age = 17
if age >= 18:
print("Majeur")
else:
print("Mineur")
int age = 17;
if (age >= 18) {
printf("Majeur\n");
}
else {
printf("Mineur\n");
}
{ } remplacent l'indentation Python.Écris en C une fonction estMajeur qui prend un age et affiche "Majeur" ou "Mineur" :
i = 1
while i <= 5:
print(i)
i = i + 1
int i = 1;
while (i <= 5) {
printf("%d\n", i);
i = i + 1;
}
a) Réécris ce programme pour afficher les nombres de 1 à 10 en C :
b) Affiche uniquement les nombres pairs de 2 à 20 :
if (i % 2 == 0)for i in range(5):
print(i)
for (int i = 0; i < 5; i++) {
printf("%d\n", i);
}
| Partie | Rôle |
|---|---|
int i = 0 | Initialisation : on crée la variable compteur |
i < 5 | Condition : on continue tant que i < 5 |
i++ | Incrémentation : équivaut à i = i + 1 |
Écris en C un programme qui calcule et affiche la somme des entiers de 1 à 100 avec une boucle for :
En C, on doit préciser le type de retour et le type de chaque paramètre :
def double(n):
return n * 2
print(double(5))
int double_val(int n) {
return n * 2;
}
int main(void) {
printf("%d\n", double_val(5));
return 0;
}
Traduis ces fonctions Python en C :
a) Fonction carre qui renvoie le carré d'un entier :
b) Fonction estPair qui renvoie 1 si pair, 0 sinon :
Écris en C une fonction estMajeur qui prend un paramètre age (entier) et renvoie 1 si age ≥ 18 et 0 sinon. Teste-la dans main() avec les valeurs 15 et 20.
Ce programme C est mal organisé : la fonction est définie après son appel. Corrige-le en ajoutant le prototype :
#include <stdio.h>
int main(void) {
printf("Resultat : %d\n", triple(7)); /* erreur ! */
return 0;
}
int triple(int n) {
return n * 3;
}
int triple(int n); — ne pas oublier le point-virgule !Programme corrigé :
Tu gères un magasin. Tout client qui achète au moins 5 fois le même article bénéficie d'une remise de 5 %.
def facture(prix_unitaire, quantite):
total = prix_unitaire * quantite
if quantite >= 5:
total = total * 0.95 # remise de 5 %
return total
Traduis cette fonction en C (type de retour : float) :
Teste avec : prix=12.50, quantité=3, puis quantité=7 :
assert vérifie qu'une condition est vraie. Si elle est fausse, Python lève une AssertionError.
assert 2 + 2 == 4 # OK assert 2 + 2 == 5 # AssertionError ! assert 2 + 2 == 5, "Erreur" # avec message
Sans exécuter le code, indique lesquelles de ces assertions vont lever une erreur :
assert 10 > 5☐ Passe☐ Erreurassert len('NSI') == 4☐ Passe☐ Erreurassert 3 * 3 == 9☐ Passe☐ Erreurassert 'a' in 'python'☐ Passe☐ Erreurassert 2 ** 8 == 256☐ Passe☐ Erreurdef double(n):
return n * 2
Écris 5 tests unitaires pertinents (cas standard, zéro, négatif, grand nombre…) :
def estMajeur(age):
if age >= 18:
return 1
else:
return 0
Écris des tests unitaires pour les cas suivants :
| Cas à tester | Valeur attendue |
|---|---|
| Âge clairement mineur (ex: 12) | |
| Âge clairement majeur (ex: 25) | |
| Cas limite : exactement 18 ans | |
| Cas limite bas : 17 ans |
from math import sqrt
def distance(point1, point2):
return sqrt((point1[0]-point2[0])**2 + (point1[1]-point2[1])**2)
Aide-toi des propriétés mathématiques pour choisir des cas dont tu connais le résultat :
| Appel | Résultat attendu | Indice |
|---|---|---|
distance((0,0), (0,0)) | Même point | |
distance((0,0), (1,0)) | Segment horizontal de longueur 1 | |
distance((0,0), (3,4)) | Triangle 3-4-5 de Pythagore | |
distance((0,0), (0,5)) | Segment vertical |
==. Préfère : assert abs(distance(...) - valeur) < 0.001Assertions :
Voici une mini-calculatrice Python complète avec ses tests :
def addition(a, b): return a + b
def soustraction(a, b): return a - b
def multiplication(a, b): return a * b
def division(a, b):
if b == 0:
print("Erreur : division par zéro !")
return 0
return a / b
assert addition(3, 4) == 7
assert soustraction(10, 3) == 7
assert multiplication(3, 4) == 12
assert division(10, 2) == 5.0
assert division(5, 0) == 0
print("Tous les tests sont passés !")
Travail à réaliser :
a) Traduis les 4 fonctions en C avec leurs prototypes.
b) Gère le cas division par zéro : affiche un message et renvoie 0.
c) Écris la fonction main() pour tester les 4 fonctions.
d) Que remplace le assert Python dans le programme C ? Explique.
