指针
所谓指针,也就是内存的地址;所谓指针变量,也就是保存了内存地址的变量。
需要注意的是,虽然变量名、函数名、字符串名和数组名在本质上是一样的,它们都是地址的助记符,但在编写代码的过程中,我们认为变量名表示的是数据本身,而函数名、字符串名和数组名表示的是代码块或数据块的首地址。
1 相关概念
- 内存地址概述:
系统给内存的每一个字节分配一个编号,这个编号就是内存地址。 - 指针变量:
本质就是一个变量 ,只是这个变量存放的是内存的地址编号(地址/指针)。在32位平台任何类型的地址编号都是4字节。
2 定义指针变量的步骤
第一步:* 修饰指针变量名
第二步:保存啥类型变量的地址就用该类型定义一个普通变量
第三步:从上往下整体替换
3 指针变量的类型
3.1 指针变量的类型包括:指针类型+指向类型
注:
3.2 指针变量的宽度
因为操作系统会给内存每个字节分配一个地址编号,对于一个int型数据有4个字节,所以占用四个地址的空间。&操作代表取这段空间的首地址,然后需要根据指针的指向来指定宽度,“首地址+宽度” 就可以得到对应空间的内容。
3.3 指针变量的跨度(指针偏移量)
小练习:
#include<stdio.h>
void test()
{
int num=0x01020304;
short *p;
p=#
printf("%#x\n",*(p+1));
}
int main()
{
test();
return 0;
}
3.4 指针的强制类型转换
【注】
4 void
4.1 void 不能定义变量
void num; //error,系统不知道num的大小
4.2 void* 可以定义变量
void *p; //p的类型为void* 指针类型,32位平台4字节,系统知道给p开辟4字节
//p也叫万能指针 p可以保存任意一级指针
对于p不能直接使用*操作。必须实现对p进行强制类型转换
void test04()
{
int num=10;
void* p;
p=#
//printf("*p=%d\n",*p); //error 因为p的指向是void类型,系统确定不了宽度
printf("*=%d\n",*(int *)p);// ok,p临时的指向类型为int 系统确定了宽度4B
}
4.3 不要对没有初始化的指针变量取 *
int *p;
printf("*p=%d\n",*p);
//因为p没有初始化,内容随机,也就是p指向了一个未知空间,系统不允许用户取值*p操作
4.4 不要对初始化为NULL的指针变量 取*
//NULL 就是(void *)0 地址,也是内存的起始地址,受系统保护
int *p=NULL;
printf("*p=%d\n",*p); //也不能 *p
4.5 不要给指针变量赋普通的数值
int *p=1000;//此时的1000对于p来说是地址编号1000
5 指针与数组、指针与指针、指针与函数
5.1 数组元素指针
注1:
注2:指向同一数组的两个指针关系
#include<stdio.h>
int main()
{
int arr[5]={10,20,30,40,50};
int *p1=arr;
int *p2=arr+3;
//1、指向同一数组的两个指针变量相减返回的是两个相差元素的个数
printf("%d\n",p2-p1);//3
//2、指向同一数组的两个指针变量可以比较大小 > < >= <= == !=
if(p2>p1)
{
printf(">\n");
}
else
{
printf("<=\n");
}
//3、指向同一数组的两个指针变量可以赋值
p1=p2; //p1和p2指向同一处
//4、指向同一数组的两个指针变量尽量不要相加
printf("p1=%u,p2=%u\n",p1,p2);
//5、[]里面在不越界的情况下,可以为负数
printf("%d\n",p2[-2]); //20
return 0;
}
5.2 指针数组与数组指针
5.2.1 指针数组
5.2.2 数组指针
#include<stdio.h>
int main()
{
int arr[5]={10,20,30,40,50};
int (*p)[5]; //指针数组:本质是一个指针变量,只是该变量保存的是数组的首地址
printf("%d\n",sizeof(p));//4
printf("p=%u\n",p);
printf("p+1=%u\n",p+1);
p=&arr; //&arr 才代表数组的首地址
}
- 二维数组分析
- 数组指针与二维数组之间的联系
5.3 多级指针
5.4 指针与函数
5.4.1 指针作为函数参数
- 数组名作为函数参数
//void my_input_array(int arr[5],int n)
//一维数组 作为函数的形参会被优化成 指针变量
void my_input_array(int *arr, int n)
{
printf("B:%d\n",sizeof(arr)); //4
}
//void my_input_array(int arr[3][4],int row,int col)
//二维数组 作为函数的形参会被优化成 数组指针
void my_print_two_array(int (*arr)[4], int row, int col)
{
printf("B:%d\n",sizeof(arr)); //4
}
- 二维数组行数和列数求法
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]);//列数
- 指针变量作为函数的参数
如果想在函数内部修改外部变量的值就需要将外部变量的地址传递给函数(以指针作为函数的参数)
5.4.2 指针函数(指针作为函数的返回值)
函数返回普通局部变量的地址无效,为了解决此问题
int* get_addr(void)
{
static int num=1000;
return # //静态变量,函数结束 不会被释放
}
void test()
{
int *p=NULL;
p=get_addr();
printf("*p=%d\n",p); //1000
}
5.4.2 函数指针
函数名代表的是函数的入口地址
对函数指针变量 取* 无意义