IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 常用字符串和内存函数是如何工作的Do you really know? -> 正文阅读

[C++知识库]常用字符串和内存函数是如何工作的Do you really know?

前言:

C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在 常量字符串 中或者 字符数组 中。
字符串常量适用于那些对它不做修改的字符串函数.


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

1.1字符串求长——strlen函数

1.1.1strlen函数的介绍

size_t strlen ( const char * str );

这里我们可以看到strlen函数的返回值是size_t
🌕:1.字符串已经 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中 ‘\0’ 前面出现的字符个数(不包
含 ‘\0’ )。
🌕:2.参数指向的字符串必须要以 ‘\0’ 结束。
🌕:3.注意函数的返回值为size_t,是无符号的( 易错 )
例子:👇

#include <stdio.h>
#include <string.h>
int main()
{
	if (strlen("abc") - strlen("abcdef") > 0)
	{
		printf("hehe\n");
	}
	else
	{
		printf("haha\n");
	}
	return 0;
}

这里输出的结果是hehe,因为返回的是无符号整数,这两哥字符串无论怎么减都是正数。


1.1.2strlen函数的模拟实现

这里直接计算个数即可,较为简单,不做赘述。

#include<stdio.h>
#include<assert.h>
size_t my_strlen(const char* str)
{
	int count = 0;
	assert(str != NULL);
	while (*str)
	{
		count++;
		str++;
	}
	return count;
}
int main()
{
	char arr[] = "pwh";
	int len = my_strlen(arr);
	printf("%d", len);
	return 0;
}

1.2字符串拷贝——strcpy函数

1.2.1strcpy函数的介绍

🌕:strcpy把含有’\0’结束符的字符串复制到另一个地址空间,返回值的类型为char*。

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

需要注意的是:👇
1.源字符串必须以 ‘\0’ 结束。
2.会将源字符串中的 ‘\0’ 拷贝到目标空间。
3.目标空间必须足够大,以确保能存放源字符串。
4.目标空间必须可变。

为什么返回值为char*

返回strDest的原始值使函数能够支持链式表达式,增加了函数的“附加值”。同样功能的函数,如果能合理地提高的可用性,也就就更加理想。

int iLength=strlen(strcpy(strA,strB));
char * strA=strcpy(new char[10],strB);

举例:👇

char a[10],b[]={"PWH"};
//定义字符数组a,b
strcpy(a,b);
//将数组b中的PWH复制到数组a中

1.2.2strcpy函数的模拟实现

该代码与strlen函数类似,只需要注意代码的严谨,源地址的不可变性,拷贝至’\0’,数组空间够大即可。且一定要是字符数组!

#include<stdio.h>
#include<assert.h>
char* my_strcpy(char* dest, const char* src)
{
	assert(dest != NULL);
	assert(src != NULL);
	char* ret = dest;
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}
int main()
{
	char arr1[] = "abcdefghi";
	char arr2[] = "bit";
	my_strcpy(arr1, arr2);
	printf("%s", arr1);
	return 0;
}

1.3字符串比较——strcmp函数

1.3.1strcmp函数的介绍

 int strcmp(const char *s1,const char *s2);

🌕:1.第一个字符串大于第二个字符串,则返回大于0的数字
🌕:2.第一个字符串等于第二个字符串,则返回0
🌕:3.第一个字符串小于第二个字符串,则返回小于0的数字
🔓:注意:
比较的是字符串的ASCII码值!
从第一个字符开始比较,第一个相等就从第二个字符比较,第一个大于返回正数,小于返回负数;然后从第二个开始比较。

#include<stdio.h>
#include<string.h>
int main()
{
	char* p1 = "abcdef";//6
	char* p2 = "aqwer";//5
	//if("abcdef"=="sqwer")//比较的是地址,所以肯定不相等
	int ret = strcmp(p1, p2);//strcmp比较的是每个字符的ASCII码值
	//从第一个开始比较,第一个相等就从第二个字符比较,第一个大于返回1,小于返回-1;然后从第二个
	printf("%d\n", ret);
	return 0;
}

1.3.2strcmp函数的模拟实现

strcmp函数的实现思路为定义两个数组,且都为不可变,使用while循环从第一个字符开始比较,如果相等则返回0,如果不相等则继续比较下个字符,字符地址+1,如果大于,返回正数,小于返回负数。

#include<stdio.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++;
	}
	if (*str1 > *str2)
	{
		return 1;//大于
	}
	else
	{
		return -1;//小于
	}
}
int main()
{
	char* p1 = "abcdef";
	char* p2 = "abqwe";
	int ret = my_strcmp(p1, p2);
	printf("ret=%d\n", ret);
	return 0;
}

1.4字符串连接——strcat函数

1.4.1strcat函数的介绍

char *strcat(char *dest, const char *src);

🌕:把src所指向的字符串(包括“\0”)复制到dest所指向的字符串后面(删除dest原来末尾的“\0”)。要保证dest足够长,以容纳被复制进来的*src。*src中原有的字符不变。返回指向dest的指针。
举例:👇

#include <stdio.h>
#include <string.h>
int main ()
{
   char src[50]="source"; 
   char dest[50]="destination";
   strcat(dest, src);
   printf("最终字符串:%s", dest);
   return 0;
}

1.4.1strcat函数的模拟实现

🌕:定义两个数组,首先遍历到’\0’,然后和strcpy函数相似开始拷贝即可。

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

char* my_strcat(char *dest, const char *src)
{
    assert(dest && src);
    char *tmp = dest;
    while (*tmp != '\0')
    {
    	tmp++;
    }       
    while ((*tmp++ = *src++) != '\0');
    return dest;
}
int main()
{
    char arr1[10] = "pwh";
    char arr2[] = "hxs";
    char* arr3 = my_strcat(arr1, arr2);
    printf("str1=%s\n", arr1);
    printf("str2=%s\n", arr2);
    printf("str3=%s\n", arr3);
    return 0;
}

1.5字符串查找——strstr函数

1.5.1strstr函数的介绍

🌕:返回字符串中首次出现子串的地址
举例:

int main()
{
	char* p1 = "abcdefghi";
	char* p2 = "defq";
	char* ret = strstr(p1, p2);//查找第一次出现的位置
	if (ret == NULL)
	{
		printf("子串不存在\n");
	}
	else
	{
		printf("%s\n", ret);
	}
	return 0;
}

1.5.1strstr函数的模拟实现

思路:
尽量不要让p1,p2走,这样无法记住它的位置
当相等时,cur作为一个大前提,先在cur的位置使用s1 s2进行查找,如果没找到,cur++,从下一个字符查找。
这里定义了cur s1 s2,这样可以互不干扰,然后s1重新等于cur,从此位置++。

char* my_strstr(const char* p1, const char* p2)
{
	assert(p1 != NULL);
	assert(p2 != NULL);
	//这样写可以互不干扰,适用于比较复杂的场景
	char* s1 = p1;
	char* s2 = p2;
	char* cur =(char*) p1;
	if (*p2 == '\0')
	{
		return p1;
	}
	while (*cur)
	{
		s1 = cur;
		s2 = (char*)p2;
		while (*s1 && *s2 && (*s1 == *s2))
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			return cur;
		}
		cur++;
	}
	return NULL;
}
int main()
{
	char* p1 = "abbbcdef";
	char* p2 = "bbc";
	char* ret = my_strstr(p1, p2);//查找第一次出现的位置
	if (ret == NULL)
	{
		printf("子串不存在\n");
	}
	else
	{
		printf("%s\n", ret);
	}
	return 0;
}

二、长度受限制的字符串函数

2.1strncpy函数

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

2.1.1与strcpy的区别

🌕:拷贝num个字符从源字符串到目标空间。
🌕:如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。
🌕:strncpy越界之后,拷贝完毕会把数组后面的内容全部变为0。

2.1.2strncpy函数的模拟实现

只需要在strcpy基础上加一个n的限制条件即可。

#include<stdio.h>
#include<assert.h>
char*my_strncpy(char*dest, const char*src, size_t n)
{
	assert(dest != NULL);
	assert(src != NULL);
	char*ret = dest;
	while (n)
	{
		*dest = *src;
		src++;
		dest++;
		n--;
	}
	return ret;
}
 
int main()
{
	char arr1[20] = "hxs";
	char arr2[20]= "pwh";
	int n = 0;
	printf("请输入需要拷贝的字符个数:\n");
	scanf("%d", &n);
	char*ret = my_strncpy(arr1,arr2,n);
	printf("%s\n", ret);
	return 0;
}

2.2strncat函数

2.2.1与strncat的区别

🌕:strncat越界之后,追加完毕后依旧会自动添加’\0’,但和strncpy区别在于,不会把数组后面的元素变为0

2.2.1strncat函数的模拟实现

先遍历到目标字符串\0处,加一个n的限制条件即可。

#include <stdio.h>
#include <assert.h>
char* my_strncat(const char* dest, const char* src,unsigned n)
{
	assert(dest && src);
	char* tmp = dest;
	while (*tmp != '\0')
	{
		tmp++;
	}
	while (n)
	{
		*tmp = *src;
		tmp++;
		src++;
		n--;
	};
	return dest;
}
int main()
{
	char arr1[10] = "pwh";
	char arr2[10] = "hxs";
	int n = 0;
	printf("请输入你要拷贝的字符个数");
	scanf("%d", &n);
	my_strcat(arr1, arr2,n);
	printf("arr1=%s\n", arr1);
	return 0;
}

2.3strncmp函数

2.3.1与strcmp的区别

🌕:比较的字符数可控。

2.3.2strncmp函数的模拟实现

没什么好说的,就是很简单!这里用for循环更加方便一些。

#include<stdio.h>
#include<assert.h>
int my_strncmp(const char* str1, const char* str2,unsigned n)
{
	assert(str1 && str2);
	//比较
	unsigned i = 0;
	for (i = 0; i < n - 1 && *str1 && *str2; i++)
	{
		if (*str1 != *str2)
		{
			break;
		}
		str1++;
		str2++;
	}
	return (*str1 - *str2);
}
int main()
{
	char* p1 = "abcdef";
	char* p2 = "abqwe";
	int n = 0;
	printf("请输入要检验的字符数");
	scanf("%d", &n);
	int ret = my_strcmp(p1, p2,n);
	printf("ret=%d\n", ret);
	return 0;
}

三、内存函数

内存函数可以对任意类型的值操作,而字符串函数只能对字符串操作!

3.1memcpy函数

3.1.1memcpy函数的介绍

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

🌕:1.函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
🌕:2.这个函数在遇到 ‘\0’ 的时候并不会停下来。
🌕:3.如果source和destination有任何的重叠,复制的结果都是未定义的。

3.1.2memcpy函数的模拟实现

将所传入的指针强制类型转换为char*类型,这样可以对于各种类型的数据进行操作,其余过程和strncpy函数比较相似。

struct S
{
	char name[20];
	int age;
};
void my_memcpy(void* dest, const void* src, size_t num)//void类型不可解引用也不能算术运算
{
	assert(dest);
	assert(src);
	void* ret = dest;
	while (num--)
	{
		*(char*)dest = *(char*)src;
		++(char*)dest;
		++(char*)src;
	}
	return dest;
}
int main()
{
	struct S arr3[] = { {"张三",20},{"李四",30} };
	struct S arr4[3] = { 0 };
	my_memcpy(arr4, arr3, sizeof(arr3));
	//char *dest, const char*src
	//memcpy(arr2, arr1, sizeof(arr1));
	printf("%d", arr4->age);
	return 0;
}

3.2memove函数

3.2.1memove函数的介绍

void *memmove( void* dest, const void* src, size_t count );

memmove用于拷贝字节,如果目标区域和源区域有重叠的话,memmove能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中,但复制后源内容会被更改。但是当目标区域与源区域没有重叠则和memcpy函数功能相同。

3.2.2memove函数的模拟实现

实现思路较为复杂,需要考虑两种情况,所重复的部分是在拷贝内容之前还是之后?
那么可以分为向前拷贝或者向后拷贝两种解决方法!
在这里插入图片描述

void my_memove(void* dest, const void* src, size_t num)
{
	assert(dest);
	assert(src);
	//分情况,重合的部分在拷贝内容之前还是之后
	void* ret = dest;//dest指向的首地址,也就是总共4字节的首地址
	//之前
	if (dest<src)
	{
		while (num--)
		{
			*(char*)dest = *(char*)src;
			++(char*)dest;
			++(char*)src;
		}
	}
	else
	{
		while (num > 3)//总共需要改变17个字节
		{
			*((char*)dest + num - 4) = *((char*)src + num - 4);
			num--;
		}
	}
	//之后,从后向前面拷贝
	return ret;

}
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memove(arr+2, arr, 20);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

本章完!
在这里插入图片描述

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-09-23 11:16:21  更:2021-09-23 11:18:27 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 23:15:00-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码