【问题标题】:How to compare pointers?如何比较指针?
【发布时间】:2012-02-23 13:48:25
【问题描述】:

假设我有 2 个指针:

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

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

【问题讨论】:

  • IIRC 比较指针是未定义的,除非它们指向同一个数组中的元素
  • @sehe 嘿,您在下面的回答取消了这条旧评论。

标签: c++ pointers


【解决方案1】:

关于事实这里是规范中的相关文本

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

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

来自 C++11 标准的 § 5.10

相同类型的指针 (指针转换后)可以比较是否相等。相同类型的两个指针比较相等 if 并且只有当它们都为空,都指向同一个函数,或者都代表同一个地址时(3.9.2)。

(省略了比较成员指针和/或空指针常量的详细信息 - 它们继续沿着“Do What I mean”的同一行:)

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

最“显着”的警告与虚拟有关,这似乎也是合乎逻辑的事情:

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

关系运算符 (,=)

来自 C++11 标准的 § 5.9

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

  1. 如果两个相同类型的指针 p 和 q 指向同一个对象或 函数,或者两者都指向同一个数组的末尾,或者两者都是 null,然后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,任何指针类型的特化都会产生一个总顺序,即使内置运算符 &lt;&gt;&lt;=&gt;= 没有。"

所以,您可以全局订购任何奇怪的void*,只要您使用std::less&lt;&gt; 和朋友,而不是单纯的operator&lt;

【讨论】:

  • int *a = arr; 行是否会受益于包含对 stackoverflow.com/questions/8412694/address-of-array 的引用?我不确定它是否与所提出的问题足够相关......
  • 今天,无与伦比的@JerryCoffin 让我意识到标准库对&lt;functional&gt; 中定义的函数对象模板有更严格的规范。已添加。
  • 在正在进行的 C++ 草案中,这一章似乎有所改变。除非我误解了,否则没有更多未指明的行为:open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf
  • 错了。 "a" + "b" == "ab" 可能是真的,字符串的值比较。操作员做他们想做的事。对于粗心的人来说,这是一个很大的陷阱。没有安全的 ptr 操作。而对于流 == 特别奇怪。
【解决方案2】:

是的,这就是raw pointer相等的定义:它们都指向同一个位置(或者是pointer aliases);通常在processvirtual address space 中运行用C++ 编码并由一些operating system 管理的应用程序(但C++ 也可用于使用具有Harward architecture 的微控制器对嵌入式设备进行编程:在此类微控制器上一些指针强制转换是被禁止的并且没有任何意义——因为只读数据可以位于代码 ROM 中)

对于 C++,请阅读 a good C++ programming book,查看 this C++ reference 网站,阅读 C++ 编译器的文档(可能是 GCCClang)并考虑使用 smart pointers 进行编码。也可以阅读一些 C++ 标准草案,例如 n4713 或从您的 ISO 代表处购买官方标准。

garbage collection 的概念和术语在管理通过动态分配获得的指针和内存区域时也很相关(例如::operator new),所以不妨阅读GC handbook

有关 Linux 机器上的指针,另请参阅 this

【讨论】:

  • 指针(用外行的话来说)本质上是计算机内存地址的整数值。这就像比较整数。
  • @KeminZhou:这在大多数当前的计算机上都是正确的,但通常是错误的。即使在 1980 年的旧 PC AT 8086 上也是错误的
  • 绝对不是。在 C++ 中,运算符只是可以随心所欲的任意函数。特别是,对于字符串 == 是值比较,而不是字符串比较。对于遍历流,它会做非常奇怪的事情。对于粗心的人来说,这是一个很大的陷阱。如果要比较指针,则需要强制转换为某物。
【解决方案3】:

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

【讨论】:

  • 如果涉及到多重继承,会稍微复杂一些。
【解决方案4】:

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

如果我们有

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

我们可以使用两个相同类型的指针:

比较内存地址:

a==b

并比较内容:

*a==*b

【讨论】:

    【解决方案5】:

    比较指针不可移植, 例如在 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}--------------------------------------------------*/
    

    【讨论】:

    • 在 2020 年底或 2021 年,是时候切换到更好的编译器了,比如 GCC
    • @BasileStarynkevitch:在 clang 和 gcc 中,指向一个对象的指针和刚好超过地址空间中恰好位于它之前的对象末尾的指针之间的相等比较——明确的场景标准所解决的问题——倾向于以一种既不符合产生真值的比较也不符合比较假的方式行事。
    【解决方案6】:

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

    粗心大意的陷阱。没有保证的指针比较运算符,但是

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

    可能相当安全。

    【讨论】:

      【解决方案7】:

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

      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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-01-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-07-01
        • 2012-10-05
        相关资源
        最近更新 更多