【问题标题】:Comparison Of Pointers指针比较
【发布时间】:2017-01-19 06:49:41
【问题描述】:

我想比较pp + 1qq + 1的内存地址和指针值。

我想了解以下值的实际含义。我无法完全理解发生了什么。

当我运行代码时:

  • 每次我将地址 p 与另一个指针进行比较时,我都会得到 00EFF680 的答案。
  • 每次我将q 的地址与另一个指针进行比较时,我都会得到00EFF670 的答案。
  • 当我查看p 的指针值时,我得到了15726208 的答案。
  • 当我查看p + 1 的指针值时,我得到了15726212 的答案。

  • 当我查看q 的指针值时,我得到了15726192 的答案

  • 我得到了15726200 的答案,我查看了q + 1 的指针值。

代码

#include <iostream>
#include <string>
using namespace std;
int main()
{
    int val = 20;
    double valD = 20;
    int *p = &val;
    double *q;
    q = &valD;
    cout << "Memory Address" << endl;
    cout << p == p + 1;
    cout << endl;
    cout << q == q + 1;
    cout << endl;
    cout << p == q;
    cout << endl;
    cout << q == p;
    cout << endl;
    cout << p == q + 1;
    cout << endl;
    cout << q == p + 1;
    cout << endl;
    cout << "Now Compare Pointer Value" << endl;
    cout << (unsigned long)(p) << endl;
    cout << (unsigned long) (p + 1) << endl;
    cout << (unsigned long)(q) << endl;
    cout << (unsigned long) (q + 1) << endl;
    cout <<"--------" << endl;
    return 0;
}

【问题讨论】:

  • 运算符优先级。
  • 你的确切问题是什么?
  • 您机器上的integers 是 4 字节长,这就是为什么 pp + 1 指针之间有 4 个字节的差异,而 doubles 是 8 字节长.
  • 如果你有一个type 和一个指向它的指针,比如type* pointer;,这个表达式将(几乎)总是正确的:((pointer + 1) - pointer) == sizeof(type)
  • @Jezor 我想比较 p、p +1 、q 和 q + 1。并了解结果的含义。

标签: c++


【解决方案1】:

有一些警告和/或错误。

首先是重载运算符

第二个是不同指针类型('int *'和'double *')的比较。

对于前者,括号必须放在比较周围以允许比较优先。对于后者,指针应转换为允许安全比较的类型(例如 size_t)。

例如在第 20 行,以下内容会很好地工作。

cout << ((size_t) p == (size_t) (q + 1));

至于第 25-28 行,这是标准的指针算法。见说明here

