【问题标题】:How pointer know the end of the pointed element?指针如何知道指向元素的结尾?
【发布时间】:2016-07-01 09:41:59
【问题描述】:

我是 C++ 的菜鸟,我对指针感到困惑。

int 可以表示 -2147483647 到 2147483647 个值,因此总共有 4294967296 个值。

这需要32bit (2^32),因此需要4 byte

我发现,如果我从 ptr“指针”到 int 执行 ptr+1(指针运算),它会正确移动 4 个内存地址(4 个字节)。

但是这些“4字节”都是用来表达4294967296个值的。

定义类型“指向 int”的数据存储在哪里?我猜指针本身应该知道它指向的数据类型,即它是哪种类型的指针。这些信息存储在哪里?除了指向数据的地址?如果我“打印”指针,我只会得到地址,而不是其他信息......

【问题讨论】:

  • 提示:指向 int 的指针的类型是什么?看看ptr的声明,是怎么说的?
  • 指针不知道,编译器知道。
  • 如果您想要该指针的大小,请尝试在该指针上使用 sizeof 函数..
  • @Henrik:所以编译器会跟踪使用的每个“指针”? :O
  • @paizza 您的 C++ 代码已被编译,因此它显然会扫描您的所有代码,从而知道指针的每次使用。当代码显示ptr+1 时,它如何编译您的代码? ptr 是编译器的已知变量,否则会出现未知标识符编译错误。那么ptr的类型是什么?

标签: c++ pointers memory-management


【解决方案1】:

首先,C 标准不要求int 可以表示超出范围-3276732767 的值。还有一些现实世界的编译器只支持 16 位 int。编译器可以支持更大的范围,但不是必须的。

为了回答你的问题,表格声明

int *x;

告诉编译器x 是一个指向int 的指针。编译器本身是用关于所有本机类型(如int)大小的知识(硬编码或配置选项,取决于编译器的实现方式)实现的 - 并且也被实现为在任何使用sizeof 表达式的代码(如sizeof(int))。因此编译器可以实现指针算法来适应。

编译器——因为它已经解析和解释了你的代码——也知道每个值和变量的类型——包括指针和ints。

打印是另一回事 - 打印指针会打印内存中的地址,但没有类型信息,除非您编写代码来显式输出类型信息。

【讨论】:

    【解决方案2】:

    编译器有可用的类型信息来了解如何生成评估ptr + 1 等所需的底层机器代码。在机器级别,它相当于说ptr + sizeof(*ptr)。所以,如果你有这样的代码:

    int *ptr = // some address;
    ptr++;
    

    然后编译器(在 x86 上)将为 ptr++ 发出类似这样的内容:

    mov eax,dword ptr [ptr]  ; Load the pointer
    add eax,4                ; Add 4 to the address as sizeof(*ptr) is 4
    mov dword ptr [ptr],eax  ; Write the new value back
    

    编译器具有类型信息这一事实意味着ptr + 1 的评估是静态的,而不是动态的,并且当您通过指向基类型的指针传递派生类型的数组时可能会导致问题。举个例子:

    struct Base
    {
      int x;
    }; 
    
    struct Derived : Base
    {
      int y;
    };
    
    void PrintAll(Base *p, int numberOfItems)
    {
      while(numberOfItems--)
      {
        std::cout << p->x << std::endl;
        p++; // Oops!!!
      }
    }
    
    Derived data[10];
    Derived *ptr = data;
    PrintAll(ptr, 10);
    

    标有“糟糕!!!”的行没有按预期工作。尽管ptr 实际上指向Derived 类型的对象,但它实际上只会尝试移动ptr 以指向下一个Base 而不是Derived,因为编译器拥有的所有编译时间(即静态)信息可供它使用。这会导致未定义的行为。如果ptr 实际上指向了Base 的数组,那么我们就可以了。像这样的错误在编译时很难检测到,这就是为什么你最好使用像 std::vector 这样的容器来处理这类事情。

    【讨论】:

      【解决方案3】:

      编译器知道int 在目标处理器架构上的大小(事实上,所有已定义类型的大小——即使是编译器用户定义的类型)。

      编译器决定将已编译程序的指针算术运算的增量大小存储在何处。通常,它可以嵌入到指令流中。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-01-04
        • 2023-02-16
        • 1970-01-01
        • 2021-02-02
        • 1970-01-01
        • 1970-01-01
        • 2011-03-14
        相关资源
        最近更新 更多