指针
理解
指针数组
数组指针
函数指针
函数指针数组
指向函数指针数组的指针
1.指针数组
指针数组到底是指针还是数组呢?
(其实它是一个数组,只是这个数组中的每个元素都是指针类型)
eg:
int *arr1[3];
char *arr2[3];
char **arr3[3];
这些都是指针数组,为了大家更清楚的理解,我们把它在内存中的排布画出来:
2.数组指针
数组指针,是指针。
eg:
int(*p)[10];
//p先和*结合([]的优先级要高于*的,所以必须加上()来保证p和*先结合),说明p是一个指针变量,然后指针指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针。
这里的p是指针,是指向一个数组的指针。
数组指针的使用
eg:
int main()
{
int *arr[10];
int(*p)[10] = arr;//这是错误的,因为arr在这里代表的是数组首元素的地址,而arr数组中每个元素的类型都是int*,所以arr代表的是int**,不能把一个int**类型的赋给一个int*类型的
int(*p)[10] = &arr;//(&arr)表示取的是数组的地址,arr中每个元素的类型是int*,而int(*p)[10]表示的是p指针指向一个数组,这个数组中的每个元素都是int型,赋值就是说让p指向arr,可是它两类型不同,所以也不能赋值
int*(*p)[10] = &arr;//这样是可以赋值的
return 0;
}
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
//void test(int arr[3][5],int row,int col)
void test(int(*arr)[5], int row, int col)//int (*arr)[5],arr是一个指针,指向一个数组,这个数组中有5个元素,每个元素的类型为int型,arr恰好指向的就是二维数组的第一行
//二维数组首元素的地址就是第一行的地址
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
printf("%d ", *(*(arr + i) + j));//在二维数组中数组名代表的是第一行的地址,*(arr+i)只是得到了第i行的数组名,数组名并没有单独放在sizeof()内部,所以要降级变为首元素地址,就是第i行第一个元素的地址,*(arr + i) + j)就是第i行第j个元素的地址,*(*(arr + i) + j))通过解引用就得到了第i行第j列的元素
}
printf("\n");
}
}
int main()
{
int arr[3][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
test(arr, 3, 5);
return 0;
}
//#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
int arr[10] = { 0 };
//arr--表示数组首元素地址
//&arr--表示数组的地址
printf("%p\n", arr);
printf("%p\n", arr + 1);
printf("%p\n", &arr + 1);
return 0;
}
我们可以看到这三个输出结果是截然不同的,这是因为数组的地址和数组首元素的地址值虽然是相同的,但意义不同。
数组的地址如何来存储?
int arr[10] = {0};
int (*p)[10] = &arr;//数组地址要放到数组指针中
//int(*)[10]--是数组指针类型
我们看看下面的代码:
#include<stdio.h>
int main()
{
int arr[5];//整型数组
int *parr1[10];//指针数组
int (*parr2)[10];//数组指针
int(*parr3[10])[5];//存放指针数组的数组
return 0;
}
首先,parr3是一个数组,这个数组存放的是指针,指针指向的是一个数组,该数组有5个元素,每个元素是int型,这个数组最终能存10个元素。给大家画图理解一下吧。
指针和数字的定义与声明
eg:
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
extern char arr[];//声明外部变量arr
int main()
{
printf("%s\n", arr);
return 0;
}
sum.c
#define _CRT_SECURE_NO_WARNINGS 1
char arr[] = "abcdef";
通过这个例子我们是想说明声明和定义其实使用的是同一块空间,不然在test.c函数中输出的结果就不一定是”abcdef”了。
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
extern char* arr;//声明外部变量arr
int main()
{
printf("%s\n", arr);
return 0;
}
sum.c
#define _CRT_SECURE_NO_WARNINGS 1
char arr[] = "abcdef";
在这里我们把arr声明成了指针,我们发现程序崩了,这是为什么呢?我们来画图帮大家分析一下:
这说明数组不是指针。
既然已经错了,那我们如何能让字符串正常输出呢?
printf("%s\n", (char*)&arr);
我们只要把输出函数改成上边的样子,就可以正常输出了。虽然arr指针只占了数组arr的4个字节,但它们指向的是同一个空间,对它取地址,同样能取到a的地址。我们为什么还要对它进行强制类型转换呢?因为我们在声明的时候,我们把它声明成了char*,再对它进行取地址的话就是char**类型,我们用%s输出,希望它是char*类型的,所以我们要强制类型转换。
现在我们再把它定义成指针,并声明成指针,我们来看看它是否能正常输出?
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
extern char* p;//声明外部变量p
int main()
{
printf("%s\n", p);
return 0;
}
sum.c
#define _CRT_SECURE_NO_WARNINGS 1
char *p = "abcdef";
我们发现程序可以正常输出“abcdef“。
那我们就再来改一下,把它声明成数组,定义成指针。
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
extern char p[];//声明外部变量p
int main()
{
printf("%s\n", p);
return 0;
}
sum.c
#define _CRT_SECURE_NO_WARNINGS 1
char *p = "abcdef";
它到底能正常输出吗?
我们可以发现这里输出的是一个随机值,到底是为什么呢?
我们再用画图的方式,给大家详解一下吧:
我们只要把perintf()函数改成下边这个样子就可以正常输出”abcdef“了:
printf("%s\n", (char*)*(int*)p);
为什么要写成这个样子呢?
因为p指向的是存放a的地址的空间(4个字节),所以我们对它进行强制类型转换(int占4个字节),对它进行解引用,就是从它向后取4个字节,就取出了a的地址,因为已经强转成了(int)型,取出的就相当于是整型,所以我们要再把它强转成(char*)。
数组参数,指针参数
在写代码的时候难免要把【数组】或【指针】传给函数,那函数的参数该如何设计呢?
一维数组传参
eg:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
void test(int arr[])
{}
void test(int arr[10])
{}
void test(int *arr)
{}
void test2(int *arr2[20])
{}
void test2(int **arr2)
{}
int main()
{
int arr[10] = { 0 };
int *arr2[20] = { 0];
test(arr);
test2(arr2);
return 0;
}
以上的函数传参形式都是正确的。
二维数组参数
eg:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
void test(int arr[3][4])
{}
void test(int arr[][])//不可以,二维数组传参时只能省略第一个[]的数字。
{}
void test(int arr[][5])
{}
void test(int *arr)//不可以,第一行的地址不能放到一个整型指针里边(太小)。
{}
void test(int *arr[5])//错误,arr是一个地址,只能放到一个指针里,不能放到一个数组中。
{}
void test(int (*arr)[5])//正确,一个数组的地址就应该放到一个数组指针中
{}
void test2(int **arr)//错误,这应该放的是一级指针变量的地址,或者直接把二级指针放在这
{}
int main()
{
int arr[3][5] = { 0 };
test(arr);//arr是一个地址(第一行的地址)
return 0;
}
一级指针传参
eg:
#include<stdio.h>
void print(int *p, int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d\n", *(p + i));
}
}
int main()
{
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int *p = arr;
int sz = sizeof(arr) / sizeof(arr[0]);
printf(p, sz);
return 0;
}
通过这个例子我们思考一下:当一个函数的参数部分为一级指针的时候,函数能接收什么参数?
它可以接收一个整型数组名,一级指针变量,一个整型变量地址。
eg:
#include<stdio.h>
void test(int *p)
{}
int main()
{
int a = 0;
int *q = &a;
int arr[10] = { 0 };
test(&a);
test(q);
test(arr);
return 0;
}
二级指针参数
eg:
#include<stdio.h>
void test(int** ptr)
{
printf("num = %d\n", **ptr);
}
int main()
{
int n = 10;
int *p = &n;
int **pp = &p;
test(pp);
test(&p);
return 0;
}
那我们再来想一下,当是的参数为二级指针的时候,可以接收什么参数?
它可以接收一个指针变量的地址,(int/char/..)型指针的数组数组名,二级指针变量本身。
eg:
#include<stdio.h>
void test(int** ptr)
{
}
int main()
{
char c = 'b';
char *pc = &c;
char **ppc = &pc;
char *arr[10];
test(&pc);
test(ppc);
test(arr);
return 0;
}
3.函数指针
我们先来看一段代码:
#include<stdio.h>
void test()
{
printf("hello\n");
}
int main()
{
printf("%p\n", test);
printf("%p\n", &test);
return 0;
}
我们可以看到输出结果是两个地址,这两个地址就test函数的地址。
那我们是如何保存函数的地址的呢?我们来看看下面这段代码:
#include<stdio.h>
void test()
{
printf("hello\n");
}
int main()
{
void (*p)() = &test;//p是一个指针,指向了一个函数,p里存的是函数名的地址。
(*p)();//对p进行解引用,就相当于取出了函数名(=test)
//()--函数调用操作符,详细解释在我的“C语言中操作符的总结”那篇博客中
return 0;
}
我们发现这样调用函数,结果也能正常输出。
void (*p)() = test;
这句代码也是正确的,函数名和取地址也函数名代表的都是函数地址(函数的地址要存到函数指针中),函数没有首元素地址,而数组有,如果只写一个数组名,则代表的是数组首元素地址。
我们来看看具体的代码实现:
#include<stdio.h>
int Add(int x, int y)
{
return x + y;
}
int main()
{
int(*p)(int ,int) = Add;
printf("%d\n", (*p)(1, 2));
return 0;
}
我们来看看输出结果:
4.函数指针数组
数组是一个存放相同类型数据的存储空间,我们已经学习了指针数组,比如:
int *arr[10];//数组的每个元素都是int*类型
那把函数的地址存到一个数组中,这个数字就叫函数指针数字,那函数指针数字是如何定义的呢?
int (*p[10])();
p先和[]结合,说明p是数组,那数组的内容是说明呢?
是int(*)()类型的函数指针。
函数指针数组的用途:转移表
eg:(计算器)
#include<stdio.h>
int Add(int x, int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
int Mul(int x, int y)
{
return x * y;
}
int Div(int x, int y)
{
return x / y;
}
int main()
{
int x, y;
int input = 0;
int ret = 0;
int(*pFun[5])(int, int) = { 0, Add, Sub, Mul, Div };//转移表
while (input)
{
printf("***************************\n");
printf(" 1.Add 2.Sub \n");
printf(" 3.Mul 4.Div \n");
printf("***************************\n");
printf("请选择:\n");
scanf("%d", &input);
if ((input<4 && input>1))
{
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = (*pFun[input])(x, y);
}
else
{
printf("输入有误\n");
}
printf("ret = %d\n", ret);
}
return 0;
}
5.指向函数指针数组的指针
指向函数指针数组的指针是一个【指针】
指针指向一个【数组】,数组的元素都是【函数指针】
如何定义?
#include<stdio.h>
int main()
{
int(*pFunArr[5])(int, int);//pFunArr--函数指针数组
int (*(*ppFunARR)[5])(int, int) = &pFunArr;//ppFunArr--指向函数指针数组的指针
return 0;
}
好了,今天的指针就先讲到这里,如果有什么不对的地方,还希望各位大佬指出来,大家互相学习。