C语言——指针(进阶)

时间:2022-10-12 10:58:56
  • 字符指针
  • 数组指针
  • 指针数组
  • 数组传参和指针传参
  • 函数指针
  • 函数指针数组
  • 指向函数指针数组的指针
  • 回调函数


一:字符指针

指针类型中字符指针     char* 

#include<stdio.h>
int main()
{
char* pa = "good bye";
//printf("%c\n",*pa);//本质是把字符串的首字符的地址存储在pa中

char* pa = "good bye";
char arr[] = "good bye";
printf("%s\n",pa);
printf("%s\n",arr);
return 0;
}

例题:

#include <stdio.h>
int main()
{
char str1[] = "game";
char str2[] = "game";
//两个不同数组,新创造的空间不同
const char* str3 = "computer";
const char* str4 = "computer";
//常量字符串是不能改的,只会存一份,所以两个指针变量都指向同一个地址
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;
}
//str1 and str2 are not same
//str3 and str4 are same


二:数组指针(指向数组的指针)

1.数组指针

#include<stdio.h>
int main()
{
int a = 10;
int* pa = &a;//整形指针

char b = 'q';
char* pb = &b;//字符指针

int arr[5] = {1,2,3,4,5};
int (*parr)[5] = &arr;//由于[]的优先级高于*,得带括号

}

从parr处开始,先与*结合,说明P 是一个指针  然后再与[]结合,说明指针所指向的内容是一个数组,然后再与int 结合,说明数组里的元素是整型的.所以parr 是一个指向由整型数据组成的数组的指针

