字符指针
指针有很多类型,char*是字符指针,我们通常是这样使用的
int main()
{
char a = 'w';
char* p = &a;
*p = 'c';
return 0;
}
还有一种使用方法
![](https://i-blog.csdnimg.cn/blog_migrate/eaf80854eb80da40aa3bd14515157cde.png)
这种用法是将常量字符串的首字符'c'的地址放到指针变量pch中,那么下面两种储存字符串的方法有什么不同呢?
(1)char str[] = "csdn" vs (2)const char* pstr = "csdn"
(1)是在内存中开辟一个空间,内部储存常量字符串
(2)计算机将常量字符串储存到一个单独的内存区域(只读数据区),指针变量指向字符串首字符的地址。
如果还是不明白没有关系,我们看一段代码:
int main()
{
char str1[] = "hello world.";
char str2[] = "hello world.";
const char* str3 = "hello world.";
const char* str4 = "hello world.";
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;
}
运行结果如下:
![](https://i-blog.csdnimg.cn/blog_migrate/b5bfd97c5020ee7d08922bcebac84b68.png)
解释:
用数组储存字符串时,每次都要开辟一个不同的内存块,那么数组首元素的地址一定不同;用字符数组存储相同常量字符串时,每次创建的字符指针都会指向相同的地址。
指针数组
指针数组就是存放指针的数组,在指针初姐提到过,这里简单回顾一下:
char* arr[2] = {"hello","world"};
for(int i = 0; i < 2; i++)
{
printf("%s ", arr[i]);
}
![](https://i-blog.csdnimg.cn/blog_migrate/e0804708e4e2f9e38e8080e4ba90d02f.png)
数组指针
数组指针的定义
数组指针就是存放数组地址的指针
int (*parr) [10]
//用()使*与parr先结合,表明parr是一个指针,指向一个大小为10的int类型的数组
数组名VS&数组名
在指针初阶我们知道指针的类型决定了它向前走一步和向后走一步有多大。
&arr表示整个数组的地址,arr表示数组首元素的地址,因此虽然&arr和arr数值上相同,但是&arr+1与arr+1的运算结果不同。
int main()
{
int arr[10] = {0};
printf("arr = %p\n", arr);
printf("&arr= %p\n", &arr);
printf("arr+1 = %p\n", arr+1);
printf("&arr+1= %p\n", &arr+1);
return 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/ec714211fa1a58ff89ab9286ed3b24a8.png)
数组指针的使用
#include<stdio.h>
void Print(int(*p)[5], int row, int col)
{
for (int i = 0;i < row;i++)
{
for (int j = 0;j < col;j++)
{
printf("%d ", *(*(p + 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 };
Print(arr, 3, 5);
return 0;
}
二维数组的数组名表示首元素的地址,而二维数组的首元素使第一行数组,总结一下,二维数组的数组名表示第一行数组的地址,是一个数组指针。因此二维数组传参时形参可以使用数组指针。
如果你掌握了指针数组和数组指针,检测一下自己的成果吧!
int arr[5]; //整型数组
int *parr1[10]; //指针数组
int (*parr2)[10]; //数组指针
int (*parr3[10])[5]; //数组指针的数组
//int (*)[5]是parr3[10]数组的元素类型
数组传参和指针传参
数组传参
一维数组传参时,函数的形参:
void fun(int* arr[]);
void fun(int* arr[10]);
void fun(int **p);
int main()
{
int* arr[10] = { 0 };
fun(arr);
return 0;
}
对于一维数组传参,可以将数组传过去,数组大小可以不指定,也可以将元素类型的指针传过去,例中arr元素都是int*,那么元素类型指针就是int**。
二维数组传参时,函数的形参:
void fun(int arr[][5]);
void fun(int arr[4][5]);
void fun(int (*p)[5]);
int main()
{
int arr[4][5] = { 0 };
fun(arr);
return 0;
}
对于二维数组传参,其实和一维数组差不多,形参为数组时,第一个[]内的数字可以省,但第二个不可省,形参也可以是元素类型数组,即前面说的数组指针。
指针传参
一级指针传参,函数的形参也是一级指针;函数形参是一级指针(以int* p为例),实参可以是&整型变量,一维整型数组,一级指针等。
void fun(int* p);
int main()
{
int a = 0;
int* p = &a;
int arr[10] = { 0 };
fun(&a);
fun(p);
fun(arr);
return 0;
}
二级指针传参,函数的形参也是二级指针;函数形参是二级指针(以int** p为例),实参可以是&一级指针,一维整型数组,二级指针等。
int main()
{
int a = 0;
int* p = &a;
int** pp = &p;
int* arr[3] = { 0 };
fun(&p);
fun(pp);
fun(arr);
return 0;
}
函数指针
函数指针定义
//函数指针就是函数的指针
//指向的函数的返回类型 + (*指针变量名) + 函数参数
type (*p)(type ,type, ...)
//例如:
int (*p)(int ,char)
函数指针的使用
注:函数名和&函数名都可以表示函数地址,函数指针使用时,可以解引用也可以直接使用。
int Add(int a, int b)
{
return a + b;
}
int main()
{
int a = 2;
int b = 3;
int (*p1)(int, int) = Add;
int (*p2)(int, int) = &Add;
printf("%d\n" ,p1(a, b));
printf("%d\n" ,(*p2)(a, b));
return 0;
}
这个例子只是演示如何使用函数指针,事实上,函数指针多用在函数回调中,我们在后面继续讲解。
掌握了函数指针,我们看几个例子:
//代码1
(*(void (*)())0)(); //表示调用0地址处的函数,该函数无返回值,无参数
//代码2
void (*signal(int , void(*)(int)))(int);
//signal函数的定义,返回值是一个函数指针,该函数无返回值,参数是int,形参是int和一个函数指针
对第一个代码拆分如下:
(* ( void(*)( ) )0 ) ( )
黄色为第一层:表示一个函数指针,无返回值,无参数
红色为第二层:将0强制类型转换为一个函数指针
绿色为第三层:对0地址处的函数调用,*可以省略
对第二个代码拆分如下:
void (* signal ( int , void(*)(int) ) ) (int)
最外层的绿色是signal函数的返回类型
红色括号内是signal函数的参数,分别是int和函数指针
函数指针数组
定义:
//指存储函数指针的数组
type (*p[n])(type, type, ...)
//例如:
int (*p[5])(int, int)
p先与[5]结合,代表它是是个有5个元素的数组,元素的类型是int(*)(int,int)函数指针。
应用:
函数指针数组常用来做转移表
//简单模拟计算器
#include<stdio.h>
double Add(double a, double b)
{
return a + b;
}
double Sub(double a, double b)
{
return a - b;
}
double Mul(double a, double b)
{
return a * b;
}
double Div(double a, double b)
{
return a / b;
}
int main()
{
double (*p[5])(double, double) = { NULL,Add,Sub,Mul,Div };
int input = 0;
double a = 0;
double b = 0;
double ret = 0;
do
{
printf("请输入>1--Add,2--Sun,3--Mul,4--div,0--Exit\n");
scanf("%d", &input);
if (input == 0)
{
break;
}
else if (input >= 0 && input <= 4)
{
printf("请输入两个操作数\n");
scanf("%lf %lf", &a, &b);
ret = p[input](a, b);
printf("ret = %lf\n", ret);
}
else
printf("输入错误\n");
} while(input);
}
指向函数指针数组的指针
type (*(*p)[n])(type, type, ...)
//例如
int (*(*p)[5])(int int)
*先与p结合,指向一个5元素的数组,数组的每个元素都是函数指针。
回调函数
定义:
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个
函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数
的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进
行响应。
使用:
函数指针主要用于回调函数,下面演示如何用回调函数简化计算器:
//计算函数略
void calc(double(*p)(double, double))
{
int a, b;
printf("请输入两个操作数\n");
scanf("%d %d", &a, &b);
printf("ret = %lf\n", p(a, b));
}
int main()
{
int input = 0;
do
{
printf("请输入>1--Add,2--Sun,3--Mul,4--div,0--Exit\n");
scanf("%d", &input);
switch (input)
{
case 1:
calc(Add);
break;
case 2:
calc(Sub);
break;
case 3:
calc(Mul);
break;
case 4:
calc(Div);
break;
case 0:
break;
default:
printf("输入错误\n");
break;
}
} while (input);
return 0;
}
回调函数在qsort中的使用:
//qsort函数定义
//void qsort (void* base,size_t num, size_t size,
//int (*compar)(const void*,const void*));
//对整型数据升序排列
int cmp_int(const void* e1,const void *e2)
{
return *(int*)e1 - *(int*)e2;
}
int main()
{
int arr[10] = {9,8,7,6,5,4,3,2,1,0};
qsort(arr, sizeof(arr)/sizeof(arr[0]),sizeof(arr[0]),cmp_int);
int i = 0;
for(i = 0;i < sizeof(arr)/sizeof(arr[0]);i++)
{
printf("%d ", arr[i]);
}
return 0;
}