C / C++: Arytmetyka Wskaźnika

Czytałem trochę w arytmetyce wskaźników, i natknąłem się na 2 rzeczy, których nie mogłem zrozumieć ani Nie wiem, czy jest to użycie

address_expression - address_expression

A także

address_expression > address_expression
Czy ktoś może mi wyjaśnić, jak działają i kiedy są używane?

Edit:

Chciałem powiedzieć, co oni produkują, jeśli wezmę dwa adresy i odjmę je

A jeśli wezmę dwa adresy i porównam je, jaki jest wynik lub porównanie na podstawie

Edytuj: Teraz rozumiem wynik odejmowania adresów, ale porównując adresy nadal tego nie rozumiem.

Rozumiem, że 1

Author: alk, 2012-07-30

6 answers

Odejmowanie wskaźnika daje liczbę elementów tablicy pomiędzy dwoma wskaźnikami tego samego typu.

Na przykład,

int buf[10] = /* initializer here */;

&buf[10] - &buf[0];  // yields 10, the difference is 10 elements

Porównanie wskaźników. Na przykład, dla operatora relacyjnego >: Operacja > daje 1 Jeśli element tablicy lub element struktury po lewej stronie znajduje się za elementem tablicy lub elementem struktury po prawej stronie i daje 0 w przeciwnym razie. Zapamiętaj tablice i struktury są uporządkowanymi sekwencjami.

 &buf[10] > &buf[0];  // 1, &buf[10] element is after &buf[0] element
 18
Author: ouah,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-27 19:48:02

Kilka odpowiedzi tutaj stwierdziło, że wskaźniki są liczbami. Nie jest to dokładny opis wskaźników określony przez standard C.

W dużej części można myśleć o wskaźnikach jako o liczbach i jako o adresach w pamięci, pod warunkiem, że (a) rozumiesz, że odejmowanie wskaźnika konwertuje różnicę z bajtów na elementy (typu odejmowanych wskaźników), oraz (b) rozumiesz granice, w których ten model się łamie.

Poniżej zastosowano standard C z 1999 roku (ISO/IEC 9899, Wydanie drugie, 1999-12-01). Spodziewam się, że poniższe informacje są bardziej szczegółowe niż wnioskował asker, ale biorąc pod uwagę niektóre błędne stwierdzenia, uważam, że należy podać dokładne i dokładne informacje.

Zgodnie z 6.5.6 pkt 9, można odjąć dwa wskaźniki, które wskazują na elementy tej samej tablicy lub jeden obok ostatniego elementu tablicy. Tak więc, jeśli masz int a[8], b[4];, możesz odjąć wskaźnik do a [5] od wskaźnika do a[2], Ponieważ a[5] I a[2] są elementami w tym samym / align = "left" / Można również odjąć wskaźnik do a [5] od wskaźnika do a[8], ponieważ[8] jest jednym obok ostatniego elementu tablicy. (a[8] nie jest w tablicy; a[7] jest ostatnim elementem.) Nie można odjąć wskaźnika do a[5] od wskaźnika do b[2], Ponieważ a[5] nie jest w tej samej tablicy co b[2]. Lub, dokładniej, jeśli wykonasz takie odejmowanie, zachowanie jest niezdefiniowane. Zauważ, że nie tylko wynik jest nieokreślony; nie możesz oczekiwać, że otrzymasz jakąś prawdopodobnie nonsensowną liczbę w rezultacie: zachowanie jest niezdefiniowane. Zgodnie ze standardem C oznacza to, że standard C nie mówi nic o tym, co dzieje się w konsekwencji. Twój program mógłby dać ci rozsądną odpowiedź, albo przerwać działanie, albo usunąć Pliki, a wszystkie te konsekwencje byłyby zgodne ze standardem C.

Jeśli wykonasz dozwolone odejmowanie, to wynikiem jest liczba elementów od drugiego elementu wskazanego-do pierwszego elementu wskazanego-do. Tak więc, a[5]-a[2] jest 3, a a[2]-a[5] jest -3. Jest to prawdą niezależnie od rodzaju a. Implementacja C jest wymagana do zamiany odległości od bajtów (lub dowolnych jednostek, których używa) na elementy odpowiedniego typu. Jeśli a jest tablicą złożoną z ośmiu bajtów każdy, to a[5]-a[2] jest 3, dla 3 elementów. Jeśli a jest tablicą znaków o jednym bajcie każdy, to a[5]-a[2] jest 3, dla 3 elementów.

