2016-01-30 7 views
8

Niedawno (w 2016 r., Jeśli pytanie pozostało wystarczająco długo), otrzymaliśmy pytanie: Are the strings in argv modifiable?.
W sekcji komentarzy do this odpowiedź, my (@ 2501 i I) argumentował, czy jest to naprawdę ciągi znaków (przykładowy charakter bycia **argv) to modyfikowalne lub wskaźniki do ciągów (an istota przykład wskaźnik *argv).Czy wskaźniki do ciągów w argv można modyfikować?

odpowiedni standard cytat z C11 standardowy projekt N1570, §5.1.2.2.1/2:

Parametry argc i argv i łańcuchy wskazywanego przez układ argv są modyfikowane Fi stanie przez program i zachowują ostatnie zapisane wartości między uruchomieniem programu i zakończeniem programu.

Czy wskaźniki są wskazane przez argv modyfikowalne?

+0

Ponieważ 'argv' jest modyfikowalne, a' argv' zawiera wskaźniki, o których mówisz, tak. –

+0

@SimonShine Ale jeśli 'argv' jest modyfikowalny, czy nie oznacza to, że tylko' argv = ... 'może być zrobione i niekoniecznie' * argv = ... '? – Downvoter

+0

Ponieważ jedną z właściwych deklaracji funkcji 'main' jest' int main (int argc, char * argv []) 'wygląda na to, że parametr' argv' jest tablicą wskaźników 'char *', a nie 'char const * argv [] "wskaźniki. Zatem ciągi powinny być modyfikowalne. – nsilent22

Odpowiedz

6

Zgodnie z PO w pytaniu, standard C11 wyraźnie stwierdza, że ​​zmienne argc i argv oraz ciągi wskazane przez tablicę argv można modyfikować. Niezależnie od tego, czy te wskaźniki są modyfikowalne, czy nie, jest to pytanie. Standard nie wydaje się jednoznacznie określać tego w taki czy inny sposób.

Istnieją dwa kluczowe punkty, aby pamiętać o brzmienia w standardzie:

  1. Jeśli wskaźniki miały być niezmienne, a średnia mógł jasno wymagając główny zostać zadeklarowana jako int main(int argc, char *const argv[]), jako haccks mentioned w innej odpowiedzi na to pytanie.

    Fakt, że nigdzie w standardzie nie jest const wymieniony w związku z argv, wydaje się celowy. Oznacza to, że brak parametru const nie wydaje się opcjonalny, ale dyktowany przez standard.

  2. Standardowo wywołuje argv konsekwentnie tablicę. Modyfikowanie tablicy odwołuje się do modyfikowania jej członków. Dlatego wydaje się oczywiste, że sformułowanie w standardzie odnosi się do modyfikacji elementów w tablicy argv, gdy stwierdza, że ​​argv jest modyfikowalne.

    Z drugiej strony, tablica parametry w C (na podstawie C11 projekcie N1570, §6.7.6.3p7) „zostaną dostosowane do«wykwalifikowanego wskaźnik do typu»”. Zatem, poniższy kod,

    int foo(int x[2], int y[2]) 
    { 
        if (x[0] > y[0]) 
         x = y; 
        return x[1]; 
    } 
    

    jest ważny C11, ponieważ x i y są dostosowane do int *x i int *y odpowiednio. (Jest to również powtórzone w C11 draft N1570, §6.3.2.1p3: "... array ...jest konwertowane na wyrażenie o typie "wskaźnik do typu", który wskazuje na początkowy element tablicy ... ".) Oczywiście, to samo by nie było, gdyby x i y zostały zadeklarowane jako tablice lokalne lub globalne, a nie parametry funkcji.

Jeśli chodzi o język-lawyerism idzie, powiedziałbym, że norma nie stanie, w jakim taki czy inny sposób, choć implikuje kursory zbyt powinny być modyfikowane. Zatem, jako odpowiedź do OP: zarówno.


W praktyce istnieje bardzo długa tradycja, że ​​wskaźniki w tablicy argv można modyfikować. Wiele bibliotek ma funkcje inicjalizacyjne, które pobierają wskaźnik do argc i wskaźnik do tablicy argv, a niektóre z nich modyfikują wskaźniki w tablicy argv (usuwając opcje specyficzne dla biblioteki); na przykład GTK+ gtk_init() i MPI_Init() (chociaż przynajmniej OpenMPI wyraźnie stwierdza, że ​​nie sprawdza ani nie modyfikuje ich). Wyszukaj deklarację parametru (int *argc, char ***argv); jedynym powodem tego - zakładając, że intencją jest wywoływanie z main() przy użyciu (&argc, &argv) - jest modyfikowanie wskaźników, analizowanie i usuwanie specyficznych dla biblioteki parametrów wiersza poleceń z parametrów wiersza polecenia, modyfikujących zarówno argc i wskaźniki w argv w razie potrzeby.

