携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第11天,点击查看活动详情
前言
学习C语言,不得不学到的就是指针,甚至可以这么说:指针是C语言的精髓之所在。
本文就来分享一波作者的C指针学习见解与心得。本篇属于进阶第一篇,主要讲解了二级指针、字符指针和指针数组的一些内容。后续还有进阶的其他内容,可以期待一下。
笔者水平有限,难免存在纰漏,欢迎指正交流。
二级指针
指针变量也是变量,是变量就有地址,那一级指针变量的地址存放在哪里?
指针变量的地址也放在一个指针变量里,我们称它为二级指针。
比如:
*ppa通过对ppa中的地址进行解引用,其实就是*&pa,这样找到的是 pa ,也就是&a 。
**ppa先通过*ppa找到 pa ,然后对 pa 进行解引用操作:*pa,也就是*&a那找到的是a。
int**ppa靠近变量名的*说明是指针变量,靠近int的*说明指向的是一级int指针。
再来给你看张图或许你就懂了(笑😀)
字符指针
讲解以及注意事项
在指针的类型中我们知道有一种指针类型为字符指针 char* ;
一般使用方式:
int main()
{
char ch = 'w';
char *pc = &ch;
*pc = 'w';
return 0;
}
还有一种使用方式如下:
int main()
{
const char* pstr = "hello world.";//这里是把一个字符串放到pstr指针变量里了吗?
printf("%s\n", pstr);
return 0;
}
代码 const char* pstr = "hello world.";
特别容易让人以为是把字符串 "hello world." 放到字符指针 pstr 里了,但是本质是把字符串的首字符的地址放到了pstr中。 其实想想也不可能是把字符串直接放到字符指针去,首先一点,字符串字面量其实是有存储在内存中的,只不过存到了常量池,生命周期从程序执行开始到程序结束,而且字符串字面量直接出现在程序中会被认作其首字符地址,其次,假如真要放进字符指针去,很明显类型完全对不上,怎么放?
注意到const没?因为字符串字面量是不能且不应该被修改的,当char* pstr = "hello world.";时,指针pstr存放的是字符'h'的地址,权限就被放大了——通过指针是可以间接修改字符串常量的值的!
比如:
int main()
{
char* pstr = "hello world.";
*pstr = 'w';
printf("%s\n", pstr);
return 0;
}
这是不希望且不允许发生的,所以运行后程序会崩溃。我们调试看看,发现有异常:
权限冲突了,因为字符常量不应该被修改,由于指针的权限大,使用指针理论上可以间接修改,但是不被允许,要是修改了就会出问题,所以要这样使用字符指针的话最好在指针前面加个const限定一下权限,保护字符串字面量。
例题
#include <stdio.h>
int main()
{
char str1[] = "hello world.";
char str2[] = "hello world.";
const char *str3 = "hello world.";
const char *str4 = "hello world.";
if(str1 ==str2)
printf("str1 and str2 are same\n");
else
printf("str1 and str2 are not same\n");
if(str3 ==str4)
printf("str3 and str4 are same\n");
else
printf("str3 and str4 are not same\n");
return 0;
}
这里str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域(只读区域),当几个指针指向同一个字符串的时候,他们实际会指向同一块内存。
但是用相同的常量字符串去初始化不同的字符数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4相同。
指针数组
指针数组是指针还是数组?
答案:是数组。是存放指针变量的数组。
样式:
指针类型 *数组名[元素个数]
比如:int * parr[3];
在操作符里[]优先级比*高,可以借助这个视角来区别指针数组(int*parr[])和数组指针(int(*arrp)[])。
指针数组模拟实现二维数组
int main()
{
int arr1[] = {1, 2, 3, 4, 5};
int arr2[] = {2, 3, 4, 5, 6};
int arr3[] = {3, 4, 5, 6, 7};
int* parr[3] = {arr1, arr2, arr3};
int i = 0;
int j = 0;
for (i = 0; i < 3; i++)
{
for (j = 0; j < 5; j++)
{
printf("%d ", parr[i][j]);
}
printf("\n");
}
return 0;
}
数组名是首元素地址,把它作为指针数组的元素,这样一来,parr[0]也就是arr1,parr[0] + 1也就是arr1 + 1对应的是&arr1[1],那么*(parr[0])<->*arr1<->arr1[0]<->1,而
*(parr[0] + 1)<->*(arr1 + 1)<->arr1[1]<->2。
还记得[]运算符吗?parr[i][j]<->*(parr[i] + j)<->*(*(parr + i) + j)。
更多内容看下图解析
指针数组和二维数组的区别
二维数组是连续存放的一整块内存空间,而指针数组则不一定是存放在一起的,完全有可能是分布在不同内存位置的几个数组通过指针联系在一起的。
以上就是本文全部内容了,感谢观看,你的支持就是对我最大的鼓励~