Jak porównać wskaźniki?

88

Załóżmy, że mam 2 wskazówki:

int *a = something;
int *b = something;

Jeśli chcę je porównać i sprawdzić, czy wskazują na to samo miejsce, czy (a == b) działa?

Joey Franklin
źródło
6
IIRC porównywaniu wskaźników jest niezdefiniowana, chyba że wskazują na elementy w tej samej tablicy
sehe
1
@sehe Hej, Twoja odpowiedź poniżej anuluje ten stary komentarz.
Spencer,

Odpowiedzi:

72

Tak, to jest definicja równości wskaźników: oba wskazują tę samą lokalizację (lub są aliasami wskaźników )

Basile Starynkevitch
źródło
2
Wskaźnik jest (w języku laika) zasadniczo wartością całkowitą dla adresu pamięci w twoim komputerze. To jest jak porównywanie liczb całkowitych.
Kemin Zhou
5
@KeminZhou: jest to prawda na większości współczesnych komputerów, ale ogólnie jest fałszywa. Nawet na starym PC AT 8086 z 1980 roku było to fałszywe
Basile Starynkevitch
109

Po trochę faktów tutaj jest odpowiedni tekst specyfikacji

Operator równości (==,! =)

Wskaźniki do obiektów tego samego typu można porównać pod kątem równości z „intuicyjnymi” oczekiwanymi wynikami:

Od § 5.10 standardu C ++ 11:

Wskaźniki tego samego typu (po konwersji wskaźnika) można porównać pod kątem równości. Dwa wskaźniki tego samego typu porównują się równo wtedy i tylko wtedy, gdy oba mają wartość null, oba wskazują na tę samą funkcję lub oba reprezentują ten sam adres ( 3.9.2 ).

(pomijając szczegóły dotyczące porównania wskaźników do składowych i / lub stałych wskaźników zerowych - kontynuują one w tym samym wierszu „Zrób to, co mam na myśli” :)

  • [...] Jeśli oba operandy są zerowe, porównują równe. W przeciwnym razie, jeśli tylko jeden jest zerowy, porównują nierówności. [...]

Najbardziej `` rzucające się w oczy '' zastrzeżenie dotyczy wirtualiów i wydaje się, że jest to również logiczne zastrzeżenie:

  • [...] jeśli którykolwiek z nich jest wskaźnikiem do wirtualnej funkcji składowej, wynik jest nieokreślony. W przeciwnym razie porównują równe wtedy i tylko wtedy, gdy odnoszą się do tego samego elementu członkowskiego tego samego najbardziej pochodnego obiektu (1.8) lub do tego samego podobiektu, gdyby zostały wyłuskane z hipotetycznym obiektem skojarzonego typu klasy. […]

Operatory relacyjne (<,>, <=,> =)

Od § 5.9 standardu C ++ 11:

Wskaźniki do obiektów lub funkcji tego samego typu (po konwersji wskaźnika) można porównać, uzyskując wynik zdefiniowany w następujący sposób:

  1. Jeśli dwa wskaźniki p i q tego samego typu wskazują na ten sam obiekt lub funkcję, lub oba wskazują jeden za koniec tej samej tablicy lub oba mają wartość null, to p<=qi p>=qoba dają wartość true p<qi p>qobie dają false.
  2. Jeśli dwa wskaźniki p i q tego samego typu wskazują na różne obiekty, które nie są elementami składowymi tego samego obiektu lub elementów tej samej tablicy lub różnych funkcji, lub jeśli tylko jeden z nich jest pusty, wyniki operacji p<q, p>q, p<=q,i p>=q są nieokreślone .
  3. Jeśli dwa wskaźniki wskazują na niestatyczne elementy składowe danych tego samego obiektu lub podobiekty lub elementy tablicy takich elementów członkowskich, rekurencyjnie, wskaźnik do później zadeklarowanego elementu członkowskiego jest większy, pod warunkiem, że dwa elementy członkowskie mają tę samą kontrolę dostępu (klauzula 11) i pod warunkiem, że ich klasa nie jest związkiem.
  4. Jeśli dwa wskaźniki wskazują na niestatyczne elementy składowe danych tego samego obiektu z różną kontrolą dostępu (klauzula 11), wynik jest nieokreślony.
  5. Jeśli dwa wskaźniki wskazują na niestatyczne elementy składowe danych tego samego obiektu unii, porównują się równo (po konwersji void*, jeśli to konieczne). Jeśli dwa wskaźniki wskazują na elementy tej samej tablicy lub jeden za końcem tablicy, wskaźnik do obiektu z wyższym indeksem jest wyższy.
  6. Inne porównania wskaźników są nieokreślone.

Tak więc, gdybyś miał:

int arr[3];
int *a = arr;
int *b = a + 1;
assert(a != b); // OK! well defined

Też dobrze:

struct X { int x,y; } s;
int *a = &s.x;
int *b = &s.y;
assert(b > a); // OK! well defined

Ale to zależy od somethingpytania:

int g; 
int main()
{
     int h;
     int i;

     int *a = &g;
     int *b = &h; // can't compare a <=> b
     int *c = &i; // can't compare b <=> c, or a <=> c etc.
     // but a==b, b!=c, a!=c etc. are supported just fine
}

