❀❀❀ 文章由@不准备秃的大伟原创 ❀❀❀
♪♪♪ 若有转载,请联系博主哦~♪♪♪
❤❤❤ 致力学好编程的宝藏博主,代码兴国!❤❤❤
许久不见,甚是想念,本大忙人已经很久没有更新博客了,我想大概我的粉丝们早已按耐不住了吧(假)!好的啊,今天的话继续我们仍未结束的指针。
在上一篇有关指针的文章中我们谈论了有关野指针,assert断言以及与指针有关的计算。还没看过的小伙伴们可以戳戳这里传送哦~ —–>指针 (Part two)
那么今天的话给大伙们介绍一下几种指针类型:字符指针,指针数组,数组指针和函数指针(当然最基础的指针变量大家都会的吧~)什么,不会?那不赶紧戳一戳这里——>指针 (Part one)
嗯哼,好的,接下来开始今天的正题:
一.字符指针
我们既然有了整形指针,那么没有字符指针是不是说不过去?整形指针是int*,那么字符指针不就是char*吗?
好的,字符指针结束了。
…………………………………………………..
大家都知道我的尿性吧?我说结束了不是结束,而屏幕前的你说结束才是结束,所以,实不相瞒,想要个赞(✿◡‿◡)
这时候问题就出来了,我们用字符指针存个 ‘a’ ‘C’ 都是没有什么问题的吧,但 ? 字符串呢?存放的是整个字符串吗?还是只存首元素?还是存放的是首元素的地址?
如下面一段代码:
int main(){ const char* pstr = "hello world!";//pstr里面到底存的是什么 printf("%s\n", pstr); return 0;}
在我们运行过后发现,打印的是整个hello world! ,那么我们的pstr存放的就是整个字符串。
—————— 对吗?
仔细考虑的同学就会发现疑点,“ 既然我用char str[]就可以存放一个字符串,那么char* 还有必要存在吗? ” 是的,我们这么仔细一想就发现,char*存放的才不是元素呢?是地址!是这个字符串的首元素的地址!但这时又有同学有疑惑了“既然存的是地址,那为什么还能打印出整个字符串呢?” 诶,好问题,这就要联系到我们的内存问题了,这部分博主以后会在博客里谈到,这里就简单给大家解释一下:
我们计算机中任何数据的存储都会占用一定的空间,而每一部分的空间都有自己的独自的地址,我们可以通过地址来寻找到这个空间存放的值。这和数组传参本质是一个道理,我们同样可以通过一个数组的首元素地址来找到数组后面的内容。
有意思吧?接下来看一个更有意思的代码:
#include int main(){ char str1[] = "hello bit."; char str2[] = "hello bit."; const char *str3 = "hello bit."; const char *str4 = "hello bit."; 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;}
怎么样,汗流浃背了?这其实没什么,记住我们数组传参的本质是地址,所以我们两个if比较的就是地址。所以答案是….?
对,答案是 not same和same。博主上面也讲到了吧?模拟实现二维数那里。我们创建的数组空间是不连续的,所以str1[] 和 str2[] 指向的是两块不同的空间,虽然他们的内容一样,但是我们if比较的是地址啊?所以是not same;而我们给字符指针str3和字符指针str4定义的是同一个内容(常量字符串), 而在C/C++中会把常量放到一块独立的空间里(常量区),这也就说明,我们的str3和str4指向的是同一块空间,所以是same。
二.指针数组
诶,在开始前我先问大家一个问题,指针数组是指针还是数组?<(* ̄▽ ̄*)/ 我们其实可以类比一下:整形指针是存放整形的指针,字符指针是存放字符的指针,那么指针数组是存放指针的数组。即:指针数组的每个元素都是⽤来存放地址(指针)的。
那我们一般定义一个数组是怎么定义的?如存放整形的数组:int arr[10] = {0},每个元素存放的是整形,对吧? 那么同样的,存放指针的数组我们一般就这么定义:int *arr[10] = {0}; 每一个元素存放的是int*,即指针(地址)。
诶,但是这个时候我想到了一个很好玩的东西,既然我们可以定义个一维的指针数组,那可不可以用指针数组模拟实现个二维数组呢?
答案是可以的(不然博主也不会提出这个问题了对吧?)
博主有个思路:先创建普通的一维数组,在通过指针数组将一维数组放进去,即通过寻找每一个一维数组的首元素地址再往后找各个元素。代码实现如下:
#include 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数组中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;}
答案如下:
是不是很容易理解?我们这样就通过指针数组模拟实现了一个二位数组,但是!但是!但是!上述的代码模拟出⼆维数组的效果,实际上并⾮完全是⼆维数组,因为每⼀⾏并⾮是连续的(每一个一维数组向内存申请的空间是不连续的)。
指针数组的知识大概就到这里了,怎么样,是不是没有觉得很难?
二.数组指针
那博主把铁汁们当一次傻子,数组指针内存放的是什么? 啊对对对,是指针,看来铁汁们有好好看上面的内容,博主很是欣慰。o(^▽^)o
那int *p1[10]; int (*p2)[10]; 这两个哪个是数组指针?“不就是第二个吗?第一个不是指针数组?” 嗯嗯嗯,好好好,看到铁汁们这么快反应过来真好,哈哈。是的,我们将指针数组的变量名与*括起来就是表达的数组指针。
原理呢?不懂得可以听博主解释一下哦~:
解释:p先和*结合,说明p是⼀个指针变量变量,然后指着指向的是⼀个⼤⼩为10个整型的数组。所以 p是⼀个指针,指向⼀个数组,叫 数组指针。
指针数组可以存放多个指针,那么数组指针有什么用呢?既然存在了,怎么会没有用呢?就如同现在的你我可能还没有发觉自己的价值,但是如果你我是无价值的,上帝怎么可能会将我们放到人间呢” />int(*p)[10] = &arr;
我们调试也能看到 &arr 和 p 的类型是完全⼀致的:
既然把数组指针学了,接下来二维数组的传参本质:
先给结论:arr[i] = *(arr+i)arr[i][j] = *(*(arr+i)+j)
那我们以前是怎么给函数传二维数组的呢?
void test(int a[3][5], int i, int j);//函数申明test(arr, 3, 5);//main函数内部的传址和传值
⾸先我们再次理解⼀下⼆维数组,⼆维数组起始可以看做是每个元素是⼀维数组的数组,也就是⼆维 数组的每个元素是⼀个⼀维数组。那么⼆维数组的⾸元素就是第⼀⾏,是个⼀维数组。
而现在我们学了数组指针,我们就有新的玩法了:
根据数组名是数组⾸元素的地址这个规则,⼆维数组的数组名表⽰的就是第⼀⾏的地址,是⼀ 维数组的地址。根据上⾯的例⼦,第⼀⾏的⼀维数组的类型就是 int [5] ,所以第⼀⾏的地址的类 型就是数组指针类型 int(*)[5] 。那就意味着⼆维数组传参本质上也是传递了地址,传递的是第⼀ ⾏这个⼀维数组的地址,那么形参也是可以写成指针形式的。
所以我们可以这么写:
void test(int (*p)[5], int r, int c);//函数申明test(arr, 3, 5);//main函数内部的传址和传值
总结:⼆维数组传参,形参的部分可以写成数组,也可以写成指针形式。
三.函数指针
之前博主说过,任何数据都是有地址的,那,函数也包括在这个 ‘数据’ 里面吗?我们可以做个测试:
#include void test(){ printf("hehe\n");}int main(){ printf("test: %p\n", test); printf("&test: %p\n", &test); return 0;}
我们可以看到确实打印出来了地址,所以函数是有地址的,函数名就是函数的地址,当然也可以通过 &函数名 的⽅式获得函数的地址。
如果我们要将函数的地址存放起来,就得创建函数指针变量咯,函数指针变量的写法其实和数组指针 ⾮常类似。如下:
void test(){ printf("hehe\n");}void (*pf1)() = &test;void (*pf2)()= test;int Add(int x, int y){ return x+y;}int(*pf3)(int, int) = Add;int(*pf3)(int x, int y) = &Add;
给大家提供两段很有意思的代码,大家私下可以仔细思考思考:
(*(void (*)())0)();
void (*signal(int , void(*)(int)))(int);
OK,那么今天的任务也就到此为止了,下一篇part four是指针的最后一篇了,希望大家继续多多支持大伟,加油!
Life if full of a lot of pain,the truly great personality is not standing on high,standing in the flowers and applause.The truly great personality is at the bottom of life,in this training,you can still cheer up,still have courage,still have hope,and still will not be casually crushed by disaster. ————黄色安全帽(抖音)
本篇博客也就到此为止了,送大家一碗鸡汤,勉励自己以及这世界上所有追逐梦想的赤子趁年华尚好努力提升自己,莫欺少年穷!