Fork me on GitHub

地址,指针和引用的表现形式

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 = &num;
    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 = &num;
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来定义常量。

举例说明

int main()
{
    const char* str = "Reverse!";

    return 0;
}

 

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

posted @ 2019-09-21 21:19 MayflyIT 阅读(...) 评论(...) 编辑 收藏