指针就是个变量,用来存放地址,地址是唯一的标识空间,内存单元有编号,编号=地址=指针
指针固定4/8字节(32位/64位)
指针有类型,类型觉得指针+1-1时的步长,决定了指针解引用时访问的大小
指针数组是数组 字符数组--存放字符的数组 整型数组--存放整型的数组 指针数组--存放指针的数组,数组中的元素都是指针元素的 int* arr[5] char* ch[6]
指针数组是数组,是存放指针的数组 数组指针是指针 字符指针--指向字符的指针 整型指针--指向整型的指针 浮点型指针-指向浮点型的指针 数组指针--指向数组的指针
数组名理解:数组名是数组首元素的地址,但是存在两个例外
sizeof(数组名),这里表示整个数组,所以计算的是整个数组所占大小,单位为字节
&数组名,这里数组名表示整个数组,取出的是数组的地址
int main(){
int arr[10]={0};
int (*p)[10]=&arr;
char *arr2[5];
char* (*pc)[5]=&arr2;
char arr3[5];
char(*p3)[5]=&arr3;
return 0;
}
数组指针到底有什么用?
sizeof数组指针可以获得数组的全部长度,所以数组指针存放的是数组的地址,而不是数组首元素的地址。
//数组作为形参时,长度可以不写
void prints(int arr[],int sz){
for (int i = 0; i < sz; i++)
{
printf("%d ",arr[i]);
}
}
//一维数组传参传递首元素的地址,本质是指针,可以用int指针接收
void prints1(int* arr,int sz){
for (int i = 0; i < sz; i++)
{
printf("%d ",arr[i]);
}
}
int main(){
int arr[]={1,2,3,4,5};
int sz=sizeof(arr)/sizeof(arr[0]);
prints1(arr,sz);
return 0;
}
void print(int arr[][5],int r,int c){
for (int i = 0; i < r; i++)
{
for (int j = 0; j < c; j++)
{
printf("%d ",arr[i][j]);
}
printf("\n");
}
}
//传递第一行数组的地址,可以用数组指针接收
void print(int (*arr)[5],int r,int c){
for (int i = 0; i < r; i++)
{
for (int j = 0; j < c; j++)
{
printf("%d ",*(*(arr+i)+j));
}
printf("\n");
}
}
//二维数组传参 传递二维数组的数组名其实是传递二维数组的首元素地址,也就是第一行数组整体的地址,注意不是第一行数组第一个元素的地址
int main(){
int arr[][5]={{1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7}};
print(arr,3,5);
return 0;
}
int arr[5];//arr是能存储五个元素的数组 int *parr1[10];//parr1是一个指针数组,元素是整形指针类型,能存储十个元素 int (*parr2)[10];//parr2是数组指针,指向元素个数为10的整型数组的地址。 int (*parr3[10])[5];//parr3是数组,数组的元素是数组指针,每个元素指向长度为5的整型数组。parr可以存储十个数组指针
写代码的时候难免要把数组或指针传给函数,那函数参数该如何设计呢
#include
void test(int arr[])//ok 数组传参,形参可以写成数组形式
{}
void test(int arr[10])//ok形参数组的大小可以写错,因为传的本质是地址
{}
void test(int *arr)//ok 本质是首元素的地址,所以可以用指针接收
{}
void test2(int *arr[20])//ok可以用数组接收
{}
void test2(int **arr)//ok 传递的是指针数组首元素的地址,指针数组的首元素是指针类型,用二级指针接收 没问题
{}
int main(){
int arr[10] = {0};
int *arr2[20] = {0};
test(arr);
test2(arr2);
}
void test(int arr[3][5])//ok?二维数组作为参数,使用二维数组进行接收
{}
void test(int arr[][])//ok?//二维数组作为参数,必须指定列长度
{}
void test(int arr[][5])//ok?二维数组作为参数,行可以省略
{}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
//这样才方便运算。
void test(int *arr)//ok?传递arr数组名,实际传递的是arr的首元素地址也就是第一行的数组地址,不能使用整型指针接收
{}
void test(int* arr[5])//ok?传递arr数组名,实际传递的是arr的首元素地址也就是第一行的数组地址,可以使用指针数组接收
{}
void test(int (*arr)[5])//ok?传递arr数组名,实际传递的是arr的首元素地址也就是第一行的数组地址,可以使用数组指针接收
{}
void test(int **arr)//ok?二级指针是用来接收一级指针的地址,所以不行
{}
int main()
{
int arr[3][5] = {0};
test(arr);
}
void print(int *p, int sz)//一级指针传参,使用一级指针接收就可以
{
int i = 0;
for(i=0; i
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;
}
当一个函数以二级指针为参数,函数能接收什么函数
void test(int **pp);
int a = 10;
int *p = &a;
int **pp=&p;
test(pp);//可以接收二级指针
test(&p)//可以接收一级指针的地址
int* arr[10];
test(arr)//可以接收指针数组的首元素地址。因为指针数组的元素为指针类型,指针类型的地址就是二级指针。
数组指针---指向数组的指针---存放的是数组的地址---&数组名得到 函数指针---指向函数的指针---存放的是函数的地址---函数名也是函数的地址
int add(int x,int y){
return x+y;
}
int main(){
int (*pf1)(int,int) = add;
int (*pf2)(int,int) = &add;
// printf("%d",pf1==pf2);
printf("%d",(pf1)(3,2));
return 0;
}
int (* p[10])(int),本质上是一个数组 指针数组---char * arr[]存放的是字符指针 整型指针数组---int * arr2[]存放的是整型指针 函数指针数组是数组,数组的元素是函数指针,也就是函数的地址
用函数指针数组完成计算机
#include
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 input = 1,ret = 0,A1 = 0,A2 = 0;
int (*cal[5])(int,int)={NULL,Add,Sub,Mul,Div};
do
{
printf("********************\n");
printf("***1.Add****2.Sub***\n");
printf("***3.Mul****4.Div***\n");
printf("***0.Exit***********\n");
printf("********************\n");
scanf("%d",&input);
if (input==0)
{
printf("退出系统");
}else if (input>0&&input<5)
{ printf("请输入两个数字\n");
scanf("%d %d",&A1,&A2);
ret = cal[input](A1,A2);
printf("ret = %d\n",ret);
/* code */
}else{
printf("输入错误\n");
}
} while (input);
return 0;
}
int(* (*p)[10])(int)
int main(){
int a = 10;
int b = 20;
int c = 30;
int *arr[] = {&a,&b,&c};//整型指针数组
int (*p)[3] = &arr;//指向整型数组的指针
//函数指针数组 - 数组 - 存放的是函数的地址
int (*pfArr[5])(int,int)={NULL,Add,Sub,Mul,Div};
//指向函数指针数组的指针
int(*(*pF)[3])(int,int) = &pfArr;
}
依赖函数指针,有函数指针才能实现回调函数 回调函数是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向函数时,我们说这就是回调函数。回调函数不应该由函数的实现方直接调用,而是在特定的事件或条件发生时由另一方调用,对于该事件或条件进行相应。
简单来说就是不可以直接使用函数名+()的形式调用,需要将回调函数的地址传给函数指针,通过函数指针来调用。
通过函数指针调用回调函数
void cals(int (*p)(int,int)){
int a1,a2;
printf("请输入两个数字\n");
scanf("%d %d",&a1,&a2);
int ret = p(a1,a2);
printf("ret = %d\n",ret);
}
int main(){
int input = 1,ret = 0,A1 = 0,A2 = 0;
// int (*cal[5])(int,int)={NULL,Add,Sub,Mul,Div};
do
{
printf("********************\n");
printf("***1.Add****2.Sub***\n");
printf("***3.Mul****4.Div***\n");
printf("***0.Exit***********\n");
printf("********************\n");
scanf("%d",&input);
switch (input)
{
case 1:
cals(Add);
break;
case 2:
cals(Sub);
break;
case 3:
cals(Mul);
break;
case 4:
cals(Div);
break;
case 0:
printf("退出系统\n");
break;
default:
printf("输入错误,重新输入\n");
break;
}
} while (input);
return 0;
}
qsort是头文件stdlib.h中的函数。此函数有四个参数 void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void*)) 第一个base代表要排序数组的第一个元素的地址一般传入数组名,如果想部分排序,需要获得其实排序元素的地址。 第二个代表要排序的元素的个数 第三个代表要排序数组中的每个元素的大小,以字节为单位 第四个需要传入一个compare函数,compare函数参数必须为void类型的指针。因为qsort()为了对所有类型排序。void类型指针可以接收任何类型的指针。compare的返回值为int。 类型为void的指针补充:类型为void类型的指针不具备访问内存空间的能力。但是此类型指针的特点是可以存储任何类型的指针,后续使用时可以把void类型转换为当时转换前的指针类型。
// int main(){
// int a = 10;
// int b = 20;
// int c = 30;
// int *arr[] = {&a,&b,&c};//整型指针数组
// int (*p)[3] = &arr;//指向整型数组的指针
// //函数指针数组 - 数组 - 存放的是函数的地址
// int (*pfArr[5])(int,int)={NULL,Add,Sub,Mul,Div};
// //指向函数指针数组的指针
// int(*(*pF)[3])(int,int) = &pfArr;
// }
struct stu
{
char name[20];
int age;
};
// void print_arr(int* arr,int len){
// for (int i = 0; i < len; i++)
// {
// printf("%d ",arr[i]);
// }
// printf("\n");
// }
// int compare(const void* p1,const void* p2){
// return *(int*)p1-*(int*)p2;
// }
// int main(){
// int arr[]={9,8,7,6,5,4,3,2,1};
// int sz=sizeof(arr)/sizeof(arr[0]);
// print_arr(arr,sz);
// qsort(arr,sz,sizeof(arr[0]),compare);
// print_arr(arr,sz);
// return 0;
// }
// void print_struct(struct stu *stu,int sz){
// for (int i = 0; i < sz; i++)
// {
// printf("name = %s , age = %d\n",(stu+i)->name,(stu+i)->age);
// }
// }
// int compare(void *p1,void *p2){
// return ((struct stu*)p1)->age-((struct stu*)p2)->age;
// }
// int compare_str(void *p1,void *p2){
// return strcmp(((struct stu*)p1)->name,((struct stu*)p2)->name);
// }
// int main(){
// struct stu Stus[3]={{"zvjing",61},{"xanjiam",20},{"yhangfei",43}};
// print_struct(Stus,3);
// // qsort(Stus,3,sizeof(Stus[0]),compare);
// qsort(Stus,3,sizeof(Stus[0]),compare_str);
// print_struct(Stus,3);
// return 0;
// }