Dlaczego wskaźniki nie byłyby tylko liczbami? Na niektórych komputerach, zwłaszcza starszych, adresowanie pamięci to było bardziej skomplikowane. Wczesne komputery miały małe przestrzenie adresowe. Kiedy producenci chcieli tworzyć większe przestrzenie adresowe, chcieli również zachować kompatybilność ze starym oprogramowaniem. Musieli również zaimplementować różne schematy adresowania pamięci, ze względu na ograniczenia sprzętowe, a te schematy mogły obejmować przenoszenie danych między pamięcią a dyskiem lub zmianę specjalnych rejestrów w procesorze, który kontrolował sposób konwertowania adresów do lokalizacji pamięci fizycznej. Za wskazówki do praca na takich maszynach musi zawierać więcej informacji niż zwykły adres. Z tego powodu standard C nie definiuje tylko wskaźników jako adresów i pozwala na arytmetykę z nimi. Zdefiniowana jest tylko rozsądna ilość arytmetyki, a implementacja C jest wymagana, aby zapewnić niezbędne operacje, aby ta arytmetyka działała, ale nie więcej.

Nawet na nowoczesnych maszynach mogą wystąpić komplikacje. Na procesorach Alpha firmy Digital wskaźnik do funkcji robi nie zawiera adresu funkcji. Jest to adres deskryptora funkcji. Deskryptor ten zawiera adres funkcji i zawiera dodatkowe informacje niezbędne do prawidłowego wywołania funkcji.

W odniesieniu do operatorów relacyjnych, takich jak >, standard C mówi, w 6.5.8 pkt 5, że można porównać te same wskaźniki, które można odjąć, jak opisano powyżej, a także można porównać wskaźniki do elementów obiektu agregującego (a struct lub union). Wskaźniki do elementów tablicy (lub jej adresu końcowego) porównują się w oczekiwany sposób: wskaźniki do elementów indeksowanych są większe niż wskaźniki do elementów indeksowanych. Wskaźniki dla dwóch członków tego samego związku są równe. Dla wskaźników do dwóch członków struktury, wskaźnik do członka zadeklarowanego później jest większy niż wskaźnik do członka zadeklarowanego wcześniej.

Dopóki pozostajesz w granicach powyższych ograniczeń, możesz myśleć o wskaźnikach jako o liczbach, które są adresami pamięci.

Zazwyczaj implementacja C jest łatwa do zapewnienia zachowania wymaganego przez standard C. Nawet jeśli komputer ma złożony schemat wskaźników, taki jak adres bazowy i offset, zwykle wszystkie elementy tablicy będą używać tego samego adresu bazowego co każdy inny, a wszystkie elementy struktury będą używać tego samego adresu bazowego co każdy inny. Więc kompilator może po prostu odjąć lub porównać przesunięte części wskaźnika, aby uzyskać pożądaną różnicę lub porównanie.

Jednakże, jeśli odjmiesz wskaźniki do różnych tablic na takim komputerze, możesz uzyskać dziwne wyniki. Możliwe jest, że wzorzec bitowy utworzony przez adres bazowy i offset będzie większy (gdy zostanie zinterpretowany jako pojedyncza liczba całkowita) niż inny wskaźnik, nawet jeśli wskazuje na niższy adres w pamięci. Jest to jeden z powodów, dla których musisz przestrzegać zasad określonych przez standard C.

 22
Author: Eric Postpischil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-30 01:02:09

Odejmowanie dwóch adresów wskaźnika Zwraca liczbę elementów tego typu.

Więc jeśli masz tablicę liczb całkowitych i dwa wskaźniki do niej, odejmowanie tych wskaźników zwróci liczbę wartości int pomiędzy, a nie liczbę bajtów. To samo dotyczy typów znaków. Dlatego musisz być z tym ostrożny, zwłaszcza jeśli pracujesz z buforem bajtów lub szerokimi znakami, że Twoje wyrażenie oblicza odpowiednią wartość. Jeśli potrzebujesz offsetów bufora opartych na bajtach w przypadku czegoś, co nie używa pojedynczego bajtu do przechowywania (int, short, itp.), Musisz najpierw rzucić swoje Wskaźniki na znak*.

 4
