在指针的类型中,已经知道有一种指针类型叫做字符指针char*
一般使用
int main()
{
char ch = 'a';
char* pc = &ch;
*pc = 'x';
return 0;
}
此外,还有一种使用方法
int main()
{
char* ps = "hello word";
//”hello word“为常量字符串
printf("%s\n", ps);
return 0;
}
那 char* ps = "hello word",是指什么呢?
首先,ps属于字符指针,而在一个32位平台上,指针的大小只有四个字节,是不可能存放字符串的所有元素的地址,本质上就是ps内存放的是字符串中首元素的地址,并且该字符串不能够修改,最好用个const修饰下。
例题:该代码的输出结果
#include
int main()
{
const char* p1 = "abcd";
const char* p2 = "abcd";
if (p1 == p2)
printf("smple\n");
else
printf("not smaple\n");
return 0;
}
答案:sample 原因是"abcd"是一个常量字符串,不会再更改,所以机器在分配内存的时候,让p1与p2内存的是相同的地址
所谓指针数组,就是存放指针的数组。
int arr[5];//整型数组
char str[5];//字符数组
int* parr[5];//存放整形指针的数组 —— 指针数组
char *pstr[5];//存放字符型指针的数组——指针数组
案例:可以用一个指针数组去维护多个数组
#include
int main()
{
int arr1[5] = { 1,2,3,4,5 };
int arr2[5] = { 2,3,4,5,6 };
int arr3[5] = { 3,4,5,6,7 };
int* arr[] = { arr1,arr2,arr3 };
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 5; j++)
printf("%d ", *(arr[i] + j));
printf("\n");
}
return 0;
}
顾名思义,数组指针,就是能够指向数组的指针,那么又该如何的去定义一个数组指针呢
int *pa[10];
// '[' 与pa结合优先级比 '*'的要高,这样写只能代表pa表示一个数组,应当保证pa先于'*'结合
int(*pa)[10];
//pa与'*'结合,表示pa为一个指针,"int(* )[10]"表示一个指向数组的指针,
//并且该数组的类型,存十个元素
数组指针在一维数组中没啥用,但它可以用来维护多维数组
以一个二维数组为例:已经知道,一维数组的数组名代表的是该数组的首个元素的地址,而二维数组的数组名代表的是该数组第一行所有的元素
样例(该样例目的为,按照行和列打印一个二维数组)
#include
//未使用数组指针
void Printf1(int arr[][4], int m, int n)
//在一般情况下传二维数组的行可以省略,而列不可以省略
{
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
printf("%d ", arr[i][j]);
printf("\n");
}
}
//使用数组指针
void Printf2(int(*pa)[4], int m, int n)
{
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
printf("%d ", *(*(pa + i)+j));
//pa+i表示第几行的地址,然后解引用,再加j表示该行第j个元素的地址
//printf("%d ", pa[i][j]);
//也可以选择用这种方式因为pa与数组名arr等价都是首元素地址
//printf("%d ", *(pa[i] + j));
//printf("%d ",(*(pa + i))[j]);
//这几种暑促方法的结果均相同
printf("\n");
}
}
int main()
{
int arr[4][4] = { {1,2,3,4},{2,3,4,5},{3,4,5,6},{4,5,6,7} };
Printf1(arr, 4, 4);
Printf2(arr, 4, 4);
return 0;
}
了解:int ( * pa [10] ) [5] 这里pa表示一个数组,内部储存了十个元素,每种元素的类型是数组指针,每个数组指针指向的数组内部储存了十个元素,每种元素的类型数组整型,pa即存储数组指针的数组。
函数指针,即能够指向函数的指针
一个函数的函数名,代表了一个函数在内存中的位置
#include
int Add(int x, int y)
{
return x + y;
}
int main()
{
printf("%p\n", Add);
printf("%p\n", &Add);
return 0;
}
两种方法的输出结果相同,说明函数也可以用指针来表示,那么又该如何去定义一个函数指针类型,继续以加法函数为例。
#include
int Add(int x, int y)
{
return x + y;
}
int main()
{
//int 表示该函数的返回类型为整型,*padd 表示 padd是一个指针,
(int,int)表示该函数有两个整型参数
int (*padd)(int, int) = Add;
//简单使用
printf("%d\n", (*padd)(3, 5));
printf("%d\n", padd(3, 5));
return 0;
}
注意在定义函数指针时,int (*padd)(int,int),由于'*'的优先级低于'(',故应当保证padd与‘*’优先结合,否则padd与‘(’结合说明padd为一个函数
两个阴间代码所表述的意思
1.(*(void(*)())0)();
2.void(*signal(int, void(*)(int)))(int);
1.0前面的的一串唯一的函数指针类型,该指针指向一个参数为空,返回值为空的函数,在0前面表示将0强制转换为函数指针类型,然后对0进行解引用操作,调用该函数;
2.signal是一个函数名,它有两个参数,一个是整型,另外一个是函数指针,该函数指针的指向的返回类型为空,该函数的参数为整型,signal函数的返回类型是一个函数指针,该函数指针的指向的返回类型为空;
对于第二个阴间代码可以用typedef简化一下;
void(*signal(int, void(*)(int)))(int);
//可以对这个函数指针进行简化
typedef void(* )(int) p_fun;
//这种写法是错误的
//正确写法
typedef void(*p_fun)(int);
//简化结果
p_fun signal(int,p_fun);
函数指针数组即存放函数指针的数组,是一个数组,可以去存放一些参数类型相同,返回值类型相同的函数指针
int (*pa[4])(int,int)
//pa是内部可以存放四个函数指针,每个函数指针所指向的参数均为两个整形,返回值均为整型
应用案例(简易计算器)
#include
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.+ 2.- ********\n");
printf("*******3.* 4./ ********\n");
printf("******* 0.exit ********\n");
printf("******************************\n");
}
int main()
{
int(*pa[4])(int, int) = { Add,Sub,Mul,Div };
int n;
do
{
menu();
printf("请选择>");
//选择计算类型
scanf("%d", &n);
if (n >= 1 && n <= 4)
{
int x, y;
printf("请输入两个整数:");
//需要计算的数
scanf("%d%d", &x, &y);
printf("%d\n", pa[n-1](x, y));
//进行计算并输出结果
}
else if (n == 0)
printf("退出\n");
else
printf("选择错误\n");
} while (n);
return 0;
}
(了解)指向函数指针数组的指针(持续套娃)
//函数指针数组
int(*parr[10])(int, int);
//存放函数指针数组的指针
int(*(*ppadd)[10])(int, int) = &parr;
//padd表示一个指针,它所指向的地址是一个数组,该数组有十个元素,
//每个元素的类型是函数指针,函数指针指向的函数有两个整型参数,返回类型为整型
1.qsort函数的使用
qsort可以对任何类型的数据进行排序,在使用qsort函数时需要引头文件stdlib.h;同时,qsort函数使用时有四个参数 数组名、需要排序的个数、数据类型所占的字节数、compare函数(需要自己定义)
_ACRTIMP void __cdecl qsort(
_Inout_updates_bytes_(_NumOfElements * _SizeOfElements) void* _Base,
_In_ size_t _NumOfElements,
_In_ size_t _SizeOfElements,
_In_ _CoreCrtNonSecureSearchSortCompareFunction _CompareFunction
);
案列,对整型数据进行排序
#include
#include
int cmp_int(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;
}
//此函数的作用是为了判断在使用qsort函数时,qsort函数对该函数的回调目的是为了判断所传地址指向
//的两个数的大小,如果第一个数大于给第二个数,返回小于零的数,相等返回0,小于返回小于零的数
//因此使用return *(int*)e1 - *(int*)e2可完美实现。
int main()
{
int arr[] = { 12,121,34,2,33,45,87,91,21,12 };
int sz = sizeof(arr) / sizeof(arr[0]);
//判断元素个数
qsort(arr, sz, sizeof(int), cmp_int);
return 0;
}
对于结构体类型也可使用
案例目的,根据年龄大小进行排序
#include
#include
struct stu
{
char namr[30];
int age;
};
int cmp_age(const void* e1, const void* e2)
{
return ((((struct stu*)e1)->age) - (((struct stu*)e2)->age));
//若需要对名字按照字典序进行排列,只需要将情形转化为结构体类型的指针指向name部分
}
int main()
{
struct stu s[3] = { {"zhangsan",13},{"lisi",15},{"wangwu",12} };
int sz = sizeof(s) / sizeof(s[0]);
qsort(s, sz, sizeof(s[0]), cmp_age);
return 0;
}
2.模拟实现该函数(排序方法:冒泡排序)
#include
struct stu
{
char name[20];
int age;
};
int cmp_int(const void* e1, const void*e2)
{
return *((int*)e1) - *((int*)e2);
}
int cmp_age(const void* e1, const void* e2)
{
return ((((struct stu*)e1)->age) - (((struct stu*)e2)->age));
}
void swap(char* buf1,char*buf2,int wide)
//由于不知道两个元素的类型,需要知道数组元素的宽度,每次只交换一个字节,交换宽度次
{
for (int i = 0; i < wide; i++)
{
char tmp = *buf1;
*buf1 = *buf2;
*buf2 = tmp;
buf1++;
buf2++;
}
}
void bubble_sort(void* base, int sz, int wide,int(*cmp)(void* e1,void* e2))
//用void* 指针可以接受任何类型的数据,用一个函数指针接受不同类型数据的比较方法函数
{
for (int i = 0; i < sz - 1; i++)
//判断趟数
{
for (int j = 0; j < sz - i - 1; j++)
//每个元素需要与其他元素比较的次数
{
if (cmp(((char*)base + j * wide), ((char*)base + (j + 1)*wide)) > 0)
//由于不知道所接受元素的类型,不能够直接对地址进行处理,所以将base强行转化为字符指针类型
//字符数据只占一个字节,又知道数组元素的宽度,通过这种方法,可以找到下一个元素,进行比较
{
swap(((char*)base + j * wide), ((char*)base + (j + 1)*wide),wide);
//如果满足条件,交换两个元素
}
}
}
}
void test()
{
int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr,sz,sizeof(arr[0]),cmp_int);
}
void test1()
{
struct stu s[3] = { {"zhangsan",12},{"lisi",11},{"wangwu",13} };
int sz = sizeof(s) / sizeof(s[0]);
bubble_sort(s, sz, sizeof(s[0]), cmp_age);
}
int main()
{
test1();
test();
return 0;
}
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。