目录
7.2.2 数组元素的指针变量 和 数组名(作为地址) 等价
编辑7.6.2 数组指针 本质是指针变量 保存的是数组的首地址
7.1 指针变量
7.1.1 内存的概述
在
32
位平台,每一个进程 拥有
4G
的空间。
系统为内存的每一个字节 分配一个
32
位的地址编号(虚拟地址)。这个编号称之为地址。
无论什么类型的地址,都是存储单元的编号,在
32
位平台下都是
4
个字节,即任何类型的指针变量都是
4个字节大小。
7.1.2 地址和指针变量的关系
地址 就是内存的地址编号。
指针变量:本质是变量
只是该变量 保存的是 内存的地址编号。(不是普通的数值)
7.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
7.1.4 指针变量的初始化
指针变量 在操作之前 必须指向合法的地址空间。
1
、指针变量 如果不初始化
立即操作 会出现段错误
int *p;
cout<<*p<<endl;
2
、指针变量 如果没有指向合法的空间 建议初始化为
NULL
int *p = NULL;//NULL是赋值给p int *p; p = NULL;
不要操作 指向
NULL
的指针变量
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类
7.1.5 指针变量的类型
1
、指针变量
自身的类型
:
将指针变量名拖黑,剩下的类型就是指针变量自身的类型
int *p; p自身的类型为int *
指针变自身的类型
一般用于 赋值语句的判断
int num = 10;
int *p = #
//在使用中
//num 为int &num 为int * ---->对变量名 取地址 整体类型加一个*
//p 为int * *p 为int ---->对指针变量 取* 整体类型减一个*
//在使用中 &和*相遇 从右往左 依次抵消
*&p == p
案例
1
:
int num=10, *p=&num, **q=&p;
以下结果正确的是
ABC
int num = 10;
int *p = #
int **q = &p;
A:*p = 100 B: *q = &num C:p=&num D:q=&num
2
、指针变量
指向的类型
(
重要
)
将指针变量名和离它最近的一个
*
一起拖黑,剩下的类型就是指针变量指向的类型
int *p; p指向的类型为int
3
、指针变量的指向类型 决定了取值宽度
int num = 0x01020304;
int *p = #
为啥 *p == num == 0x01020304?
4、指针变量的指向类型 决定了+1跨度
7.1.6 综合案例分析
案例1:取出0x0102的值
short *p = (short *)#
*(p+1);
案例
2
:取出
0x02
的值
char *p = (char *)#
*(p+2);
案例
3
:取出
0x0203
的值
char *p = (char *)#
*(short *)(p+1)
7.1.7 *p等价num
int num = 10;
int *p = #
//p==&num
//*p == *&num == num
7.1.8 指针变量的注意事项
1
、
void
不能定义普通变量
void num;//error 不能给num开辟空间
2
、
void *
可以定义指针变量
void *p;//ok 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;//err p指向的类型为void 无法确定p的取值宽度 所以不能*p
对
p
取
*
之前 对
p
先进行指针类型强转。
int num=10;
void *p = #
cout<<*(int *)p<<endl;
3
、指针变量 未初始化 不要取
*
int *p;
*p;//err 段错误
4
、指针变量 初始化
NULL
不要取
*
int *p = NULL;
*p;//err 段错误
5、指针变量 不要越界访问
char ch = 'a';
int *p = &ch;
*p;//error 越界访问非法内存
int num = 10;
int *p = #
p++;
*p;//越界访问
7.2 数组元素的指针
7.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];
7.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(i=0;i<n;i++)
{
//cout<<arr[i]<<" ";
//cout<<*(arr+i)<<" ";
cout<<*(p+i)<<" ";
}
cout<<endl;
7.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<<"--------------------"<<endl;
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]
的值
__ 30 __
int arr[5] = {10, 20, 30, 40, 50};
int *p = arr+3;
案例
2
:
p[1]
的值
__ 50 __
int arr[5] = {10, 20, 30, 40, 50};
int *p = arr+3;
7.2.4 指向同一数组的元素的两个指针变量间的关系
7.3 字符串与指针
7.3.1 字符数组
char str1[128]="hello world";
str1
是数组 开辟
128
字节 存放字符串
"hello world"
sizeof(str1) == 128字节
7.3.2 字符串指针变量
char *str2="hello world";
sizeof(str2) == 4字节 或 8字节
7.4 指针数组
指针数组:本质是数组
只是数组的每个元素为 指针
32
位平台:
char *arr1[4];
short *arr2[4];
int *arr3[4];
sizeof(arr1) ==16B
sizeof(arr2) ==16B
sizeof(arr3) ==16B
7.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)/sizeof(arr[0]);
int i;
for(i=0;i<n;i++)
{
cout<<*arr[i]<<" ";//10 20 30 40
}
cout<<endl;
7.4.2 字符指针数组
char *arr[4]={"hehehehe", "xixixixixi", "lalalalala", "hahahahaha"};
char *arr[4]={"hehehehe","xixixixi", "lalalala", "hahahahaha"};
int n = sizeof(arr)/sizeof(arr[0]);
int i;
for(i=0;i<n;i++)
{
cout<<arr[i]<<endl;
}
7.4.3 二维字符数组
char *arr1[4]={"hehehehe", "xixixixixi", "lalalalala", "hahahahaha"};
char arr2[4][128]={"hehehehe", "xixixixixi", "lalalalala", "hahahahaha"};
arr1是在指针数组 存放的是每个字符串的首元素的地址
arr2
是二维字符数组
存放的是每个字符串
7.5 指针的指针
int num = 10;
int *p = #
int **q = &p;
n级指针变量 可以保存 n-1级指针变量的地址
7.6 数组指针
7.6.1 数组首元素地址 和 数组首地址
数组首元素地址:
&arr[0] == arr arr+1
跳过一个元素
数组的首地址:&arr &arr+1跳过整个数组
7.6.2 数组指针 本质是指针变量 保存的是数组的首地址
int arr[5]={10, 20, 30,40,50};
int (*p)[5] = NULL;//数组指针
int arr[5]={10, 20, 30,40,50};
int (*p)[5] = &arr;//数组指针
7.6.3 数组指针的案例
int arr[5]={10, 20, 30,40,50};
int (*p)[5] = &arr;//数组指针
cout<< *((int *)(p+1)-2) <<endl;//40 分析为啥是40
总结:
int *arr[5];//指针数组 本质是数组 每个元素为int *
int (*arr)[5];//数组指针 本质是指针变量 保存的是数组的首地址(概数组必须5个元素每个元素为
int)
7.6.4 二维数组和数组指针的关系
1
、深入了解二维数组
2、二维数组和一维数组指针的关系
arr[1] => *(arr+1) 第一行第0列的列地址
&arr[1] => &*(arr+1)=>arr+1 第1行的行地址
*arr+1 => 第0行第1列的列地址
arr[1]+2 =>*(arr+1)+2 =>第1行第2列的列地址
**arr ==*(*(arr+0)+0) == arr[0][0]
2、二维数组和一维数组指针的关系
int arr[n]; int *p;
int arr[n][m]; int (*p)[m];
int arr[n][m][k]; int (*p)[m][k]
n维数组 和n-1维的数组指针 等价
7.7 多维数组的物理存储
不管几维数组在物理上 都是一维存储,在逻辑上是多维的。
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]);
int *p = &arr[0][0];
int i=0;
for(i=0;i<row*col;i++)
{
cout<<p[i]<<" ";
}
cout<<endl;
7.8 指针与函数
7.8.1 指针变量作为函数的参数
如果想在函数内部 修改外部变量的值
需要将外部变量的地址 传递给函数。(重要)
案例
1
:单向传递 之 传值
函数内部 不能修改外部变量的值
void setNum01(int data)
{
data = 100;
}
void test01()
{
int num =0;
setNum01(num);//单向传递之 传值
cout<<"num = "<<num<<endl;//0 修改不成功
}
案例
2
:单向传递 之 传地址
函数内部 就可以修改 外部变量的值
void setNum02(int *p)//int *p=#
{
//*p == num
*p = 100;
}
void test01()
{
int num =0;
setNum02(&num);//单向传递之 传地址
cout<<"num = "<<num<<endl;//100 修改成功
}
7.8.2 一维数组作为函数的参数
函数内部 想操作(读或写)外部数组元素,将数组名 传递给函数。
一维数组 作为函数的形参 会被优化成
指针变量。
//void ouputIntArray(int arr[5], int n)
//一维数组作为函数的参数 会被编译器 优化成 指针变量
void ouputIntArray(int *arr, int n)
{
cout<<"内部sizeof(arr) = "<<sizeof(arr)<<endl;//4B
int i=0;
for(i=0;i<n;i++)
{
//cout<<*(arr+i)<<" ";
cout<<arr[i]<<" ";//推荐
}
cout<<endl;
}
void test02()
{
int arr[5]={10,20,30,40,50};
int n = sizeof(arr)/sizeof(arr[0]);
cout<<"外部sizeof(arr) = "<<sizeof(arr)<<endl;//20B
//遍历数组
ouputIntArray(arr, n);// 10 20 30 40 50
}
7.8.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);
}
7.8.4 函数的返回值类型 为 指针类型
将函数内部的合法地址
通过返回值 返回给函数外部使用。
注意:函數不要返回 普通局部变量的地址
int* getAddr(void)
{
//int data = 100;//不要返回普通局部变量的地址
static int data = 100;
return &data;
}
void test04()
{
int *p = NULL;
p = getAddr();
cout<<"*p = "<<*p<<endl;//100
}
7.9 函数指针
7.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
}
7.9.2 函数指针变量注意
函数指针变量 不要
+1
无意义
不要对函数指针变量取
*
无意义
int (*p)(int, int) = my_add;
*p会被编译器优化成p
函数指针变量 判断大小
> <
无意义
函数指针变量
可以赋值
p2=p1
函数指针变量
可以判断相等
p2 ==p1
7.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
}
7.9.4 函数指针作为函数的参数
目的:让算法功能多样化
案例
1
:设计一个算法,完成加减乘除
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
}