17 字符函数、字符串函数和内存函数

news2024/9/21 8:01:16

目录

一、字符函数

(一)字符分类函数

(二)字符转换函数

二、字符串函数

(一)strlen、strcpy、strcat、strcmp的使用和模拟实现

1、strlen

(1)使用

(2)模拟实现

2、strcpy

(1)使用

(2)模拟实现

3、strcat

(1)使用

(2)模拟实现

4、strcmp

(1)使用

(2)模拟实现

(二)strncpy、strncat、strncmp的使用

1、strncpy的使用

2、strncat的使用

3、strncmp的使用

(三)strstr的使用和模拟实现、strtok、strerror的使用

1、strstr

(1)使用

(2)模拟实现

2、strtok函数的使用

3、strerror函数的使用

三、内存函数

(一)memcpy、memmove的使用和模拟实现

1、memcpy

(1)使用

(2)模拟实现

2、memmove

(1)使用

(2)模拟实现

(二)memset、memcmp的使用

1、memset的使用

2、memcmp的使用


一、字符函数

        在编程的过程中,经常要处理字符和字符串,为了方便操作字符和字符串,C语言标准库中提供了一系列库函数

(一)字符分类函数

        C语⾔中有一系列的函数是专门做字符分类的,也就是一个字符是属于什么类型的字符的;

        这些函数的使用都需要包含一个头文件: ctype.h

        这些函数的使用方法非常类似,说明一个即可:

int islower ( int c );

        islower 的参数是整型,因为可以传入字符或者字符的ASCII码值,两者本质一样

        islower 是能够判断参数部分的 c 是否是小写字母;

        通过返回值来说明是否是小写字母,如果是小写字母就返回非0的整数,如果不是小写字母,则返回 0;

        练习:写一个代码,将字符串中的小写字母转大写,其他字符不变

#include<stdio.h>
#include<string.h>
#include<ctype.h>

int main()
{
	char arr[] = "I like eat pizza!";
	int len = strlen(arr);

	for (int i = 0; i < len; i++)
	{
		if (islower(arr[i]))
			arr[i] -= 32;
		putchar(arr[i]);
	}

	return 0;
}

        小写字母转为大写字母:小写字母的ASCII码值 - 32是大写字母的ascii码值

(二)字符转换函数

        C语言提供了2个字符转换函数:

int tolower ( int c ); //将参数传进去的⼤写字⺟转⼩写
int toupper ( int c ); //将参数传进去的⼩写字⺟转⼤写

        上面的练习代码中,将小写转大写,是-32完成的;有了转换函数,就可以直接使用 tolower 函数;可以将上面的练习改为:

#include<stdio.h>
#include<string.h>
#include<ctype.h>

int main()
{
	char arr[] = "I like eat pizza!";
	int len = strlen(arr);

	for (int i = 0; i < len; i++)
	{
		if (islower(arr[i]))
			arr[i] = toupper(arr[i]);
		putchar(arr[i]);
	}

	return 0;
}

二、字符串函数

(一)strlen、strcpy、strcat、strcmp的使用和模拟实现

1、strlen

(1)使用
size_t strlen ( const char * str );

        注意:

        ① 字符串以 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包 含 '\0' )

        ② 参数指向的字符串必须要以 '\0' 结束

        ③ 注意函数的返回值为 size_t,是无符号的( 易错 )

        ④ strlen的使用需要包含头文件 string.h

(2)模拟实现

        strlen模拟实现有三种方法:

方法一:计数器

#include<stdio.h>
#include<string.h>
#include<assert.h>

size_t my_strlen(const char* arr)
{
	assert(arr);
	size_t count = 0;
	while (*arr)
	{
		count++;
		arr++;
	}
	return count;
}

int main()
{
	char arr[] = "abcdef";
	size_t re = my_strlen(arr);
	printf("字符串长度为:%zd", re);

	return 0;
}

方法二:指针相减

#include<stdio.h>
#include<string.h>
#include<assert.h>

size_t my_strlen(const char* arr)
{
	assert(arr);
	char* p = arr;
	while (*arr)
		arr++;
	return (size_t)(arr - p);
}

int main()
{
	char arr[] = "abcdef";
	size_t re = my_strlen(arr);
	printf("字符串长度为:%zd", re);
	return 0;
}

方法三:递推(不创建临时变量)

#include<stdio.h>
#include<string.h>
#include<assert.h>

size_t my_strlen(const char* arr)
{
	assert(arr);
	if (*arr == '\0')
		return 0;
	else
		return 1 + my_strlen(arr+1);
}

int main()
{
	char arr[] = "abcdef";
	size_t re = my_strlen(arr);
	printf("字符串长度为:%zd", re);
	return 0;
}

2、strcpy

(1)使用
char* strcpy(char * destination, const char * source );

        ① 源字符串必须以 '\0' 结束;

        ② 会将源字符串中的 '\0' 拷贝到目标空间;

        ③ ⽬标空间必须足够大,以确保能存放源字符串;

        ④ ⽬标空间必须可修改;

        注意:

        ① strcpy 复制原字符串包涵\0,字符拷贝使用不了;

        ② 为了实现链式访问(返回值可以用起来),strcpy的返回值是指针类型,返回的是目标空间的其实地址

(2)模拟实现
#include<stdio.h>
#include<string.h>
#include<assert.h>

char* my_strcpy(char* dest, const char* sour)
{
	assert(dest && sour);
	char* re = dest;
	while (*dest++ = *sour++);
	return re;
}

int main()
{
	char source[] = "abcdef";
	char dest[20] = { 0 };
	char* re = my_strcpy(dest, source);
	printf("目标字符为:%s", re);
	return 0;
}

3、strcat

(1)使用

        ① 源字符串必须以 '\0' 结束,否则不知道追加到什么时候;

        ② 目标字符串中也得有 \0 ,否则没办法知道追加从哪里开始;

        ③ 目标空间必须有足够的大,能容纳下源字符串的内容

        ④ 目标空间必须可修改

        ⑤ 自己给自己追加,不保证合法

        注意:

        ① 数字字符 '0' 的ascii码值为48;

        ② 终止字符 '\0' 的ascii码值为0,\0→\ddd,d表示1~3个8进制的数字,\0表示八进制的0,十进制也是0,ascii码值为0;而NULL,也表示0,空指针也可以赋0;

(2)模拟实现
#include<stdio.h>
#include<string.h>
#include<assert.h>

char* my_strcat(char* dest, const char* sour)
{
	assert(dest && sour);
	char* p = dest;
	while (*dest)
		dest++;
	while ((*dest++ = *sour++));
	return p;
}

int main()
{
	char source[] = "abcdef";
	char dest[50] = "I like eat pizza ";
	char* re = my_strcat(dest, source);
	printf("目标字符为:%s\n", re);
	return 0;
}

4、strcmp

(1)使用

        标准规定:

                第⼀个字符串大于第⼆个字符串,则返回大于0的数字;

                第⼀个字符串等于第⼆个字符串,则返回0;

                第⼀个字符串小于第⼆个字符串,则返回小于于0的数字

        注意:

                strcmp比较的是对应字符的大小,而不是长度

(2)模拟实现
#include<stdio.h>
#include<string.h>
#include<assert.h>

int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);
	while (*str1 == *str2)
	{
		if (*str1 == '\0')
			return 0;
		str1++;
		str2++;
	}
	return *str1 - *str2;
}

int main()
{
	char str1[] = "abcdef";
	char str2[] = "abcpiz";
	int re = my_strcmp(str1, str2);
	if(re == 0)
		printf("字符串相等");
	else if (re > 0)
		printf("str1大于str2");
	else
		printf("str1小于str2");
	return 0;
}

(二)strncpy、strncat、strncmp的使用

1、strncpy的使用

char * strncpy ( char * destination, const char * source, size_t num );

        ① 拷贝 num 个字符从源字符串到目标空间;

        ② 若源头字符数超过限定的拷贝字数,拷贝完毕后不会自动补 '\0',除非源头包涵 '\0';

        ③ 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加 '\0',直到num个为止

2、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];
	char str2[20];

	strcpy(str1, "To be ");
	strcpy(str2, "or not to be");

	strncat(str1, str2, 6);
	printf("%s\n", str1);

	return 0;
}

        结果如下:

3、strncmp的使用

int strncmp ( const char * str1, const char * str2, size_t num );

        比较str1和str2的前num个字符,如果相等就继续往后比较,最多比较num个字⺟,如果提前发现不一样,就提前结束,如果num个字符都相等,就是相等返回0

(三)strstr的使用和模拟实现、strtok、strerror的使用

1、strstr

(1)使用
char * strstr ( const char * str1, const char * str2);

        ① 函数返回字符串str2在字符串str1中第一次出现的位置;

        ② 字符串的比较匹配不包含 '\0' 字符,以 '\0' 作为结束标志

(2)模拟实现
#include<assert.h>
#include<stdio.h>

