
指针(千峰c++学习笔记)
1、定义步骤*修饰指针变量p保存谁的地址,就先定义谁从上往下整体替换案例1定义一个指针变量p 保存int num的地址:int *p定义一个指针变量p 保存数组int arr[5]首地址: int(*p)[5]定义一个指针变量p 保存函数入口地址: int fun(int,int);定义一个指针变量p 保存结构体变量的地址: struct stu lucy;定义一个指针变量p 保存指针变量int
8.1 指针变量
8.1.1 内存的概述
在32位平台,每一个进程拥有4G的空间。系统为内存的每一个字节分配一个32位的地址编号(虚拟地址)。这个编号称之为地址.
无论什么类型的地址,都是存储单元的编号,在32位平台下都是4个字节,即任何类型的指针变量都是4个字节大小。
一个变量有4个字节,四个地址,那么指针变量保存的是哪一个地址呢?规定保存的是变量的首地址,如上图所示,num的大小为四个字节,&num是第一个字节的地址。
8.1.2 地址和指针变量的关系
地址就是内存的地址编号。指针变量的本质是变量,只是该变量保存的是内存的地址编号,而不是普通的数值。如下图所示:
8.1.3 指针变量的定义
1、定义步骤
- *修饰指针变量p
- 保存谁的地址,就先定义谁
- 从上往下整体替换
案例1
定义一个指针变量p 保存int num的地址:int *p
定义一个指针变量p 保存数组int arr[5]首地址: int(*p)[5]
定义一个指针变量p 保存函数入口地址: int fun(int,int);int(*p)(int,int)
定义一个指针变量p 保存结构体变量的地址: struct stu lucy;struct stu *p
定义一个指针变量p 保存指针变量int *q的地址: int **p
2、在32位平台任何类型的指针变量都是4字节
3、指针变量和普通变量建立关系
int num=10;
int *p;
p=#//普通变量和指针变量建立关系
cout<<*p//10
在定义的时候,*仅仅作为修饰p为指针变量,而在使用时,*p表示取所保存的地址编号所对应的空间的内容。
8.1.4 指针变量的初始化
指针变量在操作之前,必须指向合法的地址空间
1、指针变量如果不初始化立即操作,会出现段错误
int *p;
cout<<*p<<endl;
2、指针变量如果没有指向合法的空间,建议初始化为NULL
int *p=NULL;//NULL是赋值给p int *p;p=NULL;
但是不要操作指向NULL的指针变量,因为NULL实质是地址为0的地址空间,而地址为0的内存是受严格保护的。
3、将指针变量初始化为合法的地址(变量的地址、动态申请的地址、函数入口的地址等等)
int num=10;
int *p=#//int *p;p=#
int data=10,*p=&data;//此时data为int类型,而p为int*类型
int* p,data;//p为int*类型,data为int类型
定义变量时是由基本类型修饰的,所以不要误以为p和data都是int*类型
8.1.5 指针变量的类型
1、指针变量自身的类型:将指针变量拖黑,剩下的类型就是指针变量自身的类型
int *P;p自身的类型为int *
指针变量自身的类型主要用于赋值语句的判断,以免出现类型不匹配的情况
int num=10;
int *p=#
//在使用中,num为int &num为int* 即对变量名取地址,整体类型加一个*
//p为int* *p为int 即对指针变量取*,整体类型减一个*
//在使用中&和*相遇,从左往右依次抵消
例如:*&p==p;
案例:int num=10,*p=&num,**q=&p;以下结果正确的是ABC
A:*p=10
B:*q=&num
C:p=&num
D:q=&num
2、指针变量指向的类型
将变量名和离它最近的一个*一起拖黑,剩下的类型就是指针变量指向的类型
int *p;p指向的类型为int
3、指针变量指向的类型决定了取值宽度
int num=0x01020304;
int *p=#
为啥*p==num==0x01020304?
若num为int,取0x01020304(int为4B)
若num为char,取0x04(char为1B)
4、指针变量的指向类型决定了+1的跨度
8.1.6 综合案例分析
int num=0x01020304;
案例1:取出0x0102的地址
short *p=(short *)#
*(p+1);
案例2:取出0x02的值
char *p=(char *num);
*(p+2);
案例3:取出0x0203的值
char *p=(char *)#
*(short *)(p+1);
8.1.7 *p等价于num
int num=10;
int *p=#
//p==num
//*p==*&num==num
8.1.8 指针变量的注意事项
1、void不能定义普通变量,因为系统无法为类型为void的变量开辟空间,不知道类型也就无法知道开辟多大的空间,如void num;是错误的
2、void *可以定义指针变量
void *p;//p自身的类型为void *,在32位平台任意类型的指针为4B
那么系统知道为p开辟4B空间,所以定义成功
此时p就是万能的一级指针变量,能保存任意一级指针的地址编号
int num=10;
void *p=#
short data=20;
p=&data;
万能指针一般用于函数的形参达到算法操作多种数据类型的目的
注意:不要对void *p的指针变量取*
int num=10;
void *p=#
*p;//error p指向的类型为void 无法确定p的取值宽度 所以不能取*p
对p取*之前先进性指针类型强转
int num=10;
void *p=#
cout<<*(int *)p<<endl;
3、指针变量未初始化不要取*
int *p;
*p;//error段错误
4、指针变量初始化为NULL不要取*
int *p=NULL;
*p;//error段错误
5、指针变量不要越界访问
char ch='a';
int *p=&ch;
*p;//error越界访问非法内存
p指向的类型应为char型,而此时定义为int型,故会发生越界
8.2 数组元素的指针
8.2.1 数组元素的指针概述
数组元素的指针变量,是用来保存数组元素的地址
int arr[5]={10,20,30,40,50}
int *p//定义一个指针变量,保存数组元素的地址
p=&arr[0];
p=arr;//arr作为地址第0个元素的地址arr=&arr[0]
p=&arr[3];
8.2.2 数组元素的指针变量和数组名(作为地址)等价
int arr[5]={10,20,30,40,50};
int n=sizeof(arr)/sizeof(arr[0]);
int *p=arr;//p=arr
//遍历整个数组
int i;
for(int i=0;i<n;i++)
{
//cout<<arr[i]<<" ";
//cout<<*(arr+i)<<" ";
cout<<*(p+i)<<" ";
}
cout<<endl;
8.2.3 在使用中[ ]就是*()的缩写
int arr[5]={10,20,30,40,50};
int n=sizeof(arr)/sizeof(arr[0]);
cout<<"arr[1]= "<<arr[1]<<endl;//20
cout<<"*(arr+1)= "<<*(arr+1)<<endl;//20
cout<<"*(arr+1)= "<<*(1+arr)<<endl;//20
cout<<"1[arr]= "<<1[arr]<<endl;//20
//[]是*()的缩写,[]左边的值放在+的左边 []右边的值放在+的右边 整体取*
为啥arr==&arr[0]?
&arr[0]==&*(arr+0)==arr+0==arr
案例1:p[-1]的值
int arr[5]={10,20,30,40,50};
int *p=arr+3;
//30
案例2:p[1]的值
int arr[5]={10,20,30,40,50};
int *p=arr+3;
//50
8.2.4 指向同一数组的元素的两个指针变量间的关系
8.3 字符串与指针
8.3.1 字符数组
char str1[128]="hello world";
str1是数组,开辟128字节存放字符串"hello world"
sizeof(str1)==128字节
8.3.2 字符串指针变量
char *str2="hello world";
sizeof(str2)==4字节或8字节
str2是指指针变量在栈区/全局区保存的是字符串常量"hello world"的首元素地址,而"hello world"在文字常量区
8.4 指针数组
指针数组:本质是数组,只是数组的每个元素为指针
8.4.1 数值的指针数组
int num1=10;
int num2=20;
int num3=30;
int num4=40;
int *arr[4]={&num1,&num2,&num3,&num4};
int n=sizeof(arr)/sizeod(arr[0]);
for(int i=0;i<n;i++)
{
cout<<*arr[i]<<" ";//10 20 30 40
}
8.4.2 字符指针数组
char *arr[4]={"hehehehe","xixixixi","lalalala","hahahaha"};
8.4.3 二维字符数组
char *arr[1]={"hehehehe", "xixixixixi", "lalalalala", "hahahahaha"};
char arr[1][128]={"hehehehe", "xixixixixi", "lalalalala", "hahahahaha"};
arr1是指针数组,存放的是每个字符串的首元素的地址
arr2是二维字符数组,存放的是每个字符串
8.5 指针的指针
int num=10;
int *p=num;
int **q=&p;
n级指针可以保存n-1级指针变量的地址
8.6 数组指针
8.6.1 数组首元素地址和数组首地址
数组首元素地址:&arr[0]=arr arr+1跳过一个元素
数组的首地址:&arr; &arr+1跳过整个数组
8.6.2 数组指针的本质
本质是指针变量,保存的是数组的首地址
int arr[5]={10,20,30,40,50};
int (*p)[5]=NULL;//数组指针
int (*p)[5]=&arr;//数组指针
8.6.3 数组指针的案例
int arr[5]={10,20,30,40,50};
int (*p)[5]=&arr;//数组指针
cout<<*((int *)(p+1)-2)<<endl;//40
p保存的是数组首地址,p+1跳一个数组,再将p+1强转为int *类型,(int *)(p+1)-2)后退两个元素,所以是倒数第二个元素即40
总结:
int *arr[5];//指针数组,本质是数组,每个元素为int *
int (*arr)[5];//数组指针,本质是指针变量,保存的是数组的首地址(数组的每个元素必须为int)
8.6.4 二维数组和数组指针的关系
1、
int arr[n]; int *p;
int arr[n][m]; int (*p)[m];
int arr[n][m][k]; int (*p)[m][k]
n维数组和n-1维的数组指针等价
8.7 多维数组的物理存储
不管几维数组再物理上都是一维存储,在逻辑上是多维的
8.8 指针与函数
8.8.1 指针变量作为函数的参数
如果想在函数内部修改外部变量的值,需要将外部变量的地址传递给函数。
案例:传地址
void setnum(int *p)//int *p=#
{
//*p==num
*p=100;
}
void test01()
{
int num=0;
setnum02(&num);//单向传递之传地址
cout<<"num="<<num<<endl;//修改成功
}
8.2.2 一维数组作为函数的参数
函数内部想操作(读或写)外部数组元素,将数组名传递给函数。
一维数组作为函数的形参会被优化成指针变量
//void outputIntarray(int arr[5],int n)
//优化成指针变量
void outputIntarray(int *arr,int n)
{
cout<<"内部sizeof(arr)="<<sizeof(arr)<<endl;//4B
for(int i=0;i<n;i++)
{
//cout<<*(arr+i)<<" ";
cout<<arr[i]<<" ";//推荐
}
void testo2()
{
int arr[5]={10,20,30,40,50};
int n=sizeof(arr)/sizeof(arr[0]);
cout<<"外部sizeof(arr)="<<sizeof(arr)<<endl;//20B
outputIntarray(arr,n);//10 20 30 40 50
}
8.3.3 二维数组作为函数的参数
函数内部想操作函数外部的二维数组,需要将二维数组名传递给函数
二维数组名作为函数的形参,会被优化为一维的数组指针
//void outputIntDoubleArray(int arr[3][4], int row, int col)
//二维数组 作为函数的形参 会被优化成 一维的数组指针
void outputIntDoubleArray(int (*arr)[4], int row, int col)
{
cout<<"内部sizeof(arr) = "<<sizeof(arr)<<endl;//4B
int i=0,j=0;
for(i=0;i<row;i++)
{
for(j=0;j<col;j++)
{
cout<<arr[i][j]<<" ";
}
cout<<endl;
}
}
void test03()
{
int arr[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
int row = sizeof(arr)/sizeof(arr[0]);
int col = sizeof(arr[0])/sizeof(arr[0][0]);
cout<<"外部sizeof(arr) = "<<sizeof(arr)<<endl;//48B
outputIntDoubleArray(arr, row, col);
}
8.8.4 函数的返回值为指针类型
将函数内部的合法地址通过返回值返回给函数外部使用。
注意:函数不要返回普通局部变量的地址,因为局部变量在被使用后就会被立即释放,结束后地址所指向的内存可能是空的或者已经被其它进程所使用
int *getAddr(void)
{
//int data=100;//不要返回局部变量的值
static int data=100;//静态全局变量
return &data;
}
void test()
{
int *p=NULL;
p=getAddr();
cout<<"*p="<<*p<<endl;//100
}
8.9 函数指针
8.9.1 函数指针的定义
函数名代表函数的入口地址;
函数指针:本质是一个指针变量,只是该变量保存的是函数的入口地址
//函数指针p只能保存有两int形参以及int返回值的函数入口地址
int (*p)(int,int)=NULL;
int myAdd(int x,int y)
{
return x+y;
}
void test05()
{
int (*p)(int x,int y) = NULL;
cout<<"sizeof(p) = "<<sizeof(p)<<endl;//4B
//函数指针 和 函数入口地址建立关系
p = myAdd;
//通过p调用myAdd函数(函数+(实参))
cout<<p(10,20)<<endl;//30
}
8.9.2 函数指针变量注意事项
函数指针变量不要+1 无意义
不要对函数指针变量取* 无意义
函数指针变量判断大小><无意义
函数指针变量可以赋值 p2=p1;让p2指向p1所指向的函数
函数指针变量可以判断相等p2==p1
8.9.3 函数指针变量使用typedef定义
int myAdd(int x,int y)
{
return x+y;
}
void test05()
{
//给函数指针类型取别名
typedef int (*FUN_TYPE)(int x,int y);
FUN_TYPE p=NULL;
cout<<"sizeof(p) = "<<sizeof(p)<<endl;//4B
//函数指针 和 函数入口地址建立关系
p = myAdd;
//通过p调用myAdd函数(函数+(实参))
cout<<p(10,20)<<endl;//30
}
8.9.4 函数指针作为函数的参数
目的:让算法功能多样化
案例:设计一个算法完成加减乘除
int myAdd(int x,int y)
{
return x+y;
}
int mySub(int x,int y)
{
return x-y;
}
int myMul(int x,int y)
{
return x*y;
}
int myDiv(int x,int y)
{
return x/y;
}
//设计算法 操作上面的函数
int myCalc(int x,int y, int (*func)(int,int) )
{
return func(x,y);
}
void test06()
{
cout<<myCalc(10,20, myAdd)<<endl;//30
cout<<myCalc(10,20, mySub)<<endl;//-10
cout<<myCalc(10,20, myMul)<<endl;//200
cout<<myCalc(10,20, myDiv)<<endl;//0
}
更多推荐
所有评论(0)