目录
一、内存
二、指针变量的定义
二、指针变量和普通变量建立关系
三、指针变量的初始化
四、指针变量的类型
1、指针变量自身的类型
2、指针变量指向的类型
3、&和*对类型的影响
4、指针变量指向类型决定取值宽度
5、指针变量的指向类型 决定了+1的跨度
五、万能指针
六、数组元素的指针变量
七、字符串与指针
八、指针数组
1、数值的指针数组
2、字符的指针数组
九、数组指针
1、一维数组指针
(1)数组首元素地址 和 数组首地址区别
(2)数组指针 本质是指针变量 保存的是数组的首地址。
(3)通过数组指针访问数组中的元素
2、二维数组数组名含义
3、二维数组和一维数组指针关系
4、多维数组的物理(在内存)存储
十、指针变量作为函数的形参
十一、数组作为函数的形参
1、一维数组作为函数的形参
2、二维数组作为函数的形参
十二、指针类型作为函数的返回值类型。
十三、函数指针
1、函数指针的定义
2、注意项
3、函数指针变量 使用typedef定义(了解)
十四、函数指针作为函数的参数(了解)
在32位平台/cpu,每一个进程 拥有4G的空间。
系统为内存的每一个字节 分配一个32位(4B)的地址编号。
地址编号==指针
指针变量:本质是变量 但该变量 保存的是 内存的地址编号。
*修饰指针变量p(*p),要保存,先定义,后用*p替换。
例:
1、定义一个指针变量p 其保存int类型的数的地址; int *p;
2、定义一个指针变量p 其保存的是数组首地址; int (*p)[N](N为具体数值)
3、定义一个指针变量p 其保存的是函数的入口地址; int (*p)(int,int);
4、定义一个指针变量p 其保存的是结构体变量的地址struct stu lucy; struct stu *p;
5、定义一个指针变量p 其保存的是指针变量int *q的地址; int **p(指针的指针)
在32位平台任何类型的指针变量 都是4字节
在64位平台任何类型的指针变量 都是8字节
例如在x86(32位平台)
*仅仅修饰p为指针变量,还需让普通变量与指针变量建立关系。
在初始化时:
例:int *p;
int num;
p=&num//p的值就是num的地址/p保存了num的地址/p指向了num
在使用时,*p代表取p所保存的地址编号所对应的内容。
例如:p等价与&num *p则等价于num 因*p=*&num=num;
若定义一个指针变量q,其保存的是指针变量int *p的地址 **q=&p;
则*q==p==#
**q==*p==num;
指针变量 在操作之前 必须指向合法的地址空间。
1、指针变量 如果不初始化 立即操作 会出现段错误。
int *p;
*p;//error 段错误
2、指针变量 如果没有指向合法的空间 建议初始化为NULL(编号0的地址),仍会出现段错误。
int *p = NULL;
*p;//error 段错误
int *p = NULL;//等价于NULL是赋值给p ---- int *p;p = NULL;
3、将指针变量 初始化为 合法的地址
int num = 10;
int *p = &num//等价于int *p; p = &num
或:
int num=10, *p=#
将指针变量名去掉,剩下的类型就是指针变量自身的类型
int *p; //p自身的类型为int *
将指针变量名和离它最近的一个* 一起去掉,剩下的类型就是指针变量指向的类型
int *p; //p指向的类型为int
int num = 10;
int *p = #
在使用中:
&和*相遇 从右往左 依次抵消‐‐‐‐*&p == p
对变量名 取地址 整体类型加一个*‐‐‐‐&num 为int *
对指针变量 取* 整体类型减一个*‐‐‐‐*p 为int
应用:一般用于 赋值语句的判断
举例:
int num=10, *p=&num, **q=&p;以下结果正确的是:ABC
A:*p = 100 B: *q = &num C:p=&num D:q=&num
解析:
综合案例分析:
int num=0x01020304;//16进制
int *p1=#
short *p2=#//若报错,则强制转换类型:short *p2=(short *)#
char *p3=#
则:
以综合案例继续分析,进行一下操作
1、取出0x0102的值
2、取出0x02的值
3、取出0x0203的值
若定义char *p1,则定位到*(short *)(p1+1) //(short *)表示强制类型转换
若定义short *p1,则定位到*(short *)((char *)p2+1)
1、void不能定义普通变量
void num;//error,不知道num类型大小,不能给num开辟空间。
2、void *可以定义指针变量
void *p;//p为万能一级指针变量,p类型为void *,在32位平台void *为4B
在32位平台任意类型的指针为4B,系统知道为p开辟4B空间,所以定义成功。
3、应用:用于 函数的形参 达到算法操作多种数据类型
4、注意项
(1)对p取*之前 对p先进行指针类型强转。
(2)指针变量未初始化不能取*
(3)指针变量 初始化NULL 也不能取*
(4)指针变量不能越界访问
例1:
char ch = 'a';//1B
int *p = &ch;//4B
*p;//error 越界访问非法内存
例2:
int num = 10;
int *p = #
p++;
*p;//越界访问
1、定义
int arr[5]={10, 20,30,40,50}; //需求:定义一个指针变量 保存 数组元素的地址
int *p;
p = &arr[0]; 或者p = arr;//arr作为地址 第0个元素的地址 arr==&arr[0]
也可初始化为数组其他元素:p = &arr[3];
简写:int *p=arr;
2、数组元素的指针变量 和 数组名(作为地址) 等价
例:
int arr[5] = {10, 20, 30, 40, 50};
int n = sizeof(arr) / sizeof(arr[0]);
int *p = arr;
int i=0;
for (i = 0; i < n; i++)
{
printf("%d ", *(p + i));
}
for (i = 0; i < n; i++)
{
printf("%d ", *(arr + i));
}
3、*()的缩写为【】
[]是*()的缩写 []左边的值 放在+的左边 []里面的值 放在+右边 整体取*
printf("arr[1] = %d\n", arr[1]);
printf("*(arr+1) = %d\n", *(arr + 1));
printf("*(arr+1) = %d\n", *(1 + arr));
printf("1[arr] = %d\n", 1 [arr]);//不建议该写法
例题:
int arr[5] = {10, 20, 30, 40, 50};
int *p = arr+3;
则p[1]的值为:50
4、arr ==&arr[0]的原因:&arr[0] == &*(arr+0) == arr+0 == arr
5、指向同一数组元素的两个指针变量之间的 关系
两个指针变量相减 等于他们之间元素的个数
两指针变量互相赋值 让他们指向同一处
两指针变量判断相等== 判断他们是否指向同一处
两个指针变量判断大小> < >= <= != 判断它们的位置关系
两个指针变量相加 无意义
1、字符数组:char str1[128]="hello world";
sizeof(str1) == 128(字节)
str1是数组 开辟128字节 存放字符串"hello world",存放在栈区/全局区
2、字符串指针变量:char *str2="hello world";//双引号描述其为字符串并取字符串第0个元素地址
sizeof(str2) == 4字节 或 8字节 str2是指针变量
在栈区/全局区 保存的是字符串常量"hello world"的首元素地址, 而”hello world“在文字常量区
本质为数组,数组每个元素为指针。
举例说明:
数组首元素地址:&arr[0] == arr +1跳过一个元素
数组的首地址:&arr +1跳过整个数组
数组首元素地址编号 和数组的首地址编号 一致。但是他们的指针变量类型不同
举例说明:
int arr[5]={10, 20, 30,40,50};
int (*p)[5] = NULL;(初始化为NULL)或 int (*p)[5]=&arr;(合法初始化)//数组指针
(1)对数组首地址取*==数组首元素地址:*p=*&arr=arr
以2中初始化数组arr为例访问arr[2]:*(arr+2)=*(*p+2)=*(*(p+0)+2)=*(p[0]+2)==p[0][2]
(2)通过强制类型转换访问
以2中初始化数组arr为例访问arr[3]:*((int *)(p+1)‐2)=arr[3]=*((int *)p+2)
总结:
int *arr[5];//指针数组 本质是数组,保存的是数组的首元素地址,每个元素为int *
int (*arr)[5];//数组指针 本质是指针变量,保存的是数组的首地址,每个元素为int
二维数组名代表的是第0行的行地址 +1跳过一个行
对行地址取*代表当前行第0列的列地址
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]
一维数组指针和二维数组名是完全等价 *p[4]=arr;
初始化举例:
int arr[3][4]={1,2,3,4,5,6,7,8,9,10,11,12}
int *p[4]=arr;
int arr[n]; int *p;
int arr[n][m]; int (*p)[m];
int arr[n][m][k]; int (*p)[m][k]
n维数组 和n‐1维的数组指针 等价
不管几维数组在物理上 都是一维存储
应用:降低算法时间复杂度
单向传递 之 值传递 函数内部 不能修改外部变量的值
单向传递之地址传递 函数内部 可以修改外部变量的值
应用:需要在函数内部 修改外部变量的值时,将外部变量的地址 传递给函数
操作(读或写)外部数组元素时,数组名传递给函数。
数组名会被编译器 优化成 指针变量,因数组名实际为首元素地址。
直接写成指针变量:void array(int *p, int n)
函数内部操作函数外部的二维数组,将二维数组名 传递给函数。
数组名会被编译器 优化成 一维数组指针。
3、n维数组 作为函数形参 会被优化成 n-1维的数组指针
将函数内部的合法地址 通过返回值 返回给函数外部使用。
注:不要返回值 普通局部变量的地址
初始化提高:
函数名 代表函数的入口地址;
函数指针:本质是一个指针变量 只是该变量 保存的是函数的入口地址
以两数相加为例说明:
函数指针变量加减无意义
对函数指针变量取* 无意义 ,*p会被编译器优化成p
函数指针变量 判断大小无意义
函数指针变量 可以赋值
函数指针变量 可以判断相等
以两数相加为例说明:
以两数相乘除为例说明: