大家好,我是c语言boom成家宝。今天为大家分享的是c语言中很重要的一个知识点------指针的深入讲解。
目录
指针
指针数组
数组指针
·函数指针
什么是指针?
首先,指针的本质是一个地址,指针在32位机器上的大小是4个字节,在64位机器上的大小是8个字节。指针也有各种类型,其格式如下:
char *pc = NULL;
int *pi = NULL;
short *ps = NULL;
long *pl = NULL;
float *pf = NULL;
double *pd = NULL;
指针的类型可以决定指针解引用时能访问多少个字节的内容,例如int类型我们解引用以后可以调4个字节,char类型1个字节。知晓这个原理以后我们不妨来思考一下下面这几个例子的答案是多少
int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(a));
printf("%d\n", sizeof(a+0));
printf("%d\n", sizeof(*a));
printf("%d\n", sizeof(a+1));
printf("%d\n", sizeof(a[1]));
printf("%d\n", sizeof(&a));
printf("%d\n", sizeof(*&a));
printf("%d\n", sizeof(&a+1));
printf("%d\n", sizeof(&a[0]));
printf("%d\n", sizeof(&a[0]+1));
正确答案如下: 内容代码参考博主:初阳785
int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(a));//16 sizeof(数组名)-计算的是数组总大小-单位是字节
printf("%d\n", sizeof(a+0));//4/8 这里a表示表示首元素地址 a+0 还是表示首元素地址 地址的大小就是4/8个字节
printf("%d\n", sizeof(*a));//4 这里的a表示首元素地址 *a 解引用操作拿到第一个元素 数组的每个元素的类型是int int的大小是4个字节
printf("%d\n", sizeof(a+1));//4/8 a表示首元素地址 a+1 表示第二个元素的地址 归根到底是一个地址 地址的大小就是4/8个字节
printf("%d\n", sizeof(a[1]));//4 //计算第二个元素的大小。
printf("%d\n", sizeof(&a));//4/8 &a表示整个元素的地址 但是整个元素的地址 还是个地址 而地址的大小就是4/8个字节
printf("%d\n", sizeof(*&a));//16 &a表示整个数组的地址 *&a 解引用就拿到整个数组的元素 4*4=16
printf("%d\n", sizeof(&a+1));//4/8 &a是数组的地址,&a+1虽然地址跳过整个数组,但还是地址,所以是4/8个字节
printf("%d\n", sizeof(&a[0]));//4/8 第一个元素的地址
printf("%d\n", sizeof(&a[0]+1));//4/8 第二个元素的地址
什么是指针数组?
指针数组缩句就是数组,例如整型数组就是存放整型的数组,字符串数组就是存放字符串的数组,那么指针数组就是存放指针的数组。
接下来我们可以使用指针数组来模拟二维数组的实现。代码如下:
int main() {
int arr1[] = { 1,2,3 };
int arr2[] = { 4,5,6 };
int arr3[] = { 7,8,9 };
int* arr[] = { arr1,arr2,arr3 };//数组指针的创建,类型是int*
//遍历数组指针内容并打印
int i =0 ;
for (i = 0; i < 3; i++) {
int j = 0;
for (j = 0; j < 3; j++) {
printf("%d ", arr[i][j]);
}
}
return 0;
}
什么是数组指针?
数组指针归根结底还是指针,例如整型指针就是存放整型变量地址的指针变量,字符型指针就是存放字符型变量地址的指针,那么数组指针就是存放数组地址的指针变量。
这两行代码大家认为哪一个是数组指针呢?答案是第二个。第一个是指向int*的一个指针数组,第二个有括号括起来改变优先级的才是数组指针。以下是数组指针的一个简单用法实例。
int main() {
int arr[5] = { 1,2,3,4,5 };
//数组内容存放在数组指针中
int(*p)[5] = &arr;
return 0;
}
那数组指针到底在什么场景下使用才方便一些呢?博主给出其中一个答案就是二维数组里面。 代码如下:
void Print(int arr[3][5], int row, int col) {
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");
}
}
int main() {
int arr[3][5] = { 1,2,3,4,5 ,6,7,8,9,10, 11,12,13,14,15 };
Print(arr, 3, 5);//这里Print函数是自己创建的,不是库函数printf
return 0;
}
这是我们平熟悉的常用的遍历并打印二维数组的方法,那我们学习了数组指针过后又可以怎么让形参是指针去遍历二维数组呢?首先二维数组的数组名也是数组名,也表示首元素的地址。那二维数组可以理解为由若干个一维数组组成的数组,所以二维数组首元素的地址也就是第一个一维数组的地址,所以我们实参部分传过去的是一维数组的地址,那我们形参部分就应该用一个数组指针来接收,代码如下:
void Print(int(*p)[5], int row, int col) {
int i = 0;
for (i = 0; i < row; i++) {
int j = 0;
for (j = 0; j < col; j++) {
printf("%d ", *(*(p+i)+j));//p+i拿到的就是那一行一维数组的地址,*(p+i)解引用过后就是一行数组的数据
//*(p+i)+j就是挨个遍历,再用*解引用拿到数据
}
printf("\n");
}
}
int main() {
int arr[3][5] = { 1,2,3,4,5 ,6,7,8,9,10, 11,12,13,14,15 };
Print(arr, 3, 5);//这里Print函数是自己创建的,不是库函数printf
return 0;
}
编译结果如图:
很多小伙伴看到这里或许会觉得很别扭,觉得我不如使用第一种常规的。这是因为接触的少了,看的代码看得少了所以会觉得很奇怪。这里博主强烈建议大家都去使用第二种方法,因为你自己在使用第一种方法的时候其实编译器在处理过程中它自己会转变成第二种来处理,所以直接写成这样在以后的编译中运行效率会更快,占用内存更少。
·什么是函数指针?
数组指针是指向数组的指针,函数指针当然就是指向函数的指针了。小知识:函数名表示为函数的地址,&函数名也表示函数的地址。如下图
那函数指针的形式该怎么写呢?举两个例子:如果函数形式是int add(int x,int y),那么函数指针就可以写成:int (*add)(int,int)=&add. 如果函数形式是 void test(char* pc,int arr[10]),那么函数指针可以写成 void (*p)(char* ,int [10])=&test,或者void (*p)(char* ,int *)=&test。函数指针第一个类型表示返回值的类型,然后(*p),然后(参数类型)。那么函数指针可以怎么使用呢,下面代码是一个简单的使用案例
int add(int x, int y) {
return x + y;
}
int main() {
int (*p)(int, int)=&add;//函数指针定义
int r = add(3, 5);
printf("%d\n", r);
//正常使用add函数进行计算
int m = (*p)(3, 5);
//使用指针调用add函数
printf("%d", m);
return 0;
}