Problem med typsäkerhet
Risker med variadiska funktioner.
Problem med typsäkerhet är en gratis lektion i C Academy på CoddyKit. Detta är lektion 3 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för C Academy, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i C Academy innehåller totalt 4 lektioner.
Variadiska argument kringgår typkontroll
Vanliga C-funktioner kontrollerar att argumenten stämmer överens med parametertyperna. Variadiska argument hoppar helt över denna kontroll.
Kompilatorn kan inte verifiera vad Ni skickar efter ..., vilket gör misstag enkla att begå och farliga.
#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;
}Fel typ i va_arg
Om Ni läser ett argument med fel typ blir resultatet odefinierat beteende.
Om Ni skickar en int men läser den som en double tolkas orelaterade byte om, vilket kan ge skräpdata eller en krasch.
#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;
}Inkompatibla formatsträngar
I funktioner av printf-typ är en felaktig specifikation ett klassiskt fel. printf("%d", 3.14) läser en double som om den vore en int.
Moderna kompilatorer varnar för inkompatibiliteter i bokstavliga formatsträngar, men kan inte upptäcka formatsträngar som byggs vid körning.
#include <stdio.h>
int main(void) {
double pi = 3.14;
printf("%f\n", pi);
return 0;
}Överraskningar vid heltalskonvertering
Små typer konverteras till int i den variadiska delen. Även om Ni skickar en char måste Ni alltså läsa tillbaka den som en int.
Att läsa den som char med va_arg(a, char) är odefinierat.
#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 blir double
Ett float-argument konverteras alltid till double i variadiska argument. Ni måste läsa det som double, aldrig som float.
Detta är ett av de vanligaste felen med variadiska argument.
#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;
}Läsa för många argument
Om Er kod anropar va_arg fler gånger än antalet argument som anroparen skickade, läser Ni skräpdata från stacken.
Därför är en korrekt räknare eller sentinel nödvändig.
#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;
}NULL-pekarsentinel kräver försiktighet
Att använda NULL som sentinel är riskabelt: på vissa plattformar är NULL en 0 av typen int, vilket skiljer sig i storlek från en pekare.
Gör en uttrycklig typkonvertering, till exempel (char *)NULL, för att säkerställa att rätt typ läggs på stacken.
#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;
}Kompilatorattribut för format
GCC och Clang erbjuder __attribute__((format(printf, 1, 2))) för att tala om för kompilatorn att Er funktion använder formatering av printf-typ.
Då kontrolleras formatsträngarna vid kompilering, vilket upptäcker många fel.
#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;
}Föredra säkrare alternativ
Eftersom variadiska argument lätt leder till fel bör Ni använda typsäkra lösningar när det är möjligt: skicka en explicit array, en struct eller fasta parametrar.
Använd variadiska argument endast när Ni verkligen behöver funktionalitet av printf-typ.
#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;
}Dokumentera kontraktet
Eftersom kompilatorn inte kan upprätthålla typerna för variadiska argument är tydlig dokumentation Ert säkerhetsnät.
Ange exakt vilka typer och hur många argument anroparen måste skicka, samt hur listan avslutas.
#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;
}Testa noggrant
Eftersom fel kan förbli oupptäckta bör Ni testa variadiska funktioner med många olika antal och typer av argument.
Gränsfall, till exempel noll argument eller det maximala antalet, avslöjar ofta misstag.
#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;
}Snabbtest
Testa Er förståelse av riskerna med variadiska typer.
Sammanfattning
Ni har lärt Er om riskerna med variadiska funktioner:
- Kompilatorn typkontrollerar inte variadiska argument.
- Fel typer, fel antal och felaktiga sentinelvärden orsakar odefinierat beteende.
- Kom ihåg konverteringarna: små heltal till
int,floattilldouble. - Använd kompilatorns formatattribut, dokumentera kontraktet och föredra typsäkra alternativ när det är möjligt.
Lär dig C med en AI-lärare – gratis
Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.
- Kurser
- 39
- Lektioner
- 144
Vanliga frågor
Är lektionen ”Problem med typsäkerhet” gratis?
Ja – hela texten till ”Problem med typsäkerhet” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i C Academy, kan Ni uppgradera till CoddyKit PRO. Kursen i C Academy innehåller totalt 4 lektioner.
Vad lär jag mig i ”Problem med typsäkerhet”?
Risker med variadiska funktioner. Ni övar på C Academy med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.
Behöver jag någon erfarenhet för att börja lära mig C Academy?
Du behöver inga förkunskaper. Utbildningen i C Academy på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 3 av 4.
Hur lång tid tar lektionen ”Problem med typsäkerhet”?
De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.
Kan jag skriva och köra kod i den här C Academy-lektionen?
Ja. Varje C Academy-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.
Alla lektioner i den här kursen
- Makrona i stdarg
- Skriv printf-liknande funktioner
- Problem med typsäkerhet
- Praktiska exempel