Author: paddy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-30 00:04:18

Wskaźniki mogą być często traktowane jako liczby, które reprezentują adres pamięci, jak 0x0A31FCF20 (lub 2736770848 w dziesiętnym), lub 0xCAFEDEAD (czasami systemy używają tego do wskazania błędu, nie pamiętam szczegółów.)

Porównanie wskaźników jest często używane w sortowaniu tablic wskaźników. Posortowane tablice wskaźników są pomocne, gdy musisz sprawdzić, czy wskaźnik znajduje się na liście wskaźników; jeśli lista jest posortowana, nie musisz przeglądać każdego elementu listy, aby obliczyć jeśli wskaźnik jest na tej liście. Musisz użyć porównań, aby posortować listę.

Arytmetyka wskaźnika jest często używana, gdy masz wskaźnik do fragmentu danych i musisz uzyskać dostęp do czegoś, co nie znajduje się na początku fragmentu danych. Na przykład:

const char *string = "hello world!"
const char *substring = string+6;
std::cout << string << "\n";
std::cout << substring << std::endl;

To by wyszło:

hello world!
world!

Tutaj mamy ciąg znaków po pierwszych 6 znakach " hello world!", lub "world!". Należy pamiętać, że należy użyć std::string tam, gdzie jest dostępny, zamiast, jeśli to możliwe. Koncepcja bardzo podobnie do arytmetyki wskaźników są Iteratory dostępu losowego.

Odejmowanie wskaźników może pomóc Ci znaleźć odległość między tymi dwoma wskaźnikami. Jeśli masz wskaźnik do pierwszego elementu tablicy i wskaźnik do jednego elementu poza ostatnim elementem tablicy, odejmowanie tych dwóch wskaźników pomaga znaleźć rozmiar tablicy.

Innym przypadkiem, w którym można traktować wskaźniki jako liczby całkowite, jest zoptymalizowana wersja listy połączonej, zwana listą połączoną XOR. Możesz znaleźć więcej szczegóły na ten temat tutaj . Mogę to rozwinąć, jeśli chcesz; daj mi znać w komentarzach.

 -1
Author: vedosity,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-30 00:17:16

Pierwsze wyrażenie odejmuje jeden wskaźnik od drugiego. Jako prosty przykład, dlaczego może to być przydatne, rozważ ciąg C. Łańcuch jest w ciągłej pamięci, więc jeśli masz adres pierwszego znaku łańcucha i Adres ostatniego znaku, możesz znaleźć Długość łańcucha wykonując:

int strLength = (last_char_address - first_char_address) + 1;

Taka arytmetyka wskaźnika to Typ , co oznacza, że wynik arytmetyki reprezentuje liczbę elementów-określonego typu-pomiędzy dwa punkty. W powyższym przykładzie używając char, różnica polega na liczbie znaków. Działa to podobnie dla np. wskaźników do dwóch structs.

Podobnie, twoje drugie wyrażenie jest po prostu porównaniem wskaźników i wynikiem będzie 1 lub 0. Jako bardzo prosty przykład, adres elementu 5 tablica jest zawsze > adresem elementu 4: &string[4] > &string[5] to prawda.

 -1
Author: pb2q,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-09 23:14:08

Możesz traktować adres jak int Na wiele sposobów. Jedyną różnicą jest to, że int reprezentuje liczbę rozmiarów w tym adresie. Na przykład, jeśli int * p ma wartość, powiedzmy, 234 (z jakiejś bezpiecznej instrukcji na przykład p = new int[12];), reprezentuje adres 234. Jeśli zrobimy p += 1;, to po prostu dodamy, pod względem int-size. Teraz p jest (zakładając 4-bajtowy int dla tego przykładu) 238, aka p[1]. W rzeczywistości {[8] } jest równoważne *(p+x). Można porównać i takie jak int. W niektórych kontekstach jest to przydatne, na przykład w podanym przykładzie p[0] odnosi się teraz do tego, co było p[1]. Pozwala to uniknąć konieczności robienia czegoś takiego, jak p = &p[1], co niepotrzebnie

 -2
Author: Cosine,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-14 22:26:58