Menu

Argumenty wiersza poleceń w C: argc, argv i bezpieczne parsowanie

Jak program w C odczytuje swój wiersz poleceń: budowa argc i argv, przechodzenie po argumentach, co zawiera argv[0], zamiana liczb przez strtol zamiast atoi i mały kalkulator, który łączy to wszystko.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Każdemu programowi uruchamianemu z terminala można przekazać argumenty: gcc -Wall hello.c -o hello przekazuje ich cztery. Twoje własne programy w C odczytują swoje przez dwa parametry funkcji main i to cały mechanizm: żadnego wywołania biblioteki, żadnej konfiguracji.

Sygnatura

Dotąd każdy przykład w tej dokumentacji używał int main(void). Druga standardowa forma przyjmuje wiersz poleceń:

int main(int argc, char *argv[]) {
    /* ... */
}
  • argc ("argument count", liczba argumentów) mówi, ile jest argumentów, łącznie z nazwą programu.
  • argv ("argument vector", wektor argumentów) to tablica stringów. char *argv[] oznacza "tablicę wskaźników na char", czyli tablicę stringów w stylu C.

char **argv to równoważny zapis, który zobaczysz równie często; parametry tablicowe degradują się do wskaźników, więc oba deklarują to samo.

Użyj panelu Args w edytorze, żeby podać argumenty (każde pole to jeden argument), a potem kliknij Run. Wypróbuj trzy pola: czesc, swiecie, 42.

Budowa

Uruchomienie ./greet Ada Lovelace daje:

IndeksWartość
argv[0]"./greet"nazwa programu w postaci, w jakiej go wywołano
argv[1]"Ada"pierwszy właściwy argument
argv[2]"Lovelace"drugi właściwy argument
argv[3]NULLterminator
argc3liczba łącznie z argv[0]

Cztery fakty warte zapamiętania:

  • argc wynosi w praktyce co najmniej 1, bo argv[0] to nazwa programu. Właściwe argumenty zaczynają się od indeksu 1, dlatego pętle po nich zaczynają od i = 1.
  • Wszystko jest stringiem. ./prog 42 daje ci "42", czyli dwa znaki, a nigdy liczbę 42. Konwersja to twoje zadanie.
  • argv[argc] to NULL. Gwarantuje to standard, więc jeśli wolisz, możesz przejść tablicę bez argc: for (char **p = argv + 1; *p != NULL; p++).
  • Słowa dzieli powłoka, nie twój program. ./prog "Ada Lovelace" to jeden argument; cudzysłowy zużywa powłoka i nigdy nie trafiają do argv.

Sprawdź, zanim odczytasz

Argument, którego nie przekazano, nie jest pustym stringiem: to pamięć, której nie wolno ci dotykać. Zawsze najpierw sprawdzaj argc:

W tym malutkim programie są dwie konwencje, obie standardowe w narzędziach uniksowych:

  • Komunikat o użyciu trafia na stderr, a nie na stdout, żeby nie zaśmiecać wyjścia przekazywanego gdzieś potokiem.
  • Niezerowa wartość zwracana z main sygnalizuje błąd temu, co uruchomiło program. 0 oznacza sukces; cokolwiek innego oznacza, że coś poszło nie tak. Skrypty powłoki to sprawdzają.

Wypisanie argv[0] zamiast wpisanej na sztywno nazwy sprawia, że komunikat zgadza się z tym, jak użytkownik wywołał program, nawet jeśli zmieniono mu nazwę albo uruchomiono go inną ścieżką.

Konwersja argumentów liczbowych

argv[1] to tekst. Żeby wykonać obliczenia, musisz go przekonwertować, a właściwa funkcja do tego to strtol z <stdlib.h>:

Dodaj 10, 20 i 30 jako trzy pola Args, a potem zamień jedno na banana, żeby zobaczyć ścieżkę błędu.

Trzeci parametr strtol to podstawa: 10 dla systemu dziesiętnego, 16 dla szesnastkowego albo 0, żeby wykryć ją automatycznie z prefiksu 0x lub zera na początku. Drugi to miejsce, w którym funkcja zapisze wskaźnik na pierwszy znak, którego nie zużyła, i to właśnie umożliwia sprawdzanie błędów:

  • end == text oznacza, że nic nie sparsowano: argument nie zaczynał się od liczby.
  • *end != '\0' oznacza, że po liczbie został tekst, więc "12abc" zostaje odrzucone, zamiast po cichu stać się liczbą 12.
  • errno == ERANGE oznacza, że wartość przepełniła long.

Porównaj z atoi, które jest krótsze i nie potrafi zgłosić niczego z powyższych:

int n = atoi(argv[1]);   /* "banana" -> 0, "0" -> 0, przepełnienie -> niezdefiniowane */