const char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);
	const char* s1 = NULL;
	const char* s2 = NULL;
	const char* check = str1;

	if (*str2 == '\0')
		return str1;

	while (*check)
	{
		s1 = check;
		s2 = str2;

		while (*s1 && *s2 && *s1 == *s2)
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
			return check;
		check++;
	}
	return NULL;
}

int main()
{
	char str[] = "This is a simple string";
	char str1[] = "simple";
	const char* re = my_strstr(str, str1);
	if (re == NULL)
		printf("不存在");
	else
		printf("%s", re);

	return 0;
}

2、strtok函数的使用

char * strtok ( char * str, const char * sep);

        ① sep参数指向一个字符串,定义了用作分隔符的字符集合;

        ② 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记;

        ③ strtok函数找到str中的下一个标记,并将其用 '\0' 结尾,返回一个指向这个标记的指针;(注: strtok函数会改变被操作的字符串,所以被strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)

        ④ strtok函数的第⼀个参数若不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置;strtok函数的第一个参数若为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记;

        ⑤ 如果字符串中不存在更多的标记,则返回 NULL 指针

取出被分隔符隔开的所有字符串:

#include<string.h>
#include<stdio.h>

int main()
{
	char arr[] = "i.like#eat@pizza";
	char sep[] = ".#@";

	for (char* re = strtok(arr, sep); re != NULL; re = strtok(NULL, sep))
		printf("%s ", re);

	printf("\n");
	return 0;
}

        结果如下:

3、strerror函数的使用

char* strerror ( int errnum );

         作用:将错误码对应的错误信息的字符串起始地址返回

        在不同的系统和C语言标准库的实现中都规定了一些错误码,一般是放在 'errno.h' 这个头文件中说明的,C语言程序启动的时候就会使用一个全局的变量 errno 来记录程序的当前错误码,只不过程序启动的时候errno是0,表示没有错误,当我们在使用标准库中的函数的时候发生了某种错误,就会将对应的错误码,存放在 errno 中,而一个错误码的数字是整数,很难理解是什么意思,所以每⼀个错误码都是有对应的错误信息。strerror函数就可以将错误对应的错误信息字符串的地址返回。

        打印部分错误码:

#include<string.h>
#include<stdio.h>
#include <errno.h>

int main()
{
	int i = 0;
	for (i = 0; i <= 10; i++) {
		printf("%s\n", strerror(i));
	}
	return 0;
}

         运行结果如下:

注意:

        perror —— 将errorno中对应的错误信息直接打印出来,p是printf打印的意思;

void perror( const char* str )

        先打印str指向的字符串,然后打印一个冒号和空格,再打印错误码对应的错误信息;

        perror == printf + strerror, 如下:       

printf("文件打开失败,原因是:%s", strerror(errorno));

perror("文件打开失败,原因是");

        两者输出结果一样。

三、内存函数

        内存函数:针对内存块来处理的(内存块:一块内存;面包块:一块面包);

        mem - memory - 记忆、内存

(一)memcpy、memmove的使用和模拟实现

1、memcpy

(1)使用
void * memcpy ( void * destination, const void * source, size_t num );

        ① 函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存位置

        ② 这个函数在遇到 '\0' 的时候并不会停下来

        ③ 如果source和destination有任何的重叠,复制的结果都是未定义的。

        ④ 返回的是目标空间的起始地址;

        ⑤ 参数:目标空间的地址,原空间的地址,被拷贝的字节数

(2)模拟实现
#include<assert.h>
#include<string.h>
#include<stdio.h>


void* my_memcpy(void* p1, void* p2, size_t mun)
{
	assert(p1 && p2);
	void* p = p1;
	while (mun--)
	{
		*(char*)p = *(char*)p2;
		p = (char*)p + 1;
		p2 = (char*)p2 + 1;
	}
	return p1;
}

int main()
{
	int arr1[] = {1,2,3,4,5,6,7,8,9};
	int arr2[20] = { 0 };
	int* p = my_memcpy(arr2, arr1, 13);

	for (int i = 0; i < 4; i++)
	{
		printf("%d ", *(p + i));
	}

	return 0;
}

2、memmove

(1)使用
void * memmove ( void * destination, const void * source, size_t num );

        ① 和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的

        ② 如果源空间和⽬标空间出现重叠,就得使⽤memmove函数处理

(2)模拟实现
#include<assert.h>
#include<string.h>
#include<stdio.h>

