字符串和内存函数

目录

strlen

模拟实现

长度不受限字符串函数 

strcpy

模拟实现

​编辑 strcat

模拟实现

 strcmp

 模拟实现

长度受限字符串函数

strncpy

模拟实现

strncat

strncmp

strstr 

模拟实现

 strtok

strerror

perror

字符分类函数

字符转换

示例:

​编辑内存函数

memcpy

模拟实现 

memmove

 模拟实现

memcmp

 memset


strlen

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

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

易错: 

 字符串和内存函数_第1张图片

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

模拟实现

#include 
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则会报错。目标空间要足够大,且可变

 易错:

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

模拟实现

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

测试结果: 

字符串和内存函数_第2张图片 strcat

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

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

易错:

 追加位置在\0处:

字符串和内存函数_第3张图片

模拟实现

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去追加自身,否则会造成死循环!

字符串和内存函数_第4张图片

 strcmp

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

注意:都要包含\0。

 模拟实现

//写法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;
}
//写法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。 

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,可以自己增添自己。

字符串和内存函数_第5张图片

strncmp

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

 

strstr 

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

字符串和内存函数_第6张图片

模拟实现

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

字符串和内存函数_第7张图片

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;
}

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

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会改变原数据的内容!一般修改临时拷贝的内容。

 字符串和内存函数_第8张图片

使用方法:

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

 测试:字符串和内存函数_第9张图片

 发现确实实现了分割并返回,但并不完全,我们可以写个循环:
字符串和内存函数_第10张图片

strerror

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

字符串和内存函数_第11张图片

perror

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

字符串和内存函数_第12张图片

字符分类函数

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) 

字符转换

int tolower(int c):转化字符为小写字母; 
int toupper(int c):转化字符为大写字母;

示例:

字符串和内存函数_第13张图片
内存函数

memcpy

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

模拟实现 

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;
}

字符串和内存函数_第14张图片

memmove

使用memcpy拷贝空间出现重叠时分两种情况:
字符串和内存函数_第15张图片

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

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

 模拟实现

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

字节初始化内存空间。

字符串和内存函数_第16张图片

测试: 

字符串和内存函数_第17张图片 

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

 

你可能感兴趣的:(算法,数据结构)