回调函数,字符函数,字符串函数

前言:上一趴我们学习了指针。那么今天我们来学习新的知识,回调函数,字符函数,字符串函数。

1 回调函数

什么是回调函数呢?回调函数就是通过函数指针调用的函数

如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,被调用的函数就是回调函数

2 qsort函数

在这里插入图片描述

在这里插入图片描述

//采用快排的思想,可以排序任意类型的数据
void qsort (void* base,//指向的是一个待排序数组中第一个对象的地址
		    size_t num,//base指向的数组中元素的个数
            size_t size,//待排序数组中每个元素的大小,单位是字节
            int (*compar)(const void* p1,const void* p2));//指向了一个比较两个元素的函数,
            //这个比较函数的返回类型是int,参数的类型是const void*
            //如果p1指向的元素大于p2指向的元素,返回大于0的数字
            //如果p1指向的元素等于p2指向的元素,返回等于0的数字
            //如果p1指向的元素小于p2指向的元素,返回小于0的数字

接下来我们简单的应用一下qsort函数来排序一个整型数组。

#include<stdio.h>
#include<stdlib.h>
//比较函数
int cmp_int(const void* p1, const void* p2)
{
	//void*类型的指针不能直接解引用,先进行强制类型转换再解引用
	return *(int*)p1 - *(int*)p2;
}
int main()
{
	int arr[10] = { 0 };
	//求取数组大小
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	//从标准输入设备上输入数据
	for (i = 0; i < sz; i++)
	{
		scanf("%d", &arr[i]);
	}
	//对arr数组进行排序,默认是升序排序
	qsort(arr, sz, sizeof(int), cmp_int);
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

那我们要如何对结构体数据进行排序呢?这个时候就体现出了qsort函数的重要性了。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//定义一个结构体
struct stu
{
	char name[20];//名字
	int age;//年龄
};
//通过名字进行比较
int cmp_by_name(const void* p1, const void* p2)
{
	//	字符串的比较要使用strcmp函数,包含头文件string.h
	return strcmp(((struct stu*)p1)->name, ((struct stu*)p2)->name);
}
int main()
{
	//结构体数组,进行初始化
	struct stu s[3] = { "zhangsan",20,"wangwu",18,"lisi",25 };
	//结构体的大小
	int sz = sizeof(s) / sizeof(s[0]);
	//对结构体数据进行排序
	qsort(s, sz, sizeof(struct stu), cmp_by_name);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%s,%d\n", s[i].name,s[i].age);
	}
	return 0;
}

3 qsort函数的模拟实现

#include<stdio.h>
int cmp_int(const void* p1, const void* p2)
{
	return *(int*)p1 - *(int*)p2;//这里是以整型数组为例,所以强转
	//成了int*
}
void swap(char* buf1, char* buf2, size_t width)
{
	int i = 0;
	for (i = 0; i < width; i++)//一个字节一个字节交换
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}
void bubble_sort(void* base, //使用void*类型的指针可以接收任意类型
//变量的地址,有可能排序整型类型,也可以排序结构体类型
	size_t num, //待排序数组元素的个数
	size_t size, //待排序数组每个元素的大小,单位是字节
	int(*cmp)(const void* p1, const void* p2))//这是一个指向比较两
	//个元素大小的函数指针
{
	int i = 0;
	//趟数
	for (i = 0; i < num - 1; i++)
	{
		int j = 0;
		//每趟需要比较的次数
		for (j = 0; j < num - 1 - i; j++)
		{
	//此时这里是用函数指针调用其所指向的函数,cmp_int就是回调函数
			//为了实现排序的通用,我们对其进行char*类型的强制转换,
			//(char*)base+j*width,(char*)base+(j+1)*width是需要比较两个元素的起始地址,通过函数指针cmp去调用比较函数
			if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
			{
				//交换元素的函数,size是为了决定要交换字节的个数同
				//时也是每个元素的大小,单位是字节
				swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
			}
		}
	}
}
void print(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}
int main()
{
	int arr[10] = { 6,5,2,3,1,9,7,8,10,4 };
	//求取数组的大小
	int sz = sizeof(arr) / sizeof(arr[0]);
	//模拟qsort函数
	bubble_sort(arr, sz, sizeof(int), cmp_int);//升序
	//打印数组
	print(arr, sz);
	return 0;
}