Bonus: co jeszcze jest w standardowej bibliotece?

§ 20.8.5 / 8 : „W przypadku szablonów greater, less, greater_equaloraz less_equal, że specjalizacje dla każdego rodzaju wskaźnik uzyskując całkowitą zamówienia, nawet jeśli wbudowany operatorów <, >, <=, >=nie.”

Możesz więc zamówić dowolne nieparzyste void*na całym świecie , o ile używasz std::less<>i przyjaciół, a nie gołych operator<.

sehe
źródło
Czy int *a = arr;wiersz skorzystałby na dołączeniu odwołania do stackoverflow.com/questions/8412694/address-of-array ? Nie jestem pewien, czy jest to wystarczające do zadanego pytania ...
nonsensickle
Dziś niepowtarzalny @JerryCoffin uświadomił mi, że biblioteka standardowa ma bardziej rygorystyczne specyfikacje dla szablonów obiektów funkcji zdefiniowanych w <functional>. Dodany.
sehe
Wygląda na to, że ten rozdział zmienił się w trwającym projekcie C ++. O ile nie zrozumiem tego źle, nie ma więcej nieokreślonego zachowania: open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf
SomeWittyUsername
@SomeWittyUsername było nadal prawdziwe przynajmniej w C ++ 14
Lightness Races in Orbit
25

==Operator na wskaźnikach będzie porównać swój adres numeryczny, a więc ustalić, czy wskazują one na tym samym obiekcie.

JaredPar
źródło
12
Jest to trochę bardziej skomplikowane, jeśli w grę wchodzi wielokrotne dziedziczenie.
fredoverflow
17

Podsumowując. Jeśli chcemy zobaczyć, czy dwa wskaźniki wskazują na tę samą lokalizację pamięci, możemy to zrobić. Również jeśli chcemy porównać zawartość pamięci wskazywaną przez dwa wskaźniki, możemy to również zrobić, po prostu pamiętajmy, aby najpierw je wyodrębnić.

Jeśli mamy

int *a = something; 
int *b = something;

które są dwoma wskaźnikami tego samego typu, możemy:

Porównaj adres pamięci:

a==b

i porównaj zawartość:

*a==*b
ldgorman
źródło
1

Prosty kod do sprawdzania aliasingu wskaźnika:

int main () {
    int a = 10, b = 20;
    int *p1, *p2, *p3, *p4;

    p1 = &a;
    p2 = &a;
    if(p1 == p2){
        std::cout<<"p1 and p2 alias each other"<<std::endl;
    }
    else{
        std::cout<<"p1 and p2 do not alias each other"<<std::endl;
    }
    //------------------------
    p3 = &a;
    p4 = &b;
    if(p3 == p4){
        std::cout<<"p3 and p4 alias each other"<<std::endl;
    }
    else{
        std::cout<<"p3 and p4 do not alias each other"<<std::endl;
    }
    return 0;
}

Wynik:

p1 and p2 alias each other
p3 and p4 do not alias each other
Pankaj Kumar Thapa
źródło
1

Porównywanie wskaźników nie jest przenośne, na przykład w DOS różne wartości wskaźników wskazują na tę samą lokalizację, porównanie wskaźników zwraca fałsz.

/*--{++:main.c}--------------------------------------------------*/
#include <dos.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
  int   val_a = 123;
  int * ptr_0 = &val_a;
  int * ptr_1 = MK_FP(FP_SEG(&val_a) + 1, FP_OFF(&val_a) - 16);

  printf(" val_a = %d -> @%p\n", val_a, (void *)(&val_a));
  printf("*ptr_0 = %d -> @%p\n", *ptr_0, (void *)ptr_0);
  printf("*ptr_1 = %d -> @%p\n", *ptr_1, (void *)ptr_1);

  /* Check what returns the pointers comparison: */
  printf("&val_a == ptr_0 ====> %d\n", &val_a == ptr_0);
  printf("&val_a == ptr_1 ====> %d\n", &val_a == ptr_1);
  printf(" ptr_0 == ptr_1 ====> %d\n",  ptr_0 == ptr_1);

  printf("val_a = %d\n", val_a);

  printf(">> *ptr_0 += 100;\n");
             *ptr_0 += 100;

  printf("val_a = %d\n", val_a);

  printf(">> *ptr_1 += 500;\n");
             *ptr_1 += 500;

  printf("val_a = %d\n", val_a);

  return EXIT_SUCCESS;
}
/*--{--:main.c}--------------------------------------------------*/

Skompiluj go pod Borland C 5.0, oto wynik:

/*--{++:result}--------------------------------------------------*/
 val_a = 123 -> @167A:0FFE
*ptr_0 = 123 -> @167A:0FFE
*ptr_1 = 123 -> @167B:0FEE
&val_a == ptr_0 ====> 1
&val_a == ptr_1 ====> 0
 ptr_0 == ptr_1 ====> 0
val_a = 123
>> *ptr_0 += 100;
val_a = 223
>> *ptr_1 += 500;
val_a = 723
/*--{--:result}--------------------------------------------------*/
Maciej Labanowicz
źródło