您的位置:首页 > 编程语言 > C语言/C++

用汇编的眼光看C++(之指针2)03.2

2012-01-17 09:55 183 查看
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】

(4)指针和引用

引用是C++和语言的区别之一。其实本质上说两者是一致的。朋友们可以看下面两段代码。

a)指针和指针的函数代码

[cpp] view plaincopy

void add_point(int* q)

{

(*q)++;

}

void add_ref(int& q)

{

q++;

}

b)函数的调用代码

[cpp] view plaincopy

56: int m = 10;

004012E8 mov dword ptr [ebp-4],0Ah

57: add_point(&m);

004012EF lea eax,[ebp-4]

004012F2 push eax

004012F3 call @ILT+45(process) (00401032)

004012F8 add esp,4

58: add_ref(m);

004012FB lea ecx,[ebp-4]

004012FE push ecx

004012FF call @ILT+50(add_ref) (00401037)

00401304 add esp,4

59: return 1;

00401307 mov eax,1

60: }

分析一下,我们发现其实函数add_point和函数add_ref实现的功能,都是对输入的数据进行自增处理。只不过处理的时候,一个函数的入参是指 针,一个函数的入参是引用。在函数调用的地方,大家可以发现指针和引用居然是一样的。首先看add_point,第一句获取m的地址复制给eax,第二句 压栈处理,第三句调用函数add_point,第四句出栈回溯。同样看一下add_ref,第一句获取m的地址复制给ecx,第二句ecx压栈处理,第三 句调用函数add_ref,第四句堆栈回溯处理。相信看到这里,大家就明白C++的前辈们为什么鼓励大家多多使用引用了。

(5)指针和结构体

我们在学习数据节点的时候,相信大家都学习过这样的一个数据结构定义:

[cpp] view plaincopy

typedef struct _NODE

{

int data;

struct _NODE* next;

}NODE;

当时,我们都不明白这个结构体是什么意思?其实这个定义完全修改成这样:

[cpp] view plaincopy

typedef struct _NODE

{

int data;

void* next;

}NODE;

这两个数据结构体其实是完全一致的。第一个数据保存数据,第二个数据为指针,内容为某一个数据类型的地址。这种确定的地址和void*类型的地址类型是一 样的。只不过前面一种更加直接。后面一种地址的固然方便,但是使用的时候每一次都需要进行转换,很是麻烦。如果大家感兴趣,不妨是接着看下面一道题目:

[cpp] view plaincopy

typedef struct _NODE

{

struct _NODE* next;

}NODE;

我们既可以把节点NODE的地址看是NODE*,也可以堪称是NODE**,两者之间有差别吗?(其实没有区别

linux 内核代码上面有一种计算偏移值的方法,大家可以参考一下:

[cpp] view plaincopy

int offset = (int)&(((NODE*)(0))->next);

(6)class指针

class指针比较复杂,不过大家可以从一个小范例看出一些端倪:

[cpp] view plaincopy

class fruit

{

public:

fruit() {}

~fruit() {}

void print() {printf("fruit!\n");}

};

class apple : public fruit

{

public:

apple() {}

~apple() {}

void print() {printf("apple!\n");}

};

void process()

{

fruit f;

apple* a = (apple*)&f;

a->print();

fruit* b = &f;

b->print();

}

熟悉C++的朋友可以很快知道这道题目的答案了,那么为什么a和b都指向同一个地址,使用了相同的print函数,但是结果不同。我想这主要是因为两者数 据类型不同的缘故。一旦指针和某一种数据类型绑在了一起,那么这个指针的所有行为事实上都已经被这种类型的数据所限定了。普通数据类型是这样,自定义的 class类型也是这样。只要不是在print函数前面加上virtual,我们就会发现两个print的调用都是硬编码,和普通的函数调用无异。所以 说,指针离不开数据类型,离开具体类型的地址是没有意义的。就像void*是可以原谅的,但是void却是万万不能接受的。下面的汇编代码很好的说明了这 一点。

[cpp] view plaincopy

65: fruit f;

0040132D lea ecx,[ebp-10h]

00401330 call @ILT+35(fruit::fruit) (00401028)

00401335 mov dword ptr [ebp-4],0

66: apple* a = (apple*)&f;

0040133C lea eax,[ebp-10h]

0040133F mov dword ptr [ebp-14h],eax

67: a->print();

00401342 mov ecx,dword ptr [ebp-14h]

00401345 call @ILT+0(apple::print) (00401005)

68: fruit* b = &f;

0040134A lea ecx,[ebp-10h]

0040134D mov dword ptr [ebp-18h],ecx

69: b->print();

00401350 mov ecx,dword ptr [ebp-18h]

00401353 call @ILT+25(fruit::print) (0040101e)

(全文完)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: