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


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<.


Будет ли 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

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.