0Pricing
C Academy · Lekcja

Pisanie funkcji podobnych do printf

Argumenty zmiennej liczby

Pisanie funkcji podobnych do printf to bezpłatna lekcja C Academy na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej C Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs C Academy zawiera 4 lekcji w sumie.

Funkcje przyjmujące napis formatujący

Najbardziej zaawansowanym zastosowaniem argumentów wariadycznych jest budowanie funkcji w stylu printf, które interpretują napis formatujący.

Format informuje funkcję, ile argumentów następuje dalej i jaki jest typ każdego z nich.

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

void myprint(const char *fmt, ...) {
    printf("format was: %s\n", fmt);
}

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

Przechodzenie po napisie formatującym

Analizuj format znak po znaku. Gdy napotkasz %, następny znak jest specyfikatorem konwersji określającym typ argumentu, który należy odczytać.

Wszystkie pozostałe znaki są drukowane dosłownie.

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

void scan(const char *fmt) {
    for (const char *p = fmt; *p; p++) {
        if (*p == '%') printf("[spec:%c]", *(p+1));
        else putchar(*p);
    }
    putchar('\n');
}

int main(void) {
    scan("x=%d y=%s");
    return 0;
}

Odczytywanie pasującego typu

Gdy specyfikatorem jest %d, odczytaj wartość typu int. W przypadku %s odczytaj char *. Format musi dokładnie odpowiadać rzeczywistym argumentom.

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

void mini(const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    for (const char *p = fmt; *p; p++) {
        if (*p == '%' && *(p+1) == 'd') {
            printf("%d", va_arg(args, int));
            p++;
        } else putchar(*p);
    }
    va_end(args);
}

int main(void) {
    mini("count=%d\n", 42);
    return 0;
}

Obsługa wielu specyfikatorów

Rozszerz skaner tak, aby obsługiwał kilka konwersji. Dodaj przypadki dla %d, %s i %c, za każdym razem odczytując właściwy typ.

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

void fmt_print(const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    for (const char *p = fmt; *p; p++) {
        if (*p != '%') { putchar(*p); continue; }
        p++;
        if (*p == 'd') printf("%d", va_arg(args, int));
        else if (*p == 's') printf("%s", va_arg(args, char *));
        else if (*p == 'c') putchar(va_arg(args, int));
    }
    va_end(args);
}

int main(void) {
    fmt_print("%s is %d\n", "age", 30);
    return 0;
}

Escapowanie znaku procentu

Rzeczywiste funkcje formatujące traktują %% jako dosłowny znak procentu. Dodaj odpowiedni przypadek, aby użytkownicy mogli wypisywać rzeczywisty znak %.

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

void fp(const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    for (const char *p = fmt; *p; p++) {
        if (*p == '%') {
            p++;
            if (*p == '%') putchar('%');
            else if (*p == 'd') printf("%d", va_arg(args, int));
        } else putchar(*p);
    }
    va_end(args);
}

int main(void) {
    fp("%d%% done\n", 75);
    return 0;
}

Przekazywanie do vprintf

Często nie ma potrzeby ponownego implementowania formatowania. Biblioteka standardowa udostępnia vprintf, które bezpośrednio przyjmuje va_list.

Funkcja opakowująca przekazuje tylko otrzymane argumenty dalej.

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

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

int main(void) {
    logline("value = %d, name = %s\n", 7, "box");
    return 0;
}

Dodawanie prefiksu

Podejście z vprintf ułatwia dodawanie działań wokół standardowego formatowania, takich jak wypisywanie etykiety przed każdym komunikatem.

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

void info(const char *fmt, ...) {
    printf("[INFO] ");
    va_list args;
    va_start(args, fmt);
    vprintf(fmt, args);
    va_end(args);
}

int main(void) {
    info("server started on port %d\n", 8080);
    return 0;
}

Zapisywanie do bufora za pomocą vsnprintf

Aby sformatować tekst do napisu zamiast go wypisywać, użyj vsnprintf(buf, size, fmt, args).

Rozmiar wyniku jest ograniczony przez size, co zapobiega przepełnieniu, a na końcu zawsze umieszczany jest znak null.

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

void format_into(char *buf, size_t n, const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    vsnprintf(buf, n, fmt, args);
    va_end(args);
}

int main(void) {
    char out[64];
    format_into(out, sizeof(out), "id-%d", 123);
    printf("%s\n", out);
    return 0;
}

Zwracanie długości

vsnprintf zwraca liczbę znaków, które zostałyby zapisane. Można użyć tej wartości do wykrywania obcięcia tekstu albo określenia rozmiaru bufora.

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

int build(char *buf, size_t n, const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    int len = vsnprintf(buf, n, fmt, args);
    va_end(args);
    return len;
}

int main(void) {
    char b[10];
    int needed = build(b, sizeof(b), "%d-%d", 11, 22);
    printf("%s (needed %d)\n", b, needed);
    return 0;
}

Wybór podejścia

Istnieją dwie strategie: samodzielnie analizować format, aby uzyskać pełną kontrolę, albo przekazać go do vprintf/vsnprintf, aby zapewnić poprawność przy mniejszej ilości kodu.

W większości rzeczywistych programów przekazywanie dalej jest bezpieczniejszym i prostszym rozwiązaniem.

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

void say(const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    vfprintf(stdout, fmt, args);
    va_end(args);
}

int main(void) {
    say("%s = %.1f\n", "pi", 3.14);
    return 0;
}

Niezgodność oznacza niezdefiniowane działanie

Jeśli napis formatujący wskazuje typ, który nie odpowiada rzeczywistemu argumentowi, działanie programu jest niezdefiniowane.

printf("%d", "text") może spowodować awarię. Zawsze należy zachowywać zgodność formatu i argumentów.

#include <stdio.h>

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

Szybkie sprawdzenie

Sprawdź swoją wiedzę na temat funkcji opakowujących w stylu printf.

Podsumowanie

Nauczyłeś się budować funkcje podobne do printf:

  • Przyjmuj napis formatujący jako nazwany parametr, a następnie ....
  • Samodzielnie analizuj specyfikatory % i odczytuj odpowiadające im typy albo
  • przekazuj va_list do vprintf / vsnprintf, aby uzyskać bezpieczne i poprawne formatowanie.
  • Zachowuj zgodność formatu i typów argumentów, aby uniknąć niezdefiniowanego działania.

Często zadawane pytania

Czy lekcja „Pisanie funkcji podobnych do printf” jest bezpłatna?

Tak — pełny tekst „Pisanie funkcji podobnych do printf” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu C Academy, przejdź na CoddyKit PRO. Kurs C Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Pisanie funkcji podobnych do printf”?

Argumenty zmiennej liczby Ćwiczysz C Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć C Academy?

Nie wymagamy żadnego doświadczenia. C Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.

Ile czasu zajmuje lekcja „Pisanie funkcji podobnych do printf”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji C Academy?

Tak. Każda lekcja C Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Makra stdarg
  2. Pisanie funkcji podobnych do printf
  3. Problemy z bezpieczeństwem typów
  4. Praktyczne przykłady
← Powrót do C Academy