回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一
个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该
函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或
条件进行响应。
int Add(int x, int y)
{
return x + y;
}
void Calc(int (*pf)(int, int))
{
int ret = pf(3, 5);
printf("%d\n", ret);
}
int main()
{
Calc(Add);
return 0;
}
void qsort(void* base,
size_t num, // 待排序的元素个数
size_t width, // 一个元素的大小,单位是字节
int(* cmp)(const void* e1, const void* e2)
// cmp指向的是:排序时,用来比较2个元素的函数
);
void* - 无具体类型的指针
能够接收任意类型的地址
缺点:不能进行运算,不能±整数,不能解引用
#include
#include
#include
int cmp_int(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;
}
void print_arr(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
void test1()
{
int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp_int);
print_arr(arr, sz);
}
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 cmp_by_age(const void* e1, const void* e2)
{
return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
void printStuName(struct Stu* s, int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%s : %d\n", s->name, s->age);
s++;
}
}
void printStuAge(struct Stu* s, int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%s : %d\n", s->name, s->age);
s++;
}
}
void test2()
{
struct Stu s[3] = { {"张三", 15}, {"李四", 30}, {"王五", 10} };
int sz = sizeof(s) / sizeof(s[0]);
// 按照名字排序
qsort(s, sz, sizeof(s[0]), cmp_by_name);
printStuName(s, sz);
// 李四: 30
// 王五 : 10
// 张三 : 15
printf("\n");
// 按照年龄排序
qsort(s, sz, sizeof(s[0]), cmp_by_age);
printStuAge(s, sz);
// 王五 : 10
// 张三 : 15
// 李四 : 30
}
int main()
{
// 测试排序整型
//test1();
// 测试排序结构体数据
test2();
return 0;
}
int cmp_int(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;
}
void print_arr(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
void Swap(char* buf1, char* buf2, int width)
{
int i = 0;
for (i = 0; i < width; i++)
{
char tmp = *buf1;
*buf1 = *buf2;
*buf2 = tmp;
buf1++; //以字节交换
buf2++;
}
}
// 使用回调函数实现一个通用的冒泡排序函数
void BubbleSort(void* base, size_t num, size_t width, int (*cmp)(const void* e1, const void* e2))
{
size_t i = 0;
// 趟数
for (i = 0; i < num - 1; i++)
{
size_t j = 0;
for (j = 0; j < num - 1 - i; j++)
{
// 比较
if (cmp((char*)base+j*width, (char*)base+(j+1)*width) > 0)
{
// 交换
Swap((char*)base+j*width, (char*)base+(j+1)*width, width);
}
}
}
}
// 测试自定义的BubbleSort();
void test3()
{
int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
BubbleSort(arr, sz, sizeof(arr[0]), cmp_int);
print_arr(arr, sz);
}
// 测试自定义的BubbleSort() 排序结构体
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);
}
void test4()
{
struct Stu s[3] = { {"张三", 15}, {"李四", 30}, {"王五", 10} };
int sz = sizeof(s) / sizeof(s[0]);
// 按照名字排序
qsort(s, sz, sizeof(s[0]), cmp_by_name);
// 按照年龄排序
//qsort(s, sz, sizeof(s[0]), cmp_by_age);
}
int main()
{
//test3();
test4();
return 0;
}
Gitee 9_2
#include
int main()
{
int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(a)); // 数组名a单独放在sizeof内部,数组名表示整个数组,计算的是整个数组的大小
printf("%d\n", sizeof(a + 0)); // a表示首元素的地址,a+0还是首元素的地址,地址的大小就是4/8字节
printf("%d\n", sizeof(*a)); // a表示首元素的地址,*a就是首元素 --> a[0],大小就是4
// *a <==> *(a+0) <==> a[0]
printf("%d\n", sizeof(a + 1)); // a表示首元素的地址,a+1是第二个元素的地址,大小就是4/8
printf("%d\n", sizeof(a[1])); // a[1] 就是第二个元素,4
printf("%d\n", sizeof(&a)); // &a - 数组的地址 - 4/8 - int(*)[4]
printf("%d\n", sizeof(*&a)); // *&a - &a是数组的地址,对数组的地址解引用拿到的是的数组,所以大小是16
// printf("%d\n", sizeof(&a)); // 16
printf("%d\n", sizeof(&a + 1)); // &a是数组的地址,&a+1是数组的的地址+1,跳过整个数组,虽然跳过了数组,还是地址 4/8
printf("%d\n", sizeof(&a[0])); // 首元素地址 4/8
printf("%d\n", sizeof(&a[0] + 1)); // 第二个元素的地址 4/8
return 0;
}
int main()
{
//字符数组
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", sizeof(arr)); // 6 单独放在sizeof 整个数组
printf("%d\n", sizeof(arr + 0)); // 4/8 首元素地址
printf("%d\n", sizeof(*arr)); // 1 char*解引用一字节
printf("%d\n", sizeof(arr[1])); // 1
printf("%d\n", sizeof(&arr)); // 4/8
printf("%d\n", sizeof(&arr + 1)); // 4/8 跳过整个数组,还是地址
printf("%d\n", sizeof(&arr[0] + 1)); // 4/8 b的地址
// strlen - 库函数
// 求字符串长度
// sizeof - 操作符,单位字节
// 求变量所占空间的大小
// 求类型创建的变量所占空间的大小
printf("%d\n", strlen(arr)); // 随机值
printf("%d\n", strlen(arr + 0)); // 随机值-1
printf("%d\n", strlen(*arr)); // err
// *arr - 'a' - 97 strlen以为传进来的'a'得ascii码值就是地址
printf("%d\n", strlen(arr[1])); // err 'b' - 98
printf("%d\n", strlen(&arr)); // 随机值
// &arr类型是char(*)[6],strlen的类型是char*,类型不匹配,但还是指向首元素,值一样,类型转换a的地址往后找
printf("%d\n", strlen(&arr + 1)); // 随机值-6
printf("%d\n", strlen(&arr[0] + 1)); // 随机值-1
return 0;
}
int main()
{
char arr[] = "abcdef";
printf("%d\n", sizeof(arr)); // 7
printf("%d\n", sizeof(arr + 0)); // 4/8 arr是首元素地址
printf("%d\n", sizeof(*arr)); // 1 arr是首元素的地址,*arr就是首元素
printf("%d\n", sizeof(arr[1])); // 1 第二个元素
printf("%d\n", sizeof(&arr)); // 4/8 数组的地址,数组的地址也是地址
printf("%d\n", sizeof(&arr + 1)); // 4/8 &arr是数组的地址,&arr+1是跳过整个数组后的地址
printf("%d\n", sizeof(&arr[0] + 1)); // 4/8 第二个元素的地址
printf("%d\n", strlen(arr)); // 6
printf("%d\n", strlen(arr + 0)); // 6 首元素地址+0还是首元素地址
printf("%d\n", strlen(*arr)); // err
printf("%d\n", strlen(arr[1])); // err
printf("%d\n", strlen(&arr)); // 6 类型不匹配,但还是以char*找
printf("%d\n", strlen(&arr + 1)); // 随机值
printf("%d\n", strlen(&arr[0] + 1)); // 5
return 0;
}
- sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。(大小是字节)
- & 数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
- 除此之外所有的数组名都表示首元素的地址。
int main()
{
const char* p = "abcdef";
printf("%d\n", sizeof(p)); // 4/8 p是一个指针变量,存放地址
printf("%d\n", sizeof(p + 1)); // p类型chat* 加1跳过1字节 指向字符b的地址
printf("%d\n", sizeof(*p)); // 1 通过地址找到a
printf("%d\n", sizeof(p[0])); // 1 p[0]--> *(p+0)--> *p
printf("%d\n", sizeof(&p)); // 4/8 取出指针p的地址
printf("%d\n", sizeof(&p + 1)); // 4/8
// &p 一级指针&是二级指针
// char** ptr = &p
// &p+1
// ptr+1
// 跳过p指向指针变量p后面,还是地址
printf("%d\n", sizeof(&p[0] + 1)); // 4/8 b的地址
printf("%d\n", strlen(p)); // 6
printf("%d\n", strlen(p + 1)); // 5
printf("%d\n", strlen(*p)); // err
printf("%d\n", strlen(p[0])); // err
printf("%d\n", strlen(&p)); // 随机值
// 指针变量的地址,不是字符串首元素的地址
printf("%d\n", strlen(&p + 1)); // 随机值
// 与&p没有关系,因为p里的4字节可能有\0 提前结束
printf("%d\n", strlen(&p[0] + 1)); // 5
return 0;
}
int main()
{
//二维数组
int a[3][4] = { 0 };
printf("%d\n", sizeof(a)); // 48
printf("%d\n", sizeof(a[0][0])); // 4 第一行第一列元素 int
printf("%d\n", sizeof(a[0])); // 16 a[0]第一行数组名,数组名单独放在sizeof
printf("%d\n", sizeof(a[0] + 1)); // 4/8
// a[0]是第一行的数组的数组名,数组名没有单独放在sizeof,也没有&,所以arr[0]是首元素的地址
// 第一行数组第一个元素的地址
// a[0] + 1 就是第一行,第二个元素的地址
printf("%d\n", sizeof(*(a[0] + 1))); // 4 第一行第二个元素 int
printf("%d\n", sizeof(a + 1)); // 4/8
// 数组名a,没有单独放在sizeof内部,也没有&,所以a表示首元素(第一行)的地址
// 二维数组的数组名,表示首元素(第一行)的地址
// +1 跳过一行 指向第二行地址
// a+1 --> &a[1]
printf("%d\n", sizeof(*(a + 1))); // 16 a首元素地址,第一行地址+1第二行地址,第二行地址* 就是第二行
// *(a + 1)) - 第二行数组名 单独放在sizeof 整个数组大小 16
printf("%d\n", sizeof(&a[0] + 1)); // 4/8
// a[0]第一行数组名
// 数组名没有单独放在sizeof内部,但是&,表示首元素地址
// &a[0] 拿到的是第一行的地址
// &a[0]+1 就是第二行的地址
printf("%d\n", sizeof(*(&a[0] + 1))); // 16 第二行的地址* 拿到第二行
// *(&a[0] + 1) --> *(&a[1]) --> a[1]
// 数组名单独放在sizeof内部求大小
printf("%d\n", sizeof(*a)); // 16
// a表示首元素(第一行)的地址
// *a - 第一行 - 第一行的数组名
// *a --> *(a+0) --> a[0]
printf("%d\n", sizeof(a[3])); // 16
// sizeof内部表达式不参与运算
// a[3]假设存在,就是第四行的数组名,sizeof(a[3])就相当于把第四行
// 的数组名单独放在sizeof内部
return 0;
}
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int* ptr = (int*)(&a + 1);
// int(*)[5] --强制类型转换 (int*)
// ptr指向5后 减1减一个int 指向5 解引用取出5
printf("%d,%d", *(a + 1), *(ptr - 1));
// *(a + 1) -- 数组名表示首元素地址,+1第二个元素地址,*是第二个元素
// 2 5
return 0;
}
//由于还没学习结构体,这里告知结构体的大小是20个字节
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
struct Test
{
int Num;
char* pcName;
short sDate;
char cha[2];
short sBa[4];
}*p; // 结构体指针
int main()
{
// p的类型是struct Test* -- 0x100000的类型是int
// 强制类型转换成结构体指针类型
p = (struct Test*)0x100000;
printf("%p\n", p + 0x1); //0x1 十六进制1就是1
// 结构体指针+1 跳过一个结构体 大小20
// 0x00100014
// 1*16^1 + 4*16^0 = 20
printf("%p\n", (unsigned long)p + 0x1);
// p强制类型转化成整型
// 整型+1
// 0x100000 + 1
// 转化成10进制计算 1*16^5 + 1
printf("%p\n", (unsigned int*)p + 0x1);
// 强制类型转化成整型指针,整型指针+1,跳过一个整型4字节
// 0x00100004
// 00100014 00100001 00100004
// %#xp 默认打印0x
return 0;
}
#include
int main()
{
// 0-255
unsigned char a = 200;
// 00000000000000000000000011001000
// 11001000 - a 存到a、b中截断
unsigned char b = 100;
// 00000000000000000000000001100100
// 01100100 - b
unsigned char c = 0;
c = a + b;
// 计算:整型提升
// 无符号数,直接补0
// 00000000000000000000000011001000 - a
// 00000000000000000000000001100100 - b
// 00000000000000000000000100101100 - a+b
// 00101100 - c 存进c截断
printf("%d %d", a + b, c);
// c按%d补码打印 整型提升
// c是unsigned char 补0
// 00000000000000000000000000101100 - %d打印c 正数原反补相同
// 44
// a+b的值没有存进c,没有发生截断,直接以%d打印
// 00000000000000000000000100101100 - a+b
// 正数原反补相同
// 300
return 0;
}