c++学习6 指针变量
一 指针变量的定义
*是用来修饰指针变量的,通常情况下我们定义的手法都是“类型名”+“*”+“指针变量名称”。
有一种简单无脑的“替换法”,作用是防止小括号遗漏而导致定义出现歧义和偏颇。
方法如下:
*修饰指针变量p,那么我们需要指向谁,就先定义谁,然后用小括号+指针变量替换它。
example:
指向整型变量:
int a=100;//定义变量。
int (*p)=&a;//(*p)替换了取代目标a并存入了a的首地址,这也称为指针变量的初始化。
int *p=&a;//判断可以去掉小括号。
同理可得,若我要指向一个数组
int arr[5];
int (*p)[5];//这里判断出来不可去掉小括号,因为*的优先级低于[],这里也是一个很经典的难点,“指针数组”和“数组指针”。
p=arr;//其实和p=&arr[0]一个意思。
int *p[5];//这就是指针数组,指针这个特殊的变量以数组形式出现。
int (*p)[5];//这就是数组指针,它只有一个指针,它的作用是存储数组类型的首地址的。
继续,同理可得
指向函数:int fun(int int); int (*p)(int int);//不该去掉小括号。 p=&fun;
指向结构体:struct stu {} Mystruct; struct stu *p; p=&Mystruct;
指向指针:int *p;int *(*q);int **q;q=&p;
二 指针变量的合法初始化
指针变量如果没有初始化就立即使用的话,不让指针指向一个合法的地址(或者是有操作权限的地址),就会出现段错误。
如果没有指向合法的地址,建议初始化为NULL。
int *p=NULL;//这里有一个易错点,这里应该看作int *p;p=NULL;而不是*p=NULL。
注意,这只是缓兵之计,不要操作指向NIULL的指针。
三 指针变量的类型
判断方法:在指针定义的语句中,把指针名称忽略,剩下的部分就是指针变量的类型。
比如int *p,该指针(p)的类型名是int *,而(*p)的类型则是int。
这个的作用是对赋值语句的判断。
判断了指针变量的类型,我们如何判断该指针类型指向的类型呢?
这个也挺有意思,就是在判断指针变量类型的基础上把 * 去掉就可以了。
例:int *p,该指针(p)的类型名是int *,指向的类型呢就是int。
以此类推,多推多懂,都是些换汤不换药的玩意。
指针变量指向的类型,决定了取值的宽度。
这个很重要!
因为一个指针既然要选择存储地址,并对其内容进行操作,仅仅是“能知道地址”是远远不够的,我们还需要告诉指针“应该存储多少地址”,只有存储了一个完整的地址,指针才能正常的通过操作地址去操纵其内容。
example:
如果你面对一个4字节的int数据,但是你的指针只能存储2个字节的地址,你以为你还能操作内容吗?你不能啦。
同理,同样都是+1,但是不同的指针变量跳过的字节数目不同,这也是指针变量所指向的类型名的作用。
若我想取出0x0102的值
short *p=(short)#//c++对于格式的要求相当严格,所以必须做一个强制类型转换。
cout<<hex<<*(p+1)<<;//输出结果是102,首位0消除了。
若我想取出0x0203的值
char *p=(char)#
cout<<hex<<(short)*(p+1)<<;//原理是先用char越过第一个地址,然后通过强制类型转换为short拿下后两位地址。
取出0x02的值
char *p=(char)#
cout<<hex<<(int)*(p+2)<<;//为什么要转换为int类型呢?因为char类型有一点特殊,如果你不进行数值转换的话就会变成输出了一个ASCII值,甚至是乱码都有可能。
四 指针变量的注意事项
void不能开辟空间,所以不能定义普通变量,但是却可以定义指针变量
void num;//不行,无法开辟变量空间
void *p;//彳亍,p自身类型为void *,在平台上为任意类型的指针,32位平台上4B,64位平台上8B。
而且此时的p作为无能的一级指针变量,其本身可以保存任意一级指针的地址编号。
万能指针一般用于函数的形参,以此来达到算法操作多种数据类型的目的。
不要直接对void *的指针变量取 * ,需要取值的话必须临时强转类型,因为void无法判断取多少字节的地址,就会造成地址没取够或者越界访问非法内存的情况出现。
在使用中,“[ ]”就是* ()的缩写。
五 指向同一数组的两个指针变量的关系
两指针变量相减,等于它们之间的元素个数。
两指针变量赋值,使它们指向同一元素。
两指针变量的相等判断,它们是否指向同一处。
两指针变量不能相加,会导致数组越界,是无意义的。
六 字符串与指针
字符串数组储存的字符串,存储空间在栈区/全局区。而存储在指针变量的字符串,内容存储在文字常量区里,而首地址存储在指针变量里(字符串内容太大了,指针变量放不下)。
七 指针数组
本质是数组,只不过数组的每个元素都是指针。
int *arr[3]; char ch[8]; short str[4];
sizeof(arr)==3*8, sizeof(ch)==8*8, sizeof(str)==4*8;
可以把指针组为数组一般去使用。
int num1=1,num2=2,num3=3;
arr[0]=&num1,arr[1]=&num2,arr[2]=&num3;
在字符串数组里面,若想要输出一个完整的字符串的话只需要使用 [ ] 就可以了,但是如果在此基础上在加上 * 的话,就会导致只取出了那个字符串里的首地址的字母。详情如下:
char ch[8]; char arr="China";
ch[0]=&arr;
cout<<ch[0]<<;//输出China。
cout<<*(ch+4)<<;//输出a。
八 数组指针
数组首元素地址和数组首地址:&arr[0]=arr,arr+1跳过一个元素。 &arr,&arr+1跳过整个数组。
数组指针,本质是指针变量,保存数组的首地址:
int arr[5]={1,2,3,4,5};
int *p[5]=NULL;
对数组首地址取* == 数组首元素的地址。
数组指针的本质是指针变量,只不过变量存储的是数组的首地址。
Tips:
1.32位平台的任何指针变量都是4字节,64位平台的任何指针变量都是8字节;
2.“段错误”指的是在程序运行过程中出现了错误;
3.NULL空的本质是(void *)0,是内存中的起始位置;
4.在使用指针的过程中,&和*相遇了,从右往左依次抵消;
5.指针在读取地址的过程中,是从低地址向高地址读取的,但存入地址时却是高地址向低地址存入的;
6.一个指针变量定义的成不成功,主要在于能不能为这个变量开辟空间;
7.指针未初始化不要取 * ,指针初始化为NULL也不要取 * ,除了void *之外其他类型的指针变量的类型和访问地址的对象类型一定要相互匹配,不然容易造成越界访问的情况;
8.数组名等同于数组元素的第0个元素的地址(首地址),所以可以直接赋值给指针变量;
9.“ [ ] ”是() *的缩写,表示一个指针变量和一个取值符号 * ,[ ]右边的值,表示该数值+在右边,[ ]左边的值,表示该数值+在左边;
10.双引号有两个作用,一个是描述里面的内容为字符串,另一个是取这个字符串的首元素的地址;