1.字符指针
众所周知,字符指针变量是用来存放字符类型数据的地址,字符指针变量为char* ,所占内存大小为 4个字节。 一般使用char* :
int main()
{
char ch = 'w';
char* pc = &ch;
return 0;
}
这里主要介绍一种容易产生误解的用法:
int main()
{
const char* ps ="hello world";
return 0;
}
以下代码可以回答上述问题:
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;
}
这里最终输出是:str1和str2不同:首先str1和str2是各自数组的首元素地址(也是数组地址),str1数组和str2数组所开辟的空间不相同,地址肯定不同。 str3和str4相同:因为它们指向了同一个地址,就是"hello world." 常量字符串的首元素地址
代码const char* ps = "hello world";
并不是将字符串hello world 放入字符指针ps里,而是把字符串hello world 的首字符地址存放在ps种,因为字符串hello world 为常量,所以引用const 关键字修饰char*
2.指针数组
指针数组就是一个存放指针的数组,指针类型也保持一致符合数组的特性
int main()
{
int num1 = 10, num2 = 20, num3 = 30;
char ch1 = 'a', ch2 = 'b', ch3 = 'c';
int* arr_num[3] = {&num1, &num2, &num3};
char* arr_ch[3] = {&ch1, &ch2, &ch3};
return 0;
}
3.数组指针
3.1 数组指针
数组指针实质上是存放数组地址的指针 数组指针变量的类型需要考虑数组的类型、如何得到整个数组的地址
首先取出数组的地址,使用& 符号:
int arr[5] = {1, 2, 3, 4, 5};
&arr;
可以通过代码,得到&arr 、arr 、&arr[0] 是指向同一个地址
int main()
{
int arr[5] = {1, 2, 3, 4, 5};
printf("%p\n",&arr);
printf("%p\n",arr);
printf("%p\n",&arr[0]);
return 0;
}
但是arr 和&arr[0] 的指针类型是int* ,每次+1 或-1 地址移动4个字节 &arr 的是整个数组的指针,每次+1 或-1 地址移动整个数组的字节数
数组指针变量需要找到数组的类型:----
int main()
{
int arr[5] = {1, 2, 3, 4, 5};
int(*p)[5] = &arr;
return 0;
}
数组指针如何使用呢?
3.2 数组指针的使用
数组指针一般在二维数组中使用
void print_arr(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, 6, 7, 8, 9, 10};
print_arr(arr, 3, 5);
return 0;
}
最后再区分一下如下代码所代表的是什么
int arr[5];
int* parr1[10];
int(*parr2)[10];
int(*parr3[4])[5];
- 第一个很明显是一个整型指针,可以存放5个整型
- 第二个是指针数组,可以存放10个指针类型
- 第三个是数组指针,数组指针存放着一个整型数组且有10个整型空间的地址
- 第四个是数组指针数组,
parr3与[4] 先结合,说明parr3是一个数组 ,可以存放4个数组指针,而这4个数组指针的类型是int(*)[5] ,所以4个数组指针指向的都是具有5个整型空间的整型数组
int main()
{
int arr1[4] = { 1,1,3,4 };
int arr2[4] = { 1,3,3,4 };
int(*ps[2])[4] = {&arr1,&arr2};
return 0;
}
4.数组传参和指针传参
4.1 一维数组传参
#include<stdio.h>
void test(int arr[])
{}
void test(int arr[10])
{}
void test(int *arr)
{}
void test2(int *arr[20])
{}
void test2(int **arr)
{}
int main()
{
int arr[10] = {0};
int *arr2[20] = {0};
test(arr);
test2(arr2);
}
4.2 二维数组传参
void test(int arr[3][5])
{}
void test(int arr[][])
{}
void test(int arr[][5])
{}
void test(int *arr)
{}
void test(int* arr[5])
{}
void test(int (*arr)[5])
{}
void test(int **arr)
{}
int main()
{
int arr[3][5] = {0};
test(arr);
}
4.3 一级指针参数
一级指针形参可以通过数组名、一级指针来传参
#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]);
print(p, sz);
return 0;
}
4.4 二级指针参数
二级指针形参,可以通过二级指针或指针数组来传参
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(char** p)
{
;
}
int main()
{
char* arr[10] = { 0 };
test(arr);
return 0;
}
5.函数指针
所有数据类型都有指针,函数也不例外 函数指针变量的类型需要考虑函数的类型、如何得到整个函数的地址
首先考虑如何得到函数的地址:
#include<stdio.h>
void test()
{
printf("hehe\n");
}
int main()
{
printf("%p\n",&test);
printf("%p\n",test);
return 0;
}
得到的结果一样的,函数的地址可以用&函数名 或函数名 表示
再考虑函数指针变量的类型怎么表示,用来存放函数的地址 先搞清楚函数的类型怎么表示,以上述代码为例:
void ()
有的函数具有形参,它的类型怎么表示
int add(int x1, int x2)
{
return x1 + x2;
}
int main()
{
int num1 = 10;
int num2 = 20;
int ret = add(num1,num2);
printf("%d\n",ret);
return 0;
}
上述代码中,函数的类型为
int (int,int)
与数组指针相似,将指针变量引用函数类型就是函数指针
void(*p1)() = &test;
void(*p1)() = test;
int(*p2)(int,int) = &add;
int(*p2)(int,int) = add;
阅读两段有趣的代码:
( *(void(*)() )0 )();
void(*signal(int , void(*)(int)))(int);
分析: 第一段代码: void (*)() 是一个返回类型为void 的函数指针类型,*(void(*)())0 中() 的优先级大于* ,所以是将0类型转换为函数指针,再解引用,(*(void(*)())0)() 解引用后,再调用该函数。 总结: 代码1是调用地址在0处的函数,且函数的返回类型为void,没有参数
第二段代码:signal(int,void(*)(int)) 是一个函数声明,第一个参数为int 类型,第二个参数为函数指针类型,该函数返回类型为void ,参数为int 类型,signal这个函数的返回类型为函数指针void(*)(int) 总结: 代码2是一个函数声明,返回类型为void(*)(int) ,第一个参数为int 类型,第二个参数为void(*)(int) 类型 代码2可以换一种写法,观察起来更明朗:
typedef void(*pfun_t)(int);
pfun_t signal(int, pfun_t);
6.函数指针数组
数组是一个存放相同类型数据的存储空间 函数指针数组是一个存放函数指针类型数据的存储空间
int (*parr[10])() = { 0 };
上述就是一个函数指针数组,数组的类型为int(*)() , 可以存放10个int(*)() 类型的数据
使用函数指针数组实现计算器:
#include <stdio.h>
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a*b;
}
int div(int a, int b)
{
return a / b;
}
int main()
{
int x,y;
int input = 1;
int ret = 0;
int(*p[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("请选择:");
scanf("%d",&input);
if(input <= 4 && input >=1)
{
printf("输入操作数:");
scanf("%d %d",&x,&y);
ret = (*p[input])(x,y);
}
else
printf("输入有误\n");
printf("ret = %d\n ",ret);
printf("\n");
}
return 0;
}
7.指向函数指针数组的指针
指向函数指针数组的指针是一个指针 指针指向一个数组 ,数组的元素都是函数指针
void test(const char* str)
{
printf("%s\n",str);
}
int main()
{
void(*p_fun)(const char*) = test;
void(*pfun_arr[5])(const char*) = {0};
pfun_arr[0] = test;
void(*(*ppfun_arr)[5])(const char*) = &pfun_arr;
return 0;
}
上述代码中,函数指针数组的指针类型为void(* [5])(const char*)
8.回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,
当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接
调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
之后会手动实现一个库函数qsort 来展现回调函数
|