0Pricing
C Academy · Leçon

Problèmes de sûreté des types

Risques des fonctions variadiques

Problèmes de sûreté des types est une leçon C Academy gratuite sur CoddyKit. Ceci est la leçon 3 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage C Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours C Academy comprend 4 leçons au total.

Les fonctions variadiques contournent la vérification des types

Les fonctions C normales vérifient que les arguments correspondent aux types des paramètres. Les arguments variadiques ignorent entièrement cette vérification.

Le compilateur ne peut pas vérifier ce que vous transmettez après ..., ce qui rend les erreurs faciles à commettre et dangereuses.

#include <stdio.h>
#include <stdarg.h>

int add(int n, ...) {
    va_list a; va_start(a, n);
    int s = 0;
    for (int i = 0; i < n; i++) s += va_arg(a, int);
    va_end(a);
    return s;
}

int main(void) {
    printf("%d\n", add(2, 3, 4));
    return 0;
}

Type incorrect dans va_arg

Si vous lisez un argument avec le mauvais type, le résultat est un comportement indéfini.

Transmettre un int et le lire comme un double réinterprète des octets sans rapport et produit des données incohérentes ou un plantage.

#include <stdio.h>
#include <stdarg.h>

int read_int(int n, ...) {
    va_list a; va_start(a, n);
    int v = va_arg(a, int);
    va_end(a);
    return v;
}

int main(void) {
    printf("%d\n", read_int(1, 100));
    return 0;
}

Incompatibilités dans les chaînes de format

Dans les fonctions de style printf, un mauvais spécificateur est un bogue classique. printf("%d", 3.14) lit un double comme s’il s’agissait d’un entier.

Les compilateurs modernes signalent les incompatibilités littérales, mais ne peuvent pas détecter les chaînes de format construites à l’exécution.

#include <stdio.h>

int main(void) {
    double pi = 3.14;
    printf("%f\n", pi);
    return 0;
}

Surprises liées à la promotion des entiers

Les petits types sont promus en int dans la partie variadique. Ainsi, même si vous transmettez un char, vous devez le relire comme un int.

Le lire comme un char avec va_arg(a, char) entraîne un comportement indéfini.

#include <stdio.h>
#include <stdarg.h>

void show(int n, ...) {
    va_list a; va_start(a, n);
    int c = va_arg(a, int);
    printf("char is %c\n", c);
    va_end(a);
}

int main(void) {
    show(1, 'A');
    return 0;
}

Float devient double

Un argument float est toujours promu en double dans les fonctions variadiques. Vous devez le lire comme un double, jamais comme un float.

C’est l’une des erreurs variadiques les plus courantes.

#include <stdio.h>
#include <stdarg.h>

void show(int n, ...) {
    va_list a; va_start(a, n);
    double d = va_arg(a, double);
    printf("%.2f\n", d);
    va_end(a);
}

int main(void) {
    float f = 1.5f;
    show(1, f);
    return 0;
}

Lire trop d’arguments

Si votre code appelle va_arg plus de fois que l’appelant n’a fourni d’arguments, vous lisez des données incohérentes sur la pile.

C’est pourquoi un comptage précis ou une sentinelle est indispensable.

#include <stdio.h>
#include <stdarg.h>

int sum(int n, ...) {
    va_list a; va_start(a, n);
    int s = 0;
    for (int i = 0; i < n; i++) s += va_arg(a, int);
    va_end(a);
    return s;
}

int main(void) {
    printf("%d\n", sum(3, 1, 2, 3));
    return 0;
}

Les sentinelles NULL nécessitent de la prudence

Utiliser NULL comme sentinelle est risqué : sur certaines plateformes, NULL est un 0 de type int, dont la taille diffère de celle d’un pointeur.

Effectuez une conversion explicite, comme (char *)NULL, afin de garantir que le type correct est transmis.

#include <stdio.h>
#include <stdarg.h>

void print_all(const char *first, ...) {
    va_list a; va_start(a, first);
    const char *s = first;
    while (s) { printf("%s\n", s); s = va_arg(a, const char *); }
    va_end(a);
}