atoi zwraca 0 dla niepoprawnego wejścia, czego nie da się odróżnić od prawdziwego zera. Używaj strtol (i strtod dla liczb zmiennoprzecinkowych) zawsze, gdy wejście pochodzi spoza twojego programu. Więcej w artykule o konwersji stringów.

Mały kalkulator

Wszystko razem: program, który przyjmuje liczbę, operator i liczbę:

Wypróbuj 12 + 30, potem 7 x 6, potem 5 / 0.

Zwróć uwagę na strcmp(op, "+") == 0 zamiast op == "+". Porównywanie wartości char * przez == porównuje adresy, a nie zawartość, i dałoby fałsz nawet dla identycznego tekstu. Zobacz funkcje do stringów.

x zamiast * to szczegół z prawdziwego życia: większość powłok rozwija samotną * do listy plików w bieżącym katalogu, zanim twój program w ogóle się uruchomi. Ujęcie jej w cudzysłów ('*') też działa, ale wybranie znaku, który powłoka ignoruje, jest przyjaźniejsze.

Parsowanie flag

Opcje zwyczajowo zaczynają się od -. Prosta, ręcznie napisana pętla obsługuje typowe przypadki:

Wypróbuj go z czterema polami Args: -v, -n, 3, czesc.

Sprawdzenie i + 1 >= argc przed argv[++i] to kluczowa linia: opcja, która oczekuje wartości, musi potwierdzić, że ta wartość naprawdę istnieje, bo inaczej -n jako ostatni argument czyta za końcem tablicy. W prawdziwych systemach uniksowych getopt z <unistd.h> robi to wszystko za ciebie, łącznie z połączonymi krótkimi opcjami typu -vn3; warto po niego sięgnąć, gdy program ma więcej niż dwie czy trzy flagi.

Argumenty w edytorze w przeglądarce

Bloki edytora na tej stronie są kompilowane i uruchamiane za ciebie, a panel Args to miejsce, w którym podajesz to, co w terminalu następowałoby po nazwie programu. Każde pole dodane przyciskiem Add arg staje się dokładnie jednym wpisem w argv: nie ma po drodze powłoki, więc nic nie jest dzielone na spacjach i nie trzeba cudzysłowów; pole zawierające czesc swiecie dociera jako jeden argument czesc swiecie. Żeby przekazać -n 3 czesc, dodaj trzy pola. Pusty panel daje argc == 1, dlatego przy pierwszym uruchomieniu zadziała zabezpieczenie argc < 2 i właśnie dlatego warto je pisać.

Częste błędy

  • Odczyt argv[1] bez sprawdzenia argc. Niezdefiniowane zachowanie, gdy nie przekazano argumentów, i najczęstsza przyczyna awarii w tym obszarze.
  • Zaczynanie pętli od i = 0. Wtedy nazwa programu jest przetwarzana tak, jakby była danymi.
  • Traktowanie argv[1] jak liczby. To tekst, dopóki go nie przekonwertujesz.
  • Używanie atoi na danych od użytkownika. Nie da się odróżnić złego argumentu od zera.
  • Porównywanie stringów przez ==. Używaj strcmp.
  • Zapisywanie do stringów w argv. Standard pozwala je modyfikować, ale miejsce jest ograniczone i zależne od platformy; jeśli musisz coś zmienić, skopiuj je do własnego bufora.

Najczęściej zadawane pytania

Co to są argc i argv w C?

To dwa parametry int main(int argc, char *argv[]). argc to liczba argumentów wiersza poleceń łącznie z nazwą programu; argv to tablica tych argumentów w postaci stringów. argv[0] to nazwa programu, argv[1] to pierwszy właściwy argument, a argv[argc] jest zawsze NULL.

Jak przekazać argumenty do programu w C?

Wpisz je w terminalu po nazwie pliku wykonywalnego: ./program czesc 42. Daje to argc == 3, gdzie argv[1] to string "czesc", a argv[2] to string "42". W edytorze na tej stronie argumenty wpisuje się w panelu Args zamiast w terminalu.

Jak zamienić argument wiersza poleceń na int w C?

Użyj strtol: char *end; long n = strtol(argv[1], &end, 10);, a potem sprawdź, czy end nie wskazuje nadal na początek (nic nie sparsowano) i czy *end to '\0' (brak śmieci na końcu). atoi jest krótsze, ale zwraca 0 zarówno dla "0", jak i dla "banana", więc nie potrafi zgłosić błędu.

Co to jest argv[0] w C?

Nazwa, pod którą program został wywołany, zwykle ./program albo pełna ścieżka. Przydaje się w komunikatach o użyciu (fprintf(stderr, "uzycie: %s PLIK\n", argv[0])), żeby komunikat zgadzał się z tym, jak użytkownik faktycznie wywołał program. W nietypowych sytuacjach może być pustym stringiem, więc nie zakładaj, że zawsze jest.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