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:
| Indeks | Wartość | |
|---|---|---|
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] | NULL | terminator |
argc | 3 | liczba łącznie z argv[0] |
Cztery fakty warte zapamiętania:
argcwynosi w praktyce co najmniej 1, boargv[0]to nazwa programu. Właściwe argumenty zaczynają się od indeksu 1, dlatego pętle po nich zaczynają odi = 1.- Wszystko jest stringiem.
./prog 42daje ci"42", czyli dwa znaki, a nigdy liczbę 42. Konwersja to twoje zadanie. argv[argc]toNULL. Gwarantuje to standard, więc jeśli wolisz, możesz przejść tablicę bezargc: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ą doargv.
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 nastdout, żeby nie zaśmiecać wyjścia przekazywanego gdzieś potokiem. - Niezerowa wartość zwracana z
mainsygnalizuje błąd temu, co uruchomiło program.0oznacza 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 == textoznacza, ż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 == ERANGEoznacza, że wartość przepełniłalong.
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 sprawdzeniaargc. 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
atoina danych od użytkownika. Nie da się odróżnić złego argumentu od zera. - Porównywanie stringów przez
==. Używajstrcmp. - 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.