4 sizeof和strlen的对比

. sizeof是一个操作符也是一个关键字。计算的是变量所占内存空间的大小,单位是字节。如果操作数是类型的话,计算的是使用类型创建的变量所占内存空间的大小

sizeof只关注占用内存空间的大小,不关注存放什么数据

. strlen是一个库函数,功能是求取字符串的长度

size_t strlen(const char* str);

统计的是从strlen函数的参数str中这个地址开始向后,\0之前字符串中字符的个数。strlen函数会一直向后找\0字符,直到找到为止。所以可能会存在数组越界

5 字符分类函数

这些函数都需要包含头文件ctype.h

在这里插入图片描述

所有控制字符
在这里插入图片描述

这些函数都是非常简单且类似的一些库函数,我们简单举两个函数体验一下。

#include<stdio.h>
#include<ctype.h>
int main()
{
	if(iscntrl('\t'))
	{
		printf("控制字符\n");
	}
	return 0;
}

在这里插入图片描述

在这里插入图片描述

#include<stdio.h>
#include<ctype.h>
int main()
{
	if (isspace(' '))
	{
		printf("空格字符\n");
	}
	return 0;
}

在这里插入图片描述

在这里插入图片描述

6 字符转换函数

int tolower(int c);//转换成小写字符
int toupper(int c);//转换成大写字符

上实战,将字符串中的小写字符转换成大写字符。

#include<stdio.h>
#include<ctype.h>
#include<string.h>
int main()
{
	char ch[] = "WelCome to ChinA";
	size_t len = strlen(ch);//字符串的长度
	char* pch = ch;//字符指针存储字符数组首字符的地址
	while (len)
	{
		//islower判断字符是不是小写字母,条件为真返回非0的值,为假
		//返回0
		if (islower(*pch))
		{
			//toupper小写字母转换大写字母
			*pch = toupper(*pch);
		}
		pch += 1;
		len--;
	}
	printf("%s\n", ch);
	return 0;
}

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

7 strlen的使用和模拟实现

strlen函数的功能是求取字符串中\0之前字符的个数。返回值类型是size_t类型(无符号)。strlen的使用需要包含头文件string.h

size_t strlen(const char* str);
#include<stdio.h>
#include<string.h>
int main()
{
	char ch[20] = "hello world";
	size_t len = strlen(ch);
	printf("%zd\n", len);
	return 0;
}

简单了解了strlen的使用,接下来让我们模拟实现strlen函数。

#include<stdio.h>
#include<assert.h>
size_t my_strlen(char* pch)
{
	assert(pch!=NULL);
	char* start = pch;//记录字符串的起始地址
	while (*pch)
	{
		pch++;
	}
	return pch - start;//指针-指针
}
int main()
{
	char ch[20] = "welcome to china";
	size_t len = my_strlen(ch);
	printf("%zd\n", len);
	return 0;
}

利用这段代码回顾一下以前的知识。指针-指针的绝对值计算的是元素的个数。利用strlen函数的特性,以\0为字符串结束标志同时作为循环的结束条件。开始记录下字符串的起始地址防止指针pch后续使用时起始地址丢失

在这里插入图片描述

除了指针-指针之外,还有其他方法。例如递归实现。

#include<stdio.h>
#include<assert.h>
int my_strlen(char* pch)
{
	assert(pch!=NULL);
	if (*pch == '\0')
	{
		return 0;
	}
	else
	{
		return 1 + my_strlen(pch + 1);
	}
}
int main()
{
	char ch[20] = "welcome to china";
	int len = my_strlen(ch);
	printf("%d\n", len);
	return 0;
}

画图分析:
在这里插入图片描述

现在我们应该对strlen函数有了更深层次的理解,那么让我们来看下面的一段代码。

#include <stdio.h>
#include <string.h>
int main()
{
	const char* str1 = "abcdef";
	const char* str2 = "bbb";
		//    3              6
		//strlen返回值类型是size_t类型,是一个unsigned int类型
		//-3会被当做一个无符号的整数
		//10000000000000000000000000000011原码
		//11111111111111111111111111111100反码
		//11111111111111111111111111111101补码,最高位不再是符号位,被当做数值位,是一个非常大的整数
	if (strlen(str2) - strlen(str1) > 0)
	{
		printf("str2>str1\n");
	}
	else
	{
		printf("srt1>str2\n");
	}
	return 0;
}

