C语言——字符串函数,内存函数集锦以及模拟实现

目录

前言

一、概括

二、字符串函数

2.字符串拷贝函数 strcpy与strncpy

3.拼接字符串函数 strcat与strncat

4.字符串比较函数 strcmp与strncmp

5.字符串中找子字符串 strstr

 6.切断字符串/查找字符串 strtok 

 7.错误信息报告 strerror

8.字符操作函数

8.1字符分类

8.2 字符转换

三、内存函数

1.内存拷贝函数 memcpy(来源与目标之间不能有重叠)

 2.内存拷贝函数 memmove(来源与目标之间可以有重叠)

 3.内存比较函数 memcmp

4.内存批量设置函数 memset 

总结


前言

在C语言学习过程中经常会零散的使用到字符串函数以及内存函数,本文梳理了常用的字符串函数以及内存函数,帮助巩固大家对于字符串和内存的学习,并且从原理上模拟实现部分函数,那就开始本文的学习吧!


一、概括

求字符串长度
        ०strlen
长度不受限制的字符串函数
        ०strcpy
        ०strcat
        ०strcmp
长度受限制的字符串函数介绍
        ०strncpy
        ०strncat
        ०strncmp
字符串查找
        ०strstr
        ०strtok
错误信息报告
        ०strerror
字符操作
内存操作函数
        ०memcpy
        ०memmove
        ०memset
        ०​​​​​​​memcmp

二、字符串函数

在C语言类里其实并没有字符串这个类型,在C中字符串通常表示字符数组以及常量字符串。

字符数组是可以改变的,而常量字符串用const char*定义作为常量无法被改变。

在介绍各种字符串函数之前,先要理解字符串数组的两种定义形式:

#define _CRT_SECURE_NO_WARNINGS 
#include<stdio.h>
int main() {

	char str1[] = { 'a','b','c','d','e'};
	char str2[] = "abcde";

	return 0;

}

先看str1的定义是单独将每一个字符赋给str1[]里,打开内存监视发现如下: 

 

 str2的定义是讲一整个字符串赋值给str2[],打开内存监视如下:

 

对比两者发现,str2会在最后末尾加上‘\0’,而str1并没有!

请务必记住'\0'这一点,对学习字符串函数有很大的影响 !


 1.求字符串长度函数 strlen 

 

size_t strlen ( const char * str );

strlen函数用于测量字符串的长度,可以看出参数是 const char* 我们可以理解为为了保护目标字符串不被破坏,传入的是字符串的起始地址并从起始地址开始测量,那么从哪里截至呢?到下一个‘\0’截至且不包含‘\0’

返回类型 size_t 就是无符号整型 

 注意:如果目标字符串是用 char str1[] = { 'a','b','c','d','e'}; 这种形式定义,那么在字符串后面并没有添加‘\0’所以 strlen(str1);就是随机值

改写方法:

char str1[] = { 'a','b','c','d','e','\0'}; 或者 就采用第二种形式进行定义。

运用演示:

#define _CRT_SECURE_NO_WARNINGS 
#include<stdio.h>
#include<string.h>
int main() {

	char str1[] = { 'a','b','c','d','e'};
	char str2[] = "abcde";
	int len1 = strlen(str1);
	int len2 = strlen(str2);
	printf("%d\n", len1);
	printf("%d\n", len2);
	return 0;

}

my_strlen的模拟实现:思路简单清晰,一个简单的递归,还有很多其他方法,都可以去尝试实现!

#define _CRT_SECURE_NO_WARNINGS 
#include<stdio.h>
#include<string.h>
#include<assert.h>
size_t my_strlen(const char* str) {
	assert(str);
	if (*str) {
		return 1 + my_strlen(str + 1);
	}
	else
	{
		return 0;
	}
}
int main() {

	char str1[] = { 'a','b','c','d','e'};
	char str2[] = "abcde";
	int len1 = strlen(str1);
	int len2 = strlen(str2);
	int len3 = my_strlen(str2);
	printf("%d\n", len1);
	printf("%d\n", len2);
	printf("%d\n", len3);

	return 0;

}

补充说明assert()的作用是断言字符串是否为NULL,如果对一个NULL的指向的地址进行操作会发生危险,所以要先判断是否为空,如果为空会报错,以防发生危险。

需要包含assert.h库

strlen注意事项:

1.字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包含 '\0' )。

2.参数指向的字符串必须要以 '\0' 结束。
3.注意函数的返回值为size_t,是无符号的( 易错 )

2.字符串拷贝函数 strcpy与strncpy

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

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

strcpy的作用是将一个字符串拷贝到另一个字符串中去包含’\0‘;

参数char* destination即目标字符串的首元素地址,const char* source 被拷贝的字符串首地址

返回值char* 即目标字符串的首地址。

strcpy作用演示 

#define _CRT_SECURE_NO_WARNINGS 
#include<stdio.h>
#include<string.h>
#include<assert.h>

int main() {
	char str1[] =  "#################";
	char str2[] = "123456789";
	strcpy(str1, str2);
	printf("%s", str1);
	return 0;
}

 

打开内存监视 

 

我们会发现前9项被赋为str2添加第十位'\0',其余位不变,但是用%s打印时只会打印到’\0‘截至   所以后面的#并不会出现

my_strcpy模拟实现 

第一步,先断言确保两指针均不为空

第二步,记录当前dest所指向地址,dest会在过程中改变,所以要为最终return 做准备

第三步,通过循环将来源字符串逐个复制到目标字符串内

有的同学会有疑问,那怎么停下来呢?当来源字符串将’\0‘赋给dest,while(0)就截止了

#define _CRT_SECURE_NO_WARNINGS 
#include<stdio.h>
#include<string.h>
#include<assert.h>

char* my_strcpy(char* dest, const char* cpy) {
	assert(dest && cpy);
	char* res = dest;

	while (*dest++ = *cpy++) {//先* = *再++ 
		;
	}
	return res;
}
int main() {
	char str1[] =  "#################";
	char str2[] = "123456789";
	char str3[] = "#################";
	char str4[] = "123456789";
	strcpy(str1, str2);
	printf("%s\n", str1);
	my_strcpy(str3, str4);
	printf("%s\n", str3);

	return 0;
}

strcpy注意事项 :

1.源字符串必须以 '\0' 结束。
2.会将源字符串中的 '\0' 拷贝到目标空间。
3.目标空间必须足够大,以确保能存放源字符串。
4.目标空间必须可变。

strncpy (长度受限制的字符串函数)

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

strncpy与strcpy相比参数相差一个size_t num,这个函数就通过这个参数来拷贝来源字符串前num个字符给目标字符串,这就是strcpy与strncpy的区别;

注意:不会额外添加'\0',来源字符串是什么就拷贝什么进去!

 strncpy 功能演示

#define _CRT_SECURE_NO_WARNINGS 
#include<stdio.h>
#include<string.h>
#include<assert.h>


int main() {
	char str1[] = "#################";
	char str2[] = "123456789";
	strncpy(str1, str2,5);
	printf("%s\n", str1);
	

	return 0;
}

 

 strncpy的模拟实现与strcpy相近,就不在此赘述啦♥

strncpy注意事项:

1.拷贝num个字符从源字符串到目标空间。
2.如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。

3.拼接字符串函数 strcat与strncat

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

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

将来源字符串拼接到目标字符串末尾

#define _CRT_SECURE_NO_WARNINGS 
#include<stdio.h>
#include<string.h>
#include<assert.h>

int main() {
	char str1[50] = "#################";
	char str2[] = "123456789";
	strcat(str1, str2);
	printf("%s\n", str1);


	return 0;
}

 

my_strcat模拟实现

char* my_strcat(char* dest, const char* scr) {
	char* ret = dest;
	assert(dest && scr);
	while (*dest != '\0') {
		dest++;
	}
	while (*dest++ = *scr++) {
	}

	return ret;
}
int main() {
	char str1[50] = "#################";
	char str2[] = "123456789";
	my_strcat(str1, str2);
	printf("%s\n", str1);


	return 0;
}

strcat注意事项:

1.源字符串必须以 '\0' 结束。
2.目标空间必须有足够的大,能容纳下源字符串的内容。
3.目标空间必须可修改。
4.字符串自己给自己追加,如何?答案: no,第二个参数被设置为const 不能被更改

strncat(长度受限制的字符串函数)

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

同理只将来源字符串前num位拼接在目标字符串末尾,补上'\0';

#define _CRT_SECURE_NO_WARNINGS 
#include<stdio.h>
#include<string.h>
#include<assert.h>

int main() {
	char str1[50] = "#################";
	char str2[100] = "123456789";
	strncat(str1, str2,5);
	printf("%s\n", str1);


	return 0;
}

 

4.字符串比较函数 strcmp与strncmp

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

 int strcmp ( const char * str1, const char * str2 );

标准规定:
第一个字符串大于第二个字符串,则返回大于0的数字
第一个字符串等于第二个字符串,则返回0
第一个字符串小于第二个字符串,则返回小于0的数字
那么如何判断两个字符串?
从两个字符串第一个字符开始比较,直到必出大小或者到达字符串末尾

#define _CRT_SECURE_NO_WARNINGS 
#include<stdio.h>
#include<string.h>
#include<assert.h>

int main() {
	char str1[] = "abcdef";
	char str2[] = "abccde";
	printf("%d", strcmp(str1, str2));
	return 0;
}

 

strncmp(长度受限制的字符串函数)

 int strncmp ( const char * str1, const char * str2, size_t num );

比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。

#define _CRT_SECURE_NO_WARNINGS 
#include<stdio.h>
#include<string.h>
#include<assert.h>

int main() {
	char str1[] = "abcdef";
	char str2[] = "abccde";
	printf("%d", strncmp(str1, str2,3));
	return 0;
}

5.字符串中找子字符串 strstr

strstr

char * strstr ( const char *str1, const char * str2);

从str1中找str2 ,如果找到就返回str1中第一次出现str2的首元素地址,没找到返回NULL

 strstr功能演示

#define _CRT_SECURE_NO_WARNINGS 
#include<stdio.h>
#include<string.h>
#include<assert.h>

int main() {
	char zmh[] = "123456789";
	char zmc[] = "567q";
	char* ret = strstr(zmh, zmc);
	if (ret != NULL)
		printf("%s\n", ret);
	else
		printf("字符串不存在\n");
	return 0;
}

my_strstr模拟实现

#define _CRT_SECURE_NO_WARNINGS 
#include<stdio.h>
#include<string.h>
#include<assert.h>

char* my_strstr(const char* str1, const char* str2) {
	assert(str1 && str2);
	const char* s1 = str1;
	const char* s2 = str2;
	const char* p = str1;
	while (*p) {
		s1 = p;
		s2 = str2;
		while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2) {
			s1++;
			s2++;
		}
		if (*s2 == '\0') {
			return (char*)p;
		}
		p++;
	}
	return NULL;
}
int main() {
	char zmh[] = "123456789";
	char zmc[] = "567";
	char* ret = my_strstr(zmh, zmc);
	if (ret != NULL)
		printf("%s\n", ret);
	else
		printf("字符串不存在\n");
	return 0;
}


 6.切断字符串/查找字符串 strtok 

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

●sep参数是个字符串,定义了用作分隔符的字符集合
●第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标
记。
●strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:
●strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容
并且可修改。)
●strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串
中的位置。
●strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标
记。
●如果字符串中不存在更多的标记,则返回 NULL 指针。

strtok功能模拟演示

