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指向的是现在内存中的常量值。