【C语言】字符串函数和内存函数及其模拟实现

news2024/12/22 19:18:45

文章目录

    • 前言
  • 一、常见字符串库函数
    • 1.strlen函数
    • 2.长度不受限制的字符串函数
      • 2.1 strcpy
      • 2.2 strcat
      • 2.3 strcmp
    • 3.长度受限制的字符串函数
      • 3.1 strncpy
      • 3.2 strncat
      • 3.3 strncmp
  • 二、字符串查找函数
      • strstr
      • strtok
  • 三、strerror函数
  • 四、内存操作函数
    • 1.memcpy
    • 2.memmove
    • 3.memcmp
  • 五、字符函数
    • 字符分类函数
    • 字符转换

前言

C语言中的字符串与C++不同,是没有类型的。C语言本身提供了一些对字符串进行处理的库函数,本篇整理了C语言中针对字符串或者字符数组的各种操作的库函数以及内存操作函数,包括其用法和模拟实现的过程。用到的头文件有#include<string.h>

关于库函数的使用,这里分享一个查阅工具 cplusplus,通过这个手册我们可以了解库函数的信息:头文件、函数名、函数参数、返回值类型等。

一、常见字符串库函数

1.strlen函数

size_t strlen (const char* str);

功能:求字符串长度

cplusplus的使用介绍,后面就不再展示了

在这里插入图片描述

可以看到,strlen所在头文件是string.h,返回值类型是size_t,函数参数类型是const char*,返回值表示的是字符串的长度。

1.字符串以 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中 ‘\0’ 前面出现的字符个数(不包含 ‘\0’ )。
2.size_t 是一些C/C++标准在stddef.h中定义的,size_t 类型表示C中任何对象所能达到的最大长度,它是无符号整数,32位平台上,size_t 就是unsigned int,所以一般情况下我们可以用int接收返回值。

strlen的用法:

在这里插入图片描述

strlen的模拟实现:
这里简单说一下关键字assert(断言)的用法:如果assert()括号中的表达式是假,则会中止程序执行,并且会显示错误原因和位置。
在写函数的时候,先采用断言处理,防止字符串为空从而导致后续操作非法,同时方便找错误。

方法一:常规写法

size_t my_strlen(const char* str)//与库函数保持一致
{
	assert(str);
	size_t count = 0;
	while (*str++)//*str不等于'\0',就继续往后走直到遇到'\0'
	{
		count++;
	}
	return count;
}

方法二:指针减指针

size_t my_strlen(const char* str)
{
	assert(str);
	const char* start = str;//保存字符串起始地址
	//while (*str++);//如果这样写,str会走到'\0'后一个位置,最终需要返回str - start - 1
	while (*str)
	{
		str++;
	}
	return str - start;
}

方法三:递归

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

在这里插入图片描述

2.长度不受限制的字符串函数

下面三种字符串函数都是直接对整个字符串进行操作,不能对部分内容单独操作。

2.1 strcpy

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

功能:将源字符串拷贝给目标字符串

1.源字符串必须以 ‘\0’ 结束
2.该函数会将源字符串中的“ \0 ”一同拷贝到目标空间,如果目标字符串原先就有内容,就会被拷贝的内容覆盖掉,包括“ \0 ”也是。
3.目标空间必须足够大,以确保能存放源字符串,如果放不下就会造成数组越界访问,为非法访问空间。
4.目标空间必须可变,不能是常量字符串。

2.1 strcpy用法:

在这里插入图片描述

注: 返回值是目标空间的起始地址,使用的时候可以接收也可以选择不接收返回值。

strcpy模拟实现:

char* my_strcpy(char* dest, const char* src)
{
	assert(dest && src);
	char* ret = dest;
	while (*dest++ = *src++);	//注意分号
	/*while (*dest++ = *src++)
	{
		;		//或者这样写
	}*/ 
	return ret;
}

2.2 strcat

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

功能:在目标字符串的末尾追加上源字符串

1.源字符串必须以 ‘\0’ 结束。
2.目标空间必须有足够的大,能容纳下源字符串的内容;
3.目标空间必须可修改。

strcat用法:

在这里插入图片描述

strcat模拟实现:

char* my_strcat(char* dest, const char* src)
{
	assert(dest && src);
	char* ret = dest;
	while (*dest)
	{
		dest++;   //先找到目标字符串的末尾位置'\0'
	}
	while (*dest++ = *src++); //从'\0'位置开始追加
	return ret;
}

2.3 strcmp

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

功能:比较两个字符串的大小(字典序)

1.第一个字符串大于第二个字符串,则返回大于0的数字;
2.第一个字符串等于第二个字符串,则返回0;
3.第一个字符串小于第二个字符串,则返回小于0的数字。

比如:abdef 就比 ace 小,acd 比 acde 小。
比较两个字符串中对应位置上的字符大小,如果相同,就比较下一对,直到不同或都遇到'\0'

strcmp用法:

在这里插入图片描述

strcmp模拟实现:

int my_strcmp(const char* s1, const char* s2)
{
	assert(s1 && s2);
	while (*s1 == *s2)
	{
		if (*s1 == '\0') //s1和s2都走到'\0',且前面字符都相等
			return 0;
		s1++;
		s2++;
	}
	return *s1 - *s2;
}

3.长度受限制的字符串函数

下面三个字符串函数是前面三个的衍生版本,既可以操作整个字符串,也可以单独对字符串的部分内容进行操作。

3.1 strncpy

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

功能:拷贝num个字符从源字符串到目标空间。

1.目标空间一定要足够大!
2.如果num大于源字符串的长度,则拷贝完源字符串之后,在目标的后边追加’\0’,直到num个。
3.拷贝num个字符就只会拷贝num个字符,不会再往后补一个’\0’。
4.与strcpy不同的是,strcpy会把目标字符串原本的内容销毁掉,而strncpy不会,多出来的部分仍然保留,见下图。

strncpy的使用:

在这里插入图片描述

3.2 strncat

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

功能:目标空间后面追加num个源字符串的字符

1.确保目标空间足够大;
2.会在追加完num个字符之后再追加一个’\0’

strncat的使用:

在这里插入图片描述

3.3 strncmp

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

功能:比较两个字符串前num个字符的大小

二、字符串查找函数

strstr

char* strstr (const char* str1, const char* str2);

功能:判断str2是否是str1的子串,并返回str2在str1中首次出现位置的地址。

strstr的使用:

在这里插入图片描述

strstr模拟实现:

char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);
	const char* s1 = str1;
	const char* s2 = str2;
	const char* p = str1;//p用来标记起始判断位置
	while (*p)
	{
		s1 = p;	  //s1重新回到起始判断位置并开始判断
		s2 = str2;//只要遇到不一样的,s2就回到str2起始位置重新开始判断,直到走到str2的'\0'位置
		while (*s1 && *s2 && *s1 == *s2)
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0') //走到'\0'说明在str1中找到了str2
			return p;  	//返回判断成功的起始位置
		p++;	//否则str1中的起始判断位置往后+1
	}
	return NULL;
}

strtok

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

功能:将字符串str按照sep中的分隔符进行分割

1.第一次使用将字符串传过去,在str中找到分隔符并用’\0’代替,返回并保存当前位置;
2.之后每次传参则传空指针NULL,该函数会从上次保存的位置开始向后继续分割;
3.字符串结束后会返回一个空指针;
4.strtok会修改原函数。

strtok的使用:

在这里插入图片描述

三、strerror函数

char* strerror (int errnum);

功能:返回指向错误信息字符串的指针
头文件:#include<errno.h>

这里也简单说一下perror的使用:

void perror(const char *str);

功能: 在输出错误消息时,这个字符串将作为错误消息的前缀,并在这个字符串后面自动打印错误信息。不需要处理消息的格式化和输出。
头文件#include<stdio.h>

