Co oznacza int argc, char * argv []?

507

W wielu IDE i kompilatorach C ++, gdy generuje ono dla ciebie główną funkcję, wygląda to tak:

int main(int argc, char *argv[])

Kiedy koduję C ++ bez IDE, tylko z kompilatorem wiersza poleceń, piszę:

int main()

bez żadnych parametrów. Co to oznacza i czy ma to zasadnicze znaczenie dla mojego programu?

Greg Treleaven
źródło
47
Jeśli twój program zignoruje argumenty wiersza poleceń, to co piszesz jest w porządku. Jeśli twój program musi przetwarzać argumenty wiersza poleceń, IDE robi to dobrze.
Jonathan Leffler,
30
Wskazówka dla hakerów: spróbuj zadeklarować int main(int argc, char* argv[], char* envp[])i wydrukować ostatni argument. ;)
ulidtko
7
@ulidtko, nie jest dobrze, że uczysz początkujących, jak wprowadzać luki w swoich programach;)
Gab 是 好人
13
@Gab, w jaki sposób proste drukowanie zmiennych środowiskowych prowadzi do podatności? Po prostu nie przekazuj skażonych ciągów dosłownie do system()wywołań, zapytań DB itp. Jak zwykle przy wprowadzaniu danych przez użytkownika.
ulidtko
2
@ulidtko Interesujące .. Czy możesz wyjaśnić, dlaczego nie musisz przekazywać skażonych ciągów, zapytań db itp. podczas używania char **envpargumentu?
Mistrz James

Odpowiedzi:

650

argvi w argcjaki sposób przekazywane są argumenty wiersza poleceń main()w C i C ++.

argcbędzie liczbą ciągów wskazanych przez argv. Będzie to (w praktyce) 1 plus liczba argumentów, ponieważ praktycznie wszystkie implementacje dodają nazwę programu do tablicy.

Zmienne są nazywane argc( liczba argumentów ) i argv( wektor argumentów ) zgodnie z konwencją, ale można im nadać dowolny poprawny identyfikator: int main(int num_args, char** arg_strings)jest równie ważny.

Można je również całkowicie pominąć, dając wynik int main(), jeśli nie zamierzasz przetwarzać argumentów wiersza poleceń.

Wypróbuj następujący program:

#include <iostream>

int main(int argc, char** argv) {
    std::cout << "Have " << argc << " arguments:" << std::endl;
    for (int i = 0; i < argc; ++i) {
        std::cout << argv[i] << std::endl;
    }
}

Uruchomienie go ./test a1 b2 c3spowoduje wyjście

Have 4 arguments:
./test
a1
b2
c3
Meagar
źródło
8
argcmoże wynosić 0, w którym to przypadku argvmoże mieć wartość NULL. Jest to dozwolone przez standardowy AFAIK. Nigdy nie słyszałem o systemie, który robi to w praktyce, ale z pewnością mógłby istnieć i nie naruszałby żadnych standardów.
Chuck
77
@Chuck: Ponieważ „Wartość argv[argc]wynosi 0” (C ++ 03 §3.6.1 / 2), argvnie może być zerowa.
James McNellis,
20
@Chuck: C (co najmniej C99) ma takie same wymagania.
James McNellis,
2
Pomyślałem, że powinienem dodać, że jest tak samo w większości systemów, choć czasami są one abstrakcyjne. Na przykład w Pascal / Delphi / Lazarus otrzymujesz; ParamStr i ParamCount (jeśli pamięć dobrze mi służy). Chodzi mi o to, że kiedy (jeśli w ogóle) piszesz aplikacje natywne w innych językach / językach, istnieje duża szansa, że ​​powyższe jest zdefiniowane do użycia, i działają one idealnie tak samo (lista / lista ciągów) we wszystkich systemach, które obsługują im.
Christian
8
@ EmilVikström Nie, to poważny błąd, który prawdopodobnie skutkuje awarią. *NULLzdecydowanie nie jest równy NULL.
meagar
52

argcjest liczbą argumentów przekazywanych do twojego programu z linii poleceń i argvjest tablicą argumentów.

Możesz przeglądać argumenty, znając ich liczbę, np .:

for(int i = 0; i < argc; i++)
{
    // argv[i] is the argument at index i
}
John Boker
źródło
19

Załóżmy, że uruchamiasz program w ten sposób (używając shskładni):

myprog arg1 arg2 'arg 3'

Jeśli zadeklarowałeś swój główny jako int main(int argc, char *argv[]), to (w większości środowisk) main()zostaniesz wywołany tak, jakby:

p = { "myprog", "arg1", "arg2", "arg 3", NULL };
exit(main(4, p));

Jeśli jednak zadeklarujesz swój główny jako int main(), będzie to nazywać coś w rodzaju

exit(main());

i nie dostaniesz argumentów.

Dwie dodatkowe rzeczy do zapamiętania:

  1. Są to jedyne dwa podpisy standardowe dla mandatu main. Jeśli dana platforma akceptuje dodatkowe argumenty lub inny typ zwrotu, to jest to rozszerzenie i nie należy na nim polegać w przenośnym programie.
  2. *argv[]i **argvsą dokładnie równoważne, więc możesz pisać int main(int argc, char *argv[])jako int main(int argc, char **argv).
