Problemi di sicurezza dei tipi
Rischi delle funzioni variadiche
Problemi di sicurezza dei tipi è una lezione C Academy gratuita su CoddyKit. Questa è la lezione 3 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento C Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso C Academy include 4 lezioni in totale.
Le funzioni variadiche aggirano il controllo dei tipi
Le normali funzioni C verificano che gli argomenti corrispondano ai tipi dei parametri. Gli argomenti variadici saltano completamente questo controllo.
Il compilatore non può verificare ciò che viene passato dopo ..., rendendo gli errori facili da commettere e pericolosi.
#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;
}Tipo errato in va_arg
Se legge un argomento con il tipo sbagliato, il risultato è un comportamento indefinito.
Passare un int ma leggerlo come double reinterpreta byte non correlati e produce dati senza senso o un arresto anomalo.
#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;
}Mancata corrispondenza nella stringa di formato
Nelle funzioni nello stile di printf, uno specificatore errato è un bug classico. printf("%d", 3.14) legge un double come se fosse un int.
I compilatori moderni avvisano in caso di mancata corrispondenza nei formati letterali, ma non possono rilevare le stringhe di formato costruite a runtime.
#include <stdio.h>
int main(void) {
double pi = 3.14;
printf("%f\n", pi);
return 0;
}Sorpresa delle promozioni degli interi
Nella parte variadica, i tipi piccoli vengono promossi a int. Quindi, anche se passa un char, deve leggerlo nuovamente come int.
Leggerlo come char con va_arg(a, char) è indefinito.
#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 diventa double
Un argomento float viene sempre promosso a double nelle funzioni variadiche. Deve leggerlo come double, mai come float.
Questo è uno degli errori variadici più comuni.
#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;
}Leggere troppi argomenti
Se il codice chiama va_arg più volte degli argomenti forniti dal chiamante, legge dati casuali dallo stack.
Per questo è essenziale avere un conteggio accurato o una sentinella.
#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;
}Le sentinelle con puntatore NULL richiedono attenzione
Usare NULL come sentinella è rischioso: su alcune piattaforme NULL è 0 come int, che ha una dimensione diversa da quella di un puntatore.
Esegua un cast esplicito, come (char *)NULL, per assicurarsi che venga passato il tipo corretto.
#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;
}Attributi di formato del compilatore
GCC e Clang offrono __attribute__((format(printf, 1, 2))) per indicare al compilatore che la funzione usa una formattazione nello stile di printf.
Il compilatore controllerà quindi le stringhe di formato in fase di compilazione, rilevando molti bug.
#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;
}Preferire alternative più sicure
Poiché le funzioni variadiche sono soggette a errori, quando possibile preferisca progetti con sicurezza dei tipi: passi un array esplicito, una struct o parametri fissi.
Riservi le funzioni variadiche alle reali esigenze nello stile di 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;
}Documentare il contratto
Poiché il compilatore non può imporre i tipi degli argomenti variadici, una documentazione chiara è la Sua rete di sicurezza.
Indichi esattamente quali tipi e quanti argomenti deve fornire il chiamante e come termina la lista.
#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;
}Eseguire test approfonditi
Poiché i bug possono rimanere nascosti senza segnali evidenti, testi le funzioni variadiche con molti numeri e tipi di argomenti.
I casi limite, come zero argomenti o il numero massimo, spesso rivelano gli errori.
#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;
}Verifica rapida
Verifichi la Sua comprensione dei rischi relativi ai tipi nelle funzioni variadiche.
Riepilogo
Ha imparato i rischi delle funzioni variadiche:
- Il compilatore non verifica i tipi degli argomenti variadici.
- Tipi errati, conteggi errati e sentinelle non valide causano un comportamento indefinito.
- Ricordi le promozioni: gli interi piccoli diventano
intefloatdiventadouble. - Usi gli attributi di formato del compilatore, documenti il contratto e, quando possibile, preferisca alternative con sicurezza dei tipi.
Domande Frequenti
La lezione «Problemi di sicurezza dei tipi» è gratuita?
Sì — il testo completo di «Problemi di sicurezza dei tipi» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso C Academy, passa a CoddyKit PRO. Il corso C Academy include 4 lezioni in totale.
Cosa imparerò in «Problemi di sicurezza dei tipi»?
Rischi delle funzioni variadiche Eserciti C Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare C Academy?
Non è richiesta alcuna esperienza precedente. C Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 3 di 4.
Quanto tempo richiede la lezione «Problemi di sicurezza dei tipi»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione C Academy?
Sì. Ogni lezione C Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Macro stdarg
- Scrivere funzioni simili a printf
- Problemi di sicurezza dei tipi
- Esempi pratici