目录
①(●'◡'●)前言
1.字符指针
✌字符指针和数组笔试题
2.指针数组 和数组指针
👊指针数组
👊数组指针
👊&数组名和数组名
3.数组传参和指针传参
👊一维数组传参
👊二维数组传参
👊一级指针传参
👊二级指针传参
4.函数指针
5.函数指针数组
👊函数指针数组应用
6.函数指针数组的指针
7.回调函数
👊qsort()
冒泡排序通用版
①(●'◡'●)前言
在之前【C语言】入门——指针介绍了指针的概念
1. 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
2. 指针的大小是固定的4/8个字节(32位平台/64位平台)。
3. 指针是有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限。
4. 指针的运算。
这一篇介绍更深度的指针内容
1.字符指针
步长最短的字符型指针,字符指针就是用来存放字符变量(或字符串变量)的指针
当存储字符串变量时,会像数组一样只存入首字母的地址,在解引用时可以根据首地址依次往后访问并解引用,直到遇到结束标志 '\0' ,
由此看来指针貌似和数组有点相似。
int main()
{
char s1 = 'a';
char* p = &s1;
char* p2 = "abcdef";
printf("%c\n", *p);
printf("%s\n", p2);
return 0;
}
✌字符指针和数组笔试题
//字符指针笔试题
int main()
{
char arr1[] = { "Hello World" };
char arr2[] = { "Hello World" };
const char* str1 = "Hello World";
const char* str2 = "Hello World";
if (arr1 == arr2)
printf("arr1 and arr2 are same\n");
else
printf("arr1 and arr2 are not same\n");
if (str1 == str2)
printf("str1 and str2 are same\n");
else
printf("str1 and str2 are not same\n");
return 0;
}
arr1和arr2是两个独立的数组,自然地址不一样,独立空间,所以不相等;
str1和str2是因为指向同一块空间,因为两个相同的常量在内存中只会开辟一块空间;
2.指针数组 和数组指针
指针数组是数组,数组内存放的是指针;
数组指针是指针,存放数组的地址。
数组指针与指针数组容易混淆
int arr[5];
arr是一个数组,每个元素是int类型的 ,有5个元素
int* parr1[10];
parr1是一个数组,数组10个元素,每个元素的类型是int*
int (*parr2)[10];
parr2是一个指向数组的指针,指向的数组有10个元素,每个元素的类型是int
int(* parr3[10])[5];
parr3是一个数组,数组有10个元素,每个元素的类型是:int(*)[5]
parr3是存放数组指针的数组
👊指针数组
//指针数组
int main()
{
int a = 1, b = 2, c = 3;
int* pa = &a;
int* pb = &b;
int* pc = &c;
int* arr[3] = { pa,pb,pc };
int i = 0;
for (i = 0; i < 3; i++)
{
printf("地址:%p\n", arr[i]);
printf("值为:%d\n", *arr[i]);
}
return 0;
}
定义三个变量,取各自他们的地址赋给不同的指针,指针为int*,再将指针放到数组内存放,
&arr的类型为:int* (*arr)[3];
arr的类型为int* arr;
👊数组指针
//数组指针
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
int (*p)[10] = &arr;//数组指针,存放整形数组arr的地址
return 0;
}
👊&数组名和数组名
//&数组名与数组名
int main()
{
int arr[5] = { 1,2,3,4,5 };
int(*pa)[5] = &arr;
printf("这是起始地址:%p %p\n", arr, pa);
printf("这是+1后的地址:%p %p\n", arr + 1, pa + 1);
return 0;
}
&arr+1,加一个步长,但是是整个数组的地址+1,所以是跳过整个数组大小;
数组5个元素,0x00fbfe58是首元素地址,58-5c-60-64-68-6c;
跳过了整个数组大小20个字节;
arr+1; 首元素地址+1,是跳过一个元素大小;
58-5c;整型数组,一个整型4个字节,跳过一个元素即是4个字节;
3.数组传参和指针传参
👊一维数组传参
//一维数组传参
void test1(int arr[])
{}//一维数组可以省略元素数,也可以写上
void test1(int*pa)
{}//用一级指针接收一维数组
void test2(int*arr2[10])
{}//形参用指针数组接收指针数组传参
void test2(int**ppa)
{}//指针数组本质上是二级指针
int main()
{
int arr1[10] = { 0 };
int* arr2[10] = { 0 };
test1(arr1);
test2(arr2);
return 0;
}
👊二维数组传参
三种方式:
完整传参,行和列都不省略;
省略行;
数组指针接受
//二维数组传参
void test(int arr[3][5])
{}//完整接收
void test(int arr[][5])
{}//省略行,是可以的
void test(int(*pa)[5])
{}//用我们前面的数组指针接收
void test(int** pa)
{}//是错误的
int main()
{
int arr[3][3] = { 0 };
test(arr);
return 0;
}
👊一级指针传参
//一级指针传参
void test1(int* pa, int sz)
{}//传数组名,用指针接收
void test2(int* pa, int sz)
{}//传指针,用指针接收
int main()
{
int arr[3] = { 1,2,3 };
int sz = sizeof(arr) / sizeof(arr[0]);
int* pa = arr;
test1(arr, sz);
test2(pa, sz);
return 0;
}
👊二级指针传参
//二级指针传参
void test1(int**pa)
{}//接收的是二级指针
void test2(int**pa)
{}//接收的一级指针的地址
int main()
{
int a = 10;
int* pa = &a;//一级指针
int** ppa = &pa;//二级指针
test1(ppa);//传二级指针
test2(&pa);//将一级指针的地址取出来
return 0;
}
4.函数指针
函数指针是指向函数的指针;函数名就是地址
函数指针由三部分组成:
类型、指针、形参,
类型和形参可以为空,当想要调用函数时,只需要通过指针,并传入参数,就能正常使用函数。
int Add(int x,int y)
{
return x + y;
}
int main()
{
int (*p)(int, int) = &Add;
printf("%d", p(2, 3)); //5
return 0;
}
来个函数指针趣题
void (*signal(int, void(*)(int)))(int);
1.函数指针: void(*)(int)
2.函数名: signal
3.signal函数的参数: int, void(*)(int); 一个整型,一个函数指针
4.返回值:signal函数的返回值是指针
5.函数指针数组
将一些函数地址存入数组中,就得到了函数指针数组。
//函数指针数组
int Add(int x, int y)
{
return x + y;
}
int Sub(const int x, const int y)
{
return x - y;
}
int main()
{
int(*pfun[2])(const int x, const int y) = { Add,Sub };
printf("%d\n", pfun[0](2, 3)); //5
printf("%d\n", pfun[1](5, 3)); //2
return 0;
}
函数名就是地址名,不需要&地址,函数形参不能省略
👊函数指针数组应用
//简易整型计算器
#include<stdio.h>
void menu()
{
printf("*****计算器*****\n");
printf("**1.Add 2.Sub**\n");
printf("**3.Mul 4.DIV**\n");
printf("*****0.exit*****\n");
}
int add(const int x, const int y)
{
return x + y;
}
int sub(const int x, const int y)
{
return x - y;
}
int mul(const int x, const int y)
{
return x * y;
}
int div(const int x, const int y)
{
return x / y;
}
int main()
{
int input = 1;
int(*calc[5])(const int x, const int y) = { 0,add,sub,mul,div };
//放0的原因是和菜单中的序号对应上
while (input)
{
menu();
printf("请输入:>");
scanf("%d", &input);
if (input > 0 && input < 5)
{
int x = 0, y = 0;
printf("请输入两个数:");
scanf("%d %d", &x, &y);
printf("计算结果为%d\n", calc[input](x, y));
}
else if (input >= 5)
printf("选择错误,请重新选择!\n");
}
printf("退出计算器\n");
return 0;
}
6.函数指针数组的指针
套娃式,本质是指针;
void test(const char* str)
{
printf("%s\n", str);
}
int main()
{
//函数指针pfun
void (*pfun)(const char*) = test;
//函数指针的数组pfunArr
void (*pfunArr[5])(const char* str);
pfunArr[0] = test;
//指向函数指针数组pfunArr的指针ppfunArr
void (*(*ppfunArr)[5])(const char*) = &pfunArr;
return 0;
}
例子2:
//函数指针数组的指针
int add(int x, int y)
{
return x + y;
}
int main()
{
//这是函数指针数组
int (*pa[5])(int x, int y) = { add };
//这是函数指针数组的指针,需要取出地址
int(*(*ppa)[5])(int x, int y) = &pa;
printf("这是函数指针数组的指针%p\n", ppa);
printf("这是&函数指针数组后的地址%p\n", &pa);
return 0;
}
7.回调函数
回调函数就是一个通过函数指针调用的函数。
如果你把函数的指针(地址)作为参数传递给另一个 函数,当这个指针被用来调用其所指向的函数时,这就是回调函数。
(依赖函数指针,有了函数指针,才能实现回调函数)
👊qsort()
qsort():快速排序
库函数,头文件#include<stdlib.h>
qsort函数可以进行各种数据的排序
void qsort(void* base, //待排序数组的第一个元素的地址
size_t num, //待排序数组的元素个数
size_t size, //待排序数组中一个元素的大小
int (* cmp)(const void* e1, const void* e2) //函数指针,自己定义排序函数
整型实例
//qsort()
#include<stdio.h>
#include<stdlib.h>
int cmp(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;
}
int main()
{
int arr[] = {5,4,3,2,1 };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
函数指针-cmp指向了一个函数,这个函数是用来比较两个元素的
e1和e2中存放的是需要比较的两个元素的地址
void* 不能直接进行计算,需要强转成其他类型;
结构体示例
#include<string.h>
#include<stdlib.h>
struct Stu
{
char name[20];
int age;
};
int cmp_by_name(const void* e1,const void* e2)
{
return strcmp(((struct Stu*)e1)->name,((struct Stu*)e2)->name);
}
int main()
{
struct Stu arr[] = { {"zhangsan",20},{"wangwu",15} };
//元素个数
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp_by_name);
return 0;
}
strcmp()是根据字符字典顺序比较的,后面的大,需要包含头文件#include<stdlib.h>
qsort函数中就用到了回调函数的知识,使用qsort,它都会去调用比较函数。
冒泡排序通用版
用冒泡排序的思路去模仿qsort();
#include<string.h>
struct Stu
{
char name[20];
int age;
};
//结构体年龄比较
int cmp_age(void* e1, void* e2)
{
return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
//字节交换,size是一个数据的字节大小
void swap(char* buf1, char* buf2, size_t size)
{
int k = 0;
for (k = 0; k < size; k++)
{
char temp = *buf1;
*buf1 = *buf2;
*buf2 = temp;
buf1++;
buf2++;
}
}
//整型比较
int cmp_int(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;
}
//打印
void print(int* arr, int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
//冒泡模拟qsort
void my_qsort(void* base, size_t num, size_t size, int(*cmp_name)(const void* p1, const void* p2))
{
int i = 0;
for (i = 0; i < num - 1; i++)
{
int j = 0;
for (j = 0; j < num - 1 - i; j++)
{
if (cmp_name((char*)base + j * size, (char*)base + (j + 1) * size) > 0) //得到的返回值,如果大于0,就交换
{
swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
}
}
}
}
int main()
{
struct Stu arr[] = { {"zhangsan",15},{"lisi",12},{"wangwu",30} };
int iarr[] = { 9,8,7,6,5,4,3,2,1,0 };
//整型数组的元素个数
int isz = sizeof(iarr) / sizeof(iarr[0]);
my_qsort(iarr, isz, sizeof(iarr[0]), cmp_int);
print(iarr, isz);
//结构体的元素个数
int sz = sizeof(arr) / sizeof(arr[0]);
my_qsort(arr, sz, sizeof(arr[0]), cmp_age);
return 0;
}
感谢你看到这里
以上就是我对进阶指针的介绍,身为初学者,自知有很多不足和需要改善的地方,希望大佬们指点一二,感激不急!!!
⭐愿星光照亮每一位赶路人 ⭐