strerror的使用:

在这里插入图片描述

四、内存操作函数

内存函数可以直接在内存中修改

1.memcpy

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

功能:从source位置拷贝num个字节的数据到目标空间的内存位置

1.单位是字节,不再局限于拷贝字符串;
2.遇到 ‘\0’ 的时候并不会停下来;

memcpy的使用:
在这里插入图片描述

memcpy模拟实现:

void* my_memcpy(void* dest, const void* src, size_t num)
{
	assert(dest && src);
	void* ret = dest;
	while (num--)
	{
		*(char*)dest = *(char*)src;
		//dest = (char*)dest + 1;
		//src = (char*)src + 1; 
		++(char*)dest;
		++(char*)src;
	}
	return ret;
}

注: 这里模拟实现的功能是和以前的memcpy的功能一样,如果dest和src的内存块发生位置重叠,例如上图中代码修改为:my_memcpy(arr2 + 3, arr2, 5 * sizeof(int));则会出现问题,导致原本想要拷贝的内容被覆盖。

而如今C语言中的memcpy库函数已经与下面即将登场的memmove函数功能一样了,可以处理源空间和目标空间出现重叠的问题。

2.memmove

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

功能:与memcpy功能一样,但是可以处理源空间和目标空间出现重叠的问题。

memmove的使用:

在这里插入图片描述

memmove模拟实现:

