1.指针的概念:
指针是一种数据类型,它存储了另一个变量的内存地址。通过指针,程序可以直接访问和操作内存中的值。
&:取地址运算符 %p:取地址运算符
int a = 10;
//&:取地址运算符 %p:取16进制地址
printf("a的十进制地址 = %d\n", &a);
printf("a的16进制地址 = %p\n", &a);
printf("\n");
return 0;
运行结果:
2.定义一个指针变量
int* p;
3.如何给指针变量初始化?
char* pc = &c;
//*:取值 &:取地址
printf("*pc = %c\n", *pc);
printf("pc的十进制地址 = %d\n", pc);
printf("a的16进制地址 = %p\n", pc);
运行结果:
4.定义一个空指针
int* pnull = NULL;
printf("pnull = %d\n", pnull);
运行结果:
5.间接访问
间接访问是指不直接通过变量名或数据本身来访问数据,而是通过一个或多个中间层(如指针、引用等)来访问。
*:间接访问运算符
int b = 20;
int* pb = &b;
printf("*pb = %d\n", *pb);
printf("\n");
//*pb <==> b
可以给*pb重新赋值
*pb = 30;
printf("*pb = %d\n", *pb);
printf("\n");
5.1 空指针:不能进行间接访问
int* pnull = NULL;
printf("pnull = %d\n", *pnull);
直接报错:
一般用if(pnull !=NULL)进行判断
int* pnull = NULL;
if(pnull != NULL)
printf("pnull = %d\n", *pnull);
else
printf("这是一个空指针\n");
5.2 非法指针:不能进行间接访问
非法指针是指向错误内存地址的指针,也称“野指针
简单来说,就是没有被初始化的指针
int* pd;
printf("*pc=%d\n", *pd);
直接报错
Run-Time Check Failure #3 - The variable 'pd' is being used without being initialized.
意思是:运行时检查失败#3 -未初始化就使用了变量` pd `。
6. 在指针中,&与 * 的使用
6.1 &的使用(升级)
int a; //定义一个int型变量
int* b; //定义一个int*型变量
已知a的类型是int型
那在a的前面加一个&号,&a的类型就是int*型,这个用通俗的话就叫做升级
以此类推
已知b的类型是int*型
那在b的前面加一个&号,&b的类型就是int**型
-------------------------------------------------------------------------
当然&a的意思是取a的地址,即&a就是一个常量。
所以&&a这种写法是错误的。
6.2 * 的使用(降级)
int* pint; //定义一个int*型变量
int** ppint; //定义一个int**型变量
已知pint的类型是int*型
那在pint的前面加一个 * 号,*pint的类型就是int型,这个用通俗的话就叫做降级
以此类推
已知ppint的类型是int**型
那在ppint的前面加一个 * 号,*ppint的类型就是int*型
加两个 * 号,**ppint的类型就是int型
--------------------------------------------------------------------------
当然,如果变量本身就是int型,那就不能在这个变量前面加 * 号
因为 * 的操作数必须是指针,而int型变量不是一个指针
用通俗的话说就是无级可降。
*&b = ?
*&b先升级后降级,*和&相互抵消,所以 *&b <==> b
int a = 30;
printf("%d", *&a);
运行结果:
7.二级指针
二级指针:指向一级指针的指针
二级指针存储的是一级指针的地址
int b = 30;
int* pb = &b; //*pb <=> b
printf("pb的十进制地址是:%d\n", &pb);
printf("pb的十六进制地址是:%p\n", &pb);
//定义一个二级指针
int** ppb = &pb;
printf("**ppb = %d\n", **ppb); //**ppb <=> *pb
printf("ppb的十进制地址是:%d\n", ppb);
printf("ppb的十六进制地址是:%p\n", ppb);
运行结果:
使用二级指针来修改整数的值
//b = 100; //*pb = b
//*pb = 100; //*ppb = pb
**ppb = 100; //**ppb = *(*ppb) = *pb = b
printf("b = %d\n", b);
运行结果:
8.多级指针的定义
//以此类推 多级指针的定义
//三级指针
int*** pppb = &ppb;
//四级指针
int**** ppppb = &pppb;
9.指针所占内存大小
16位机器的代码时,指针占2个字节。
32位机器的代码时,指针占4个字节。
64位机器的代码时,指针占8个字节。
int* pint;
char* pchar;
double* pdouble;
printf("pint的内存大小:%d\n", sizeof(pint));
printf("pchar的内存大小:%d\n", sizeof(pchar));
printf("pdouble的内存大小:%d\n", sizeof(pdouble));
printf("pppb的内存大小:%d\n", sizeof(pppb));
运行结果:
10.指针与数组的关系
数组是自带地址的。
数组名实际上代表了数组元素存储的内存地址的首地址,这意味着当你声明一个数组时,它自动获得了与之关联的内存地址。因此,数组名本身就是指向数组首元素地址的指针,这表明数组确实自带地址。此外,数组首元素地址与数组地址的值相等,进一步证明了数组确实有一个固定的内存地址与其相关联。
int arr[] = { 0 };//定义一个整型数组
数组名arr是一个int*型常量,使用不能作为左值。
10.1 指针与一维数组的关系
int ShuZu1[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("ShuZu1 + 0=%d\n",ShuZu1 + 0);
printf("&ShuZu1p[0]=%d\n", &ShuZu1[0]);
printf("ShuZu1 + 1=%d\n", ShuZu1 + 1);
printf("&ShuZu1p[1]=%d\n", &ShuZu1[1]);
printf("ShuZu1 + 9=%d\n", ShuZu1 + 9);
printf("&ShuZu1p[9]=%d\n", &ShuZu1[9]);
putchar(10);
printf("*(ShuZu1 + 0)=%d\n", *(ShuZu1 + 0));
printf("ShuZu1p[0]=%d\n", ShuZu1[0]);
printf("*(ShuZu1 + 1)=%d\n", *(ShuZu1 + 1));
printf("ShuZu1p[1]=%d\n", ShuZu1[1]);
printf("*(ShuZu1 + 9)=%d\n", *(ShuZu1 + 9));
printf("ShuZu1p[9]=%d\n", ShuZu1[9]);
运行结果:
由运行结果可得这样的关系:
ShuZu1 + i = &ShuZu[ i ] *(ShuZu1 + i) = ShuZu[ i ]
结合下面的定义,*parr++、*++parr 、*arr++、*++arr 分别代表什么?
int arr[10];//定义一个int型数组
int* parr; //定义一个int* 型变量 <==> 定义一个一级指针
首先,由如下的网址可知
* 号和 ++ 的优先级是一样的,并且结合的方向是从右到左
*parr++:
- ①parr++是后置++,会先返回pa原来的值,即arr[0]的地址,再对parr自增,此时parr中存储的是arr[1]的地址。
- ②有第①点可得:parr++返回的值为arr[0]的地址,所以此时*是与arr[0]的地址结合,访问arr[0]的空间。但是parr中存储的是arr[1]的地址。
代码验证:
int arr[3] = { 0,1,2 };
//打印arr数组所以元素的地址
printf("%p %p %p\n", arr, arr + 1, arr + 2);
int* pint = arr; //将数组名arr赋值给pint指针
int x = *(pint++);
printf("x = %d *pint = %p\n", x, pint);
运行结果:
*++parr :
- ①++parr是前置++,会先将parr自增,此时parr中存储的是arr[1]的地址,再返回parr中的值,即返回的是arr[1]的地址。
- 由第①点可得:++parr返回的是arr[1]的地址,所以此时*与arr[1]的地址结合,访问arr[1]的空间。
代码验证:
int arr[3] = { 0,1,2 };
//打印arr数组所以元素的地址
printf("%p %p %p\n", arr, arr + 1, arr + 2);
int* pint = arr; //将数组名arr赋值给pint指针
int y = *++pint;
printf("y = %d *pint = %p\n", y, pint);
运行结果:
*arr++、*++arr :
先计算arr++,++arr,但是arr是int*型常量,常量不可作为左值
10.2 指针与二维数组的关系
int ShuZu2[4][5] = {
{ 1, 2, 3, 4, 5},
{ 6, 7, 8, 9,10},
{11,12,13,14,15},
{16,17,18,19,20}
};
//推导
printf("&ShuZu2[0][1] = %d\n", &ShuZu2[0][1]);
printf("*(ShuZu2 + 0) + 1 = %d\n", *(ShuZu2 + 0) + 1);
printf("&ShuZu2[1][2] = %d\n", &ShuZu2[1][2]);
printf("*(ShuZu2 + 1) + 2 = %d\n", *(ShuZu2 + 1) + 2);
printf("&ShuZu2[2][3] = %d\n", &ShuZu2[2][3]);
printf("*(ShuZu2 + 2) + 3 = %d\n", *(ShuZu2 + 2) + 3);
putchar(10);
printf("ShuZu2[0][1] = %d\n", ShuZu2[0][1]);
printf("*(*(ShuZu2 + 0) + 1) = %d\n", *(*(ShuZu2 + 0) + 1));
printf("ShuZu2[1][2] = %d\n", ShuZu2[1][2]);
printf("*(*(ShuZu2 + 1) + 2) = %d\n", *(*(ShuZu2 + 1) + 2));
printf("ShuZu2[2][3] = %d\n", ShuZu2[2][3]);
printf("*(*(ShuZu2 + 1) + 2) = %d\n", *(*(ShuZu2 + 2) + 3));
putchar(10);
运行结果:
由运行结果可得这样的关系:
*(ShuZu2 + i) + j <=> &ShuZu2[i][j] *(*(ShuZu2 + i) + j) <=> ShuZu2[i][j]
根据一维数组的关系可得:
ShuZu2[i] + j <=> &ShuZu2[i][j] *(ShuZu2[i] + j) <=> ShuZu2[i][j]
说到二维数组,那我们复习一下怎么遍历二维数组吧
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 5; j++)
{
//printf("%2d ", ShuZu2[i][j]);
printf("%2d ", *(*(ShuZu2 + i) + j));
}
printf("\n");
}
11.指针与字符串的关系
输出字符串的首地址,指针
printf("%d\n", "IliveYou");//输出字符串的首地址,指针
这个指针类型为:char * 型
可以理解为是一个char型数组,但是char*型变量存储的是常量字符串,是只读的。
所以不能修改char*中存储字符串的任意一个字符的值
关系:
char* ZiFuCuan = "I love you";
printf("ZiFuCuan = %s\n", ZiFuCuan);
printf("ZiFuCuan + 0 = %d\n", ZiFuCuan + 0);
printf("&ZiFuCuan[0] = %d\n", &ZiFuCuan[0]);
printf("ZiFuCuan + 2 = %d\n", ZiFuCuan + 2);
printf("&ZiFuCuan[2] = %d\n", &ZiFuCuan[2]);
putchar(10);
printf("%c\n", ZiFuCuan[0]);
printf("%c\n", *(ZiFuCuan + 0));
printf("%c\n", ZiFuCuan[2]);
printf("%c\n", *(ZiFuCuan + 2));
运行结果:
ZiFuCuan + i = &ZiFuCuan[ i ] *(ZiFuCuan + i) = ZiFuCuan[ i ]
与一维数组同理
12.指针数组与数组指针的区别
指针数组:是一个数组,存的全部是指针
int* ShuZU3[4] = { &b,&pb,&ppb,NULL };
数组指针:是一个指针,可看成一个一维数组
数组的首地址,就是一个数组指针
int ShuZU4[4] = { 1,2,3,4 };//&ShuZu4[0]就是一个数组指针
定义一个数组指针
int (*p)[4];
13.函数指针与指针函数的区别
函数指针:
指针函数: