字符串函数以及内存操作函数

本文详细介绍了C语言中的字符串函数(如strlen,strcpy,strncpy,strcat,strncat,strcmp,strstr等)和内存操作函数(memcpy,memmove,memset,memcmp),包括它们的用途、参数和注意事项,以及如何进行模拟实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、字符串函数:

1、stlren函数:

求字符串长度;

2、strcpy函数:

复制字符串(拷贝字符串);

3、strncpy函数:

有限制的复制字符串:

4、strcat函数:

连接字符函数——>将源字符串放到目标空间末尾;将str追加到dest末尾;(指的是字符串,字符的末尾,而不是在\0后面)

5、strncat函数:

有限制的追加函数,

 6、strstr函数:

字符串查找函数

7、strtok函数:

分割字符串:

注意:

 牛客网实战!!!!!!

​​​​​编辑

8、总结:

二、内存操作函数:

1、mencpy函数:

内存拷贝:

2、memmove函数:

空间发生重叠的拷贝:

1号图——第一种情况;

2号图——第二种情况;

3号图——第三种情况;

总结:

3、memset函数:

设置内存函数:

4、memcmp函数:

内存块比较函数:

5、总结:

三、综上所诉



一、字符串函数:

这类函数都用到了统一的 头文件

#include<string.h>

1、stlren函数:

求字符串长度;

关于定义-——>

//size_t 代表的是无符号整形;
size_t strlen(const char *s);

传上一个字符串给指针s,计算到\0之前字符个数;即不包括\0;传回的长度一直是正整数;

关于模拟实现——>https://blog.youkuaiyun.com/SWsunlight/article/details/136563759

2、strcpy函数:

复制字符串(拷贝字符串);

//返回类型为char* 
char* stecpy(char*dest,const char*str);

其中dest目标空间str源空间,将str的内容复制到dest中;

注意:传给str的字符串必须有\0;且目标空间足够大,能够装下需要复制的字符串;

3、strncpy函数:

有限制的复制字符串:

char* strncpy(char* dest, const char* str, size_t n);

相较于strcpy,多一个参数n,代表要复制str中字符的个数;        

满足如下规则:当字符串str的长度小于n时,因为后面的字符不够复制到dest中,所以会补\0;

                          当字符串str的长度大于n时,复制到那个字符就是那个字符,后面不会补\0;

模拟实现》》》》》》》》

#include<assert.h>
char* my_strncpy(char* dest, const char* str, size_t n)
{
	//断言,防止传参,传的空指针;
	assert(dest && str);
	char* p = dest;
	while (n--)
	{
		*dest++ = *str++;
	}
	return p;
}

int main()
{
	char arr1[20] = { "xx" };
	char arr2[] = { "abcdefghj" };
	char* p = my_strncpy(arr1,arr2,4);
	printf("%s\n", p);

	return 0;
}

为了更好观察,将arr1[20]的数组中赋值很多x,看下图,会发现复制到达字符d后,后面没有补\0;

4、strcat函数:

连接字符函数——>将源字符串放到目标空间末尾;将str追加到dest末尾;(指的是字符串,字符的末尾,而不是在\0后面)

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

5、strncat函数:

有限制的追加函数,

char* strcat(char* dest, const char* str,size_t n);

n表示将str中n个字符追加到dest末尾;

若是 str >n;截掉超出部分字符;

若是 str <n;则追加并且补\0;

char* my_strncat(char* dest, const char* str,size_t n)
{
	//断言
	assert(dest && str);
	char *p = dest;
	//将dest的位置(地址)移动到\0处;
	while (*dest)
		dest++;	
	while (n--)
	{
		if(*str == '\0')
			*dest++ = '\0';
		else
			*dest++ = *str++;
	}
	return p;
}
int main()
{
	char arr1[20] = { "xxxxx" };
	char arr2[] = { "abcdefghj" };
	char* p = my_strncat(arr1,arr2,4);
	printf("%s\n", p);

	return 0;
}

6、strcmp函数:

字符串比较函数》》    对字符串进行比较,从第一个字符开始,对应的字符进行比较,第一次遇到不同的字符时,谁大,则说明那个字符串大;(本质上是ASCII码值进行比较

int strcmp(const char*dest,const char*str);

 若 dest > str  则 返回整数;

 若 dest str  则 返回  0   

 若 dest < str  则 返回整数;

模拟实现:

#include<stdio.h>
#include<assert.h>
//模拟实现字符串大小比较
int my_strcmp(const char* s1, const char* s2)
{
	//断言
	assert(s1 && s2);
	//相等则进入循环,直到找到不相等的
	while (*s1 == *s2)
	{
		//相等就返回0;因为进入循环前提条件为*s1==*s2无论是谁等于\0都行的,所以写一个就够了;
		if (*s1 == '\0')
		{
			return 0;
		}
		*s1++;
		*s2++;
	}
	if (*s1 - *s2 > 0)
	{
		return  1;
	}
	else
	{
		return -1;
	}
}

 6、strstr函数:

字符串查找函数

        返回第一次相同的字符以及后面的内容(即第一次找到时的首元素地址)

没找到则返回空指针

char* strstr(const char* dest, const char* str);

两个空间都不需要进行改变,所以const锁死他两个;

模拟实现

/指针str1是目标空间,str2是源字符串
char* my_strstr(const char* str1, const char* str2)
{
	
	const char* dest1 = NULL;
	const char* dest2 = NULL;
	const char* stu = str1;//将空间指针地址放入stu
	while (*stu)
	{
		dest1 = stu;
		dest2 = str2;
		while(*dest1 != '\0' && *dest2 != '\0' && *dest1 == *dest2)
		{
			
			dest1++;
			dest2++;
		}
        //找到以后则此时*dest2为\0;
		if (*dest2 == '\0')
		{
            //返回的就是第一次找到的时首元素首地址
			return stu;
		}
		stu++;
	}
	return NULL;
}


int main()
{
	char* sz = my_strstr("abbcde", "bcd");
	printf("%s\n", sz);


	return 0;
}

尽量不去改变传上来的参数;因此设置了临时变量来分别接受目标空间和源空间;还有一个变量stu则是存放当遇到需要查找的字符串时的地址;

7、strtok函数:

分割字符串:

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

 sep里面为分割符(标记),可以是一个或者多个,str则是指定一个字符串,里面可以包含0个或者多个sep里面的分割符,

str从首元素地址开始遍历,若是遇到分割符(标记)将str中这个字符标记,并将这个标记改成\0并返回指向这个标记的指针;strtok会改变被操作的字符串,所以使用它进行切分的字符串一般都是临时拷贝的内容且可以修改,也就是单独创建了一个指针ret来接受返回的字符串


注意:

strtok第一个传参在对同一个字符串使用时第一次不能为NULL,strtok函数找到str中第一个标记,并会保存改位置,接下来,想要继续后面的进行标记,就传NULL,会在上次分割的位置继续进行下去,当遇到标记时,再次分割出新的字符串拷贝到ret里面   如下显示!!

当不存在更多标记时,返回NULL

不需要标记符时,不要给sepNULL,而是传 “ ” (空字符)因为本身传的参数是字符串(字符)!!!!


 牛客网实战!!!!!!

​​​​

8、总结:

//求字符串长度
size_t strlen(const char* s);
//进行字符串复制(拷贝)
char* stecpy(char* dest, const char* str);
//有条件限制的复制
char* strncpy(char* dest, const char* str, size_t n);
//字符串的追加
char* strcat(char* dest, const char* str);
//有条件的追加
char* strcat(char* dest, const char* str,size_t n);
//字符串大小的比较
int strcmp(const char* dest, const char* str);
//字符串的查找
char* strstr(const char* dest, const char* str);
//字符串分割
char* strtok(char* str, const char* sep);

二、内存操作函数:

针对内存进行操作的函数,在内存中以字节为单位进行操作,对于任何数据类型都能进行操作;头文件同上是一个;(dest目标空间,str源空间,不再作解释

1、memcpy函数:

内存拷贝:

针对内存块进行拷贝,将源空间的拷贝到目标空间,接受任何类型数据

void* memcpy(void* dest, const void* str, size_t num);

注意:目标空间与源空间不能冲重叠(这里的变量num 表示的是长度,即字节个数

模拟实现:

void* my_memcpy(void* s1, const void* s2,size_t num)
{
	//断言
    assert(s1 && s2);
    //将源空间存放在一个void*指针中,保证传参回去时,地址为s1的首地址
	void* ret = s1;
	while (num--)
	{
		*(char*)s1 = *(char*)s2;
		//不能写(char*)s1++;因为()强制性转换具有临时性,会变成(char*)s1 == s1+1;所以就得((char*)s1)++;
		s1 = (char*)s1 + 1;
		s2 = (char*)s2 + 1;
	}
	return ret;
}

需要注意的就是将void*指针转变char*,这样才能进行访问并且访问的字节数是以1为单位的

将每个字节的内容进行解引用拷贝到s1中,如此就能完成拷贝了;相当于把每个字节的代表的地址拷贝到进去

2、memmove函数:

空间发生重叠的拷贝:

若是空间发生重叠,也需要拷贝,就可以用到如下函数了

void* memmove(void* dest, void* str, size_t num);

关于模拟实现:

---->这个函数模拟实现有点难度;就是在交换时,要怎么保证拷贝能按理想情况进行?让我用粗糙的画图技巧来说简单讲讲,以int型数组进行讲解🐉🐉🐉🐉🐉

绿色框  (^///^) 就是要拷贝的内容·

1号图——第一种情况;

因为地址是有低地址向高地址建立的;将2  3  4拷贝到1  2   3 中,直接从前往后进行拷贝 (dest  <  str的情况),看图可以发现,重叠了,但是每次拷贝时,却没有覆盖我们需要的数 就是说,当 str 的 3 拷贝到 dest 的2时,2变成3,当 4 去替换dest的 3 时,都能替换;   


2号图——第二种情况;

(str < dest)  讲2 3 4 拷贝到 3 4 5  ,若是从前面往后拷贝,则 2 放到 3 的位置 ,当要将 3 放到 dest 的 4时,3 在第一次拷贝2的时候就被踢掉了,所以这样拷贝就会出现问题  —— 那么试下  从后往前  :先将 str 的 4 拷贝到 dest 的 5 ,然后接着 3 到 dest的4 ,最后就是 2 到dest 的 3 ; 

3号图——第三种情况;

没有相交的部分;无论从那边进行都行;


总结:

dest>str 时就是后往前进行拷贝;

dest<str 时就是前往后进行拷贝;

由此可以编写程序:

void* my_memmove(void* s1, void* s2, size_t num)
{
	assert(s1 && s2);
    //存放首元素地址
	void* p = s1;
	//s1<s2,从前往后;
	if (s1< s2)
	{
		while (num--)
		{
            //类型转换
			*(char*)s1 = *(char*)s2;
			s1 = (char*)s1 + 1;
			s2 = (char*)s2 + 1;
		}
	}
    //否则,从后往前
	else
	{  
		while (num--)
		{
            //将void*指针转换成char*,加字节数,就是最后一个元素地址,然后解引用
			*((char*)s1 + num) = *((char*)s2 + num);
		}
	}
	return p;
}

3、memset函数:

设置内存函数:

将内存块设置成想要的内容:

void* memset(void* ptr, int value, size_t num);

ptr指向的要填充要设置的内存块,value就是要设置的内容(可以传字符的,因为字符以ASCII码值进行存放的);num则是字节个数,不同类型的字节个数不一样

比如: int型的数组,你想要将一个元素设置为 1 ,则要 4 个字节;

4、memcmp函数:

内存块比较函数:

内存块间进行比较:

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

ptr1和ptr2指针指向的内容进行比较,到num个字节为止;

ptr1 >  ptr2返回 1
ptr1 = ptr2返回 0
ptr1 < ptr2返回-1

5、总结:

void* memcpy(void* dest, const void* str, size_t n);
//重叠空间下的内存拷贝
void* memmove(void* dest, void* str, size_t num);
//内存填充
void* memset(void* ptr, void* value, size_t num);
//内存块的比较
void* memcmp(const void* ptr1, const void* ptr2, size_t num);

注意:我们不是对解引用的值进行操作,而是对地址进行操作,因为内存函数是对字节进行操作,存储在内存中的数据可以是char int 型等等,所以这里是对目标和源空间 字节的地址进行操作 

并没有用到 *

三、综上所诉

        其实无论时那个函数的模拟实现,只要知道每个参数的任务,经过作图进行思考,明白其中的原理,注意容易出错的地方,模拟实现就很容易了,虽然对于这些函数只要有一定了解,但是仍然建议大家试着自己模拟实现,这样更深刻;加油!各位观众老爷们!!!!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值