如何比较指针

How to compare pointers?

本文关键字:指针 比较 何比较      更新时间:2023-10-16

假设我有两个指针:

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

如果我想比较它们,看看它们是否指向同一个地方(a==b)有效吗?

相等运算符(==,!=)

指向相同类型对象的指针可以与"直观"的预期结果进行比较:

来自C++11标准的§5.10

相同类型的指针(指针转换后)可以进行相等性比较。如果并且只有当它们都为空,都指向同一函数,或者都表示同一地址时(3.9.2)。

(省略了指向成员和/或空指针常量的指针的比较细节-它们在"Do What I Mean"的同一行继续:)

  • […]如果两个操作数都为null,则它们的比较结果相等。否则,如果只有一个为空,则它们比较不相等。[…]

最"明显"的警告与虚拟技术有关,这似乎也是合乎逻辑的:

  • […]如果其中一个是指向虚拟成员函数的指针,则未指定结果。否则他们比较equal当且仅当它们引用同一派生对象的同一成员时(1.8)或者相同的子对象(如果它们被关联类类型的假设对象取消引用)。[…]

关系运算符(<,>,<=,>=)

来自C++11标准的§5.9

可以比较指向相同类型的对象或函数的指针(在指针转换之后),结果定义如下:

  1. 如果同一类型的两个指针p和q指向同一对象,或者函数,或者两个都指向同一数组末尾的一个,或者都是则p<=qp>=q都产生true,而p<qp>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:"对于模板greaterlessgreater_equalless_equal,任何指针类型的专门化都会产生总顺序,即使内置运算符<><=>=没有。"

因此,您可以全局订购任何奇数void*,只要您使用std::less<>和朋友,而不是裸operator<

是的,这是原始指针相等的定义:它们都指向同一位置(或是指针别名);通常在运行用C++编码并由某些操作系统管理的应用程序的进程的虚拟地址空间中(但C++也可以用于使用具有Harward架构的微控制器对嵌入式设备进行编程:在这样的微控制器上,一些指针投射是被禁止的,而且毫无意义,因为只读数据可能位于代码ROM中)

对于C++,请阅读一本好的C++编程书籍,查看这个C++参考网站,阅读C++编译器的文档(可能是GCC或Clang),并考虑使用智能指针进行编码。也许还可以阅读一些C++标准草案,比如n4713,或者从你的ISO代表那里购买官方标准。

垃圾收集的概念和术语在管理通过动态分配获得的指针和内存区域(例如::operator new)时也是相关的,因此请阅读GC手册。

有关Linux机器上的指针,请参阅此。

指针上的==运算符将比较它们的数字地址,从而确定它们是否指向同一对象。

综上所述。如果我们想看看两个指针是否指向同一个内存位置,我们可以这样做。此外,如果我们想比较两个指针指向的内存内容,我们也可以这样做,只需记住先取消引用它们。

如果我们有

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

这是两个相同类型的指针:

比较内存地址:

a==b

并比较内容:

*a==*b

比较指针是不可移植的,例如,在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 -> @%pn", val_a, (void *)(&val_a));
  printf("*ptr_0 = %d -> @%pn", *ptr_0, (void *)ptr_0);
  printf("*ptr_1 = %d -> @%pn", *ptr_1, (void *)ptr_1);
  /* Check what returns the pointers comparison: */
  printf("&val_a == ptr_0 ====> %dn", &val_a == ptr_0);
  printf("&val_a == ptr_1 ====> %dn", &val_a == ptr_1);
  printf(" ptr_0 == ptr_1 ====> %dn",  ptr_0 == ptr_1);
  printf("val_a = %dn", val_a);
  printf(">> *ptr_0 += 100;n");
             *ptr_0 += 100;
  printf("val_a = %dn", val_a);
  printf(">> *ptr_1 += 500;n");
             *ptr_1 += 500;
  printf("val_a = %dn", 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}--------------------------------------------------*/

它取决于值的类型以及运算符的定义方式。例如,字符串比较是按值进行的,而不是按地址进行的。但char*通常是按地址排列的(我认为)。

对粗心的人来说是个大陷阱。没有保证的指针比较运算符,但是

  (void *)a == (void *)b 

可能相当安全。

检查指针别名的简单代码:

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