(początkowo stwierdził, że zakład w POSIX getopt() opiera się na wskaźnikach jest modyfikowalny - funkcja sięga roku 1980, przyjęta przez większość Unix i ujednolicone w POSIX.2 w 1997 roku - ale to jest nieprawidłowe, jak Jonathan Leffler podkreślił w komentarzu: POSIX getopt() nie zmienia rzeczywiste wskaźniki; tylko GNU getopt() robi, i to tylko wtedy, gdy zmienna POSIXLY_CORRECT środowisko nie jest ustawiony Zarówno GNU getopt_long() i BSD getopt_long() modyfikować wskazówek chyba POSIXLY_CORRECT jest ustawiony. , ale są znacznie młodsze i mniej rozpowszechnione w porównaniu do getopt().)

W krainie Uniksa uznano za "przenośne" modyfikowanie zawartości łańcuchów wskazanych przez argv[] tablicę, i modyfikowanie łańcuchów widocznych na liście procesów. Jednym z przykładów tego, jak to było przydatne, jest pakiet daemontools DJB, readproctitle. (Zauważ, że ciągi musiałyby zostać zmodyfikowane na miejscu i nie można ich rozszerzyć, aby zmiany były widoczne na liście procesów.)

Wszystko to wskazuje na bardzo długą tradycję, prawie od narodzin C jako język programowania, i zdecydowanie poprzedzający standaryzację C, traktowania argc, argv, wskaźników w macierzy argv i zawartości łańcuchów wskazanych przez te wskaźniki, jako modyfikowalne.

Ponieważ celem normy C nie jest definiowanie nowego zachowania, ale kodyfikowanie istniejącego zachowania we wszystkich implementacjach (w celu zwiększenia przenośności i niezawodności itd.), Można bezpiecznie założyć, że było to niezamierzone opuszczenie części strony. Standardowe programy piszące nie określają bezpośrednio wskaźników w tablicy argv jako modyfikowalne. Wszystko inne złamałoby tradycję i byłoby wyraźnie sprzeczne ze standardem POSIX (który ma również na celu promowanie przenoszenia w różnych systemach i rozszerza funkcje C nie zawarte w standardzie ISO C).

+0

Standardowe 'getopt()' nie opiera się na 'argv' będącym modyfikowalnym; GNU 'getopt()' robi, ponieważ permutuje listę argumentów. –

+0

@ JonathanLeffler: Cholera, prawda! Sprawdziłem nawet niektóre stare implementacje getopt() systemu Unix, i one również zachowują nienaruszoną listę argumentów. Tylko GNU 'getopt()' modyfikuje wskaźnik. (Chociaż zarówno GNU, jak i BSD 'getopt_long()' modyfikują wskaźniki, * nawet jeśli są oznaczone * 'const', chyba że ustawiona jest zmienna środowiskowa" POSIXLY_CORRECT ".) Będę musiał poprawić moją odpowiedź. –

+0

Zastąpiłem niepoprawną część dotyczącą 'getopt()' wskazaną przez @ JonathanLeffler z odniesieniami do funkcji inicjalizujących GTK + i MPI (i sygnatury można szukać innych funkcji inicjalizacji biblioteki). Jeśli ty (lub ktoś inny) znajdziecie jakieś inne błędy, proszę je wskazać. –

1

To, czy wskaźnik jest modyfikowalny czy nie, zależy od wskaźnika constness. Parametr argv jest zadeklarowany jako char *argv[] lub char **argv. Zależy od środowiska, czy traktują to jako char *const argv[], czy też nie (nie jestem tego świadomy).

+3

Ale czy naprawdę możesz wypowiadać się na temat wskazówek, które można zapisać, po prostu wiedząc, że nie są "const"? Zobacz [mój komentarz] (http://stackoverflow.com/questions/35105918/are-the-pointers-to-strings-in-argv-modifiable?noredirect=1#comment57932896_35105918) na to pytanie. – Downvoter

+0

@ cad; Pytałeś w pytaniu o modyfikację wskaźnika, a nie o treść, którą on wskazuje: * Czy wskaźniki do ciągów w "argv' modyfikowalne? *. 'char const * a' i' char * const a' mają różne znaczenie. – haccks

+1

Ale co mi mówią pierwsze dwa zdania? W zasadzie mówią mi, że wskaźniki do ciągów są modyfikowalne, ponieważ są odpowiednio zadeklarowane. Więc mówisz, że są one modyfikowalne w zależności od implementacji? – Downvoter

Powiązane problemy