温故指针一

温故指针之data pointer

指针不过是有类型信息的地址, 它可以是一个函数的地址 function pointer, 也可是一个数据的地址 data pointer. 在我的 64bit macbook上写个测试程序做一点简单回顾

变量的地址和变量的类型有关

1. 全局和局部静态变量

已初始化的全局以及局部静态变量都是放在目标文件的 .data 段中
而未初始化的全局以及局部静态变量则放在目标文件的 .bss 段中

int statistic(const char* file, int line, FILE* fd=stderr) {
static uint64_t total_count = 0;
//…
}
(gdb) p &total_count
$1 = (uint64_t *) 0x100002290

2. 栈变量

函数的局部变量是在栈上分配的, 其地址是从栈顶向下增长的, 也就是说这个栈是个倒过来的, 栈顶在下面(低地址方向)

int test_point(int argc, char* argv[]) {
    int var = 1;
    int val = 2;
    int ptrSize = sizeof(void*);
    printf("pointer size=%d\n", ptrSize);
    printf("pointer addr--var:%p, val: %p\n", &var, &val);
}
pointer size=8
pointer addr--var:0x7fff5fbffaa4, val: 0x7fff5fbffaa0

(gdb)info register
栈底 rbp            0x7fff5fbffab0    0x7fff5fbffab0
栈顶 rsp            0x7fff5fbffaa0    0x7fff5fbffaa0

由于声明了两个int 变量, 栈顶向下移动了两次, 一次4个字节

3. 堆变量

堆变量是在堆上分配的, 其地址是向上增长的

class Point {
public:
    Point(double x):m_x(x) {}
    virtual ~Point() {}

    double x() const { return m_x; }
    virtual void dump() { printf("Point::dump x=%f\n", m_x); }
    virtual void move() { printf("Point::move point\n"); }
private:
    double m_x;

};

Point* pObj = new Point(30.0);
(gdb) p pObj
$4 = (Point *) 0x100200000

(gdb) p *pObj
$1 = {_vptr$Point = 0x100002120 <vtable for Point+16>, m_x = 30}

Class Point size is 16 bytes:

  • 一开始 8 bytes 是个vptr, 指向一个virtual table, 这个table的元素就是一个个虚函数地址, 用 gdb 看下这个指针的内容

    (gdb) p/x ((uint64_t)0x100200000)
    $6 = 0x100002120

    这是一个指针的指针, 指向的是virtual table 的地址, 看看这个虚表的内容

    (gdb) p/a ((uint64_t)0x100002120)@7
    $23 = {0x100001780 <_ZN5PointD1Ev>, 0x100001790 <_ZN5PointD0Ev>, 0x1000017a0 <_ZN5Point4dumpEv>, 0x1000017c0 <_ZN5Point4moveEv>, 0x7fff79823390, 0x100001be8 <_ZTS5Point>, 0x0}

    1) _ZN5PointD1Ev: 虚析构函数
    D0Ev mangled: heap object destructor is called when that version (ie with delete ; p destructor)

    2) _ZN5PointD0Ev: 虚析构函数
    D1Ev mangled: at the bottom of the class hierarchy, that most derived class, call the version of the stack so any object destructor are called that version.
    D0Ev mangled: located in a non-underlying class hierarchy, which is used as a base class, call the version.

    3) _ZN5Point4dumpEv: 虚函数 Point::dump

    4) _ZN5Point4moveEv: 虚函数 Point::move

    5) 0x7fff79823390, 不太清楚

    6) _ZTS5Point: typeinfo name for Point

    这里函数的名称不太容易看懂, 它们是经过C++编译器修饰过的, 可以用 binutils 里的 c++filt 来看 (macosx 上是gc++filt )

gc++filtZN5PointD1EvPoint:: Point() gc++filt _ZN5PointD0Ev
Point::~Point()
$ gc++filt _ZTS5Point
typeinfo name for Point

  • 接下来 8 bytes 是成员变量 m_x

    (gdb) p &pObj->m_x
    $3 = (double *) 0x100200518

C++的多重继承

需要用堆变量来支持C++的继承和多态的机制, 举例如下

class Printable
{
public:
    virtual int print() = 0;
    virtual ~Printable() {}
};

class Shape {
public:
    Shape(): m_color(0) {}
    virtual ~Shape() {}
    int GetColor() {return m_color; };
    void SetColor(int color) { m_color = color; };
    virtual int draw() = 0;
    virtual void dump() { printf("Shape::dump: color=%d", m_color);}
private:
    int m_color;    
};

class Circle: public Shape {
public:
    Circle(double x, double y, double r): Shape(), m_center_x(x), m_center_y(y), m_radius(r) { SetColor(1);}
    virtual int draw() { printf("Circle::draw: circle , color=%d\n", GetColor()); return 0; }
    virtual void dump() { printf("Circle::dump: x=%f, y=%f, r=%f\n", m_center_x, m_center_y, m_radius);}
private:
    double m_center_x;
    double m_center_y;
    double m_radius;
};

class Line: public Shape, public Printable {
public:
    Line(double x1, double y1, double x2, double y2): Shape(),m_start_x(x1), m_start_y(y1), m_end_x(x2), m_end_y(y2) {SetColor(2);}
    virtual int draw() { printf("Line::draw: line, color=%d\n", GetColor()); return 0;}
    virtual void dump() { printf("Line::dump: x1=%f, y1=%f, x2=%f, y2=%f\n", m_start_x, m_start_y, m_end_x, m_end_y);}
    virtual int print() { printf("Line::print\n"); return 0;}
private:
    double m_start_x;
    double m_start_y;
    double m_end_x;
    double m_end_y;
};

这里有四个类, Printable 是接口类, Circle 继承自 Shape, Line 继承自 Printable 和 Shape

Line* pLine = new Line(20.0, 30.0, 40.0, 60.0);

(gdb) p/a *pLine
$25 = {<Shape> = {_vptr$Shape = 0x1000021e8 <vtable for Line+16>, m_color = 0x2}, <Printable> = {_vptr$Printable = 0x100002220 <vtable for Line+72>}, m_start_x = 0x14, m_start_y = 0x1e,
  m_end_x = 0x28, m_end_y = 0x3c}

Line size=56, 其中

  • 一个对应于父类 Shape的 虚表, 8 bytes
  • 一个对应于父类 Printable 的虚表, 8 bytes
  • shape的一个成员变量 4 个字节, 8 bytes, 其中 4 bytes 是 padding
  • class line自己有四个成员变量占了 8*4 = 32 个字节

故此, 8 + 8 + (4+4) + 8 * 4 = 56

在做指针转型的时候要注意

Line* pLine = new Line(20.0, 30.0, 40.0, 60.0);
Printable* pPrinter = (Printable*)pLine;
printf("pLine:%p, pPrinter: %p\n", pLine, pPrinter);

--- output ---
pLine:0x7fd283c04d00, pPrinter: 0x7fd283c04d10

注意这两个指针的值是不相等的, 最好用dynamic_cast来转型

Printable* pPrinter = dynamic_cast<Printable* >(pLine);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值