Як порівняти покажчики?


88

Припустимо, у мене є 2 вказівники:

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

Якщо я хочу порівняти їх і подивитися, чи вказують вони на одне і те ж місце (a == b), працює?


6
IIRC порівняння покажчиків не визначено, якщо вони не вказують на елементи в одному масиві
sehe

1
@sehe Привіт, ваша відповідь нижче скасовує цей старий коментар.
Спенсер

Відповіді:


72

Так, це визначення рівності покажчика: вони обидва вказують на одне і те ж розташування (або є псевдонімами вказівника )


2
Покажчик - це (по-простому кажучи) по суті ціле значення для адреси пам'яті у вашому комп'ютері. Це як порівняння цілих чисел.
Kemin Zhou

5
@KeminZhou: це справедливо для більшості сучасних комп’ютерів, але загалом помилково. Навіть на старому ПК 1980 року AT 8086 це було помилковим
Базиль Старинкевич

109

Для кількох фактів наведемо відповідний текст із специфікацій

Оператор рівності (==,! =)

Покажчики на об'єкти одного типу можна порівняти за рівністю з "інтуїтивними" очікуваними результатами:

З § 5.10 стандарту C ++ 11:

Покажчики одного типу (після перетворення покажчиків) можна порівняти за рівністю. Два покажчики одного типу порівнюють рівні тоді і тільки тоді, коли обидва вони є нульовими, обидва вказують на одну і ту ж функцію, або обидва представляють одну і ту ж адресу ( 3.9.2 ).

(залишаючи деталі щодо порівняння покажчиків з константами члена та / або нульового вказівника - вони продовжують той самий рядок "Робити те, що я маю на увазі" :)

  • [...] Якщо обидва операнди є нульовими, вони порівнюються рівними. В іншому випадку, якщо лише один є нульовим, вони порівнюють нерівне. [...]

Найбільш помітне застереження пов’язане з віртуальними програмами, і, схоже, логічно це також очікувати:

  • [...] якщо будь-який є вказівником на функцію віртуального члена, результат не вказаний. В іншому випадку вони порівнюють рівне тоді і тільки тоді, коли вони посилалися б на одного і того ж члена того самого найбільш похідного об'єкта (1.8) або на той самий суб'єкт, якщо б вони були визначені гіпотетичним об'єктом відповідного типу класу. [...]

Реляційні оператори (<,>, <=,> =)

З § 5.9 стандарту C ++ 11:

Покажчики на об'єкти або функції одного типу (після перетворення покажчиків) можна порівнювати, і результат визначається наступним чином:

  1. Якщо два вказівники p і q одного і того ж типу вказують на один і той же об'єкт або функцію, або обидва вказують один за одним кінцем одного масиву, або обидва є нульовими, тоді p<=qі p>=qобидва дають true, p<qі p>qобидва дають false.
  2. Якщо два вказівники p і q одного і того ж типу вказують на різні об'єкти, які не є членами одного і того ж об'єкта або елементів одного масиву або різних функцій, або якщо лише один з них є нульовим, результати p<q, p>q, p<=q,та p>=q не вказані .
  3. Якщо два покажчики рекурсивно вказують на нестатичні члени даних одного і того ж об'єкта, або на суб'єкти або елементи масиву таких членів, вказівник на пізніше оголошений член порівнює більше за умови, що два члени мають однаковий контроль доступу (пункт 11) і за умови, що їхній клас не є об’єднанням.
  4. Якщо два вказівники вказують на нестатичні члени даних одного і того ж об'єкта з різним контролем доступу (пункт 11), результат не вказаний.
  5. Якщо два вказівники вказують на нестатичні члени даних одного об'єкта об'єднання, вони порівнюють рівні (після перетворення в void*, якщо потрібно). Якщо два вказівники вказують на елементи одного і того ж масиву або один за кінцем масиву, вказівник на об'єкт із старшим індексом порівнюється вище.
  6. Інші порівняння покажчиків не вказані.

Отже, якщо у вас було:

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

Також добре:

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

Але це залежить від somethingВашого запитання:

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
}

Бонус: що ще є у стандартній бібліотеці?

§ 20.8.5 / 8 : «Для шаблонів greater, less, greater_equalі less_equal, спеціалізації для будь-якого типу покажчика дають повний порядок, навіть якщо вбудовані оператори <, >, <=, >=цього не роблять.»

Отже, ви можете замовити будь-яке непарне void*, якщо ви використовуєте std::less<>друзів, а не голі operator<.


Чи int *a = arr;виграє рядок включенням посилання на stackoverflow.com/questions/8412694/address-of-array ? Я не впевнений, наскільки це відповідає запитанню, хоча ...
безглуздий

Сьогодні неповторний @JerryCoffin дав мені зрозуміти той факт, що стандартна бібліотека має більш жорсткі специфікації для шаблонів об'єктів функції, визначених у <functional>. Додано.
sehe

Здається, цей розділ змінився в поточному проекті C ++. Якщо я не зрозумію це неправильно, більше не буде вказано
SomeWittyUsername


25

==Оператор покажчиків буде порівнювати їх числову адресу і , отже , визначити , якщо вони вказують на той же об'єкт.


12
Це трохи складніше, якщо йдеться про багаторазове успадкування.
fredoverflow

17

Підсумовуючи. Якщо ми хочемо перевірити, чи вказують два вказівники на одне і те ж місце в пам'яті, ми можемо це зробити. Крім того, якщо ми хочемо порівняти вміст пам'яті, на яку вказують два вказівники, ми можемо зробити це теж, просто пам'ятаємо, щоб спершу їх розмежувати.

Якщо маємо

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

які є двома покажчиками одного типу, ми можемо:

Порівняйте адресу пам'яті:

a==b

та порівняти вміст:

*a==*b

1

Простий код для перевірки псевдонімів покажчика:

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;
}

Вихід:

p1 and p2 alias each other
p3 and p4 do not alias each other

1

Порівняння покажчиків не є портативним, наприклад, у DOS різні значення покажчиків вказують на одне і те ж розташування, порівняння покажчиків повертає значення false.

/*--{++: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}--------------------------------------------------*/

Складіть його під Borland C 5.0, ось результат:

/*--{++: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}--------------------------------------------------*/
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.