在这里插入图片描述

结果是否和小伙伴们想的一样呢?

8 strcpy的使用和模拟实现

char* strcpy(char* destination,const char* source);

注意

. 源字符串必须以\0结尾

. 会将源字符串中的\0拷贝进目标空间

. 目标空间必须足够大,以确保能存放源字符串

. 目标空间必须是可修改的

#include<stdio.h>
#include<string.h>
int main()
{
	char ch1[30] = { 0 };
	char ch2[20] = "welcome to china";
	char* start = strcpy(ch1, ch2);
	printf("%s\n", start);
	return 0;
}

模拟实现strcpy。

#include<stdio.h>
#include<assert.h>
char* my_strcpy(char* pch1, const char* pch2)
{
	assert(pch1 && pch2);
	char* start = pch1;//记录pch1的起始地址
	while (*pch1++ = *pch2++)//先将pch2里的内容赋值给pch1再判断是
	//否满足循环条件
	{
		;
	}
	return start;//返回pch1的起始地址
}
int main()
{
	char ch1[30] = { 0 };
	char ch2[20] = "hello world";
	char* ret = my_strcpy(ch1, ch2);
	printf("%s\n", ret);
	return 0;
}

9 strcat的使用和模拟实现

char * strcat ( char * destination, const char * source );

注意

. 源字符串必须以\0结尾

. 目标字符串也必须以\0结束,否则不知道从哪里开始追加

. 目标空间必须是可修改的

. 目标空间必须足够大,能够容纳源字符串中的内容

#include<stdio.h>
#include<string.h>
int main()
{
	char ch1[30] = "hello everyone ";
	char ch2[20] = "good morning";
	char* start = strcat(ch1, ch2);
	printf("%s\n", start);
	return 0;
}

模拟实现strcat。

#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strcat(char* dest, char* src)
{
	char* start = dest;//记录目标空间的起始地址
	assert(dest && src);
	while (*dest)//找到目标空间\0处的地址
	{
		dest++;
	}
	while (*dest++ = *src++)//从目标空间\0处的地址开始追加
	{
		;
	}
	return start;
}
int main()
{
	char ch1[30] = "hello everyone ";
	char ch2[20] = "good morning";
	char* ret = my_strcat(ch1, ch2);
	printf("%s\n", ret);
	return 0;
}

不建议使用strcat函数自己给自己追加。

10 strcmp的使用和模拟实现

int strcmp(const char* str1,const char* str2);

标准规定

第一个字符串大于第二个字符串,返回大于0的数字

第一个字符串等于第二个字符串,返回0

第一个字符串小于第二个字符串,返回小于0的数字

那么如何判断两个字符串的大小呢?比较两个字符串对应位置字符ASCII码值的大小。

#include<stdio.h>
#include<string.h>
int main()
{
	char str1[20] = "hello world";
	char str2[20] = "hello china";
	int ret = strcmp(str1, str2);
	if (ret > 0)
	{
		printf("str1>str2\n");
	}
	else if (ret == 0)
	{
		printf("str1=str2\n");
	}
	else
	{
		printf("str1<str2\n");
	}
	return 0;
}

模拟实现strcmp。

#include<stdio.h>
#include<assert.h>
int my_strcmp(char* p1, char* p2)
{
	assert(p1 && p2);
	while (*p1==*p2)//p1指向的内容等于p2指向的内容,就比较下一对内
	//容
	{
		//如果两个字符串相等,返回0
		if (*p1 == '\0')
		{
			return 0;
		}
		p1++;
		p2++;
	}
	return *p1-*p2;
}
int main()
{
	char str1[10] = "abcdef";
	char str2[10] = "abcdghi";
	int ret = my_strcmp(str1, str2);
	if (ret > 0)
	{
		printf("str1>str2\n");
	}
	else
	{
		printf("str1!=str2\n");
	}
	return 0;
}

11 strncpy的使用

char* strncpy(char* destination,const char* source,size_t num);
//拷贝num个字符从源字符串到目标空间
//如果源字符串的长度小于num个字符,则拷贝完源字符串之后在目标的后边追加
//0直到num个字符
#include<stdio.h>
#include<string.h>
int main()
{
	char str1[10] = "good";
	char str2[10] = "morning";
	char* ret = strncpy(str1, str2, 7);
	printf("%s\n", ret);
	return 0;
}