#define _CRT_SECURE_NO_WARNINGS 
#include<stdio.h>
#include<string.h>
#include<assert.h>

int main()
{
	char* p = "971751980@qq.com";
	const char* sep = ".@";
	char arr[30];
	char* str = NULL;
	strcpy(arr, p);//将数据拷贝一份,处理arr数组的内容
	for (str = strtok(arr, sep); str != NULL; str = strtok(NULL, sep))
	{
		printf("%s\n", str);
	}
}


 7.错误信息报告 strerror

char * strerror ( int errnum );
errnum是错误码,一个系统预定义好的全局变量,可以通过strerror来打印出错误码对应的内容 

需要包含头文件 #include <errno.h> 


8.字符操作函数

8.1字符分类

函数如果他的参数符合下列条件就返回真
iscntrl任何控制字符
isspace空白字符:空格‘ ’,换页‘\f’,换行'\n',回车‘\r’,制表符'\t'或者垂直制表符'\v'
isdigit十进制数字 0~9
isxdigit十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F
islower小写字母a~z
isupper大写字母A~Z
isalpha字母a~z或A~Z
isalnum字母或者数字,a~z,A~Z,0~9
ispunct标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph任何图形字符
isprint任何可打印字符,包括图形字符和空白字符

8.2 字符转换

int tolower ( int c );
int toupper ( int c );


三、内存函数

1.内存拷贝函数 memcpy(来源与目标之间不能有重叠)

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

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

#define _CRT_SECURE_NO_WARNINGS 
#include<stdio.h>
#include<string.h>
#include<assert.h>

int main()
{
	int arr1[] = {1,2,3,4,5,6,7,8,9};
	int arr2[10] = { 0 };
	memcpy(arr2,arr1 ,sizeof(arr1));
	return 0;
}

 my_memcpy模拟实现

因为不知道传入的类型,所以用void* 来接收,但void*不能进行解引用操作,而且并不知道传入的类型所以只能转换为最小单位char* 再来解引用赋值.

Do you get it?

void* my_memcpy(void* dest, const void* src, size_t num) {
	assert(dest && src);
	void* ret = dest;
	while (num--) {
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return ret;
	//拷贝独立空间的两块空间 ,要有重叠请使用memmove 

}
int main()
{
	int arr1[] = {1,2,3,4,5,6,7,8,9};
	int arr2[10] = { 0 };
	my_memcpy(arr2,arr1 ,sizeof(arr1));
	return 0;
}

 2.内存拷贝函数 memmove(来源与目标之间可以有重叠)

 

memove

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

1.和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
2.如果源空间和目标空间出现重叠,就得使用memmove函数处理。

 memove模拟实现

void* my_memmove(void* dest,const void* src,size_t num){
	assert(dest&&src);
	void* ret = dest;  
	if(dest<src){
		//前->后
		
		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; 
} 

 3.内存比较函数 memcmp

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

1. 比较从ptr1和ptr2指针开始的num个字节

 memcmp功能演示

#include <stdio.h>
#include <string.h>
int main ()
{
char buffer1[] = "DWgaOtP12df0";
char buffer2[] = "DWGAOTP12DF0";
int n;
n=memcmp ( buffer1, buffer2, sizeof(buffer1) );
if (n>0) printf ("'%s' is greater than '%s'.\n",buffer1,buffer2);
else if (n<0) printf ("'%s' is less than '%s'.\n",buffer1,buffer2);
else printf ("'%s' is the same as '%s'.\n",buffer1,buffer2);
return 0;
}

4.内存批量设置函数 memset 

memset功能演示

int main()
{
	char buffer1[] = "DWgaOtP12df0";
	memset(buffer1, 'm', 5);
	printf("%s", buffer1);
	return 0;
}

 

总结

本文列举了大部分常用的字符串函数与内存函数,可以对本文进行收藏作为复习的材料,更重要的是自己去实践,去运用,加油吧码友们!

  • 6
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值