void* my_memmove(void* dest, void* sour, size_t mun)
{
	assert(dest && sour);
	void* p = dest;

	if (dest < sour)
	{
		while (mun--)
		{
			*(char*)dest = *(char*)sour;
			dest = (char*)dest + 1;
			sour = (char*)sour + 1;
		}
	}
	else 
	{
		while (mun--)
		{
			*((char*)dest + mun) = *((char*)sour + mun);
		}
	}
	return p;
}

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	my_memmove(arr+2, arr, 16);

	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}

	return 0;
}

(二)memset、memcmp的使用

1、memset的使用

void * memset ( void * ptr, int value, size_t num );

        memset是用来设置内存的,将内存中的值以字节为单位设置成想要的内容,使用例子如下:

#include<string.h>
#include<stdio.h>

int main()
{
	char str[] = "hello world";
	memset(str, 'x', 6);
	printf(str);
	return 0;
}

        运行结果如下:

2、memcmp的使用

int memcmp ( const void * ptr1, const void * ptr2, size_t num );

        ① 比较从ptr1和ptr2指针指向的位置开始,向后的num个字节;

        ② 返回值如下:


        以上内容仅供分享,若有错误请多指正

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1977453.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java中的5种线程池类型

Java中的5种线程池类型 1. CachedThreadPool &#xff08;有缓冲的线程池&#xff09;2. FixedThreadPool &#xff08;固定大小的线程池&#xff09;3. ScheduledThreadPool&#xff08;计划线程池&#xff09;4. SingleThreadExecutor &#xff08;单线程线程池&#xff09;…

基于切片法计算点云体积 双向最近点三维点排序

具体内容源代码&#xff1a;基于切片法计算点云体积 双向最近点三维点排序 效果 主要方法&#xff1a; 点云切片&#xff08;基于一定厚度度的点云切片投影&#xff09; &#xff1b;切片后的点云分割 &#xff1b;边缘点排序【双向最近邻】&#xff08;可以进行多边形拟合计…

JAVA项目基于Spring Boot的美食烹饪互动平台的设计与实现

目录 一、前言 二、技术介绍 三、系统实现 四、论文参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末 一、前言 随着互联网…

复盘:项目负责人的经验之谈

复盘&#xff1a;项目负责人的经验之谈 前言基础复盘&#xff1a;从无到有的自我觉醒客观分析&#xff1a;数据驱动的决策专业工具&#xff1a;科学分析的利器模型构建&#xff1a;从个案到普遍规律的提炼能力提升&#xff1a;从知识到行动的转变结语 前言 在项目管理和竞赛的世…

SOMEIP_ETS_002:数组长度过长

测试目的&#xff1a; 确保DUT在接收到的SOME/IP消息中数组长度超出实际数组长度时&#xff0c;能够返回错误消息。 描述 本测试用例旨在验证当DUT接收到一个声明数组长度超过其实际长度的SOME/IP消息时&#xff0c;DUT是否能够正确地返回错误消息&#xff08;MALFORMED_MES…

高并发下的分布式缓存 | 设计和实现LFU缓存

什么是 LFU 缓存&#xff1f; 最少使用频率 (LFU) 是一种用于管理计算机内存的缓存算法。在这种算法中&#xff0c;系统会跟踪缓存中每个数据被引用的次数。当缓存已满时&#xff0c;系统会删除引用频率最低的数据。 LFU 缓存问题描述 我们的目标是设计一个LFU 缓存&#xf…

手机号码归属地数据源,让您随时掌握通话对方位置!

手机号码归属地数据源&#xff0c;这是一个非常实用的数据源&#xff0c;可以帮助我们随时掌握通话对方的位置。无论是普通民众还是企业用户&#xff0c;都可以从中受益。 在这个数据源中&#xff0c;我们可以通过手机号码的前7位来查询该手机号码的归属地&#xff0c;包括省市…

美容院管理小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;服务类型管理&#xff0c;产品服务管理&#xff0c;预约信息管理&#xff0c;产品分类管理&#xff0c;产品信息管理&#xff0c;订单管理&#xff0c;系统管理 微信端账号功能包…

笔试练习day2

目录 BC64 牛牛的快递题目解析解法模拟代码方法1方法2 DP4 最小花费爬楼梯题目解析解法动态规划状态表示状态转移方程代码 数组中两个字符串的最小距离题目解析解法方法1暴力解法(会超时)方法2贪心(动态规划)代码 感谢各位大佬对我的支持,如果我的文章对你有用,欢迎点击以下链接…

