qsort函数:
可以排序任意类型的数据
这里的返回值是 e1-e2 ,在比较函数内部是先把无类型指针,转化成其原本的类型之后才操作的。
这里的返回值 e2-e1
结构数据排序
模拟qsort函数的实现,很不熟练,可以消化的点还是蛮多的,一个一个字节交换的那个循环再写一个函数好一点,还有用 flag 来判断原序列是否有序这个点感觉也很不错
#include <stdio.h>
//数据交换
void swap(char*buf1,char*buf2,int width)
{
int i = 0;
char k = 0;
for (i = 0;i < width; i++)
{
k = *buf1;
*buf1 = *buf2;
*buf2 = k;
buf1++;
buf2++;
}
}
//模拟qsort函数的功能
void bubble_qsort(void* base,int sz,int width,int (*cmp)(const void* e1, const void* e2) )//注意最后函数指针变量cmp的位置
{
int i = 0;
for (i = 0; i < sz; i++)
{
int j = 0;
int flag= 1;//假设本来就是有序
for (j = 0; j < sz - i - 1; j++)//这里要-1,不然会栈溢出,老是忘
{
if ( (*cmp) ( (char*)base+j*width, (char*)base+(j+1)*width )>0 )
//j*width是为了找到对应的地址,因为这里是不知道指针所指对象的类型的,但可以通过width跳过一个元素的字节,从而找到下一次元素的首地址
{
//int n = 0;
//for (n = 0; n < width; n++)//这个循环也是因为不知道指向类型,所以就直接一个字节一个字节的进行交换了
//{
// char k = *((char*)base + j * width + n);
// *((char*)base + j * width + n) = *((char*)base + (j + 1) * width + n);
// *((char*)base + (j + 1) * width+n) = k;
//}
flag = 0;//进入if语句了,表示顺序错误
}
}
if (flag)//如果第一次大循环就没有进入if语言,表示假设成立,数据有序,之后不再运行,break直接跳出循环
break;
}
}
//结构体
struct stu
{
char name[20];
int age;
};
//比较函数
int cmp_stu_by_name(const void* e1, const void* e2)
{
return ((struct stu*)e1)->age - ((struct stu*)e2)->age;
}
int main()
{
struct stu s[3] = { {"zhang",10},{"li",11},{"gao",12} };
int sz = sizeof(s) / sizeof(s[0]);
bubble_qsort(s,sz,sizeof(s[0]), cmp_stu_by_name);//头文件 stdlib.h
int i = 0;
for(i=0;i<3;i++)
{
printf("%-7s %d\n", &s[i].name, s[i].age);
}
return 0;
}
字符串函数:
1、strlen
sieze_t strlen (const char* str);
字符串以 ' \0 ' 作为结束标志,strlen函数返回的是在字符串中 ‘ \0 ’ 前面出现的字符个数(不包括空白符)。
参数指向的字符串必须要以 ' \0 ' 结束。
注意strlen函数的返回值为size_t,是无符号整型.
模拟实现:
//1、计数器方法
int my_strlen1(char* arr)
{
int count = 0;
while (*arr != '\0')
{
count++;
arr++;
}
return count;
}
//2、指针-指针
int my_strlen2(char* arr)
{
char* beg = arr;
while (*arr != '\0')
{
arr++;
}
return arr - beg;
}
//3、递归的方式
int my_strlen3(char* arr)
{
if (*arr != '\0')
return 1 + my_strlen3(arr+1);
else
return 0;
}
int main()
{
char arr[] = "abcdef";
int ret = my_strlen1(arr);
printf("%d\n", ret);
ret = my_strlen2(arr);
printf("%d\n", ret);
ret = my_strlen3(arr);
printf("%d\n", ret);
return 0;
}
2、strcmp
char* strcpy (char* destination,const char* source);
strcpy函数是有返回值的,返回的是目标的首地址。
再次提醒,字符串是有值的,值是首字符的地址,下面这个也间接说明的了,strcpy会拷贝空白符。
源字符串必须以 ' \0 ' 结束;
会将源字符串中的 ' \0 ' 拷贝到目标空间;
目标空间必须足够大,以确保能存放源字符串;这里实际上也会运行,但会报错,应该是栈溢出。
目标空间必须可变;
这个就是属于目标空间不可变的情况,p指向的是常量a的地址,而常量是没有办法修改的。
模拟实现strcpy
#include <assert.h>
//普通版
char* my_strcpy1(char* dest,const char* src)//因为源字符串不需要改变,所以可以用 const 修饰一下,防止意外情况
{
char* ret = dest;//保留首地址,用以返回
while (*src != '\0')
{
*dest = *src;
dest++;
src++;
}
*dest = *src;//这里是赋值 '\0' ;
return ret;
}
//高级版
char* my_strcpy2(char* dest, const char* src)
{
assert(dest && src);//防止空指针
char* ret = dest;
while (*dest++=*src++)//这里的判断条件是被赋值后的*dest,之所以可以作为判断条件是因为 \0 也要赋值,赋值之后*dest的值就变成 0 ,然后退出循环
{
;
}
return ret;
}
int main()
{
char arr[20] = { 0 };
my_strcpy1(arr, "abcdef");
printf("%s\n", arr);
my_strcpy2(arr, "abcdefgh");
printf("%s\n", arr);
return 0;
}