当前位置:首页 » 《我的小黑屋》 » 正文

[收藏吃灰系列]C语言字符串函数详解_byte_weibo的博客

7 人参与  2021年12月23日 16:32  分类 : 《我的小黑屋》  评论

点击全文阅读


字符串函数详解及模拟实现

  • 前言
  • 求字符串长度
    • strlen
      • 介绍
      • 作用
      • 注意事项
      • 模拟实现
  • 长度不受限制的字符串函数
    • strcpy
      • 介绍
      • 作用
      • 注意事项
      • 模拟实现
    • strcat
      • 介绍
      • 作用
      • 注意事项
      • 模拟实现
    • strcmp
      • 介绍
      • 作用
      • 模拟实现
  • 长度受限制的字符串函数
    • 说明
    • strncpy
      • 介绍
      • 注意事项
      • 模拟实现
    • strncat
      • 介绍
      • 注意事项
      • 模拟实现
    • strncmp
      • 介绍
      • 模拟实现
  • 字符串查找函数
    • strstr
      • 介绍
      • 模拟实现
    • strtok
      • 介绍
  • 错误信息报告
    • strerror
      • 介绍
  • 内存操作函数
    • memcpy
      • 介绍
      • 模拟实现
    • memmove
      • 介绍
      • 模拟实现
    • memset
      • 介绍
      • 模拟实现
    • memcmp
      • 介绍
      • 模拟实现

前言

C语言中有许多库函数,有我们常见的IO函数(printf,scanf),时间函数(time),数学函数(fabs),字符串函数等等。
C语言中对字符和字符串的处理很是频繁,但是C语言本身没有字符串类型,字符串通常放在常量字符串中或者字符数组中。
字符串常量适用于那些对它不做修改的字符串函数
这篇文章会为你介绍字符串函数,来帮助你更好的理解学习字符串函数。

求字符串长度

strlen

介绍

strlen的参数:size_t strlen( const char *string );
strlen函数位于<string.h>头文件下。其返回值是求得字符串的长度。
PS:长度一定是正数,size_t是通过宏定义实现的,其类型是unsigned int无符号整型。本次说明之后本文将不会再介绍
size_t

作用

计算字符串长度
strlen

strlen对于接受到的地址以1个字节为单位向前计算,当遇到终止符’\0’时停止计算。

注意事项

如果输入的字符串没有确切的大小,比如未初始化等,那么返回的会是随机值,因为只有遇到’\0’,函数才会停止。如图:
在这里插入图片描述
并且,由于strlen返回值是无符号整数,其相减结果也一定会是正整数,所以不能随意的相减,乘负数也不可以。如图:
在这里插入图片描述
当然,可以通过强制类型转换来得到想要的结果
在这里插入图片描述

模拟实现

知道了原理,很容易实现strlen
有三种实现方法,分别是

  1. 计数
  2. 指针减去指针(指针减去指针为两个地址之间的元素个数)
  3. 递归
    代码如下
size_t my_strlen1(const char* str) {//计数法,遇到不是\0的数便+1
	char *s = (char*)str;
	size_t count = 0;
	while (*s++ != '\0')
		count++;
	return count;
}
size_t my_strlen2(const char* str) {//地址减去地址是两个地址之间的元素个数
	char *s = (char*)str;
	while (*s++ != '\0');
	return (size_t)(s - str)-1;//这里-1是因为此时s在\0处
}
size_t my_strlen3(const char* str) {//递归方法
	if (*str == '\0')
		return 0;
	else
		return 1 + my_strlen3(str + 1);
}

执行结果如下:
在这里插入图片描述

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

strcpy

介绍

strcpy的参数:char *strcpy( char *str1, const char *str2 );
strcpy函数位于<string.h>头文件下。其返回值是char*类型指针。

作用

将字符串str2的内容复制到字符串str1里。

注意事项

  1. strcpy会将\0也放进去
    在这里插入图片描述

  2. strcpy不会在意空间大小,将8字节的字符串放进4字节大小中,代码仍然能够执行且会强制放进去,结果会报错,因为“放不下”;所以目标空间必须足够大
    在这里插入图片描述

  3. (字符串必须以\0结束)字符串str2应该有具体大小,类似strcpy,否则会将随机值放入str1中并报错:因为和strcpy一样,只会str2遇到了\0才会停止复制
    在这里插入图片描述

模拟实现

代码:

char* my_strcpy(char* s1, const char*s2) {
	assert(s1&&s2);//断言函数,判断空指针
	char* ret = s1;
	while (*s1++ = *s2++)
		;
	return ret;
}

结果:
在这里插入图片描述

strcat

介绍

strcat的参数:char *strcat( char *str1, const char *str2 );
strcat函数位于<string.h>头文件下。其返回值是char*类型指针。

作用

将字符串str2追加到str1后面,并返回str1的指针。
在这里插入图片描述

注意事项

  1. str1要预留一定空间给str2的位置,否则会越界。
    在这里插入图片描述
  2. str2 需要有固定的大小,否则会将随机值录入,且会有越界的风险。
    程序崩溃
    在这里插入图片描述
    解决方案:放入\0即可
    在这里插入图片描述

模拟实现

代码:

char* my_strcat(char* str1, const char* str2) {
	assert(str1 && str2);//断言
	char* ret = str1;//保留str1地址
	while (*str1)
		str1++;
	//找到了str1的\0的位置
	while (*str1++ = *str2++)
		;
	//追加
	return ret;
}

结果:
在这里插入图片描述

strcmp

介绍

strcmp的参数:int strcmp( const char *str1, const char *str2 );
strcmp函数位于<string.h>头文件下。

作用

比较字符串大小内容(不是长度)(参考字典序)(ASCII值),若str1>str2,返回一个大于0的数,若str1=str2,返回0,若str1《str2,返回小于0的数。
通常会返回1,0,-1。
在这里插入图片描述

模拟实现

代码:

int my_strcmp (const char* str1,const char* str2) {
	assert(str1 && str2);
	while (*str1 && *str2){//遍历
		if (*str1 > *str2)//大于返回1
			return 1;
		else if (*str1 < *str2)//小于返回-1
			return -1;
		*str1++;//等于则继续
		*str2++;
	}
	return 0;//遍历结束,相等,返回0
}

结果:
在这里插入图片描述

长度受限制的字符串函数

说明

因为strcpy,strcat,ctrcmp长度不受限制,在上面的三个函数的注意事项里面也已经提到了,所以这些函数并不安全,会出现一些问题,比如越界访问。
所以,为了更好的使用函数,C语言又规定了一些长度受限制相对安全的函数使用,接下来将会介绍这些函数。
注意,并不是绝对安全

strncpy

介绍

strncpy的参数:
char *strncpy( char *str1, const char *str2, size_t count );
和strcpy一样,位于<string.h>头文件下。
相对于strcpy,多了一个count参数,其作用是规定复制count个字节,用于解决strcpy不看大小直接复制容易出错的问题
在这里插入图片描述

注意事项

  1. 若count大于str2的长度,即要复制进去的内容不够,那么会补’\0’
    当然,如果str2并未规定大小,那么这种情况下不会录入\0,而是会存入随机值。
    在这里插入图片描述
    在这里插入图片描述

模拟实现

代码:

char * my_strncpy(char * str1, const char *str2, size_t n)
{
	assert(str1 && str2);

	char *ret = str1;
	for (size_t i = 0; i < n; i++) {
		if (n>strlen(str1)){
			*(str1 + i) = '\0';//对超出部分进行处理
		}
		else{
			*(str1 + i) = *str2++;
		}
	}
	return ret;
}


结果:
在这里插入图片描述

strncat

介绍

strncat的参数:
char *strncat( char *str1, const char *str2, size_t count );
位于<string.h>头文件下。
和strncpy相对于strcpy一样,strncat多了一个count参数,其作用是规定追加count个字节。

注意事项

  1. strncat并不是真的追加count个字节,而是会帮你多放了个’\0’进去,用来结束字符串。
    在这里插入图片描述
  2. strncat最多只会追加count+1个字符,如果count大于要追加的字符串长度,则无事发生
    在这里插入图片描述

模拟实现

代码:

char* my_strncat(char* str1, const char* str2,size_t count) {
	assert(str1 && str2);//断言
	char* ret = str1;//保留str1地址
	while (*str1)
		str1++;
	//找到了str1的\0的位置
	while(count--){
		if (*str2 == '\0'){//处理count过大的情况
			*str1 = '\0';
			break;
		}
		else{
			*str1++ = *str2++;
		}
	}
	//追加
	return ret;
}

结果:
在这里插入图片描述

strncmp

介绍

strncmp的参数:
char *strncat( const char *str1, const char *str2, size_t count );
位于<string.h>头文件下。
多了一个count参数,其作用是比较count个字节。
在这里插入图片描述

模拟实现

代码:

int my_strncmp(const char* str1, const char* str2,size_t count) {
	assert(str1 && str2);
	while (count--){//遍历
		if (*str1 > *str2)//大于返回1
			return 1;
		else if (*str1 < *str2)//小于返回-1
			return -1;
		*str1++;//等于则继续
		*str2++;
	}
	return 0;//遍历结束,相等,返回0
}

结果:
在这里插入图片描述

字符串查找函数

顾名思义,用于查找字符串的函数

strstr

介绍

参数:char *strstr( const char *str1, const char *str2 );
在str1中查找str2是否存在,返回str1中str2第一次出现的位置的起始地址,否则返回空指针。
在这里插入图片描述

模拟实现

代码:

char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);
	char* start = (char*)str1;//在这里需要强制类型转换成char*
	char* find = (char*)str2;//寻找的字符串
	char* p = (char*)str1;//cp就是用来保存首地址的
	while (*p)
	{
		start = p;//吧p赋给start,用start来寻找,防止找不到已经找到str2的起始点
		while (*start != '\0' && *find != '\0' && *start == *find)
		{
			//循环,寻找字符串
			start++;
			find++;
		}
		if (*find == '\0')//find遇到了\0,意味着找到了
		{
			return p;//返回此时的p
		}
		find = (char*)str2;//没找到,但是find可能已经改变过,所以重新赋值
		p++;//p++可以得到原起始位置的下一个位置
	}
	return NULL;
}

结果:
在这里插入图片描述

strtok

介绍

strtok的参数:char *strtok( char *str1, const char *str2 );
在str1中寻找和str2其中相等的字符然后置为\0

  1. 第一个参数str1指定一个字符串,它包含了0个或者多个由str2字符串中一个或者多个分隔符分割的标记。
  2. strtok函数找到str1中的下一个标记,井将其用\0结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
  3. strtok函数的第一个参数不为NULL,函数将找到str1中第一个标记,strtok函数将保存它在字符串中的位置。
  4. strtok函数的第一个参数不为NULL,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
  5. 如果字符串中不存在更多的标记,则返回NULL指针。
  6. 这是用static静态变量实现的。
    在这里插入图片描述

错误信息报告

strerror

介绍

参数:char *strerror( int errnum );
strerror函数从内部数组中搜索错误号 errnum,并返回一个指向错误消息字符串的指针。strerror 生成的错误字符串取决于开发平台和编译器。
在这里插入图片描述

内存操作函数

memcpy

介绍

参数:void *memcpy( void *str1, const void *str2, size_t count );

  1. 函数memcpy从str2的位置开始向后复制count个字节的数据到str1的内存位置。
  2. 这个函数在遇到’\0’的时候并不会停下来。
  3. 如果str2和str1有任何的重叠,复制的结果都是未定义的。
  4. memcpy能够拷贝的数据不会受限制,可以拷贝多种类型的数据
    在这里插入图片描述

模拟实现

代码:

void* my_memcpy(void* arr1, const void* arr2, size_t count) {
	assert(arr1 && arr2);
	void* ret = arr1;
	while(count--) {
		*(char*)arr1 = *(char*)arr2;
		arr1 = (char*)arr1 + 1;
		arr2 = (char*)arr2 + 1;
	}
	return ret;
}

结果:
在这里插入图片描述

memmove

介绍

参数:void *memmove( void *str1, const void *str2, size_t count );
其作用和memcpy类似,都是复制拷贝。
但是memmove比memcpy要更强,因为在部分情况下,memcpy无法完成覆盖拷贝。如图。
在vs下,memcpy可以完成覆盖拷贝,但是在某些编译器下无法完成,所以在这种情况下尽量使用memmove。
在这里插入图片描述

模拟实现

对于数据重复区间的情况进行特殊处理
代码:

void* my_memmove(void* dest, const void* src, size_t count) {
	void *ret = dest;
	if (dest < src){//从前向后拷贝

		while (count--) {
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	else{//从后向前拷贝
		while (count--) {
			*((char*)dest + count) = *((char*)src+count);
		}
	}
	return ret;
}

结果:
在这里插入图片描述

memset

介绍

参数:void *memset( void *dest, int c, size_t count );
该函数可以将dest开始的count个字节全部置为c
在这里插入图片描述

模拟实现

代码:

void* my_memset(void *arr, const int c, size_t count){
	void*ret = arr;
	for (size_t i = 0; i < count; i++){
		*((char*)arr + i) = c;
	}
	return ret;
}

结果:
在这里插入图片描述

memcmp

介绍

参数:int memcmp(const void *str1, const void *str2, size_t n);
C 库函数 memcmp 把存储区 str1 和存储区 str2 的前 n 个字节进行比较。

模拟实现

代码:

void* my_memcpy(void* dest, const void* src, size_t n)
{
	assert(dest);
	assert(src);
	char* pdest = (char*)dest;
	const char* psrc = (const char*)src;
	while (n--)
	{
		*pdest++ = *psrc++;
	}
	return dest;`

🐂🐸完结撒花
彩蛋就是没有彩蛋
在这里插入图片描述


点击全文阅读


本文链接:http://zhangshiyu.com/post/32043.html

字符串  函数  介绍  
<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1