int main(void) {
    print_all("a", "b", (char *)NULL);
    return 0;
}

Attributs de format du compilateur

GCC et Clang proposent __attribute__((format(printf, 1, 2))) pour indiquer au compilateur que votre fonction utilise un formatage de style printf.

Le compilateur vérifie alors vos chaînes de format à la compilation et détecte de nombreux bogues.

#include <stdio.h>
#include <stdarg.h>

void logf(const char *fmt, ...)
    __attribute__((format(printf, 1, 2)));

void logf(const char *fmt, ...) {
    va_list a; va_start(a, fmt);
    vprintf(fmt, a);
    va_end(a);
}

int main(void) {
    logf("%d items\n", 3);
    return 0;
}

Préférer des solutions plus sûres

Comme les fonctions variadiques sont sujettes aux erreurs, préférez lorsque c’est possible des conceptions sûres du point de vue des types : transmettez un tableau explicite, une structure ou des paramètres fixes.

Réservez les fonctions variadiques aux véritables besoins de style printf.

#include <stdio.h>

int sum_array(const int *a, int n) {
    int s = 0;
    for (int i = 0; i < n; i++) s += a[i];
    return s;
}

int main(void) {
    int nums[] = {1, 2, 3, 4};
    printf("%d\n", sum_array(nums, 4));
    return 0;
}

Documenter le contrat

Comme le compilateur ne peut pas imposer les types des arguments variadiques, une documentation claire constitue votre filet de sécurité.

Indiquez précisément quels types et combien d’arguments l’appelant doit fournir, ainsi que la manière dont la liste se termine.

#include <stdio.h>
#include <stdarg.h>

/* sum_ints(n, ...): pass exactly n int values */
int sum_ints(int n, ...) {
    va_list a; va_start(a, n);
    int s = 0;
    for (int i = 0; i < n; i++) s += va_arg(a, int);
    va_end(a);
    return s;
}

int main(void) {
    printf("%d\n", sum_ints(2, 5, 5));
    return 0;
}

Effectuer des tests approfondis

Comme les bogues peuvent rester silencieux, testez les fonctions variadiques avec de nombreux nombres et types d’arguments.

Les cas limites, comme zéro argument ou le nombre maximal d’arguments, révèlent souvent les erreurs.

#include <stdio.h>
#include <stdarg.h>

int sum(int n, ...) {
    va_list a; va_start(a, n);
    int s = 0;
    for (int i = 0; i < n; i++) s += va_arg(a, int);
    va_end(a);
    return s;
}

int main(void) {
    printf("%d\n", sum(0));
    printf("%d\n", sum(1, 9));
    return 0;
}

Vérification rapide

Vérifiez votre compréhension des risques liés aux types variadiques.

Récapitulatif

Vous avez appris les dangers des fonctions variadiques :

  • Le compilateur ne vérifie pas les types des arguments variadiques.
  • Les types incorrects, les nombres incorrects et les mauvaises sentinelles entraînent un comportement indéfini.
  • N’oubliez pas les promotions : les petits entiers deviennent des int et les float deviennent des double.
  • Utilisez les attributs de format du compilateur, documentez le contrat et préférez les solutions sûres du point de vue des types lorsque c’est possible.

Questions Fréquemment Posées

La leçon « Problèmes de sûreté des types » est-elle gratuite ?

Oui — le texte complet de « Problèmes de sûreté des types » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours C Academy, passe à CoddyKit PRO. Le cours C Academy comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Problèmes de sûreté des types » ?

Risques des fonctions variadiques Tu pratiques C Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer C Academy ?

Aucune expérience préalable n'est requise. C Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 3 sur 4.

Combien de temps prend la leçon « Problèmes de sûreté des types » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon C Academy ?

Oui. Chaque leçon C Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Macros stdarg
  2. Écrire des fonctions semblables à printf
  3. Problèmes de sûreté des types
  4. Exemples pratiques
← Retour à C Academy