Kiedy wskaźnik do określonego typu (powiedzmy int
, char
, float
, ..) jest zwiększany, jego wartość jest zwiększana o wielkości tego typu danych. Jeśli void
wskaźnik, który wskazuje na dane o rozmiarze, x
jest zwiększany, w jaki sposób ma wskazywać x
bajty do przodu? Skąd kompilator wie, aby dodać x
do wartości wskaźnika?
c
pointers
void-pointers
pointer-arithmetic
Siva Sankaran
źródło
źródło
void
wskaźnik wskazujący na dane o rozmiarzex
jest zwiększany, w jaki sposób ma on wskazywaćx
bajty do przodu?” Tak nie jest. Dlaczego ludzie, którzy mają takie pytania, nie mogą przetestować ich przed zapytaniem - wiesz, przynajmniej do absolutnego minimum, gdzie sprawdzają, czy faktycznie się kompiluje, a tak nie jest. -1, nie mogę uwierzyć, że to ma +100 i -0.Odpowiedzi:
Końcowy wniosek: arytmetyka na a
void*
jest niedozwolona zarówno w C, jak i C ++.GCC dopuszcza to jako rozszerzenie, patrz Arytmetyka na
void
- i wskaźnikach funkcji (zwróć uwagę, że ta sekcja jest częścią rozdziału „Rozszerzenia C” podręcznika). Clang i ICC prawdopodobnie zezwalają navoid*
arytmetykę dla celów zgodności z GCC. Inne kompilatory (takie jak MSVC) nie zezwalają na arytmetykęvoid*
, a GCC nie zezwala na to, jeśli-pedantic-errors
flaga jest określona lub-Werror-pointer-arith
flaga jest określona (ta flaga jest przydatna, jeśli baza kodu musi również kompilować się z MSVC).Mówi standard C.
Cytaty pochodzą z projektu n1256.
Standardowy opis operacji dodawania stwierdza:
Zatem pytanie tutaj brzmi, czy
void*
jest to wskaźnik do „typu obiektu”, czy też równoważnie, czyvoid
jest to „typ obiektu”. Definicja „typu obiektu” to:A standard definiuje
void
jako:Ponieważ
void
jest to niekompletny typ, nie jest to typ obiektowy. Dlatego nie jest poprawnym operandem do operacji dodawania.Dlatego nie można wykonywać arytmetyki wskaźnika na
void
wskaźniku.Uwagi
Początkowo sądzono, że
void*
arytmetyka jest dozwolona ze względu na następujące sekcje normy C:Jednak,
Oznacza to, że
printf("%s", x)
ma to samo znaczenie, niezależniex
od tego, czy ma typ,char*
czyvoid*
, ale nie oznacza to, że możesz wykonywać działania arytmetyczne navoid*
.Uwaga redaktora: ta odpowiedź została zredagowana, aby odzwierciedlić ostateczny wniosek.
źródło
void*
arytmetyka wskaźników jest niedozwolona. GCC ma rozszerzenie, które to umożliwia.void*
arytmetykę (przynajmniej domyślnie).Arytmetyka wskaźników nie jest dozwolona w przypadku
void*
wskaźników.źródło
void
jest niekompletnym typem , którego z definicji nigdy nie można ukończyć.rzuć go na wskaźnik typu char i zwiększ swój wskaźnik do przodu o x bajtów do przodu.
źródło
char
, zwiększanie ox
, a następnie reinterpretacja nowej wartości jako innego typu jest zarówno bezcelowym, jak i niezdefiniowanym zachowaniem.man 3 qsort
powinna miećvoid qsort(void *base, size_t nmemb, size_t size, [snip])
, to nie masz możliwości poznania „właściwego typu”Standard C nie zezwala na arytmetykę wskaźnika void . Jednak GNU C jest dozwolony przez biorąc pod uwagę wielkość pustych Is
1
.Standard C11 §6.2.5
Akapit - 19
Poniższy program działa dobrze w kompilatorze GCC.
Być może inne kompilatory generują błąd.
źródło
Nie możesz wykonywać arytmetyki wskaźników na
void *
typach, właśnie z tego powodu!źródło
Musisz rzucić to na inny typ wskaźnika przed wykonaniem arytmetyki wskaźnika.
źródło
Wskaźniki pustki mogą wskazywać na dowolny fragment pamięci. Dlatego kompilator nie wie, ile bajtów należy zwiększyć / zmniejszyć, gdy próbujemy wykonać arytmetykę wskaźnika na pustym wskaźniku. Dlatego wskaźniki void muszą być najpierw rzutowane na znany typ, zanim będą mogły zostać włączone do jakiejkolwiek arytmetyki wskaźników.
źródło
Kompilator rozpoznaje rzutowanie typu. Biorąc pod uwagę
void *x
:x+1
dodaje jeden bajt dox
, wskaźnik przechodzi do bajtux+1
(int*)x+1
dodajesizeof(int)
bajty, wskaźnik przechodzi do bajtux + sizeof(int)
(float*)x+1
adresysizeof(float)
bajty itp.Chociaż pierwszy element nie jest przenośny i jest sprzeczny z Galateo z C / C ++, niemniej jednak jest poprawny w języku C, co oznacza, że będzie się kompilował do czegoś na większości kompilatorów, prawdopodobnie wymagających odpowiedniej flagi (jak -Wpointer-arith)
źródło
Althought the first item is not portable and is against the Galateo of C/C++
Prawdziwe.it is nevertheless C-language-correct
Fałszywe. To podwójne myślenie! Arytmetyka wskaźnika navoid *
jest składniowo niedozwolona, nie powinna się kompilować, a jeśli tak, to powoduje niezdefiniowane zachowanie. Jeśli nieostrożny programista może go skompilować, wyłączając niektóre ostrzeżenia, to nie jest wymówka.unsigned char*
na np. dodaniesizeof
wartości do wskaźnika.