代码之家  ›  专栏  ›  技术社区  ›  sharptooth

除了存储地址,指针如何实现?

  •  10
  • sharptooth  · 技术社区  · 13 年前

    another question asking for questions every decent C++ programmer should be able to answer . My suggestion

    Q: How does a pointer point to an object?
    A: The pointer stores the address of that object.
    

    但是 user R.. 不同意A我向Q提议-他说 正确的答案是“它是特定于实现的”。虽然现在的实现将数字地址存储为指针,但没有理由不做更详细的事情 .

    我当然不能不同意 可以是

    除了在整数类型变量中存储地址之外,C++中指针的其他实际使用还有哪些?铸造如何(尤其是 dynamic_cast

    6 回复  |  直到 7 年前
        1
  •  6
  •   Jander    13 年前

    在概念层面上,我同意你的观点——我将对象的地址定义为“在内存中定位对象所需的信息”。不过,地址的样子可能会有很大的不同。

    看到了吗 http://c-faq.com/null/machexamp.html

        2
  •  5
  •   Matthieu M.    13 年前

    我会打电话的 Boost.Interprocess

    进程间指针是从映射内存区域开始的偏移量。这允许从另一个进程获取指针,映射内存区域(该指针地址可能与传递指针的进程中的地址不同),并且仍然可以获取相同的对象。

    因此,进程间指针不表示为地址,但可以解析为一个地址。

        3
  •  3
  •   Vijay Mathew Chor-ming Lung    13 年前

    如果我们熟悉使用指针算法访问数组元素,就很容易理解对象在内存中是如何布局的以及如何布局的 dynamic_cast 作品。考虑以下简单类:

    struct point
    {
        point (int x, int y) : x_ (x), y_ (y) { }
        int x_;
        int y_;
    };
    
    point* p = new point(10, 20); 
    

    p 分配给内存位置 0x01 . 比如说,它的成员变量存储在各自不同的位置 x_ 0x04 y_ 0x07 . 使物体形象化比较容易 p (就我们而言( 0x1 )指向数组的开头:

    0x01
    +-------+-------+
    |       |       |
    +---+---+----+--+
        |        |
        |        |
       0x04     0x07
     +-----+   +-----+
     |  10 |   | 20  |
     +-----+   +-----+
    

    因此,访问字段的代码实际上将变成使用指针算法访问数组元素:

    p->x_; // => **p
    p->y_; // => *(*(p + 1))
    

    对象数组

    0x01
    +-------+-------+-------+
    |       |       |       |
    +--+----+---+---+----+--+
       |        |        |
       |        |        |
      0x02     0x04     0x07
    +--+---+  +-----+   +-----+
    |  rc  |  |  10 |   | 20  |
    +------+  +-----+   +-----+
    

    第一个单元格指向引用计数的地址。编译器将发出适当的代码来访问

    p->x_; // => *(*(p + 1))
    p->y_; // => *(*(p + 2))
    

    现在很容易理解 作品。编译器通过向底层表示添加一个额外的隐藏指针来处理多态类。此指针包含另一个名为 函数表 ,它依次包含此类中虚拟函数实现的地址。但是第一次进入 函数表 很特别。它不指向函数地址,而是指向一个名为 type_info . 此对象包含对象的运行时类型信息和指向的指针 键入\u信息 它的基类。考虑以下示例:

    class Frame
    {
    public:
        virtual void render (Screen* s) = 0;
        // ....
    };
    
    class Window : public Frame
    { 
    public:
        virtual void render (Screen* s)
        {
            // ...
        }
        // ....
    private:
       int x_;
       int y_;
       int w_;
       int h_;
    };
    

    Window 将具有以下内存布局:

    window object (w)
    +---------+
    | &vtable +------------------+
    |         |                  |
    +----+----+                  |
    +---------+     vtable       |            Window type_info    Frame type_info
    |  &x_    |     +------------+-----+      +--------------+    +----------------+
    +---------+     | &type_info       +------+              +----+                |
    +---------+     |                  |      |              |    |                |
    |  &y_    |     +------------------+      +--------------+    +----------------+
    +---------+     +------------------+
    +---------+     | &Window::render()|
    +---------+     +------------------+    
    +---------+                     
    |  &h_    |
    +---------+
    

    现在想想当我们试着 Window* Frame* :

    Frame* f = dynamic_cast<Frame*> (w);
    

    将遵循 键入\u信息 w Frame 在其基类和赋值的列表中 w f . 如果找不到 在列表中, 0 表明铸造失败。这个 提供了一种经济的方式来表示 键入\u信息 动态播放 仅适用于具有 virtual 功能。限制 动态播放 从逻辑的角度来看,多态类型也是有意义的。也就是说,如果一个对象没有虚函数,那么在不知道其确切类型的情况下就不能安全地对其进行操作。

    不必是多态的。这允许我们将具体类型包装为多态类型:

    // no virtual functions
    class A 
    {
    };
    
    class B
    {
    public:
        virtual void f() = 0;
    };
    
    class C : public A, public B
    {
        virtual void f() { }
    };
    
    
    C* c = new C;
    A* a = dynamic_cast<A*>(c); // OK
    
        4
  •  2
  •   Necrolis    13 年前

    +-----------------------------------------------------------+
    |Segment 1 (addr: 0x00)                                     |
    | +-------------------------------------------------------+ |
    | |Block 1|Block 2|Block 3|Block 4|Block 5|Block 6|Block 7| |
    | +-------------------------------------------------------+ |
    +-----------------------------------------------------------+
    |Segment 2 (addr: 0xE0)                                     |
    | +-------------------------------------------------------+ |
    | |Block 1|Block 2|Block 3|Block 4|Block 5|Block 6|Block 7| |
    | +-------------------------------------------------------+ |
    +-----------------------------------------------------------+
    |Segment 3 (addr: 0x1C0)                                    |
    | +-------------------------------------------------------+ |
    | |Block 1|Block 2|Block 3|Block 4|Block 5|Block 6|Block 7| |
    | +-------------------------------------------------------+ |
    +-----------------------------------------------------------+
    

    假设我们有指针 2:5 可以通过执行以下操作转换为x86类型指针 ((2 - 1) * (7 * 32)) + (5 * 32) 是的 0x180 从第一段开始

        5
  •  1
  •   Abyx    13 年前

    智能指针是 指针

    非静态成员函数可能是复杂的结构,包含有关虚函数表的信息。

    迭代器是泛型 .

    可能正确的问题应该是这样的:

    Q: How does T* point to an object of type T? (T is not a type of non-static member function)
    A: When you dereference value of type T*, it contains the address of that object. (In any other time it can contain anything)
    
        6
  •  1
  •   YeahStu    13 年前

    我认为这样说是公平的,他们“存储”地址,只是这样说并不能传达太多。这只是另一种表达指针的方式 . 它们还可以存储其他信息,并且可以通过引用其他一些结构来存储实际的物理/虚拟数字地址,但是在C++语义方面,指针变量包含地址。

    Abyx提出了一个问题,即只有对象和函数指针表示地址。指向成员的指针不一定代表地址。但是C++标准明确地指出,标准中的“指针”一词不应该包括指向成员的指针。所以你可能不算。

    除segment:offset (这显然是一个由两个数字组成的地址),我能想到的最可信的“有趣的指针”应该是指针中包含某些类型信息的指针。在C++中,你不可能以降低你可以处理的空间为代价来优化RTTI,但你永远不知道。

    另一种可能性是,如果你正在执行垃圾回收C++,那么每个指针可以存储关于它指向堆栈还是堆的信息,也许你可以偷偷地输入一些信息来帮助精确地与保守的标记相关联。

    不过,我没有遇到任何人用C++中的指针做这些事情,所以我不能保证它们是真正的用途。还有其他存储类型和GC信息的方法,这可能更好。