指针二
先前介绍了指针的一些基本的知识,但都没有提到地址的概念,下面承接之前的笔记,继续学习指针
下面要介绍三个相关的内容:获取变量的数据类型 、 取变量地址和取地址中存储的数据
获取变量的数据类型
在C语言中可以使用下面的方法获取一个变量的数据类型:
代码
#include "stdafx.h"
#include <typeinfo>
void function(){
char**** a;
printf("%s\n",typeid(a).name());
}
int main(int argc, char* argv[])
{
function();
return 0;
}
运行结果
使用方式
通过上面的例子可以知道使用方式首先要包含一个相关的头文件typeinfo
然后使用该头文件的方法typeid(变量).name()即可获得变量对应的数据类型
取变量地址
在C语言中可以在变量前加上&符号来获取一个变量的地址
首先看看取回来地址的类型
取地址的返回类型
#include "stdafx.h"
#include <typeinfo>
void function(){
char a;
short b;
int c;
char* d;
printf("%s\n",typeid(&a).name());
printf("%s\n",typeid(&b).name());
printf("%s\n",typeid(&c).name());
printf("%s\n",typeid(&d).name());
}
int main(int argc, char* argv[])
{
function();
return 0;
}
运行结果
分析结果
不难发现,所有取地址返回的类型都为原本变量的类型后加个*,也契合了本笔记的主题——指针
取地址的内容
通过前面的分析得出了取出的地址类型是一个指针类型,现在要观察其存储的内容
代码
#include "stdafx.h"
#include <typeinfo>
//为了方便观察地址 先声明为全局变量
int a;
void function(){
a=610;
int* b=&a;
printf("%x\n",b);
}
int main(int argc, char* argv[])
{
function();
return 0;
}
运行结果
反汇编代码
11: a=610;
00401038 mov dword ptr [a (00427c48)],262h
12: int* b=&a;
00401042 mov dword ptr [ebp-4],offset a (00427c48)
13: printf("%x\n",b);
通过a的赋值语句可以看到a存储在00427c48这个内存地址中
11: a=610;
00401038 mov dword ptr [a (00427c48)],262h
再看下面的指针赋值语句
12: int* b=&a;
00401042 mov dword ptr [ebp-4],offset a (00427c48)
这里的offset a是vc6.0为了方便使用者查看生成的,实际上的语句为:
mov dword ptr ss:[ebp-0x4],0x427C48
也就是直接将全局变量a的地址00427c48赋给b
代码二
前面声明的变量为全局变量,现在来看看局部变量的情况:
#include "stdafx.h"
#include <typeinfo>
void function(){
//这里a声明为局部变量
int a=610;
int* b=&a;
printf("%x\n",b);
}
int main(int argc, char* argv[])
{
function();
return 0;
}
运行结果二
此时的地址显然就是一个堆栈中的地址,对应了变量存储在堆栈中
反汇编代码二
11: int a=610;
00401038 mov dword ptr [ebp-4],262h
12: int* b=&a;
0040103F lea eax,[ebp-4]
00401042 mov dword ptr [ebp-8],eax
13: printf("%x\n",b);
可以看到此时是通过lea指令将变量a的地址ebp-4传给eax,然后再将eax赋值给b
取地址中存储数据
前面讲了如何获取一个变量的地址,那么在获取完地址后,再说说如何获取这地址中存储的数据
在C语言中,在一个指针类型的变量前面加上*符号,即可取出该地址里所存储的内容
取地址数据的返回类型
如法炮制,观察取地址数据的返回类型
代码
#include "stdafx.h"
#include <typeinfo>
void function(){
int***a=(int***) 610;
printf("%s\n",typeid(*a).name());
printf("%s\n",typeid(**a).name());
printf("%s\n",typeid(***a).name());
}
int main(int argc, char* argv[])
{
function();
return 0;
}
运行结果
分析结果
不难发现,所有取地址数据返回的类型都为原本变量的类型后减个*,可以说是和&取地址正好相反
不同的是对于多级指针,可以一次使用多个*来多次取地址中存储的数据
取地址数据的内容
前面了解了*符号的使用,现在来看个稍微复杂点的例子
代码
#include "stdafx.h"
#include <typeinfo>
int a;
void function(){
a=610;
int* b=&a;
int** c=&b;
int*** d=&c;
c=*d;
b=*c;
a=*b;
b=**d;
a=**c;
a=***d;
}
int main(int argc, char* argv[])
{
function();
return 0;
}
反汇编代码
11: a=610;
00401038 mov dword ptr [a (00427c50)],262h
12: int* b=&a;
00401042 mov dword ptr [ebp-4],offset a (00427c50)
13: int** c=&b;
00401049 lea eax,[ebp-4]
0040104C mov dword ptr [ebp-8],eax
14: int*** d=&c;
0040104F lea ecx,[ebp-8]
00401052 mov dword ptr [ebp-0Ch],ecx
15: c=*d;
00401055 mov edx,dword ptr [ebp-0Ch]
00401058 mov eax,dword ptr [edx]
0040105A mov dword ptr [ebp-8],eax
16: b=*c;
0040105D mov ecx,dword ptr [ebp-8]
00401060 mov edx,dword ptr [ecx]
00401062 mov dword ptr [ebp-4],edx
17: a=*b;
00401065 mov eax,dword ptr [ebp-4]
00401068 mov ecx,dword ptr [eax]
0040106A mov dword ptr [a (00427c50)],ecx
18:
19: b=**d;
00401070 mov edx,dword ptr [ebp-0Ch]
00401073 mov eax,dword ptr [edx]
00401075 mov ecx,dword ptr [eax]
00401077 mov dword ptr [ebp-4],ecx
20: a=**c;
0040107A mov edx,dword ptr [ebp-8]
0040107D mov eax,dword ptr [edx]
0040107F mov ecx,dword ptr [eax]
00401081 mov dword ptr [a (00427c50)],ecx
21:
22: a=***d;
00401087 mov edx,dword ptr [ebp-0Ch]
0040108A mov eax,dword ptr [edx]
0040108C mov ecx,dword ptr [eax]
0040108E mov edx,dword ptr [ecx]
00401090 mov dword ptr [a (00427c50)],edx
分析反汇编
首先将各变量信息整理出来,方便后面分析:
代码中涉及的变量较多,这里只拿最复杂的 a=***d来作分析,其它留作样例
22: a=***d;
00401087 mov edx,dword ptr [ebp-0Ch]
0040108A mov eax,dword ptr [edx]
0040108C mov ecx,dword ptr [eax]
0040108E mov edx,dword ptr [ecx]
00401090 mov dword ptr [a (00427c50)],edx
1.这里的ebp-0Ch对应的是d的地址,此时就是将d赋值给edx
00401087 mov edx,dword ptr [ebp-0Ch]
结合内存里的数据可以得到:d的地址=ebp-0Ch=0012FF20,d=[ebp-0Ch]=0012FF24
这里的代码相当于
00401087 mov edx,0012FF24h(d)
2.将前面edx地址里存储的数据赋值给eax,此时的[edx]存储的其实就是c
0040108A mov eax,dword ptr [edx]
结合内存里的数据可以得到:d=edx=0012FF24,[edx]=0012FF28=c
这里的代码相当于
0040108A mov eax,0012FF28(c)
3.将前面eax地址里存储的数据赋值给ecx,此时的[eax]存储的其实就是b
0040108C mov ecx,dword ptr [eax]
结合内存里的数据可以得到:c=eax=0012FF28,b=[eax]=00427C50
这里的代码相当于
0040108C mov ecx,00427C50(b)
4.将前面ecx地址里存储的数据赋值给edx,此时的[ecx]存储的其实就是a
0040108E mov edx,dword ptr [ecx]
结合内存里的数据可以得到:b=ecx=00427C50,a=[ecx]=262h=610
这里的代码相当于
0040108E mov edx,262h
5.最后将edx赋值给a
00401090 mov dword ptr [a (00427c50)],edx
小总结
可以看到,被赋值变量 = * 赋值变量 在汇编中的形式为:mov 被赋值变量,[赋值变量]
如果有多个*,则多执行几次来取值
总结
- 可以在变量前加上&符号来获取变量地址
- 取地址返回的类型都为原本变量的类型后加个*,也就是个指针类型
- 在一个指针类型的变量前面加上*符号,即可取出该地址里所存储的内容
- 取地址数据返回的类型都为原本变量的类型后减个*,可以说是和&取地址正好相反
- 对于多级指针,可以一次使用多个*来多次取地址中存储的数据