Kolejne tablice
Ciekawi mnie zdanie w standardzie C18:
Dwa wskaźniki porównują się równo wtedy i tylko wtedy, gdy oba są wskaźnikami zerowymi, oba są wskaźnikami do tego samego obiektu (w tym wskaźnika do obiektu i podobiektu na jego początku) lub funkcji, oba są wskaźnikami do jednego za ostatnim elementem tej samej tablicy obiekt lub jeden jest wskaźnikiem do jednego poza koniec jednego obiektu tablicy, a drugi jest wskaźnikiem do początku innego obiektu tablicy, który zdarza się, że następuje natychmiast po pierwszym obiekcie tablicy w przestrzeni adresowej. § 6.5.9 6
Dlaczego obiekt następujący po tablicy musi koniecznie być inną tablicą? Czy nie może to być po prostu obiekt tego samego typu, co typ podstawowy tablicy (np. intBezpośrednio następujący po znaku int[])?
Nic dziwnego, że wypróbowałem ten kod:
#include <stdio.h>
struct test { int arr[10]; int i; };
int main() {
struct test t;
int *p, *q;
p = t.arr + 10;
q = &t.i;
if (p == q)
printf("Equal pointers.");
return 0;
}
I daje równe wskaźniki. Czy to zachowanie nie jest w ogóle gwarantowane, a jedynie zbieg okoliczności zdefiniowany w implementacji?
Odpowiedzi
OP: Dlaczego obiekt następujący po tablicy musi koniecznie być inną tablicą?
To nie. „… początek innej tablicy …” to uproszczenie. Następna specyfikacja to:
Na potrzeby tych operatorów wskaźnik do obiektu, który nie jest elementem tablicy, zachowuje się tak samo, jak wskaźnik do pierwszego elementu tablicy o długości jeden z typem obiektu jako typem elementu. C17dr § 6.5.9 7
OP: Czy nie mógł to być po prostu obiekt, którego typ był taki sam jak typ bazowy tablicy (np.
intBezpośrednio następujący po znakuint[])?
Tak.
Po pierwsze, określenie tablicy tutaj nie wyklucza / nie zabrania pojedynczego obiektu. Pojedynczy obiekt w pamięci jest nie do odróżnienia od tablicy o rozmiarze 1.
( Edytuj : Przeczytaj tę odpowiedź, aby uzyskać cytat ze standardu, który wyraźnie to stwierdza, odnosząc się do wskaźników)
Po drugie, standard próbuje również wyjaśnić cytowane przez Ciebie stwierdzenie, z następującym przypisem wskazującym scenariusze, w których reguła ma zastosowanie:
Dwa obiekty mogą przylegać do siebie w pamięci, ponieważ są sąsiednimi elementami większej tablicy lub sąsiednimi elementami struktury bez wypełnienia między nimi lub ponieważ implementacja zdecydowała się je tak umieścić, nawet jeśli nie są ze sobą powiązane.
Podsumowując wszystko razem, standard stara się tutaj powiedzieć, że generalnie dwa wskaźniki do odmiennych obiektów nie powinny być równe. Jednakże, ponieważ dozwolone jest wskazanie jednego poza obiektem tablicy w pamięci, jeśli zdarzy się, że w tym miejscu znajduje się inny obiekt (tablica), nadal legalne jest, aby taki wskaźnik porównywał się ze wskaźnikiem do sąsiedniego obiektu. Teraz może, ale nie musi, istnieć prawidłowy obiekt w tej lokalizacji ze względu na wybory wyrównania i wypełnienie, ale jeśli istnieje, to jest dopuszczalne, aby te wskaźniki były równe.
W twoim przykładzie, gdybym zmienił tablicę na char, wskaźniki prawdopodobnie porównałyby nierówności, ponieważ kompilator wybrałby wyrównanie int do 4 bajtów (na większości platform 32- lub 64-bitowych), wprowadzając w ten sposób dopełnienie. Takie zachowanie jest nadal legalne zgodnie ze standardem.
#include <stdio.h>
struct test { char arr[10]; int i; };
int main() {
struct test t;
int *p, *q;
p = (int*)(t.arr + 10);
q = &t.i;
if(p == q)
printf("Equal pointers.");
else
printf("Unequal pointers.");
return 0;
}