【讨论】:

    【解决方案2】:

    如果要打印指针的值,可以将其强制转换为void *,例如:

    cout << static_cast<void*>(p) << endl;
    

    void* 是一个不定类型的指针。 C 代码经常使用它来指向在编译时类型未知的任意数据; C++ 通常为此使用类层次结构。不过,这里的意思是:将此指针视为内存位置。

    将整数添加到指针会得到另一个指针,因此您想在此处使用相同的技术:

    cout << static_cast<void*>(p+1) << endl;
    

    然而,两个指针之间的区别是一个有符号整数(精确的类型,如果你需要的话,在&lt;cstddef&gt; 中定义为ptrdiff_t,但幸运的是你不需要担心@ 987654327@),所以你只想直接使用它:

    cout << (p+1) - p << endl;
    cout << reinterpret_cast<char*>(p+1) - reinterpret_cast<char*>(p) << endl;
    cout << (q - p) << endl;
    

    第二行转换为 char*,因为 char 的大小始终为 1。这是一个很大的提示。

    至于幕后发生了什么:比较你得到的数字sizeof(*p)sizeof(*q),它们是pq指向的对象的大小。

    【讨论】:

      【解决方案3】:

      关于你的问题:

      我想比较 p、p+1、q 和 q+1。并了解结果的含义。

      如果 p 位于地址 0x80000000,则 p+1 位于地址 0x80000000 + sizeof(*p)。如果 *p 是 int,那么这是 0x80000000 + 0x8 = 0x80000008。同样的推理也适用于 q。

      因此,如果您执行p == p + 1,那么编译器将首先执行添加:p+1,然后进行比较,因此您将得到0x80000000 == 0x80000008,这会导致错误。

      现在到你的代码:

      cout << p == p + 1;
      

      实际上等价于:

      (cout << p) == p + 1;
      

      这是因为&lt;&lt;precedence 高于==。实际上你应该得到一个编译错误。

      另一件事是将double*int* 等不相关类型的指针进行比较,如果不进行强制转换,它不应该编译。

      【讨论】:

        【解决方案4】:

        也许一张图片会有所帮助(对于 64 位机器)

        p 是一个 64 位指针,指向一个 32 位(4 字节)的 int。绿色指针 p 占用 8 个字节。 p指向的数据,黄色的int val占4个字节。 p 加 1 到 val 的第 4 个字节之后的地址。

        与指针 q 类似,它指向一个 64 位(8 字节)双精度。将 1 加到 q 会到达 valD 的第 8 个字节之后的地址。

        【讨论】:

          【解决方案5】:

          打印的指针值可能会在每次执行时发生变化(请参阅 why the addresses of local variables can be different every timeAddress Space Layout Randomization

          • 每次我将地址 p 与另一个指针进行比较时,我都会得到 00EFF680 的答案。

            int val = 20;
            double valD = 20;
            int *p = &val;
            cout << p == p + 1;
            

            由于operator &lt;&lt;operator == 上的优先级更高,它被翻译成(cout &lt;&lt; p) == p + 1;

            它打印&amp;val的十六进制值,main函数的堆栈帧上的第一个地址。

            请注意,在堆栈中,地址是递减的(请参阅why does the stack address grow towards decreasing memory addresses)。

          • 每次我将 q 的地址与另一个指针进行比较时,我都会得到 00EFF670 的答案。

            double *q = &valD;
            cout << q == q + 1;
            

            由于operator &lt;&lt;operator ==上的优先级,它被翻译成(cout &lt;&lt; q) == q + 1;

            它在main函数的堆栈帧上打印&amp;valD的十六进制值,第二个地址。

            注意&amp;valD &lt;= &amp;val - sizeof(decltype(valD) = double) == &amp;val - 8 因为val 就在堆栈上的valD 之后。这是一个尊重一些对齐约束的编译器选择。

          • 当我查看 p 的指针值时,我得到的答案是 15726208。

            cout << (unsigned long)(p) << endl;
            

            它只是打印&amp;val的十进制值

          • 当我查看 p + 1 的指针值时,我得到的答案是 15726212。

            int *p = &val;
            cout << (unsigned long) (p + 1) << endl;
            

            它打印&amp;val + sizeof(*decltype(p)) = &amp;val + sizeof(int) = &amp;val + 4 的十进制值,因为在您的机器上int = 32 位

            请注意,如果p 是指向t 类型的指针,则p+1p + sizeof(t),以避免数组索引中的内存重叠。

            请注意,如果p 是指向void 的指针,则p+1 应该是未定义的(请参阅void pointer arithmetic

          • 当我查看 q 的指针值时,我得到的答案是 15726192

            cout << (unsigned long)(q) << endl;
            

            打印&valD的十进制值

          • 我得到的答案是 15726200 Wehn 我查看了 q + 1 的指针值。

            cout << (unsigned long) (q + 1) << endl;
            

            它打印&amp;val + sizeof(*decltype(p)) = &amp;valD + sizeof(double) = &amp;valD + 8的十进制值

          【讨论】:

            【解决方案6】:

            在 C 和 C++ 中,指针运算与数组操作密切相关。目标是

            int array[3] = { 1, 10, 100 };
            int *ptr     = { 1, 10, 100 };
            
            std::cout << array[2] << '\n';
            std::cout << *(ptr + 2)  << '\n';
            

            输出两个100s。这允许语言将数组和指针视为等价的 - 这与“相同”或“相等”不同,请参阅the C FAQ 以获得澄清。

            这意味着该语言允许:

            int array[3] = { 1, 10, 100 };
            int *ptr     = { 1, 10, 100 };
            

            然后

            std::cout << (void*)array << ", " << (void*)&array[0] << '\n';
            

            两次输出第一个元素的地址,第一个array表现得像一个指针。

            std::cout << (void*)(array + 1) << ", " << (void*)&array[1] << '\n';
            

            打印数组的第二个元素的地址,同样array在第一种情况下表现得像一个指针。

            std::cout << ptr[2] << ", " << *(ptr + 2) << '\n';
            

            打印 ptr (100) 的元素 #3 两次,这里 ptr 在第一次使用时表现得像一个数组,

            std::cout << (void*)ptr << ", " << (void*)&ptr[0] << '\n';
            

            两次打印ptr 的值,再次打印ptr 在第二次使用时表现得像一个数组,

            但这会让人们措手不及。

            const char* h = "hello";  // h points to the character 'h'.
            std::cout << (void*)h << ", " << (void*)(h+1);
            

            这会打印出h 的值,然后是高一的值。但这纯粹是因为h 的类型是一个指向one-byte-sized 数据类型的指针。

            h + 1;
            

            h + (sizeof(*h)*1);
            

            如果我们写:

            const char* hp = "hello";
            short int* sip = { 1 };
            int* ip = { 1 };
            
            std::cout << (void*)hp << ", " << (void*)(hp + 1) << "\n";
            std::cout << (void*)sip << ", " << (void*)(sip + 1) << "\n";
            std::cout << (void*)ip << ", " << (void*)(ip + 1) << "\n";
            

            输出的第一行将显示两个相隔 1 字节 (sizeof char) 的值,后两个值将相隔 2 个字节 (sizeof short int),最后一个将相隔四个字节 (sizeof int)。

            &lt;&lt; 运算符调用

            template<typename T>
            std::ostream& operator << (std::ostream& stream, const T& instance);
            

            运算符本身的优先级很高,高于==,所以你实际写的是:

            (std::cout << p) == p + 1
            

            你需要写的是

            std::cout << (p == p + 1)
            

            如果值不同,这将打印0int(false) 的结果),如果值相同,则打印1int(true) 的结果)。

            【讨论】: