指针是我们内存中最小单元的地址编号,因此指针能储存我们程序中各种变量在内存中的对应地址(&取出地址放入指针变量内,使用时通过*解引用访问操作即可),指针使用起来高效迅速,能够用一个指针变量记住复杂变量的地址,然后对其进行远程操作;但因为指针代表的是计算中的底层地址,不容易观察,因此很多人认为指针很难,根本学不懂,其实没那么夸张,指针不过是地址,是我们用来辅助完成程序设计的工具而已。下面让我带大家进入指针的世界!🎉🎉🎉
指针跟数据一样也有自己的类型,比如整型指针、字符型指针、结构体指针等,不同的变量类型最好对应使用不同的指针类型(特殊情况除外)
我们知道不同数据类型大小不相同,比如 int 是4字节,char 是1字节,那么对应的指针变量大小是否也是如此呢? 答案是否,指针是个很公平的东西,无论上面类型的指针,大小都是4字节(64位平台下是8字节),下面看我用 sizeof 证明这一特点:
既然指针大小都是一样的,那为什么还要区分类型呢?相信这是学习指针时大多数同学的疑惑点,既然C语言是经过巧妙设计完善的,所以指针类型存在肯定有它的作用,比如下面这段代码:
//指针类型-移动步长
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* pa = arr;
char* pb = arr;
pa++;
pb++;
printf("数组首元素地址:%p\n\n", &arr[0]);
printf("整型指针+1:%p\n\n", pa);
printf("字符型指针+1:%p\n\n", pb);
printf("pa加1后:%d\n",*pa);
printf("pb加1后:%d\n",*pb);
return 0;
}
既然不同的类型的指针移动距离不同,那么解引用权限是否也不相同呢?
//指针类型-解引用权限
int main()
{
int a = 0x11223344;
int b = 0x11223344;
int* pa = &a;
char* pb = &b;
*pa = 0;
*pb = 0;
printf("%d %d\n", a, b);
return 0;
}
指针类型有它存在的意义:
俗话说能力越大,责任越大。既然指针这么强大,那么在使用它时肯定要谨慎,避免产生野指针(指向位置不可知,即指针与变量间的强绑定关系不可控),野指针可能会内存泄露的危害(如果野指针指向某个变量,并且野指针没被销毁,那么在使用指针的过程中可能误使用到野指针,从而导致指向变量内存的改变)
1.指针未初始化
2.指针越界访问
3.指针指向空间被释放
在使用指针时一定要小心谨慎,其次注意以下五点:
指针与整数、指针间都有运算,比如前面提到了的指针 + - 整数得出不同指针类型的移动步长和解引用权限不同,其实指针 + - 整数在数组身上能起到代替下标寻找元素的作用。
//指针 + - 整数
int main()
{
int arr[5] = { 1,2,3,4,5 };
int* pa = arr;
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d ", *(pa + i));
}
return 0;
}
指针 - 指针得到的是两个指针间的元素个数,就像日期 - 日期得到的是中间的天数一个道理。不过没有指针 + 指针这一说法,这样是没有意义的。
//指针 - 指针
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* pa = arr;
int* pb = &arr[9] + 1;//指向数组后一处位置
int sz = pb - pa;
printf("%d\n", sz);
return 0;
}
指针在使用时需要注意一些细节,比如在使用指针遍历数组时,最好从前往后遍历,不推荐从后往前遍历,因为这是标准未定义的,盲目使用可能会造成bug。
//指针间的关系运算
int main()
{
int arr[5] = { 1,2,3,4,5 };
int* pa = arr;
for (; pa <= &arr[4]; pa++)
{
printf("%d ", *pa);
}
return 0;
}
指针与数组间有着密不可分的关系,比如指针 + - 整数能够像数组下标一样直接访问元素,此外数组名还能作为首元素地址直接被指针指向。 数组名就是首元素地址,两种情况除外:
//指针与数组
int main()
{
int arr[5] = { 1,2,3,4,5 };
int* pa = arr;
int i = 0;
for (i = 0; i < 5; i++)
{
printf("arr[%d] %p<==>%p pa+%d\n", i, &arr[i], pa + i, i);
}
return 0;
}
一级指针用来指向变量地址,二级指针则是指向一级指针的地址,二级指针也能通过解引用两次的方式远程控制变量。一颗 * 为一级指针,两颗 * 就是二级指针,依次类推可以有 n 级指针,指针指向层数越多,程序就越难理解,我们在写程序时都是一、二级指针用的多。
//二级指针
int main()
{
int a = 10;
int* pa = &a;
int** ppa = &pa;
**ppa = 20;
printf("初始变量a %d\n\n", a);
printf("一级指针pa %d\n\n", *pa);
printf("二级指针ppa %d\n\n", **ppa);
return 0;
}
指针数组是存储指针的数组,本质上仍是数组,不过因为数组中存储的是元素地址,因此在使用指针对其中的元素进行访问时,需要使用二级指针。
//指针数组
int main()
{
int* arr[5] = { 0 };
int** pa = arr;
int i = 0;
for (i = 0; i < 5; i++)
{
arr[i] = &i;
printf("%d ", **pa);
}
return 0;
}
从指针是什么到指针数组的,指针初阶的内容至此我们就介绍完了。现在我们可以开始愉快的使用指针变量了,记住不要造出野指针,也不要让指针的指向关系过于复杂,避免我们在学习指针的过程中出现从入门到放弃的情况。
如果你觉得本文写的还不错的话,期待留下一个小小的赞👍,你的支持是我分享的最大动力!