Как сравнить указатели?

88

Предположим, у меня есть 2 указателя:

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

Если я хочу сравнить их и посмотреть, указывают ли они на одно и то же место, работает ли (a == b)?

Джои Франклин
источник
6
Указатели сравнения IIRC не определены, если они не указывают на элементы в одном массиве
см.
1
@sehe Эй, ваш ответ ниже отменяет этот старый комментарий.
Спенсер

Ответы:

72

Да, это определение равенства указателей: они оба указывают на одно и то же место (или являются псевдонимами указателя )

Василий Старынкевич
источник
2
Указатель - это (в терминологии непрофессионала) по сути целочисленное значение для адреса памяти вашего компьютера. Это похоже на сравнение целых чисел.
Кемин Чжоу
5
@KeminZhou: это верно на большинстве современных компьютеров, но в целом неверно. Даже на старом ПК 8086 1980 года это было ложно
Старынкевич
109

Немного фактов вот соответствующий текст из спецификаций

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

Указатели на объекты одного типа можно сравнить на предмет равенства с «интуитивно понятными» ожидаемыми результатами:

Из § 5.10 стандарта C ++ 11:

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

(опуская подробности сравнения указателей на константы члена и / или нулевого указателя - они продолжаются в той же строке «Do What I Mean» :)

  • [...] Если оба операнда равны нулю, они сравниваются как равные. В противном случае, если только один из них равен нулю, они сравнивают неравные. [...]

Самая «заметная» оговорка связана с виртуальными машинами, и этого, похоже, тоже логично ожидать:

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

Операторы отношения (<,>, <=,> =)

Из § 5.9 стандарта C ++ 11:

Указатели на объекты или функции одного и того же типа (после преобразования указателя) можно сравнивать с результатом, определяемым следующим образом:

  1. Если два указателя р и д из одной и той же точки типа к тому же объекту или функции, или как точка одной мимо конца того же массива, или оба нуль, то p<=qи p>=qкак выход истинного и p<qи p>qодновременно выход ложным.
  2. Если два указателя p и q одного и того же типа указывают на разные объекты, которые не являются членами одного и того же объекта или элементов одного и того же массива или разных функций, или если только один из них имеет значение NULL, результаты 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<.

sehe
источник
Будет ли int *a = arr;полезно включать в строку ссылку на stackoverflow.com/questions/8412694/address-of-array ? Я не уверен, что это достаточно уместно для заданного вопроса ...
nonsensickle
Сегодня неподражаемый @JerryCoffin сообщил мне о том, что стандартная библиотека имеет более строгие спецификации для шаблонов объектов функций, определенных в <functional>. Добавлен.
sehe
Похоже, что эта глава была изменена в текущем проекте C ++. Если я не понял это неправильно, больше не будет неопределенного поведения: open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf
SomeWittyUsername
25

==Оператор указателей будет сравнивать их числовой адрес и , следовательно , определить , если они указывают на тот же объект.

ДжаредПар
источник
12
Это немного сложнее, если задействовано множественное наследование.
fredoverflow
17

Подводить итоги. Если мы хотим увидеть, указывают ли два указателя на одно и то же место в памяти, мы можем это сделать. Также, если мы хотим сравнить содержимое памяти, на которую указывают два указателя, мы тоже можем это сделать, просто не забудьте сначала разыменовать их.

Если у нас есть

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

которые представляют собой два указателя одного типа, мы можем:

Сравните адрес памяти:

a==b

и сравните содержимое:

*a==*b
ldgorman
источник
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}--------------------------------------------------*/
Мацей Лабанович
источник