美丽的外貌千篇一律,有趣的灵魂万里挑一
指针是C语言的灵魂
应该有不少小伙伴对指针还是比较迷迷糊糊的吧
本期 阿紫 接着带大家一起过关斩将,擒 “ 指针 ”
目录
1.字符指针
2.指针数组
3.数组指针
3.1数组指针的定义
3.2 &数组名 vs 数组名
3.3数组指针的使用
4.函数指针
5.函数指针数组
6. 指向函数指针数组的指针
7.回调函数
字符指针:指向字符的指针,指针类型为 char*
char ch = 'a';
char* pc = &ch;
一般使用方法:
#include
int main()
{
char ch = 'a';
char* pc = &ch;
*pc = 'b';
printf("%c\n", ch);
return 0;
}
还有一种使用方法:
#include
int main()
{
const char* pc = "abcdef";
printf("%s\n", pc);
return 0;
}
思考1:为什么 const char* pc = "abcdef" 前面要加 const?
答:因为字符指针 pc 指向的是常量字符串,常量是不能发生改变的,所以加 const 避免修改常量字符串。
思考2:const char* pc = "abcdef",是把 abcdef 字符串放入字符指针变量 pc 中吗?
答:不是,是把字符串中首字符的地址放入了 pc 中,也就是把 a 的地址放在 pc 中 。
面试题:
#include
int main()
{
char arr[] = { "abcdef" };
char brr[] = { "abcdef" };
const char* crr = "abcdef";
const char* drr = "abcdef";
if (arr == brr)
{
printf("arr and brr are same\n");
}
else
{
printf("arr and brr not are same\n");
}
if (crr == drr)
{
printf("crr and drr are same\n");
}
else
{
printf("crr and drr not are same\n");
}
return 0;
}
运行结果:
arr 和 brr 是字符数组,是把 abcdef 存储在字符数组中,它们的内存不是同一块,所以它们的地址也就不相同。而 crr 和 drr 是字符指针,是把 abcdef 常量字符串的首元素地址存储字符指针中,所以它们指向的是同一块内存空间。
指针数组:它是一个数组,数组中的每个元素都是类型指针
int* arr[10]; //整形指针的数组
首先 arr 先跟 [10] 结合说明它是一个有十个元素数组,每个元素都是 int* 类型的指针。
数组指针:是一个指针,指向的是一个数组
int arr[6] = { 1, 2, 3, 4, 5, 6 };
int(*pi)[6] = &arr;
pi 先和 * 结合说明它是一个指针变量,[6] 说明它指向了一个数组,数组中有 6 个元素,int 说明每个元素都是 int 类型。
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
arr 和 &arr 它们打印出来的地址虽然一样 ,但它们代表的含义不一样。arr 表示数组首元素的地址,而 &arr 表示整个数组的地址,arr + 1跳过了一个元素,而&arr + 1跳过了整个数组。
sizeof(arr[0])是计算 arr[0] 所占空间大小,而 sizeof(arr)是计算整个数组所占空间大小 。
结论:&数组名 和 sizeof(数组名) 表示整个数组,其余的数组名都表示数组首元素的地址。
既然数组指针指向的是数组,那数组指针中存放的也就是数组的地址。
#include
int main()
{
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int(*pi)[10] = &arr;
return 0;
}
把数组 arr 的地址赋值给数组指针变量 p ,但我们一般很少这样写代码 ,因为这样写代码没有任何的意义。
一般我们将数组指针用于二维数组
#include
void print_arr(int(*arr)[3], int row, int col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
int main()
{
int arr[3][3] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
print_arr(arr, 3, 3);
return 0;
}
数组名arr,表示首元素的地址 。但是二维数组的首元素是二维数组的第一行 ,所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址。
学了指针数组和数组指针我们来一起回顾并看看下面代码的意思:
int arr[5];//整型数组
int *parr1[10];//数组指针
int (*parr2)[10];//指针数组
int (*parr3[10])[5];//???
int (*parr3[10])[5]:首先 parr3 跟 [10] 结合说明它是一个数组,每个元素都是指针数组类型。
函数指针:首先是它是一个指针,它指向的是一个函数
函数名和&函数名:都表示函数的地址
要是想保存函数的地址,就得用到函数指针
p 先跟 * 结合 说明它是指针,后面的一个 () 表示它指向了一个无参的函数,前面的 void 表示返回值 void。
《 C陷阱和缺陷 》 这本书中有这样两个代码:
//代码1
(*(void (*)())0)();
将 0 强制类型转换为 void(*)() 函数指针类型,那么 0 就被当成了一个函数的地址,然后解引用调用,实际上这就是一次函数调用,调用的是 0 作为地址处的函数。
//代码2
void (*signal(int , void(*)(int)))(int);
signal 先跟后面的括号结合,说明 signal 是函数名,函数参数第一个参数是 int,第二个参数是函数指针(该函数指针指向的函数参数是int,返回类型是void),返回值也是一个函数指针,该函数指针指向的函数参数是int,返回类型是void,所以这是一次函数声明。
函数指针数组:首先它是一个数组,数组的每一个元素都是指针指向的是函数。
int(*arr[10])();
首先 arr 先跟 [10] 结合说明它是一个数组,数组中的每一个元素都是 int(*)()类型的函数指针。
函数指针数组的用途:转移表
#include
void menu()
{
printf("*********************\n");
printf("*** 1.add 2.sub ***\n");
printf("*** 3.mul 4.div ***\n");
printf("****** 0.exit *******\n");
printf("*********************\n");
}
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 a, b;
int input = 1;
int ret = 0;
int(*p[5])(int a, int b) = { 0, add, sub, mul, div }; //转移表
while (input)
{
menu();
printf("请选择:");
scanf("%d", &input);
if ((input <= 4 && input >= 1))
{
printf("输入操作数:");
scanf("%d %d", &a, &b);
ret = (*p[input])(a, b);
}
else
printf("输入有误\n");
printf("ret = %d\n", ret);
}
return 0;
}
指向函数指针数组的指针 :首先它是一个指针,它指向了一个数组,数组中每个元素都是函数指针。
void(*p)();//函数指针
void(*p1[1])();//函数指针数组
void(*(*p2)[2])();//指向函数指针数组指针
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个 函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数 的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进 行响应。
qosrt 函数使用:
#include
//qosrt函数的使用者得实现一个比较函数
int int_cmp(const void * p1, const void * p2)
{
return (*( int *)p1 - *(int *) p2);
}
int main()
{
int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
int i = 0;
qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), int_cmp);//把一个函数地址传给另一个函数
for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++)
{
printf( "%d ", arr[i]);
}
printf("\n");
return 0;
}