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


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.