一、指针的基本用法
先来总结一下,以后复习看到这就懂了:指针就是一种新的数据类型,格式是在任何数据类型后面加
*
,而且一般来说:指针中存的是地址,具体是什么数据的地址就要具体分析了;但是由于指针也是一种类型,所以我们可以手动给指针类型变量赋自己想赋的数值;但是一般后面使用最多的就是指针变量中存的就是某个数据/变量/常量在内存中的地址值(首地址)!
1.带*类型的宽度
下面提到的都是针对32位计算机而言
-
带
*
类型的变量宽度永远是4字节、无论类型是什么,无论有几个*
-
先分析一下以前的类型变量的宽度
#include "stdafx.h" struct Student{ int a; short b; char c; }; int main(){ char x; //byte 1字节宽度 int y; //dword 4字节宽度 Student s; //通过字节对齐判断出结构体分配的内存大小为8字节宽度 x = 1; y = 1; s.a = 1; s.b = 1; s.c = 1; }
-
通过反汇编分析:VC6默认提升堆栈的大小为0x40,现在为0x50,前面学过,变量作为函数的局部变量,会按照本机尺寸分配内存,即一个char类型会分配4字节,int类型分配4字节,s结构体类型由于占8字节,所以会分配8字节内存,一共是16字节内存,即0x10,所以可以发现堆栈提升的大小为0x40 + 0x10 = 0x50;但是最后实际给变量赋值时,都是按照类型宽度分配的,所以通过这几条指令中使用的byte、word还是dword,就可以判断这些类型的宽度
-
-
再分别给这些变量都加上
*
号,变成新的带*类型变量(这就是指针)#include "stdafx.h" struct Student{ int a; short b; char c; }; int main(){ char* x; //4字节宽度 int* y; //4字节宽度 Student* s; //4字节宽度 x = (char*)1; y = (int*)1; s = (Student*)4; }
-
通过反汇编分析:char后面跟了
*
后变成带*类型,此时通过dword可以判断char*
数据类型的宽度为4字节;同理int*
宽度也为4字节;而结构体类型后面跟*
那么此时的新类型的变量是可以直接赋值的,但是要告诉4应该被当做Student*
类型看待,可以发现,此时的Student*
也同样为4字节
-
-
如果给这些类型加上两个
*
或者多个*
呢?#include "stdafx.h" struct Student{ int a; short b; char c; }; int main(){ char**** x; //4字节宽度 int*** y; //4字节宽度 Student** s; //4字节宽度 x = (char****)1; y = (int***)1; s = (Student**)4; }
-
同样宽度还是4字节
-
-
2.声明带*类型变量
-
原来类型的声明方法:
char x; short y; int z; float f; double d; struct Student{ int level; char name[20]; }; Student st;
-
在我们现在所学过的任何数据类型的后面加
*
就是变成了一个新的类型(指针类型!)char* x; //char *x是同一个意思,但是按照海哥的讲解,写成char* x更好理解 short* y; int* z; float* f; double* d; Student* st;
-
总结:
- 带有
*
的变量类型的标准写法:变量类型* 变量名
- 任何类型都可以带
*
,加上*
以后是新的类型 *
可以是任意多个(有极限值)
- 带有
3.带*类型的变量赋值
-
先前学过的类型变量如何赋值:
-
简化赋值
char x; short y; int z; x = 'c'; //其实这是一种简略的写法 y = 12; z = 2;
-
实际上我们给一个变量赋值,应该要说明这个值应该是什么类型,即告诉编译器如何看待这数值;但是之所以可以用上述简化的写法,是因为编译器现在都很智能,可以通过变量定义时的数据类型来自己将后面赋的值看待成什么类型
char x; short y; int z; x = (char)'c'; y = (short)12; z = (int)2;
其实就是转型
-
-
给带*类型的变量赋值
char*** x; short* y; int** z; x = (char***)'c'; y = (short*)12; z = (int**)2;
那么用同样的方法也可以给带*类型变量赋值,同样也是要告诉编译器这个值应该当成什么类型看待,因为带
*
类型编译器是无法自动解析的,所以带*
类型的变量赋值时只能使用“完整写法”!
二、指针的算数特征
1.带*类型变量做++或者–
-
我们知道不带*号类型的变量,++或者–,都是加1或者减1
-
带
*
类型的变量,++ 或者 – 新增(减少)的数量是去掉一个*后变量的宽度-
如果带一个
*
号变量++(或者–),就是先去掉一个*
号,然后加上或者减去此时类型的宽度#include "stdafx.h" int main(){ char* x; short* y; int* z; x = (char*)100; y = (short*)100; z = (int*)100; x++; //得到的结果依然是原来的带*类型 y++; z++; printf("%d %d %d",x,y,z); //101 102 104 return 0; }
char*
去掉*
此时类型为char,所以x++即100 + 1short*
去掉*
此时类型为short,所以y++即100 + 2int*
去掉*
此时类型为int,所以z++即100 + 4 -
如果带两个
*
号变量++(或者–),同样先去掉一个*
号,然后加上或者减去此时类型的宽度#include "stdafx.h" int main(){ char** x; short** y; int** z; x = (char**)100; y = (short**)100; z = (int**)100; x++; y++; z++; printf("%d %d %d",x,y,z); return 0; }
char**
去掉一个*
此时类型为char*
,所以x++即100 + 4(我们全面学过任何类型加了不管多少个*
的变量宽度都是4字节)short**
去掉一个*
此时类型为short*
,所以y++即100 + 4int**
去掉一个*
此时类型为int*
,所以z++即100 + 4 -
带多个
*
号变量++(或者–),同样是+4或者-4
-
2.带*类型变量加或减一个整数
-
带*类型的变量可以加、减一个整数,但不能乘或者除(也不能加减浮点数)。得到的结果依然是带
*
类型 -
带*类型变量与其他整数相加或者相减时:
- 带
*
类型变量 + N = 带*
类型变量 + N ***** (去掉一个*
后类型的宽度) - 带
*
类型变量 - N = 带*
类型变量 - N ***** (去掉一个*
后类型的宽度)
- 带
-
举例:
#include "stdafx.h" int main(){ char* a ; short* b ; int* c ; a = (char*)100; b = (short*)100; c = (int*)100; a = a + 5; b = b + 5; c = c + 5; printf("%d %d %d",a,b,c); //105 110 120 return 0; }
char*
去掉一个*
号后为char
,即宽度为1字节,所以a + 5 = 100 + 5 * 1short*
去掉一个*
号后为short
,即宽度为2字节,所以b + 5 = 100 + 5 * 2int*
去掉一个*
号后为int
,即宽度为4字节,所以c + 5 = 100 + 5 * 4#include "stdafx.h" int main(){ char** a ; short*** b ; int**** c ; a = (char**)100; b = (short***)100; c = (int****)100; a = a - 4; b = b - 4; c = c - 4; printf("%d %d %d",a,b,c); //84 84 84 return 0; }
char**
去掉一个*
为char*
,即宽度为4字节,所以a - 4 = 100 - 4 * 4下面同理,b - 4 = b - 4 * 4;c - 4 = c - 4 * 4
3.两个带*类型变量求差值
-
两个类型相同的带*类型的变量可以进行减法操作(不能进行加法!)
-
相减的结果要除以去掉一个
*
的数据的宽度,因为结果是int类型,结果向下取整(因为汇编中使用的sar,表示右移)-
举例:
#include "stdafx.h" int main(){ char* a ; char* b ; a = (char*)200; b = (char*)100; int x = a - b; //两个带*号类型必须相同!减完的结果是int类型! printf("%d\n",x); //(200-100)/1 = 100 return 0; }
先用a - b,
char*
去掉一个*
后为char,即宽度为1,那么(a - b)还要除以1 -
举例2:
#include "stdafx.h" int main(){ int* a ; int* b ; a = (int*)200; b = (int*)100; int x = a - b; printf("%d\n",x); //(200-100)/4 = 25 return 0; }
先用a-b,
int*
去掉一个*
后为int,即宽度为4,那么(a-b)还要除以4 -
举例3:
#include "stdafx.h" int main(){ char**** a ; char**** b ; a = (char****)200; b = (char****)100; int x = b - a; printf("%d\n",x); //(100-200)/4 = -25 return 0; }
先用b - a,
char****
去掉一个*
后为char***
,即宽度为4,那么(b-a)还要除以4
-
4.两个带*类型做大小比较
-
带
*
的变量,如果类型相同,可以做大小的比较#include "stdafx.h" int main(){ char**** a; char**** b; a = (char****)200; b = (char****)100; if(a>b){ printf("1"); //1 } else{ printf("2"); } return 0; }
-
注意:是把两个带
*
号类型的赋值结果当成无符号数比较,我们看到下面的反汇编中用的jbe#include "stdafx.h" int main(){ char**** a; char**** b; a = (char****)-200; b = (char****)100; if(a>b){ printf("1"); //1 } else{ printf("2"); } return 0; }
三、作业
-
char
类型占几字节?char*
类型占几字节?int*****
占几字节?char
占1字节;char*
类型占4字节;int*****
占4字节 -
char** arr[10]
占多少个字节?char** arr[10]
就表示一个数组中存了10个元素,这10个元素都是char**
类型的,一个char**
类型宽度为4字节,所以10个就为40字节。可以用sizeof(arr)验证一下 -
自定义结构体如下:
#include "stdafx.h" struct Student{ int x; int y; }; int main(int argc,char* argv[]){ Student**** s; s = (Student****)100; s++; //s的值是多少? 100 + 4 = 104 s = s+2; //s的值是多少? 100 + 2 * 4 = 108 s = s-3; //s的值是多少? 100 - 3 * 4 = 88 return 0; }
#include "stdafx.h" struct Student{ int x; int y; }; int main(int argc,char* argv[]){ Student**** s1; Student**** s2; int x; s1 = (Student****)200; s2 = (Student****)100; x = s1-s2; //x的值是多少? (200 - 100) / 4 = 25 return 0; }
#include "stdafx.h" struct Student{ int x; int y; }; int main(int argc,char* argv[]){ Student* s; s = (Student*)100; s++; //s的值是多少? 100 + 8 = 108 s = s+2; //s的值是多少? 108 + 2 * 8 = 124 s = s-3; //s的值是多少? 124 - 3 * 8 = 100 return 0; }
#include "stdafx.h" struct Student{ int x; int y; }; int main(int argc,char* argv[]){ Student* s1; Student* s2; int x; s1 = (Student*)200; s2 = (Student*)100; x = s1-s2; //x的值是多少? (200 - 100) / 8 = 12.5(向下取整) = 12 }