void* my_memmove(void* dest, const void* src, size_t num)
{
	assert(dest && src);
	void* ret = dest;
	if (dest < src)
	{
		while (num--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	else
	{
		while (num--)
		{
			*((char*)dest + num) = *((char*)src + num);//从后往前拷贝,避免内容被覆盖
		 }
	}
	return ret;
}

3.memcmp

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

功能:比较从ptr1和ptr2指针开始的num个字节

1.第一个内存块中的数据大于第二个内存块中的数据,则返回大于0的数字;
2.第一个内存块中的数据等于第二个内存块中的数据,则返回0;
3.第一个内存块中的数据小于第二个内存块中的数据,则返回小于0的数字。

在这里插入图片描述memvmp模拟实现:

int my_memcmp(const void* s1, const void* s2, size_t num)
{
	assert(s1 && s2);
	while (num--)
	{
		if (*(char*)s1 > *(char*)s2)
			return 1;
		else if (*(char*)s1 < *(char*)s2)
			return -1;
		else
		{
			s1 = (char*)s1 + 1;
			s2 = (char*)s2 + 1;
		}
	}
	return 0;
}

五、字符函数

字符分类函数

char c = 'a';
int ret = 0;
ret = isdigit(c);//检查是否是十进制数字(0~9)
ret = isxdigit(c);//检查是否是十六进制数字(0~9, a~f, A~F)
ret = isalpha(c);//检查是否是字母(a~z, A~Z)
ret = isalnum(c);//检查是否是字母或者数字(a~z, A~Z, 0~9)
ret = islower(c);//检查是否是小写字母(a~z)
ret = isupper(c);//检查是否是大写字母(A~Z)
ret = isspace(c);//检查是否是空白字符(空格、TAB、换行、垂直TAB、换页、回车)
ret = iscntrl(c);//检查是否是控制字符
ret = isgraph(c);//检查是否是图形字符
ret = ispunct(c);//检查是否是标点符号(任何不属于数字或者字母的图形字符)
ret = isprint(c);//检查是否是可打印的

字符转换

char ch = 'a';
ch = tolower(ch);//ch转换为小写字母
ch = toupper(ch);//ch转换为大写写字母

字符和字符串函数终于整理完啦,感谢您的观看与支持!

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

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

相关文章

Unity之Unity面试题(三)

内容将会持续更新&#xff0c;有错误的地方欢迎指正&#xff0c;谢谢! Unity之Unity面试题&#xff08;三&#xff09; TechX 坚持将创新的科技带给世界&#xff01; 拥有更好的学习体验 —— 不断努力&#xff0c;不断进步&#xff0c;不断探索 TechX —— 心探索、心进取…

简单工厂模式大解析:让代码创造更高效、更智能!

个人主页: danci_ &#x1f525;系列专栏&#xff1a;《设计模式》《MYSQL应用》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 &#x1f680; 转载自热榜文章&#xff1a;探索设计模式的魅力&#xff1a;简单工厂模式 简单工厂模式&#x…

【零基础学数据结构】双向链表

1.双向链表的概念 1.1头节点 1.2带头双向循环链表 注意&#xff1a; 哨兵位创建后&#xff0c;首尾连接自己 1.3双链表的初始化 // 双向链表的初始化 void ListInit(ListNode** pphead) {// 给双链表创建一个哨兵位*pphead ListBuyNode(-1); } 2.双向链表的打印 // 双向…

PE文件的分析和构造超详细过程

本文详细讲述如何从0构造一个PE文件&#xff0c;运行该文件会弹出一个HelloPE的窗口 目录 预备知识 1. 构造DOS头IMAGE_DOS_HEADER 1.1 构造DOS_MZ头 1.2 构造DOS_STUB 2、构造PE头IMAGE_NT_HEADERS 248字节 2.1 signature 2.2 IMAGE_FILE_HEADER 2.3 IMAGE_OPTI…

MOS管电路的应用及注意事项

1,buck电路的上下桥死区时间多少合适 死区时间由驱动芯片控制的&#xff0c;外围电路增加阻容会导致上升沿时间变长 死区时间从单片机PWM到驱动电路再到MOS管的栅极都有一定的硬件延时&#xff0c;所以具体时间需要根据调试确定。 例如充电芯片的buck电路&#xff0c;有死区配置…

mysql8.0高可用集群架构实战

MySQL :: MySQL Shell 8.0 :: 7 MySQL InnoDB Cluster 基本概述 InnoDB Cluster是MySQL官方实现高可用读写分离的架构方案,其中包含以下组件 MySQL Group Replication,简称MGR,是MySQL的主从同步高可用方案,包括数据同步及角色选举Mysql Shell 是InnoDB Cluster的管理工具,用…

数据库系统概论(超详解!!!)第四节 数据库安全性

问题的提出&#xff1a; 数据库的一大特点是数据可以共享 数据共享必然带来数据库的安全性问题 数据库系统中的数据共享不能是无条件的共享。 1.数据库的安全概述 数据库的安全性是指保护数据库以防止不合法使用所造成的数据泄露、更改或破坏 。 系统安全保护措施是否有效…

基于ssm的土家风景文化管理平台(java源码+文档)

项目简介 土家风景文化管理平台实现了以下功能&#xff1a; 土家风景文化管理平台的主要使用者分为管理员&#xff1a;管理员使用本平台涉到的功能主要有&#xff1a;首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;景点分类管理&#xff0c;热门景点管理&#xf…

深度学习学习日记4.7

1.梯度下降 w 新 w旧 - 学习率梯度 训练的目的就是让 loss 减小 2.前向传播进行预测&#xff0c; 反向传播进行训练(每一个参数通过梯度下降进行更新参数)&#xff0c;(1前向传播 2求 loss 3反向传播 4梯度更新) 能够让损失下降的参数&#xff0c;就是更好的参数。 损失…

基于SSM物业管理系统

摘要 进入二十一世纪以来&#xff0c;计算机技术蓬勃发展&#xff0c;人们的生活发生了许多变化。很多时候人们不需要亲力亲为的做一些事情&#xff0c;通过网络即可完成以往需要花费很多时间的操作&#xff0c;这可以提升人们的生活质量。计算机技术对人们生活的改变不仅仅包…

单列模式1.0

单列模式 单例模式能保证某个类在程序中只存在唯⼀⼀份实例, ⽽不会创建出多个实例 1.饿汉模式 只要程序一启动就会立即创建出一个对象 class Signleton{private static Signleton instancenew Signleton();//防止在以后的代码中再创建对象&#xff0c;我们将构造方法private,…

FreeFileSync|本地自动备份设置教程,终于可以不用手动同步了

前言 昨天小白给各位小伙伴分享了FreeFileSync软件&#xff0c;由于篇幅过长&#xff0c;所以整个教程中并没有教小伙伴们如何设置自动同步的办法。 今天小白就来唠唠&#xff1a;如何让FreeFileSync自动同步。 教程分为几种&#xff1a; 开机自动同步 开机之后自动执行一次…

LeetCode617:合并二叉树

题目描述 给你两棵二叉树&#xff1a; root1 和 root2 。 想象一下&#xff0c;当你将其中一棵覆盖到另一棵之上时&#xff0c;两棵树上的一些节点将会重叠&#xff08;而另一些不会&#xff09;。你需要将这两棵树合并成一棵新二叉树。合并的规则是&#xff1a;如果两个节点重…

天地人和•大道不孤——卢禹舜中国画作品展在重庆美术馆隆重开幕

2024年4月12日&#xff0c;由中国国家画院、重庆市文化和旅游发展委员会主办&#xff0c;重庆美术馆&#xff08;重庆画院、重庆国画院&#xff09;、北京八荒锦绣美术馆、中国国际文化交流基金会卢禹舜艺术基金承办的“天地人和•大道不孤——卢禹舜中国画作品展”开幕式在重庆…

SF58-ASEMI适配器二极管SF58

编辑&#xff1a;ll SF58-ASEMI适配器二极管SF58 型号&#xff1a;SF58 品牌&#xff1a;ASEMI 封装&#xff1a;DO-27 最大平均正向电流&#xff08;IF&#xff09;&#xff1a;5A 最大循环峰值反向电压&#xff08;VRRM&#xff09;&#xff1a;600V 最大正向电压&…

C语言函数指针应用——计算器(转移表)的使用

对与上一节&#xff0c;我们对指针函数已经有了深刻意识了&#xff1b;练一练吧 如果还没有学习到&#xff0c;也是没有关系的&#xff0c;可以看一看这一篇 C语言详解指针-CSDN博客https://blog.csdn.net/Asuku_/article/details/137690083希望能提高你对指针的理解 计算器的实…

使用Riverpod在Flutter中创建Todo列表

使用Riverpod在Flutter中创建Todo列表 视频 https://youtu.be/mlbeSD1KSIo https://www.bilibili.com/video/BV1jj42197c8/ 前言 原文 https://ducafecat.com/blog/flutter-todo-list-with-riverpod-guide-02 学习如何使用Riverpod在Flutter中构建一个功能完整的Todo列表应用…

《AI创业浪潮:展望未来,解锁颠覆性创新机遇》

在科技革命的浪潮中&#xff0c;人工智能&#xff08;AI&#xff09;犹如一艘引领航向的旗舰&#xff0c;以其强大的变革力量重塑各行各业。随着技术的日益成熟与应用场景的不断拓宽&#xff0c;AI技术为创业者铺就了一条充满机遇与挑战的道路。本文将深入探讨未来AI技术领域的…

CPU问题排查

经常发现生产环境CPU运行很高&#xff0c;我们想知道到底是什么代码这么消耗CPU TOP命令 此时我们经常使用top来找到 CPU 使用率比较高的一些线程 容器中的docker 备注&#xff1a; 如果是docker 中的top命令。需要关注&#xff0c;一般来说不需要&#xff0c;挂载内容的多…

TSINGSEE青犀AI智能分析网关V4叉车载货出入库检测算法介绍及应用

随着物流行业的快速发展&#xff0c;叉车作为物流运输的重要设备&#xff0c;其安全性和效率性越来越受到人们的关注。然而&#xff0c;在实际操作中&#xff0c;由于人为因素和操作环境的复杂性&#xff0c;叉车事故时有发生&#xff0c;给企业和个人带来了巨大的损失。为了提…