(怎么判断呢?    看变量先与谁结合

2:&数组名和数组名

C语言——指针(进阶)

差分别为4 ,40。

&arr取的是整个数组的地址,而arr是数组首元素的地址

3:应用

//二维数组
#include<stdio.h>
void print1(int arr[3][5], int m, int n)
{
int i = 0;
int j = 0;
for (i = 0; i < m; i++)
{
int j = 0;
for (j = 0; j < n; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
//数组指针
void print2(int(*arr)[5], int r, int c)
{
int i = 0;
for (i = 0; i < r; i++)
{
int j = 0;
for (j = 0; j < c; j++)
{
printf("%d ", *(*(arr + i) + j));
}
printf("\n");
}
}
int main()
{
int arr[3][5] = { 1,2,3,4,5,2,3,4,5,6,3,4,5,6,7 };
print1(arr, 3, 5);
print2(arr, 3, 5);
return 0;
}
//print1,2
//1 2 3 4 5
//2 3 4 5 6
//3 4 5 6 7

注意:

int(*parr[10])[5];
//首先parr[10]是个数组,去掉parr[10]后为指向的类型
//parr是以个存放数组指针的数组,能存放10个数组指针
//每个数组指针都能指向一个数组,数组有五个元素,均为int类型


三:指针数组(是一个存放指针的数组

#include<stdio.h>
int main()
{
//指针数组-数组中存放指针(地址)
int a = 10;
int b = 20;
int c = 30;
int d = 40;
int* arr[5] = {&a,&b,&c,&d};
int i = 0;
for (i=0;i<4;i++)
{
printf(" %d\n ", *(arr[i]));
}
}
#include<stdio.h>
int main()
{
int a1[] = { 1,2,3,4,5 };
int a2[] = { 2,3,4,5,6 };
int a3[] = { 3,4,5,6,7 };
int* arr[3] = { a1,a2,a3 };//代表各数组首元素的地址
int i = 0;
for (i=0;i<3;i++)//行
{
int j = 0;
for (j=0;j<5;j++)//列
{
printf("%d ", *(arr[i] + j));
//可以写成 printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
//1 2 3 4 5
//2 3 4 5 6
//3 4 5 6 7


四:数组传参和指针传参

1:一维数组传参

#include <stdio.h>
void test1(int arr[10])//可以
{}
void test1(int arr[])//可以,元素个数可以省略
{}
void test1(int* arr)//可以,指针
{}

void test2(int* arr[20])//可以,存放int* 的整型指针数组
{}
void test2(int** arr)//可以,二级指针
{}
int main()
{
int arr1[10] = {0};
int* arr2[10] = {0};//存放int*的数组
test1(arr1);

test2(arr2); //指向int* 的首元素地址
}


2:二维数组传参

实参     只用填数组名 就行

形参可以为   二维数组  或 指针

//二维数组
void test(int arr[3][5])//可以
{}
void test(int arr[][])//错误。行可省略,列是不能省的
{}
void test(int arr[][5])//可以
{}
//指针
void test(int* arr)//不行。接收第一行的地址,第一行是5个整形的数组,不能用指针接收
{}
void test(int* arr[5])//不行。这是指针数组,得要整型数组指针才行
{}
void test(int(*arr)[5])//可以。为整型数组指针
{}
void test(int** arr)//不行。
{}
int main()
{
int arr[3][5] = { 0 };
test(arr);
return 0;
}


3:一级指针传参

#include <stdio.h>
void print(int* pstr, int sz) //用一级指针接收
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", *(pstr + i));
}
}

void test(char* p)
{
}
int main()
{
int arr[7] = { 2,0,2,2,10,0,2 };
int* p = arr;
int sz = sizeof(arr) / sizeof(arr[0]);
print(p, sz);//p是一级指针

char ch = 'a';
test(&ch);//char*
//char* p1 = &ch;
//test(p1);
return 0;
}


4:二级指针传参

#include <stdio.h>
void test(int** pstr) //二级指针接收
{
printf("num = %d\n", **pstr);//a的值
}
int main()
{
int a = 10;
int* pa = &a;//pa是一级指针
int** ppa = &pa;//ppa是二级指针
test(ppa);//二级指针传参

test(&pa);取一级指针的地址就是二级指针

int* arr[10] = {0};
test(arr);//也可以,传存放一级指针的数组
//,传的是整形指针数组的首元素的地址,而首元素为int*类型,结果为二级指针传参
return 0;
}


五:函数指针(存放函数地址的指针)

&函数名——取出函数的地址

注意:数组名 != &数组名

         函数名   == &函数名

                              int (*pf)(int,int) = &Add;

#include<stdio.h>
int Add(int x,int y)
{
return x + y;
}
int main()
{
printf("%p\n", Add);
printf("%p\n", &Add);
//地址相同,意义一样,无区别

int (*pf)(int,int) = &Add;// 函数指针中,由于()优先级高于*,得带括号
int ret = (*pf)(7,5);//通过函数指针调用函数.1
//int ret = pf(7,5);//2
//int ret = Add(7,5);//3
//1,2,3都一样。*的作用不大,可有可无,但只能在函数指针中这样
//int ret = *pf(7,5);//这样是不行的,等于对函数的结果12进行解引用。若加*得括起来
printf("%d\n",ret);//12
return 0;
}

例子:

#include<stdio.h>
void test(char* str)
{
}
int main()
{
void (*pt)(char*) = &test;//与函数要对应
return 0;
}


六:函数指针数组(存放函数指针的数组

它可以存放多个参数相同,返回类型相同 的函数地址

                          int (*arr[3])(int, int) = { Add,Sub,Mul};

#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 main()
{
//parr就是 函数指针数组
int (*parr[3])(int, int) = { Add,Sub,Mul};
return 0;
}


写一个计算器(加,减,乘,除):

(函数指针数组的用途:转移表

#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;
}

void menu()
{
printf("********************************\n");
printf("**** 1.Add 2.Sub ****\n");
printf("**** 3.Mul 4.Div ****\n");
printf("**** 0.exit ********\n");
printf("********************************\n");
}
int main()
{
int input = 0;
do{
menu();//菜单
//pfArr是函数指针数组
//转移表
int (*pfArr[5])(int, int) = { NULL, Add, Sub, Mul, Div };//

int x = 0;
int y = 0;
int ret = 0;
printf("请选择:>");
scanf("%d",&input);
if(input>=1 && input<=4)
{
printf("请输入2个数:>");
scanf("%d %d", &x, &y);
ret = (*pfArr[input])(x, y);
printf("%d\n",ret);
}
else if(input == 0)
{
printf("退出程序\n");
}
else
{
printf("选错了,重新选择");
}
}while(input);
return 0;
}

,

七:指向函数指针数组指针

int(*p)(int,int);//函数指针
int(* p2[4])(int,int);//函数指针的数组

int(*(*p3)[4])(int,int);//取出函数指针数组的地址
//p3是一个指向函数指针数组的指针

补充:int arr[5];

数组元素类型:去掉数组名和元素个数 , 剩下的就是——>int

数组类型   :仅去掉数组名                        ——>int[5]     

   

八:回调函数

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针作为参数传给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。


回调函数应用例子:

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;
}

void menu()
{
printf("***************************\n");
printf("***** 1.add 2. sub ****\n");
printf("***** 3.mul 4. div ****\n");
printf("***** 0.exit ****\n");
printf("***************************\n");
}
//回调函数
int Cald(int (*pf)(int, int))
{
int x = 0;
int y = 0;
printf("请输入2个值:>");
scanf("%d %d", &a, &b);
return pf(x,y);
}

int main()
{
int input = 0;
int x = 0;
int y = 0;
int ret = 0;
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
ret = Cald(Add);//把Add函数指针传过去
//Add函数并不是直接调用的,而是通过函数指针调用的
printf("%d\n", ret);
break;
case 2:
ret = Cald(Sub);//把Sub函数指针传过去
printf("%d\n", ret);
break;
case 3:
ret = Cald(Mul);//把Mul函数指针传过去
printf("%d\n", ret);
break;
case 4:
ret = Cald(Div);//把Div函数指针传过去
printf("%d\n", ret);
break;
case 0:
printf("退出程序\n");
break;
default:
printf("选错了,重新选择\n");
break;
}
} while (input);
}

qsort 库函数(进行快速排序的实现)

(得引用头文件    stdlib.h  )

void qsort (void* base,//void*能接收任意类型的地址,应用更加广泛;base中存放的是 待排序的数据里的第一个对象的地址
size_t num,//元素个数
size_t size,//一个元素占几个字节
int (*compar)(const void*,const void*)//用来比较待排序数据中的2个元素的函数
);

//
注意:int compareMyType (const void * a, const void * b)
{
if ( *(MyType*)a < *(MyType*)b ) return -1;
if ( *(MyType*)a == *(MyType*)b ) return 0;
if ( *(MyType*)a > *(MyType*)b ) return 1;
}

例子:用qsort函数进行排序

#include<stdio.h>
#include<stdlib.h>

int compar(const void* d1, const void* d2)
{
return *(int*)d1 - *(int*)d2 ;
}

void print_arr(int arr[],int sz)
{
int i = 0;
for(i=0;i<sz;i++)
{
printf("%d\n",arr[i]);
}
printf("\n");
}
int main()
{
int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr)/sizeof(arr[0]);
qsort(arr,sz, sizeof(arr[0]), compar);//排序
print_arr(arr, sz);
return 0;
}


以后继续补充qsort函数内容