字符串和内存函数

目录

strlen

模拟实现

长度不受限字符串函数

strcpy

模拟实现

[​编辑 strcat](#编辑 strcat)

模拟实现

strcmp

模拟实现

长度受限字符串函数

strncpy

模拟实现

strncat

strncmp

strstr

模拟实现

strtok

strerror

perror

字符分类函数

字符转换

示例:

​编辑内存函数

memcpy

模拟实现

memmove

模拟实现

memcmp

memset


strlen

功能:统计字符串中\0之前的字符个数。

注意:如果没有\0则返回一个随机值

易错:

strlen的返回值为size_t,也就是无符号整形,使用时要注意这点。

模拟实现

cpp 复制代码
#include <assert.h>
size_t my_strlen(const char* str)
{
	assert(str);
	const char* start = str;
	const char* end = str;
	while (*end != '\0')
	{
		end++;
	}
	return end - start;
}

长度不受限字符串函数

strcpy

功能:将一个源头字符串拷贝到一个目标字符串中

注意:如果源头字符串没有\0则会报错。目标空间要足够大,且可变

易错:

cpp 复制代码
	char* p = "hello";//常量字符串
	char arr[] = "world";
	strcpy(p, arr);

模拟实现

cpp 复制代码
char* my_strcpy(char* dest, const char* src)
{
	assert(dest);
	assert(src);
	char* str = dest;
	while (*dest++ = *src++)\\取巧写法,注意后置++不会对程序产生影响
	{
		;
	}
	return str;//能够被接收
}

测试结果:

strcat

功能:在一个字符串后面追加另一个字符串。

注意:目标空间要足够大,且可变 。都需要包含**\0**。

易错:

追加位置在\0处:

模拟实现

cpp 复制代码
char* my_strcat(char* dest, const char* src)
{
	assert(dest);
	assert(src);
	char* str = dest;
	while (*dest)//\0处追加,循环体内++
	{
		dest++;
	}
	while (*dest++ = *src++)
	{
		;
	}
	return str;
}

注意不要利用strcat去追加自身,否则会造成死循环!

strcmp

功能:比较字符串对应位置的ascii码大小

注意:都要包含\0。

模拟实现

cpp 复制代码
//写法1
int my_strcmp(const char* s1, const char* s2)
{
	assert(s1 && s2);
	while (*s1 == *s2)
	{
		if (*s1 == '\0')//全等
		{
			return 0;
		}
		s1++;
		s2++;
	}
	if (*s1 > *s2)
		return 1;
	else 
		return -1;
}
cpp 复制代码
//写法2
int my_strcmp(const char* s1, const char* s2)
{
	assert(s1 && s2);
	while (*s1 == *s2)
	{
		if (*s1 == '\0')
		{
			return 0;
		}
		s1++;
		s2++;
	}
	return *s1 - *s2;
}

长度受限字符串函数

strncpy

模拟实现

模拟实现一次,后续不再模拟实现。

具体逻辑是用一个无符号整数加入循环条件,如果需要拷贝的源头字符串超过了自身长度可以考虑给多余的地方拷贝\0。

cpp 复制代码
char* my_strncpy(char* dest, const char* src,size_t size)
{
	assert(dest);
	assert(src);
	char* str = dest;
	while (size && (*dest++ = *src++))//=优先级最低,所以加()
	{
		size--;
	}
	if(size)	//超出源头字符串长度
		while (--size)
		{
			*dest++ = '\0';
			size--;
		}
	return str;
}

strncat

将n个子串拷贝到目标字符串后,自动补\0,可以自己增添自己。

strncmp

比较n个子串的大小,大于返回正数,小于返回负数,等于返回0。

strstr

查找一个字符串里的子串,返回首次匹配所有子串的目标字符串中相应字符串的首地址。

模拟实现

法一:我的逻辑是能不能用strncmp负责比较字符串,用一个指针去遍历目标数组,注意结束条件为\0。但这样做有个漏洞,一次性比较多个字符串势必有越界的情况,我们以\0为突破口,能不能用它们的地址进行比较?于是思路就出现了。

cpp 复制代码
char* my_strstr(const char* str1, const char* str2)
{
	assert(str1);
	assert(str2);
	const char* ptr = str1;
	int len1 = strlen(str1);
	int len2 = strlen(str2);
	while (strncmp(str1, str2, len2)!=0 && str1)
	{
		if (str1 + len2 > ptr + len1)
		{
			return NULL;
		}
		str1++;
	}
	return (char*)str1;
}

法二:四指针遍历法。两指针负责移动,两指针负责记录。

cpp 复制代码
char* my_strstr(const char* str1, const char* str2)
{
	assert(str1);
	assert(str2);
	const char* p = str1;
	const char* src = str2;

	while (*p)
	{
		str1 = p;
		str2 = src;
		while (*str1 != '\0' && *str2 != '\0' && * str1 == *str2)//注意\0
		{
			str1++;
			str2++;
		}
		if (*str2 == '\0')
		{
			return (char*)p;
		}
		p++;
	}
	return NULL;
}

strtok

功能:分隔字符串,strtok函数找到str的下一个标记,并将其用\0替换,返回一个指向这个标记的指针。

注意:strtok会改变原数据的内容!一般修改临时拷贝的内容。

使用方法:

  • 第一个参数传字符串 ,第二个参数传字符串里分隔符
  • 第一个参数首次传递不为空,通过strtok函数找到指定分隔符位置后,将其替换成\0并记录下它的地址 再返回回去,后面第一个参数一律为NULL (否则重置),从被保存的位置开始查找下一个分隔符所在位置。
  • 如果本次找不到分隔符,就返回NULL。

测试:

发现确实实现了分割并返回,但并不完全,我们可以写个循环:

strerror

功能:当函数调用失败时,将错误码(errno)转换成对应的错误信息或指向对应的地址。

perror

等价于printf + strerror函数,如需打印可以使用这个函数。

字符分类函数

cpp 复制代码
int isalnum(int c):检查字符是否为数字或字母;(0~9,a~z,A~Z) 
int isalpha(int c):检查字符是否为字母;(a~z, A~Z) 
int iscntrl(int c):检查字符是否为控制字符;(八进制000~037以及177的字符) 
int isdigit(int c):检查字符是否为十进制数字;(0~9) 
int isgraph(int c):检查字符是否为图形表示,依赖于使用语言的环境;0~9,a~z,A~Z,以及标点符号) 
int islower(int c):检查字符是否为小写的字母;(a~z) 
int isprint(int c):检查字符是否为可打印的;(数字、字母、标点符号、空白字符) 
int ispunct(int c):检查字符是否为标点符号;(! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ ] ^ _ ` { | } ~等) 
int isspace(int c):检查字符是否为空白字符;(TAB、换行、垂直TAB、换页、回车、空格) 
int isupper(int c):检查字符是否为大写字母;(A~Z) 
int isxdigit(int c):检查字符是否为十六进制数字;(0 1 2 3 4 5 6 7 8 9 A B C D E F a b c d e f) 

字符转换

cpp 复制代码
int tolower(int c):转化字符为小写字母; 
int toupper(int c):转化字符为大写字母;

示例:

内存函数

memcpy

功能: 将一个内存空间的数据按字节复制到另一块空间。

模拟实现

cpp 复制代码
void* my_memcpy(void* dest, void* src, size_t num)
{
	void* ret = dest;
	assert(dest);
	assert(src);

	while(num--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}

	return ret;
}

memmove

使用memcpy拷贝空间出现重叠 时分两种情况:

memcpy显然只能从一方进行拷贝,如果出现另一种情况,则会存在内存覆盖 现象。而memmove的作用就是对重叠空间进行拷贝。(有些编译器memcpy可以实现重叠拷贝)

字符串重叠空间拷贝用memmove代替strncpy。

模拟实现

cpp 复制代码
void* my_memmove(void* dest, void* src, size_t num)
{
	assert(dest);
	assert(src);
	//(char*)dest++;不能这样写,强制暂时改变了类型,void*无法自增
	void* ret = dest;
	//从前往后
	if (src > dest)
	{
		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;
}

测试:

memcmp

内存比较函数,不多赘述。

memset

字节初始化内存空间。

测试:

由于小端的存储方式,我们修改前9个字节实则将前3个整形变量修改成了0。

相关推荐
Mephisto.java16 分钟前
【力扣 | SQL题 | 每日四题】力扣2082, 2084, 2072, 2112, 180
sql·算法·leetcode
robin_suli17 分钟前
滑动窗口->dd爱框框
算法
丶Darling.19 分钟前
LeetCode Hot100 | Day1 | 二叉树:二叉树的直径
数据结构·c++·学习·算法·leetcode·二叉树
labuladuo52029 分钟前
Codeforces Round 977 (Div. 2) C2 Adjust The Presentation (Hard Version)(思维,set)
数据结构·c++·算法
Indigo_code40 分钟前
【数据结构】【链表代码】合并有序链表
数据结构·windows·链表
jiyisuifeng199140 分钟前
代码随想录训练营第54天|单调栈+双指针
数据结构·算法
我言秋日胜春朝★1 小时前
【C++】红黑树
数据结构
꧁༺❀氯ྀൢ躅ྀൢ❀༻꧂1 小时前
实验4 循环结构
c语言·算法·基础题
新晓·故知1 小时前
<基于递归实现线索二叉树的构造及遍历算法探讨>
数据结构·经验分享·笔记·算法·链表
总裁余(余登武)1 小时前
算法竞赛(Python)-万变中的不变“随机算法”
开发语言·python·算法