我们学习C语言也有了一段时间了,对指针也有了初步的认识,今天,我们就来进一步了解一下指针。
指针
目录
字符指针
什么是字符指针呢,顾名思义,字符指针就是指向字符的指针,那么字符指针该怎么用,如何用,学字符指针又能用来干什么的,下面我就举一个简单的例子:
int mian() { char ch = 'w'; char* pc = &ch; return 0; }
创建一个数组,再用字符指针来接收该字符的地址,就完成字符指针的简单运用了。
当然,字符指针也没有这么简单,下面就是一个例子:
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; }
这里读者就可以思考一下,打印出来的结果到底是什么呢?又为什么会打印出这个结果?
打印结果:
char str1[] = "hello bit."; char str2[] = "hello bit.";
从这两行代码来看,虽然str1和str2数组当中的字符串是相同的,但是str1和str2的地址是明显不同的,因此会打印出str1 and str2 are not same。
const char* str3 = "hello bit."; const char* str4 = "hello bit.";
相比于上面两行代码,str3和str4被const修饰了,我们都知道,被const修饰后该变量就变成了常量,那么str3和str4的看起来明明是两个不同的字符数组啊,难道它们指向的都是同一个地址?为此,我们可以调试来看看:
调试之后,我们就可以肯定了,str3和str4确实是指向了同一个地址(常量字符串的地址),但要注意,str3和str4本身的地址是不同的。
指针数组
如同其名,指针数组就是存放指针的数组,对指针数组的应用有很多,今天我们就简单的用指针数组来模拟二维数组。
模拟二维数组
如何来模拟二维数组呢,首先,我们的创建几个一维数组:
int arr1[] = { 1,2,3,4,5 }; int arr2[] = { 2,3,4,5,6 }; int arr3[] = { 3,4,5,6,7 };
之后就需要一个数组来存放这几个一维数组的地址了,当然就是我们的指针数组啦:
int* arr[] = { arr1,arr2,arr3 };
想要模拟二维数组,这里我们就可以使用循环来进行实现:
int i = 0; for (i = 0; i < 3; i++) { int j = 0; for (j = 0; j < 5; j++) { printf("%d ", arr[i][j]); } printf("\n"); }
这里,i是指向数组的首地址,想要访问元素,就得再进行一个循环,因此就成了我们看到的两次for循环,在最后的打印中,arr[i][j]中的[j]就是访问元素。
打印结果如下:
完整代码:
int main() { int arr1[] = { 1,2,3,4,5 }; int arr2[] = { 2,3,4,5,6 }; int arr3[] = { 3,4,5,6,7 }; int* arr[] = { arr1,arr2,arr3 }; int i = 0; for (i = 0; i < 3; i++) { int j = 0; for (j = 0; j < 5; j++) { printf("%d ", arr[i][j]); } printf("\n"); } return 0; }
数组指针
在了解完指针数组的用法后,我们就可以再了解一下名字及其相似的数组指针,我们知道,指针数组就是存放指针的数组,那么数组指针又是来干嘛的呢?我们可以通过它们的命名来进行一些推断。
1.
1)指针数组是存放指针的数组---->数组指针是一个指针。
2.
1)字符指针是指向字符的指针
2)整型指针是指向整型的指针
3)浮点型指针是指向浮点型的指针那么数组指针就是指向数组的指针。
对数组名的回顾
数组指针是一个数组,谈起数组,我们首先可以对数组名进行回顾:
数组名是首元素的地址,但是是存在两个例外:
1.sizeof(数组名)是计算整个数组的长度。
2.&(数组名)是整个数组的地址(并不是数组首元素的地址)。
我们可以通过代码来进行分析:
int main() { int arr[10]; printf("%p\n", arr); printf("%p\n", arr + 1); printf("%p\n", &arr[0]); printf("%p\n", &arr[0] + 1); printf("%p\n", &arr); printf("%p\n", &arr + 1); return 0; }
我们随便创造一个数组,将数组,数组首元素,&数组名和它们+1后的地址在x86环境下进行打印:
我们可以很明显的观察到arr与arr+1,&arr[0]与&arr[0]+1都是增加4个字节,但到了&arr和&arr+1,一下子增加了40个字节,我们可以更加肯定&数组名是整个数组的地址的结论。
创建一个数组指针
对数组名进行回顾后,我们就可以创建一个数组指针了 ,代码如下:
int mian() { int arr[10] = { 0 }; int(*p)[10] = &arr; return 0; }
数组指针是一个指针,所以我们首先将变量名前面加上*。(数组指针中,[]中的值必须与创建的数组当时的值一致) ,接下来用一个例子来加深一下印象:
eg:有一个char* arr[5],如何创建数组指针
首先展现错误示例:
int mian() { char* arr[5]; char(*pc)[5] = &arr; return 0; }
这是错误的,创建数组指针要注意,是什么样的类型就创建什么样的类型,这里是char*类型,那么自然要创建char*类型的数组指针,这里的数组指针是char类型的,正确的应该如下:
int main() { char* arr[5]; char* (*pc)[5] = &arr; return 0; }
应用
这里我们来简单进行一个运用:
void print(int (*p)[5], int r, int c) { int i = 0; for (i = 0; i < 3; i++) { int j = 0; for (j = 0; j < 5; j++) { printf("%d ", p[i][j]); } printf("\n"); } } int main() { int arr3[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} }; print(arr3, 3, 5); return 0; }
当二维数组取地址后。首元素是指向第一行的,所以在这时就可以使用数组指针来存放地址,进而达到方便快捷的效果。
打印结果如下:
函数指针
函数指针我们也可以通过 类似的方法进行理解:
1)数组指针--->指向数组的指针--->存放的是数组的地址
2)函数指针--->指向函数的指针--->存放的是函数的地址
紧接着我们就会想,那么函数的地址又该如何得到呢,难道是&(函数名)?
我们可以通过一个简单的代码来证明自己的猜想:
int Add(int x, int y) { return x + y; } int main() { printf("%p\n", &Add); return 0; }
运行结果如下:
运行出的结果确实是一个地址,我们可以肯定猜想,函数的地址就是&函数名。
但是,我们曾经学习传址调用时 ,我们直接将函数名运用的,那么是不是可以将&去掉呢,我们将&去掉后再次运行,结果如下:
运行结果一致,肯定猜想,是可以将&去掉直接用的。
创建函数指针
那么函数指针该如何来写呢,我们就用刚才的代码举例:
int Add(int x,int y)
函数名是Add,两个参数都是int类型的,我们则可以如下创建:
int (*pf)(int int)= &Add;
简单应用
为了方便,我们就拿刚才的代码来进行运用 ,我们将Add返回的值打印出来,代码如下:
int Add(int x, int y) { return x + y; } int main() { int (*pf)(int, int) = &Add; int ret = (*pf)(2, 3);//这里*pf的*可以不写,类似于函数调用 printf("%d\n", ret); return 0; }
打印结果如下;
代码正确。
小结
这次我们就先到这里啦,指针的内容多且有趣,小编之后还会进行编写,学习C有一段时间了,大家还需要加油啊,坚持加努力,我们一定可以抵达我们想要的那个未来,我们下次再见!