2016-03-12 11 views
11

mam następujący program:Array zadeklarowane jako int v [100], ale i (v [100]) daje żadnego ostrzeżenia

#include <stdio.h> 

int main() { 

    int v[100]; 
    int *p; 

    for (p = &(v[0]); p != &(v[100]); ++p) 
     if ((*p = getchar()) == EOF) { 
      --p; 
      break; 
     } 

    while (p != v) 
     putchar(*--p); 

    return 0; 
} 

i to jest wyjście gcc --version na terminalu:

Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1 
Apple LLVM version 7.0.2 (clang-700.1.81) 
Target: x86_64-apple-darwin15.3.0 
Thread model: posix 

Dlaczego coraz adres elementu po ostatnim tablicy daje mi żadnego ostrzeżenia, ale się na przykład adres v[101] daje mi następujące ostrzeżenie

test.c:8:29: warning: array index 101 is past the end of the array (which 
     contains 100 elements) [-Warray-bounds] 
    for(p = &(v[0]); p != &(v[101]); ++p) 
          ^~~~ 
test.c:5:5: note: array 'v' declared here 
    int v[100]; 
    ^
1 warning generated. 

Wiem, że elementy indeksujące poza obszarem bufora to niezdefiniowane zachowanie, dlaczego więc kompilator nie narzeka na pierwszy przypadek?

+1

Twój program wywoła * niezdefiniowane zachowanie *, jeśli odczytane zostaną tylko zero znaków przed EOF. – MikeCAT

+2

Nie powinieneś zmniejszać 'p' na' EOF'. – chqrlie

+0

@chqrlie Mówisz tak, ponieważ w następującej pętli I najpierw zmniejszam 'p', a następnie usuwam z niej dereferencję? – nbro

Odpowiedz

14

Przenoszenie wskaźnika na jeden za ostatnim elementem tablicy jest dozwolone, chyba że usuniesz wskaźnik, więc twój program jest ważny, jeśli odczytany zostanie jeden lub więcej znaków przed trafieniem EOF.

N1256 6.5.2.1 Tablica indeksowanie

Definicja operatora indeksem [] jest E1 [E2] jest identyczna z (* ((E1) + (E2))).

N1256 6.5.3.2 adresowa i zadnie operatorzy

Jeśli argument jest wynikiem jednoargumentowego * operatora, ani tego operatora, ani operator & jest uwzględniany, a wynik jest jakby obie były pominięto, z tym że ograniczenia obowiązujące operatorów nadal obowiązują, a wynik nie jest równy l. . Podobnie, jeśli operand jest wynikiem operatora [], ani operator &, ani nie jest jednoznaczny * implikowany przez [], a wynik jest taki, jakby operator & został usunięty i operator [] był zmieniono na operator +.

N1256 6.5.6 Dodatek operatorzy

Ponadto, jeżeli punkty ekspresja P do ostatniego elementu obiektu tablicy wyrażenie (P) +1 punktów jeden ostatnim elementem z tablica obiektu, jeśli punkty Q ekspresja jednego ostatnim elementem obiektu tablicowej wyrażenie (Q) -1 wskazuje ostatni element obiektu tablicy

+2

Szczerze mówiąc, cytaty nie odpowiadają na moje pytanie. Mówisz także, że przeniesienie wskaźnika do jednego za ostatnim elementem tablicy jest dozwolone (a cytaty nie są w rzeczywistości powiązane z tym, co mówisz), ale ja też tak naprawdę otrzymuję adres elementu za ostatni element (przez pierwszy dostęp do niego) ... – nbro

+0

@nbro Jest też cytat, że '& * X' jest zdefiniowany jako ten sam co' X', więc kiedy połączysz to z pierwszym cytatem z tej odpowiedzi, zobaczysz że '& v [100]' jest zdefiniowane jako oznaczające '(v + 100)', które jest zgodne z ostatnim cytatem –

+0

Mówiąc inaczej, mówienie o '* (v + 100)' jest nieokreślone, jeśli próbujesz uzyskać dostęp do wyznaczonego miejsca w pamięci (co zrobiłbyś w kontekście innym niż przedkładanie 'i' lub 'sizeof') –

0

Chodzi o Compa Tolerancja z niedbale napisanym kodem.

Jak cytuje MikeCAT, dla tablicy int ar[N] wyrażenie ar+N jest poprawne i powoduje, że wskaźnik wskazuje na pozycję końca do końca. Chociaż tego wskaźnika nie można usunąć, można go porównać z dowolnym innym wskaźnikiem w tablicy, co pozwala napisać ładną pętlę for (p = ar; p != ar+N; ++p).

Ponadto, programiści lubią pisać kod czytelny, i prawdopodobnie, jeśli chcesz wskaźnik do XX elementu tablicy i, pisanie &ar[i] przekazuje swój zamiar jaśniej niż pisanie ar + i.

połączyć te dwa, a dostaniesz programiści piszący &ar[N] aby uzyskać wskaźnik past-the-end, a gdy jest to technicznie dostępu nieprawidłowy indeks tablicy, nie kompilator będzie kiedykolwiek zaimplementować to jako cokolwiek innego niż ar + N - w Fakt, kompilator musiałby zejść z drogi, aby zrobić to inaczej. Dość daleko w rzeczywistości.

Tak więc, ponieważ każdy kompilator, który nie bardzo ściśle opiera się na niezdefiniowanym zachowaniu, będzie robił to, czego programiści oczekują na wyrażenie, nie ma powodu, aby go nie pisać, więc wiele osób go napisało. A teraz mamy masywne podstawy kodu, które używają tego idiomu, co oznacza, że ​​nawet nowoczesne kompilatory ze śledzeniem ich wartości i rozumowaniem o niezdefiniowanym zachowaniu muszą wspierać ten idiom dla kompatybilności. A ponieważ ostrzeżenia Clanga mają być użyteczne, to to ostrzeżenie zostało napisane tak, aby nie ostrzegać o sprawie, która i tak zadziała, z jakiegoś nieumiejętnego pedanterii.

+0

* Chodzi o kompatybilność z niechlujnie napisanym kodem. * Nie zgadzam się. Nie ma nic podejrzanego w konstruowaniu wskaźnika po końcu tablicy. Zachowanie jest dobrze zdefiniowane i jest przydatną techniką. –