12 strncat的使用

char* strncat(char* destination,const char* source,size_t num);
//将source指向的字符串前num个字符追加到destination指向的字符串末尾,再追
//加一个\0字符
//如果source指向字符串的长度小于num个字符,只会将字符串中\0之前的字符追
//加到destination指向的字符串末尾
#include<stdio.h>
#include<string.h>
int main()
{
	char str1[20] = "hello ";
	char str2[20] = "world";
	char* ret = strncat(str1, str2, 6);
	printf("%s\n", ret);
	return 0;
}

13 strncmp的使用

int strncmp(const char* str1,const char* str2,size_t num);
//比较str1和str2字符串中前num个字符,如果相等就继续向后比较,最多比较num
//个字母,如果提前发现不一样,就提前终止。
#include<stdio.h>
#include<string.h>
int main()
{
	char str1[20] = "hello everyone ";
	char str2[20] = "hello world";
	int ret = strncmp(str1, str2, 5);
	if (ret > 0)
	{
		printf("str1>str2\n");
	}
	else if (ret == 0)
	{
		printf("str1=str2\n");
	}
	else
	{
		printf("str1<str2\n");
	}
	return 0;
}

在这里插入图片描述

14 strstr函数的使用和模拟实现

char* strstr(const char* str1,const char* str2);
//函数返回字符串str2在字符串str1中第一次出现的位置
//字符串的比较不包含\0字符,以\0作为结束标志
#include <stdio.h>
#include <string.h>
int main()
{
	char str[] = "This is a simple string";
	char* pch = strstr(str, "simple");
	strncpy(pch, "sample", 6);
	printf("%s\n", str);
	return 0;
}

模拟实现strstr。

#include<stdio.h>
#include<assert.h>
char* my_strstr(char* str1, char* str2)
{
	assert(str1 && str2);
	char* s1 = str1;//用来遍历字符串
	char* s2 = str2;//用来遍历字符串
	char* cur = str1;//记录可能开始匹配的位置
	if (*str2 == '\0')
	{
		return str1;
	}
	while (*cur)
	{
		//完成一次匹配
		s1 = cur;
		s2 = str2;
		while (*s1 && *s2 && *s1 == *s2)
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			return cur;
		}
		cur++;
	}
	return NULL;
}
int main()
{
	char str1[20] = "abbbcdef";
	char str2[20] = "bbc";
	char* ret = my_strstr(str1, str2);
	printf("%s\n", ret);
	return 0;
}

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

15 strtok函数的使用

char* strtok(char* str,const char* sep);
//sep参数指向了一个字符串,定义了用作分隔符的字符集合
//第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记
//strtok函数找到str中的下一个标记,并将其用\0结尾,返回一个指向这个标记的指针
//strtok函数会改变原字符串的内容,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容
//strtok函数的第一个参数不为NULL,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置
//strtok函数的第一个参数为NULL,函数将在同一个字符串中被保存的位置开始,查找下一个标记
//如果字符串中不存在更多标记,返回NULL
#include<stdio.h>
#include<string.h>
int main()
{
	char str[] = "https:csdn.net";
	char sep[] = ":.";
	char str1[30] = { 0 };
	strcpy(str1, str);
	char* p = NULL;
	for (p = strtok(str1, sep); p != NULL; p = strtok(NULL, sep))
	{
		printf("%s\n", p);
	}
	printf("%s\n", p);
	return 0;
}

16 strerror函数的使用

char* strerror(int errnum);
//strerror函数可以把参数部分错误码对应的错误信息的字符串地址返回来
//一般是放在errno.h这个头文件中说明的,程序启动时会使用一个全局变量errno来记录程序的当前错误码,
//只不过程序启动的时候errno=0,表示没有错误,发生错误的时候,会将对应的错误码放在errno中
#include<stdio.h>
#include<errno.h>
#include<string.h>
int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	//打开成功

	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}
perror函数直接将错误信息打印出来。perror函数打印完参数部分的字符串后,再
//打印一个冒号和空格,再打印错误信息
#include<stdio.h>
#include<errno.h>
#include<string.h>
int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("文件打开失败的原因是");
		return 1;
	}
	//打开成功

	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}
  • 27
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值