演示qsort函数的使用
qsort函数是快速排序函数,它是一个库函数。学习qsort函数的使用前,我们先回顾一下冒泡排序。
//冒泡排序 #include <stdio.h> void bubble_sort(int arr[],int sz) { int i = 0; int j = 0; //冒泡排序的趟数 for (i = 0; i < sz - 1; i++) { //一趟冒泡排序 for (j = 0; j < sz - 1 - i; j++) { int temp = 0; if (arr[j] > arr[j + 1]) { temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } } void print_arr(int arr[], int sz) { int i; for (i = 0; i < sz; i++) { printf("%d ", arr[i]); } printf("\n"); } int main() { //升序 int arr[10] = { 9,8,7,6,5,4,3,2,1,0 }; int sz = sizeof(arr) / sizeof(arr[0]); printf("排序前\n"); print_arr(arr, sz); bubble_sort(arr, sz); printf("排序后\n"); print_arr(arr, sz); return 0; }
那冒泡排序跟我们要学的qsort函数有什么关系吗?其实qsort函数就能帮助我们快速实现升序排序,而且还能对字符串数组和结构体数据进行排序。但是在这里,我们只需要知道怎么使用qsort函数就行了,并不需要它是怎么来的。
前置知识
介绍qsort函数前,需要先解释一个东西 - void* 。平常,我们定义一个 XX 类型的变量,就必须使用 XX 类型的指针来指向变量的地址。而void* 类型,就可以很好地接收各种类型变量的地址并存储起来。不恰当的比喻:void* 就像一个 “垃圾桶” ,什么都可以装。
注意事项:1.void*类型 —— ⽆类型,不能进⾏解引⽤操作。因为不能确定访问⼏个字节
(指针的类型确定访问的字节⼤⼩) 2.void*类型,不能进⾏+、-整数的操作,因为不能确定⼀步⾛⼏个字节(指针的类型确定步⻓)只有将void*类型的指针强制类型转换才能进行解引用操作和+-整数。
使用qsort函数排序整型数组(升序)
#include <stdio.h> #include <stdlib.h> //使用qsort函数需要包含头文件stdlib.h void print_arr(int arr[], int sz) { int i; for (i = 0; i < sz; i++) { printf("%d ", arr[i]); } printf("\n"); } int cmp_int(const void* e1, const void* e2) { return *(int*)e1 - *(int*)e2; //因为待排序的数据是整型,所以将e1和e2强制类型转换为整型指针 //只有这样才能进行解引用操作和+-整数 } int main() { int arr[10] = { 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); return 0; }
使用qsort函数排序结构体数据(年龄)
#include <stdio.h> #include <stdlib.h> struct Stu { char name[20]; int age; }; //定义结构体 int cmp_s_age(const void* e1, const void* e2) { return (((struct Stu*)e1)->age - ((struct Stu*)e2)->age); } //将e1和e2强制类型转换为struct Stu* int main() { //使用qsort函数排序结构体数据 struct Stu s[3] = { {"zhangsan",18},{"lisi",30},{"wangwu",20} }; int sz = sizeof(s) / sizeof(s[0]); //按照年龄来排序 qsort(s, sz, sizeof(s[0]), cmp_s_age); for (int i = 0; i < sz; i++) { printf("%s:%d\n", s[i].name, s[i].age); //打印数据 } return 0; }
使用qsort函数排序结构体数据(名字)
#include <stdio.h> #include <stdlib.h> #include <string.h> struct Stu { char name[20]; int age; }; int cmp_s_name(const void* e1, const void* e2) { return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name); //strcmp函数可以比较字符串的大小,使用它需要引头文件string.h } int main() { //使用qsort函数排序结构体数据 struct Stu s[3] = { {"zhangsan",18},{"lisi",30},{"wangwu",20} }; int sz = sizeof(s) / sizeof(s[0]); //按照名字来排序 qsort(s, sz, sizeof(s[0]), cmp_s_name); for (int i = 0; i < sz; i++) { printf("%s:%d\n", s[i].name, s[i].age); } return 0; }
注意:如果想要使用qsort函数实现降序排序的话,可以在return后面多加一个负号也可以将e1和e2的位置对调
其实,qsort函就也是一个回调函数的应用。
10.自定义bubble_qsort函数
#include <stdio.h> #include <stdlib.h> #include <string.h> struct Stu { char name[20]; int age; }; int cmp_int(const void* e1, const void* e2) { return *(int*)e1 - *(int*)e2; } int cmp_by_age(const void* e1, const void* e2) { return (((struct Stu*)e1)->age - ((struct Stu*)e2)->age); } int cmp_by_name(const void* e1, const void* e2) { return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name); } void Swap(char* buf1, char* buf2, int width) { //因为数据在内存是一个字节一个字节存储的,所以我们将两个元素的全部字节 //交换了,这两个元素就完成交换了 int i = 0; for (i = 0; i < width; i++) //width是一个元素的大小,单位是字节 { //交换一个字节 char temp = *buf1; *buf1 = *buf2; *buf2 = temp; //字符指针自加,准备交换下一个字节 buf1++; buf2++; } } //模仿qsort函数实现一个冒泡排序的通用算法 void bubble_qsort(void* base, int sz, int width, int (*cmp)(const void* e1, const void* e2)) { int i = 0; //趟数 for (i = 0; i < sz; i++) { //一趟的排序 int j = 0; for (j = 0; j < sz - 1 - i; j++) { //两个元素比较 //类比arr[j]和arr[j+1] if (cmp((char*)base+j*width,(char*)base+(j+1)*width) > 0) { //交换 Swap((char*)base + j * width, (char*)base + (j + 1) * width,width); } } } } void test1() { //整型数据的排序 int arr[10] = { 1,3,5,7,9,0,2,4,6,8 }; int sz = sizeof(arr) / sizeof(arr[0]); //排序 bubble_qsort(arr, sz, sizeof(arr[0]), cmp_int); //打印 for (int i = 0; i < sz; i++) { printf("%d ", arr[i]); } } void test2() { //使用qsort函数排序结构体数据 struct Stu s[3] = { {"zhangsan",18},{"lisi",30},{"wangwu",20} }; int sz = sizeof(s) / sizeof(s[0]); struct Stu* p = s;//p为结构体指针 //按照年龄来排序 //bubble_qsort(s, sz, sizeof(s[0]), cmp_by_age); //按照名字来排序 bubble_qsort(s, sz, sizeof(s[0]), cmp_by_name); for (int i = 0; i < sz; i++) { //结构体数据的两种打印方法 //printf("%s:%d\n", s[i].name, s[i].age); printf("%s:%d\n", (p+i)->name, (p+i)->age); } } int main() { //test1(); test2(); }
分析 (这样可以看一下,可以帮助你理解上面的代码)
输出结果
11.指针和数组笔试题解析
为了答案的统一,下面的这些代码都是在32位平台下运行的。因为在不同的平台下运行,指针的大小会出现结果不统一。但如果32位平台的结果和64位平台的结果不一样的话,我会将两个结果都写出来。
在前面,我们已经学过数组名的意义:1.sizeof(数组名),数组名表示整个数组,计算的是整个数组的大小;2.&数组名,数组名表示整个数组,取出的是整个数组的大小;3.除此之外,所有的数组名都表示数组首元素的地址。这个知识将会帮助我们分析下面的代码。
总结:数组名的意义 1.sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
2.&数组名,这里的数组名表示整个数组,取出的是整个数组的地址。 3.除此之外所以的数组名都表示数组首元素的地址
12.指针笔试题
笔试题1
#include <stdio.h> int main() { int a[5] = { 1, 2, 3, 4, 5 }; int* ptr = (int*)(&a + 1);//将数组指针强制类型转换为整型指针 printf("%d,%d", *(a + 1), *(ptr - 1)); return 0; }
笔试题2
注意:在32位平台下,结构体的大小为20个字节;在64位平台下,结构体的大小为32个字节。该程序是在32位平台下运行的。
#include <stdio.h> //由于还没学习结构体,这里告知结构体的大小是20个字节 struct Test { int Num; char* pcName; short sDate; char cha[2]; short sBa[4]; }*p; //假设p 的值为0x100000。 如下表表达式的值分别为多少? //已知,结构体Test类型的变量大小是20个字节 int main() { printf("%p\n", p + 0x1); printf("%p\n", (unsigned long)p + 0x1); printf("%p\n", (unsigned int*)p + 0x1); return 0; }
笔试题3
#include <stdio.h> int main() { int a[4] = { 1, 2, 3, 4 }; int* ptr1 = (int*)(&a + 1); int* ptr2 = (int*)((int)a + 1); printf("%x,%x", ptr1[-1], *ptr2); return 0; }
笔试题4
#include <stdio.h> int main() { int a[3][2] = { (0, 1), (2, 3), (4, 5) }; int* p; p = a[0]; printf("%d", p[0]); return 0; }
笔试题5
#include <stdio.h> int main() { int a[5][5]; int(*p)[4]; p = a; printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]); return 0; }
笔试题6
#include <stdio.h> int main() { int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int* ptr1 = (int*)(&aa + 1); int* ptr2 = (int*)(*(aa + 1)); printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1)); return 0; }
笔试题7
#include <stdio.h> int main() { char* a[] = { "work","at","alibaba" }; char** pa = a; pa++; printf("%s\n", *pa); return 0; }
笔试题8
#include <stdio.h> int main() { char* c[] = { "ENTER","NEW","POINT","FIRST" }; char** cp[] = { c + 3,c + 2,c + 1,c }; char*** cpp = cp; printf("%s\n", **++cpp); printf("%s\n", *-- * ++cpp + 3); printf("%s\n", *cpp[-2] + 3); printf("%s\n", cpp[-1][-1] + 1); return 0; }
以上就是C语言指针的全部内容了。如果你能够弄懂上面的知识的话,相信你对C语言的指针已经掌握了差不多了。这篇内容断断续续写了6天,终于写完了,真的没想到自己居然写完了。真的很不容易,希望大家可以点个赞支持一下,谢谢大家了!!!