Toby Speight
źródło
2
Jeśli jesteśmy techniczni, basic.start.main/2wyraźnie zezwalamy na dodatkowe wersje zdefiniowane main()przez implementację, pod warunkiem, że implementacja zapewnia dwie predefiniowane wersje. Nie są więc dokładnie niezgodne. Najczęstszym z nich jest envp, który jest tak dobrze znana zarówno w C i C ++, który jest dosłownie pierwszy wpis w sekcji J.5 (wspólne rozszerzenia) standardu C .
Justin Time - Przywróć Monikę
1
Dzięki za fajną pedanterię @Justin. Odpowiedź zaktualizowana, aby była bardziej poprawna.
Toby Speight
Nie mam pojęcia - proponuję stworzyć minimalny, powtarzalny przykład i zapytać go (zakładając, że proces nie jest wystarczający, aby pomóc ci odpowiedzieć na to pytanie).
Toby Speight,
9

Parametry mainreprezentujące parametry wiersza poleceń dostarczone programowi podczas jego uruchamiania. argcParametr reprezentuje liczbę argumentów wiersza poleceń, a char *argv[]jest tablicą ciągów znaków (wskaźniki) reprezentujących poszczególne argumenty podane w wierszu poleceń.

BlueMonkMN
źródło
2
Argv [] zawsze ma argv [arg] jako wskaźnik zerowy. a Argv [0] jest zawsze (pełna ścieżka) /
nazwa_wykonywalna
3
@ user3629249: Niekoniecznie; argv[0]to bez względu na program rozpoczyna program C dał ją jako argv[0]. W przypadku Bash często jest to (może zawsze) nazwa pliku wykonywalnego, ale Bash nie jest jedynym programem, który wykonuje inne programy. Jest permissisble, choć ekscentryczny, aby użyć: char *args[] = { "cat", "/dev/null", "/etc/passwd", 0 }; execv("/bin/ls", args);. W wielu systemach wartość jest postrzegana przez program jako argv[0]taka cat, mimo że jest to plik wykonywalny /bin/ls.
Jonathan Leffler
6

mainFunkcja może mieć dwa parametry, argci argv. argcjest intparametrem liczby całkowitej ( ) i jest liczbą argumentów przekazanych do programu.

Nazwa programu jest zawsze pierwszym argumentem, więc program będzie miał co najmniej jeden argument, a minimalna wartość argcbędzie wynosić jeden. Ale jeśli program ma dwa argumenty, wartość argcbędzie równa trzy.

Parametr argvwskazuje na tablicę łańcuchów i jest nazywany wektorem argumentów . Jest to jednowymiarowa tablica ciągów argumentów funkcji.

mooshtagh
źródło
5
int main();

To prosta deklaracja. Nie może przyjmować żadnych argumentów wiersza poleceń.

int main(int argc, char* argv[]);

Ta deklaracja jest używana, gdy program musi przyjmować argumenty wiersza polecenia. Po uruchomieniu tak:

myprogram arg1 arg2 arg3

argclub Liczba argumentów zostanie ustawiona na 4 (cztery argumenty) i argvlub Wektory argumentów zostaną wypełnione wskaźnikami łańcuchowymi do „myprogram”, „arg1”, „arg2” i „arg3”. Wywołanie programu ( myprogram) jest zawarte w argumentach!

Alternatywnie możesz użyć:

int main(int argc, char** argv);

Jest to również ważne.

Istnieje jeszcze jeden parametr, który możesz dodać:

int main (int argc, char *argv[], char *envp[])

Ten envpparametr zawiera również zmienne środowiskowe. Każdy wpis ma ten format:

VARIABLENAME=VariableValue

lubię to:

SHELL=/bin/bash    

Lista zmiennych środowiskowych jest zakończona zerem.

WAŻNE: Nie należy stosować żadnych argvlub envpwartości bezpośrednio w wywołań system()! Jest to ogromna dziura w zabezpieczeniach, ponieważ złośliwi użytkownicy mogą ustawiać zmienne środowiskowe na polecenia wiersza poleceń i (potencjalnie) powodować ogromne szkody. Ogólnie po prostu nie używaj system(). Prawie zawsze istnieje lepsze rozwiązanie zaimplementowane w bibliotekach C.

adrian
źródło
3

Pierwszy parametr to liczba dostarczonych argumentów, a drugi parametr to lista ciągów reprezentujących te argumenty.

Nick Gerakines
źródło
7
pierwszy wpis w argv [0] to nazwa programu, a nie argument
user3629249
@ user3629249 Nazwa programu ze ścieżką programu. ;)
Mistrz James
1

Obydwa

int main(int argc, char *argv[]);
int main();

są prawnymi definicjami punktu wejścia dla programu C lub C ++. Stroustrup: Często zadawane pytania dotyczące stylu i techniki C ++ wyszczególniają niektóre warianty, które są możliwe lub legalne dla twojej głównej funkcji.

Chris Becke
źródło
4
Może chcesz unieważnić ... int main()==> int main(void)... dla kompatybilności i czytelności. Nie wiem, czy wszystkie starsze wersje C zezwalają, aby funkcje void miały pustą listę parametrów w deklaracji.
dylnmc
1
@dylnmc nie daje to żadnego zwiększenia czytelności i jest dokładnie równoważne we wszystkich wersjach C ++. Tylko w C ma to różnicę, ale tylko w deklaracjach, a nie w definicji.
Ruslan
@ Ruslan Przepraszam, opublikowałem to, gdy dopiero uczyłem się języka C, i mogłem przeczytać, że w bardzo wczesnych wersjach języka C voidjest to wymagane. Nie cytuj mnie w tej sprawie, a teraz wiem, że to nieco głupi komentarz. Ale to nie może zranić.
dylnmc,
co jeśli argc <3 zwróci błąd? co mogło pójść nie tak?
AVI,