前言:上一趴我们学习了指针。那么今天我们来学习新的知识,回调函数,字符函数,字符串函数。
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;
}
标签:字符,return,函数,int,str1,char,字符串,include
From: https://blog.csdn.net/2402_84532723/article/details/141197575