Utfordringer med typesikkerhet
Risiko ved variadiske funksjoner
Utfordringer med typesikkerhet er en gratis leksjon i C Academy på CoddyKit. Dette er leksjon 3 av 4. Du kan lese hele leksjonen gratis nedenfor – og deretter øve praktisk i nettleseren med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i C Academy, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i C Academy inneholder totalt 4 leksjoner.
Variadiske argumenter omgår typesjekking
Vanlige C-funksjoner kontrollerer at argumentene stemmer med parametertypene. Variadiske argumenter hopper over denne kontrollen fullstendig.
Kompilatoren kan ikke kontrollere hva De sender inn etter ..., noe som gjør feil både enkle å gjøre og farlige.
#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;
}Feil type i va_arg
Hvis De leser et argument med feil type, blir resultatet udefinert oppførsel.
Hvis De sender inn en int, men leser den som en double, tolkes irrelevante byte på nytt, og resultatet blir søppeldata eller en krasj.
#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;
}Uoverensstemmelser i formatstrenger
I printf-lignende funksjoner er feil spesifikator en klassisk feil. printf("%d", 3.14) leser en double som om den var en int.
Moderne kompilatorer advarer om uoverensstemmelser i bokstavelige formatstrenger, men kan ikke oppdage formatstrenger som bygges under kjøring.
#include <stdio.h>
int main(void) {
double pi = 3.14;
printf("%f\n", pi);
return 0;
}Overraskelser ved heltallskonvertering
Små typer konverteres til int i den variadiske delen. Selv om De sender inn en char, må De derfor lese den tilbake som en int.
Å lese den som char med va_arg(a, char) gir udefinert oppførsel.
#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 til double
Et float-argument konverteres alltid til double i variadiske argumenter. De må lese det som double, aldri som float.
Dette er en av de vanligste feilene med variadiske argumenter.
#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;
}Lese for mange argumenter
Hvis koden Deres kaller va_arg flere ganger enn det antallet argumenter som ble sendt inn, leser De søppeldata fra stakken.
Derfor er en nøyaktig telling eller en sentinel helt nødvendig.
#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-pekersentineler krever forsiktighet
Det er risikabelt å bruke NULL som sentinel: På enkelte plattformer er NULL en 0 av typen int, som har en annen størrelse enn en peker.
Konverter den eksplisitt, for eksempel med (char *)NULL, slik at riktig type legges på stakken.
#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;
}Formatattributter for kompilatoren
GCC og Clang tilbyr __attribute__((format(printf, 1, 2))) for å fortelle kompilatoren at funksjonen Deres bruker formatering i printf-stil.
Deretter kontrollerer den formatstrengene ved kompilering og oppdager mange feil.
#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;
}Foretrekke tryggere alternativer
Variadiske argumenter er utsatt for feil, så foretrekk typesikre utforminger når det er mulig: send inn en eksplisitt tabell, en struct eller faste parametere.
Bruk variadiske argumenter for reelle behov i printf-stil.
#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;
}Dokumentere kontrakten
Siden kompilatoren ikke kan håndheve typene for variadiske argumenter, er tydelig dokumentasjon sikkerhetsnettet Deres.
Oppgi nøyaktig hvilke typer og hvor mange argumenter den som kaller funksjonen må oppgi, og hvordan listen avsluttes.
#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;
}Teste grundig
Siden feil kan skjule seg ubemerket, bør De teste variadiske funksjoner med mange ulike antall argumenter og typer.
Grensetilfeller, for eksempel null argumenter eller maksimalt antall argumenter, avdekker ofte feil.
#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;
}Rask kontroll
Test forståelsen Deres av risikoene ved variadiske typer.
Oppsummering
De har lært om farene ved variadiske funksjoner:
- Kompilatoren typesjekker ikke variadiske argumenter.
- Feil typer, feil antall og ugyldige sentineler fører til udefinert oppførsel.
- Husk konverteringene: små heltall til
int,floattildouble. - Bruk kompilatorens formatattributter, dokumenter kontrakten og foretrekk typesikre alternativer når De kan.
Lær deg C med en AI-veileder – gratis
Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.
- Kurs
- 39
- Leksjoner
- 144
Ofte stilte spørsmål
Er leksjonen «Utfordringer med typesikkerhet» gratis?
Ja – hele teksten i «Utfordringer med typesikkerhet» er gratis å lese her på nettet. For å øve interaktivt med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt, og for å låse opp resten av C Academy-kurset, kan du oppgradere til CoddyKit PRO. Kurset i C Academy inneholder totalt 4 leksjoner.
Hva lærer jeg i «Utfordringer med typesikkerhet»?
Risiko ved variadiske funksjoner Du øver på C Academy med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.
Trenger jeg erfaring for å begynne med C Academy?
Ingen tidligere erfaring er nødvendig. C Academy på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 3 av 4.
Hvor lang tid tar leksjonen «Utfordringer med typesikkerhet»?
De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.
Kan jeg skrive og kjøre kode i denne C Academy-leksjonen?
Ja. Alle C Academy-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.
Alle leksjonene i dette kurset
- stdarg-makroene
- Skrive printf-lignende funksjoner
- Utfordringer med typesikkerhet
- Praktiske eksempler