int main(int argc, char* argv[]) { /* 数组名可以理解为是(X [])型的变量的一个名称, v不代表内存空间,只能代表一个固定数字(数组的起始地址),编译时就已确定,所以他可以赋值给指针, 这个数字是不可改变的,给数组名赋值是不允许的,它本身就没有内存存储这个值 v[0],a[6]才代表实实在在的一段内存空间 */ int v[] = {1, 2, 3, 4}; int a[6] = {1, 2, 3, 4}; /* 指针应该理解为(X *)型的变量, 一样占有一片内存,并有值,只不过它的值是内存地址 &p1取到的是变量p1的内存地址 【指针是一个变量,教我们的老师说:作为一个C++程序员,即使有一天你溺海,救上你来后, 你可以失意,但你的第一反应就应该是"指针是一个变量",(*^__^*)】 */ int *p1 = v; // 指向开始元素(隐式转换) printf("*p1 = %d /n", *p1); printf("p1 = %d /n", p1); printf("&p1 = %d /n", &p1); printf("***************************************/n"); /* int *p2 = &v[0]; 取第一个元素的地址即数组的起始地址 p2 == p1,指的是p1和p2的值相等,即指向同一个地址,同时*p2 == *p1 *p2 == *p1,指的是p1和p2的指向地址的值相等,不代表p2 == p1 */ int *p2 = &v[0]; printf("*p2 = %d /n", *p2); printf("p2 = %d /n", p2); printf("p1 == p2 is %s /n", p1==p2?"true":"false"); printf("*p1 == *p2 is %s /n", *p1==*p2?"true":"false"); int *pp2 = &a[0]; printf("*pp2 = %d /n", *pp2); printf("pp2 = %d /n", pp2); printf("p2 == pp2 is %s /n", p2==pp2?"true":"false"); printf("*p2 == *pp2 is %s /n", *p2==*pp2?"true":"false"); printf("***************************************/n"); /* int *p3 = &v[4];取第最后一个元素后一个位置的地址即数组的结束地址 这个数字是未知的,已经超出数组范围,对这个指针指向的内存编辑存在风险 */ int *p3 = &v[4]; printf("*p3 = %d /n", *p3); printf("p3 = %d /n", p3); printf("***************************************/n"); /* 指针++移动的是指针指向类型的一个长度,称作步长 32位机int类型的一个步长是32 Bit,4 Byte 1 Byte 代表一个地址长度,所以(p1++)后的值是原来的值+4 但是p2-p1=1,因为它是(int *)型的变量 */ p1++; printf("*(p1++) = %d /n", *p1); printf("new p1 = %d /n", p1); printf("new p2 = %d /n", p2); printf("p1 - p2 = %d /n", p1 - p2); p2 += 2; printf("*(p2+2) = %d /n", *p2); printf("new p2 = %d /n", p2); /* const char * 和 char const * 都表示指向的内存对象(char)不可被改变 char *const 表示常指针,是指指针本身的值不可被改变 */ char c1[] = {"Hello"}; char c2[] = {"Hello"}; const char *pc1 = c1; char *const pc2 = c2; /* // 下面三句都会报编译时或运行时错误 *pc1 = 'F'; // error C2166: l-value specifies const object pc1[2] = 'P'; pc2 = pc1; // error C2440: '=' : cannot convert from 'const char *' to 'char *const ' */ /* 什么样的指针需要delete? 如果指针指向的内存不能被自动回收,就需要delete. 如下pint需要delete, 但是上面例子的p1,p2,pc1等不需要 因为pint指向的内存是堆中的,不会制动回收, p1,p2,pc1等指向的是局部变量数组,是栈中的内存,出了定义的域就会被自动回收 如果不小心delete p1,会报过删,甚至程序崩溃 对pint重复delete也会报过删,甚至程序崩溃 对随便一块内存delet,也是不允许的 原则:【是你的一定要记住回收,不是你的不要伸手】O(∩_∩)O 定义一个指针punknown,不初始化,这时它可能指向任一片内存 这时对punknown delete会报过删,甚至程序崩溃 punknown虽然指向堆中的内存,但是你并没有申请这片内存,这片内存不归你管, 对punknown delete也会报过删,甚至程序崩溃 */ int *pint = (int *)malloc(sizeof(int) * 4); delete pint; // delete pint; // 可以打开试试O(∩_∩)O // delete p1; // 可以打开试试O(∩_∩)O int * punknown; // delete punknown; // 可以打开试试O(∩_∩)O punknown = pint + 25; // 这块内存我没申请=_= // delete punknown; // 可以打开试试O(∩_∩)O /* 良好的编程风格是保证指针安全的保证 定义指针时就要初始化,指向申请的内存或者赋为NULL 当然定义时初始化变量,对什么类型都应如此 delete 操作后就把指针赋值为NULL, delete 操作前先判断指针是否为NULL,防止过删 【指针本身没有缺陷,有缺陷的是你编写的程序】 */ int *pexam = NULL; pexam = (int *)malloc(sizeof(int) * 4); if (NULL != pexam) { delete pexam; pexam = NULL; } if (NULL != pexam) { delete pexam; // 这样写delete,即使不小心写多少遍都没问题O(∩_∩)O pexam = NULL; } printf("***************************************/n"); /* 引用也是变量,本身占用内存空间,可以看成(X *const)类型的 引用只可以初始化,不可以被重新赋值,如何操作都是被引用的对象,不是引用本身, 因为根本没有能操作引用的运算操作符,它打了个太极,所有常规攻击都作用到了被引用的对象身上 关于这一点可以看一下反编译的结果, 下面是vc6.0 debug版本的汇编码 150: int *pp = (int *)malloc(sizeof(int) * 4); 00401A2F push 10h 00401A31 call malloc (00401ca0) 00401A36 add esp,4 00401A39 mov dword ptr [ebp-60h],eax ;[ebp-60h]就是pp 151: int* &vv = pp; 00401A3C lea edx,[ebp-60h] 00401A3F mov dword ptr [ebp-64h],edx ;[ebp-64h]就是vv 152: printf("pp = %d /n", pp); 00401A42 mov eax,dword ptr [ebp-60h] 00401A45 push eax 00401A46 push offset string "pp = %d /n" (00423048) 00401A4B call printf (00403c20) 00401A50 add esp,8 153: printf("vv = %d /n", vv); 00401A53 mov ecx,dword ptr [ebp-64h] 00401A56 mov edx,dword ptr [ecx] ;这里转了一圈相当于mov edx,dword ptr [ebp-60h],就是pp 00401A58 push edx 00401A59 push offset string "vv = %d /n" (0042303c) 00401A5E call printf (00403c20) 00401A63 add esp,8 154: printf("&vv = %d /n", &vv); 00401A66 mov eax,dword ptr [ebp-64h] ;[ebp-64h]存放的就是ebp-60h,这句相当同mov eax,[ebp-60h] 00401A69 push eax 00401A6A push offset string "&vv = %d /n" (0042302c) 00401A6F call printf (00403c20) 00401A74 add esp,8 155: printf("&pp = %d /n", &pp); 00401A77 lea ecx,[ebp-60h] 00401A7A push ecx 00401A7B push offset string "&pp = %d /n" (0042301c) 00401A80 call printf (00403c20) 00401A85 add esp,8 156: vv[0] = 0; 00401A91 mov edx,dword ptr [ebp-64h] 00401A94 mov eax,dword ptr [edx] ;这句等同mov eax,dword ptr [ebp-60h] 00401A96 mov dword ptr [eax],0 157: pp[0] = 0; 00401A9C mov ecx,dword ptr [ebp-60h] 00401A9F mov dword ptr [ecx],0 */ int *pp = (int *)malloc(sizeof(int) * 4); int* &vv = pp; // 这种引用用不好其实是危险的,这里只想与指针比较一下 printf("pp = %Xd /n", pp); printf("vv = %Xd /n", vv); printf("&vv = %Xd /n", &vv); printf("&pp = %Xd /n", &pp); vv[0] = 0; pp[0] = 0; vv[1] = 1; pp[2] = 2; vv[3] = 3; /* if (NULL != pp) { delete pp; pp = NULL; } printf("vv=%s", NULL==vv?"NULL":"not NULL"); // 打开后,注意一下这个输出vv=NULL */ /* 强行修改一下引用本身保存的值,查看内存保存成功,但是再使用的时候可能就悲催了 */ int *vp = (int *)&pp; vp--; //*(vp) = 1; //printf("vv = %d /n", vv);//这一句让程序崩溃了,报【"0x00401ac0" 指令引用的 "0x00000001" 内存。该内存不能为 "read"。】 *(vp) = (int)&pp[2]; printf("pp = %Xd /n", pp); printf("*(vp) = %Xd /n", *(vp)); /* 下面这一句,让程序崩溃了, printf("vv[1] = %Xd /n", vv[1]); debug查看汇编可以看到 210: printf("vv[1] = %Xd /n", vv[1]); 00401B0E mov edx,dword ptr [ebp-64h] ;[ebp-64h]存放的是pp[2]的首地址 00401B11 mov eax,dword ptr [edx] ;eax=2 00401B13 mov ecx,dword ptr [eax+4] ;这句报【"0x00401b13" 指令引用的 "0x00000006" 内存。该内存不能为 "read"。】 00401B16 push ecx 00401B17 push offset string "vv[1] = %Xd /n" (00423068) 00401B1C call printf (00403d70) 00401B21 add esp,8 看来vv被转型了,不是int* &,变成int &了,下面一句输出2验证了这个问题 为什么?暂时还不知道O(∩_∩)O~ */ printf("vv = %Xd /n", vv); printf("pp = %Xd /n", pp + 2); printf("&vv = %Xd /n", &vv); /* if (NULL != pp) { delete pp; pp = NULL; } printf("vv=%s", NULL==vv?"NULL":"not NULL"); // 注意一下这个输出vv=not NULL */ int ivv = 45; *vp = (int)&ivv; printf("pp = %Xd /n", pp); printf("vv = %Xd /n", vv); printf("&vv = %Xd /n", &vv); if (NULL != pp) { delete pp; pp = NULL; } printf("vv=%s", NULL==vv?"NULL":"not NULL"); // 注意一下这个输出vv=not NULL return 0; }
输出:
*p1 = 1p1 = 1310576&p1 = 1310548****************************************p2 = 1p2 = 1310576p1 == p2 is true*p1 == *p2 is true*pp2 = 1pp2 = 1310552p2 == pp2 is false*p2 == *pp2 is true****************************************p3 = 1310656p3 = 1310592****************************************(p1++) = 2new p1 = 1310580new p2 = 1310576p1 - p2 = 1*(p2+2) = 3new p2 = 1310584***************************************pp = 30FE0dvv = 30FE0d&vv = 13FF20d&pp = 13FF20dpp = 30FE0d*(vp) = 30FE8dvv = 2dpp = 30FE8d&vv = 30FE8dpp = 30FE0dvv = 2Dd&vv = 13FF14dvv=not NULLPress any key to continue