1 必要知识
地址:只有变量才有地址,常量没有地址,除了const定义的伪常量。
指针(TYPE *):任何数据类型都可以定义指针,指针本身也是一种数据类型。由于指针保存的都是地址(32位操作系统下,地址为32位),所以无论什么类型的指针都占据4字节空间。
引用(TYPE&):在C++中不能单独定义,定义就要初始化,是一个变量的别名。
2.指针的工作方式
2.1 指针寻址
因为指针只保存首地址,使用类型修饰符修饰指针(TYPE *),能够解释这个地址中的数据类型,数据类型不同,占用的空间就不同。
举例分析:
1 #include <iostream> 2 3 #pragma warning(disable:4996) 4 5 using namespace std; 6 7 int main() 8 { 9 int Value = 0x12345678; 10 int* iptr = &Value; 11 char* cptr = (char*)& Value; 12 short* sptr = (short*)& Value; 13 14 printf("%08x\n", *iptr); 15 printf("%08x\n", *cptr); 16 printf("%08x\n", *sptr); 17 18 system("PAUSE"); 19 return 0; 20 }
数据在内存中的存储为"78 56 34 12","78"为首地址,
指针iptr为int型指针,以int类型(4字节大小)对地址进行解释,以小端方式取出数据,"12345678"。指针cptr类似原理,取出1字节数据,"78"。指针sptr取出两字节数据,"5678"。
同理,可以推理指针加法的工作方式。
举例说明
1 #include <iostream> 2 3 #pragma warning(disable:4996) 4 5 using namespace std; 6 7 int main() 8 { 9 int Value = 0x12345678; 10 int* iptr = &Value; 11 char* cptr = (char*)& Value; 12 short* sptr = (short*)& Value; 13 14 printf("%08x\n", *iptr); 15 printf("%08x\n", *cptr); 16 printf("%08x\n", *sptr); 17 printf("\n"); 18 iptr += 1; 19 cptr += 1; 20 sptr += 1; 21 printf("%08x\n", *iptr); 22 printf("%08x\n", *cptr); 23 printf("%08x\n", *sptr); 24 25 system("PAUSE"); 26 return 0; 27 }
iptr += 1; 00735096 add eax,4 00735099 mov dword ptr [ebp-18h],eax cptr += 1; 0073509C mov eax,dword ptr [ebp-24h] 0073509F add eax,1 007350A2 mov dword ptr [ebp-24h],eax sptr += 1; 007350A5 mov eax,dword ptr [ebp-30h] 007350A8 add eax,2 007350AB mov dword ptr [ebp-30h],eax
通过汇编代码可以看出,指针加法也是根据指针类型进行解释。
iptr+=1,向后推移4字节后,再以int型解释地址,输出数据。
cptr+=,向后推移1字节后(首地址变成"56"),再以char型解释地址,取1字节数据,输出"56"。
sptr类似... ...
3.引用
引用实际上是C++为了简化指针操作,对指针进行了封装,让使用者看不到存放地址的内存空间。
举例说明
int main() { int num = 0x12345678; int* iptr = # int& iref = num; return 0; }
汇编代码
int main() { 00C016F0 push ebp 00C016F1 mov ebp,esp 00C016F3 sub esp,0E8h 00C016F9 push ebx 00C016FA push esi 00C016FB push edi 00C016FC lea edi,[ebp+FFFFFF18h] 00C01702 mov ecx,3Ah 00C01707 mov eax,0CCCCCCCCh 00C0170C rep stos dword ptr es:[edi] 00C0170E mov eax,dword ptr ds:[00C0A004h] 00C01713 xor eax,ebp 00C01715 mov dword ptr [ebp-4],eax 00C01718 mov ecx,0C0C000h 00C0171D call 00C011FE int num = 0x12345678; 00C01722 mov dword ptr [ebp-0Ch],12345678h int* iptr = # 00C01729 lea eax,[ebp-0Ch] 00C0172C mov dword ptr [ebp-18h],eax int& iref = num; 00C0172F lea eax,[ebp-0Ch] 00C01732 mov dword ptr [ebp-24h],eax return 0; 00C01735 xor eax,eax }
4.常量
常量数据在程序运行之前就已经存在了。可以使用#define或者const来定义常量。
#define是一个真常量,而const是由编译器判断实现的常量,是一个假常量。在编译器进行检查时,发现const定义的变量一个常量值,会将程序中所有该变量替换为常量值。
举例说明
#include <iostream> using namespace std; int main() { const int num = 0x12345678; int* ptr = (int*)& num; *ptr = 0x2; int count = num; printf("%08x\n", num); printf("%08x\n", *ptr); system("PAUSE"); return 0; }
由于编译器提前替换了num,所以我们在改变常量值后输出num依然是更改前的值,而ptr指向的是现在内存中的常量值。