【数据脱敏】数据交换平台数据脱敏建设方案

1 概述 1.1 数据脱敏定义 1.2 数据脱敏原则 1.2.1基本原则 1.2.2技术原则 1.2.3管理原则 1.3 数据脱敏常用方法 3.1.1泛化技术 3.1.2抑制技术 3.1.3扰乱技术 3.1.4有损技术 1.4 数据脱敏全生命周期 2 制定数据脱敏规程 3 发现敏感数据 4 定义脱敏规则 5 执…

GD32 IAP升级——boot和app相互切换

GD32 IAP升级——boot和app相互切换 目录 GD32 IAP升级——boot和app相互切换1 Keil工程设置1.1 修改ROM1.2 Keil烧录配置 2 代码编写2.1 app跳转2.2 软件重启2.3 app中断向量表偏移 结束语 1 Keil工程设置 1.1 修改ROM GD32内部Flash是一整块连续的内存&#xff0c;但是因为…

数学计算之JS小数精度问题(java/python)

number 小数计算会出现精度不准确问题&#xff0c;js中number是64 位双精度浮点数。 其实&#xff0c;不仅仅只有javascript&#xff0c;还有java、python等都会有类似问题&#xff0c;因为计算机中存的都是二进制&#xff08;浮点数IEEE754是被普遍使用的标准&#xff09;&am…

Java:线程安全

引子 首先来看一段代码: private static int count 0;public static void main(String[] args) throws InterruptedException {Thread t1 new Thread(()->{for (int i 0; i < 50000; i) {count;}});Thread t2 new Thread(()->{for (int i 0; i < 50000; i) {…

Java语言程序设计——篇十一(4)

&#x1f33f;&#x1f33f;&#x1f33f;跟随博主脚步&#xff0c;从这里开始→博主主页&#x1f33f;&#x1f33f;&#x1f33f; 欢迎大家&#xff1a;这里是我的学习笔记、总结知识的地方&#xff0c;喜欢的话请三连&#xff0c;有问题可以私信&#x1f333;&#x1f333;&…

深入解析 KMZ 文件的处理与可视化:从数据提取到地图展示项目实战

文章目录 1. KMZ 文件与 KML 文件简介1.1 KMZ 文件1.2 KML 文件 2. Python 环境配置与依赖安装3. 代码实现详解3.1 查找 KMZ 文件3.2 解压 KMZ 文件3.3 解析 KML 文件3.4 可视化 KMZ 数据 4. 项目实战4.1. 数据采集4.2. 项目完整代码 5. 项目运行与结果展示6. 总结与展望 在处理…

2007-2023年上市公司国内外专利申请获得情况数据

2007-2023年上市公司国内外专利申请获得情况数据 1、时间&#xff1a;2007-2023年 2、来源&#xff1a;上市公司年报 3、指标&#xff1a;证券代码、统计截止日期、报表类型、地区、申请类型编码、申请类型、专利&#xff08;件&#xff09;、发明专利&#xff08;件&#x…

动态路由协议基础

一、动态路由协议简介 动态路由协议:路由器用来计算和维护路由信息的协议;通俗的说,就算路由器用来学习路由的协议。 二、动态路由与静态路由的区别 静态路由动态路由路由表手工配置自动生成路由维护人工维护自动收敛资源消耗路由表生成不占网络资源路哟表生成占用网络资源…

学习Java的日子 Day59 学生管理系统 web1.0版本

Day59 学生管理系统 web1.0 1.项目需求 有两个角色&#xff0c;老师和学生&#xff0c;相同的功能提取到父类用户角色 2.数据库搭建 设计学生表 设计老师表 插入数据 (超级管理员) 设计学科表 3.项目搭建 处理基础页面&#xff0c;分包&#xff0c;实体类&#xff0c;导入数据…

微软AI业务最新营收数据情况(2024年7月)

Azure AI 年度经常性收入 (ARR)&#xff1a;达到50亿美元客户数量&#xff1a;60,000家平均客户价值 (ACV) 中位数&#xff1a;83,000美元同比增长率&#xff1a;达到了惊人的900% GitHub Copilot 年度经常性收入 (ARR)&#xff1a;达到3亿美元客户数量&#xff1a;77,000家…

每日两题8

买卖股票的最佳时机 III class Solution { public:int maxProfit(vector<int>& prices) {int n prices.size();int INF 0x3f3f3f3f;vector<vector<int>> f(n, vector<int>(3, -INF));auto g f;g[0][0] 0;f[0][0] -prices[0];for (int i 1; i…