回调函数qsort模拟实现
这里不使用快排, 使用冒泡排序模拟实现qsort
冒泡排序排序数组使其升序
void bubble_sort(int* arr, int sz){
for (int i=0; i<sz-1; i++) {
for (int j=0; j<sz-1-i; j++) {
if(arr[j]>arr[j+1]){
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = arr[j];
}
}
}
}
中间交换的功能封装在函数swap中
void swap(int* a, int* b){
int temp = *a;
*a = *b;
*b = temp;
}
void bubble_sort(int* arr, int sz){
for (int i=0; i<sz-1; i++) {
for (int j=0; j<sz-1-i; j++) {
if(arr[j]>arr[j+1]){
swap(&arr[j], &arr[j+1]);
}
}
}
}
模拟定义qsort
首先是参数,
void bubble_sort(void* base, size_t num, size_t size, int sz){
还需要compare函数
void bubble_sort(void* base, size_t num, size_t size, int (*compare)(const void*, const void*))
最后一个参数*compare为函数指针,指向compare函数, 函数类型为int (const void*, const void*)
比较函数
if(arr[j]>arr[j+1]){
将这一段替换为compare
因为compare函数参数为const void* ,为指针类型,存储的地址,那么,将原来起始位置的地址跳跃到size处的地址,将里面的值取出来, 因为char*的指针每次跳一个字节,char*指针类型本来位置的base加上j个size, 也就是j*size, ,跳这么多的地址,将其中的值取出来比较
代码如下
if(compare((char*)base+j*size, (char*)base+(j+1)*zise))
那么同理, swap交换函数也应该是参数为char*垃圾桶指针(在if中的compare函数参数也转换为了char*), 里面的交换中间类型为char*, 有多少字节交换多少次(char*每次跳一个字节), 那么交换size次
void swap(char* a, char* b, size_t size){
for (int i=0; i<size; i++) {
char temp = *a;
*a = *b;
*b = temp;
a++;
b++;
}
}
自定义的比较函数
int compare_arr(const void* a, const void* b){
return *(int*)a - *(int*)b;
}
为什么有两个指针呢,因为参数传的是指针,就是地址,强转为(int*)类型的指针后, 再试用*解引用
if(compare((char*)base+j*size, (char*)base+(j+1)*size)){
swap((char*)base+j*size, (char*)base+(j+1)*size, size);
}
且qsort中的compare函数
返回值<0, 那么让p1在p2前面,此处我们是使用前者的值减去后者的值p1-p2<0, 那么p1<p2
因此想实现升序, 当返回值大于0时才执行交换函数就能模拟此处的功能了
if(compare((char*)base+j*size, (char*)base+(j+1)*size)>0){
swap((char*)base+j*size, (char*)base+(j+1)*size, size);
}
swap传参
使用模拟实现的qsort函数实现结构体排序
最终代码
#include <stdio.h>
#include <string.h>
struct Stu {
char name[20];
int age;
};
void swap(char* a, char* b, size_t size){
for (int i=0; i<size; i++) {
char temp = *a;
*a = *b;
*b = temp;
a++;
b++;
}
}
int compare_arr(const void* a, const void* b){
return *(int*)a - *(int*)b;
}
int compare_by_name(const void* a, const void* b){
return strcmp(((struct Stu*)a)->name, ((struct Stu*)b)->name);
}
int compare_by_age(const void* a, const void* b){
return ((struct Stu*)a)->age - ((struct Stu*)b)->age;
}
void bubble_sort(void* base, size_t num, size_t size, int (*compare)(const void*, const void*)){
for (int i=0; i<num-1; i++) {
for (int j=0; j<num-1-i; j++) {
if(compare((char*)base+j*size, (char*)base+(j+1)*size)>0){
swap((char*)base+j*size, (char*)base+(j+1)*size, size);
}
}
}
}
void print(int* arr, int sz){
for (int i=0; i<sz; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
void print_stu(struct Stu* p, int sz){
for (int i=0; i<sz; i++) {
printf("%s %d\n", p[i].name, p[i].age);
}
printf("\n");
}
void test1(void){
int arr[] = {9,8,7,6,5,4,3,2,1,0};
int sz = sizeof(arr)/sizeof(arr[0]);
print(arr, sz);
bubble_sort(arr, sz, sizeof(arr[0]), compare_arr);
print(arr, sz);
}
void test2(void){
struct Stu arr[] = {{"lihua", 20}, {"ahua", 39}, {"bhua", 30}};
int sz = sizeof(arr)/sizeof(arr[0]);
print_stu(arr, sz);
// bubble_sort(arr, sz, sizeof(arr[0]), compare_by_name);
bubble_sort(arr, sz, sizeof(arr[0]), compare_by_age);
print_stu(arr, sz);
}
int main(int argc, const char * argv[]) {
// test1();
test2();
return 0;
}
指针和数组笔试题解析
一维数组
int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a+0));
printf("%d\n",sizeof(*a));
printf("%d\n",sizeof(a+1));
printf("%d\n",sizeof(a[1]));
printf("%d\n",sizeof(&a));
printf("%d\n",sizeof(*&a));
printf("%d\n",sizeof(&a+1));
printf("%d\n",sizeof(&a[0]));
printf("%d\n",sizeof(&a[0]+1));
-
sizeof(a)
//4*4 = 16 数组名单独放在sizeof内部, 数组名表示整个数组 计算的是整个数组的大小, 单位是字节
-
sizeof(a+0)
// 4/8 数组名并非单独放在sizeof内部 没有& 所以数组名表示首元素地址, 那么a+0仍然是首元素地址,是地址大小就是4/8字节(32位是4,64位是8)
-
sizeof(*a)
// 4BYTE a并非单独放进sizeof, 也没有&,所以数组名是数组首元素的地址, 解引用后是数组首元素,即*a == *(a+0) == a[0] ,因此是一个int字节大小, 为4字节
-
sizeof(a+1)
//4/8 a并非单独放在sizeof内部,也没有&, 所以数组名是数组首元素的地址, 是地址就是4/8Byte, 这里a+1指向a[1], 即*(a+1) == a[1]
-
sizeof(a[1])
// a[1]就是数组的第二个元素,是int类型,4字节
-
sizeof(&a)
// &数组名表示整个数组地址, 是地址就是4/8字节
-
sizeof(*&a)
// &数组名表示取出的是数组的地址, 解引用表示整个数组的大小,也就是等价于sizeof(a),相当于单独放在sizeof中,即为16
-
sizeof(&a+1)
// 取地址数组名,表示取出整个数组的地址,+1以后就是指向数组最后一个元素后面的一个地址,仍然是地址4/8Byte
-
sizeof(&a[0])
// 取地址a[0], 把数组首元素的地址取出来, 是地址,就是4/8Byte
-
sizeof(&a[0]+1)
//取地址数组首元素,往后+1一位,也就是找到了a[1]的地址,地址是4/8Byte
字符数组
sizeof字符数组
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr+0));
printf("%d\n", sizeof(*arr));
printf("%d\n", sizeof(arr[1]));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr+1));
printf("%d\n", sizeof(&arr[0]+1));
-
sizeof(arr)
// 数组名arr单独放在sizeof内部, 计算的是整个数组的大小, 单位是字节, 6*1 = 6
-
sizeof(arr+0)
// 数组名不是单独放在arr中, 表示首元素地址,加0表示首元素地址, 相当于&a[0] ,也就是地址,地址就是4/8Byte
-
sizeof(*arr)
// 解引用数组名, 数组名表示首元素地址,解引用是arr[0]的值,sizeof(char类型)就是1Byte
-
sizeof(arr[1])
// sizeof一个数组元素,一个char类型大小,1Byte
-
sizeof(&arr)
// 数组名加上取地址, 表示取出整个数组的地址,是地址就是4/8By瑞特
-
sizeof(&arr+1)
//&数组名往后+1, 也就是在'\0'地址后面一个,但是终究是地址, 地址就是4/8字节
-
sizeof(&arr[0]+1)
// 对数组首元素取出地址,往后+1,移动char*个单位, 指向arr[1], 是arr[1]的地址,但是仍然是地址,就是4/8Byte
strlen字符数组
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr+0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));
-
strlen(arr)
//strlen求字符串长度, 就是统计'\0'之前的字符数, 但是arr数组没有结束字符, arr单独放在strlen中, 表示首元素的地址, 没有结束字符就是>=6的随机值
-
strlen(arr+0)
// 随机值, arr+0, arr是首元素的地址, arr+0是首元素的地址, arr+0还是首元素的地址
-
strlen(*arr)
// err 对于strlen函数 它的定义是 size_t strlen(const char* a) 参数是指针, 传值是指针,这段直接解引用操作, 传参是字符a, - 97, 97作为地址,直接进行访问,就是非法访问
-
strlen(arr[1])
// 同理 传参b字符,- 98, 非法访问
-
strlen(&arr)
// 取出整个数组的地址, 类型是strlen (*)[6],类型不同于const char*,会报警告⚠️, 但是仍然能正常执行,因为这个数组地址其实也是从第一个数组元素地址开始的 是随机值
-
strlen(&arr+1)
取地址往后数,也就是数组最后一个元素后面的地址,也是随机值
-
strlen(&arr[0]+1)
取出首元素的地址, 往后一位,也是随机值,比前前面也就是5的随机值少1
数组--字符串类型
char arr[] = "abcdef";
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr+0));
printf("%d\n", sizeof(*arr));
printf("%d\n", sizeof(arr[1]));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr+1));
printf("%d\n", sizeof(&arr[0]+1));
char arr[] = "abcdef";
// a b c d e f \0
//0 1 2 3 4 5 6
-
sizeof(arr)
//arr表示整个数组 就是7个元素(包括\0)
-
sizeof(arr+0)
//不是单独的arr 表示首元素地址往后一位,就是地址4/8Byte
-
sizeof(*arr)
//arr解引用,表示取出首元素使用, 就是arr[0] 即char类型的大小,为1Byte\
-
sizeof(arr[1])
//类似于3
-
sizeof(&arr)
//取出数组的地址,只要是地址就是4/8Byte
-
sizeof(&arr+1)
// 取出首元素的地址后移一位, 仍然是地址
-
sizeof(&arr[0]+1)
// 取出首地址元素后移一位, 也是地址