【C语言】库函数提供的利器?字符串与内存函数介绍

  

目录

  

本文重点:

1. 前言

2. 字符串函数

2.1 strlen

2.2 strcpy

2.3 strcmp

 2.4 strcat

2.5 strstr

 3. 内存函数

3.1memcpy

3.2 memmove

3.3 (补充)memset

4. 其他好用的字符串函数

4.1 strn系列

4.2 strtok

4.3 strerror

4.4 字符分类函数

5. 结语


本文重点:


重点介绍处理字符和字符串的库函数的使用和注意事项!

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

 

1. 前言


在处理字符串时,往往需要我们进行一系列的操作,比如复制,找字串等等。

字符串一般存储在常量区或字符数组中,倘若我们想对字符串实现某些操作,可以尝试运用字符串库函数来解决,这样不仅可以减少代码里,同时也可以让处理统一起来。

另外,内存函数是直接对内存的数字进行直接操控的函数,这类函数处理的数据类型十分广泛,在需要统一修改内存数据或者进行两个未知类型的数据的操作时,我们可以使用内存函数。

下面开始正式介绍字符串函数与内存函数。

2. 字符串函数


 

2.1 strlen

strlen函数一般用于计算有效字符串的长度,比如abcd\0的长度用strlen来计算,其返回值为4。

下面介绍其原理:

我们需要传一个指针给strlen函数,由指针指向的字符开始,读到的字符若不是\0,那么计数器就加1,遇到\0则计数结束

以下是strlen的模拟实现:

size_t my_strlen(const char* str)
{
	size_t len = 0;
	while (*str++)
	{
		len++;
	}
	return len;
}

 

2.2 strcpy

strcpy函数用于复制字符串到指定地址,复制的对象可以是常量字符串,也可以是字符数组中的字符串。而粘贴的对象只能是字符数组。

strcpy的使用样例:

int main()
{
    a[5]="abcd";
    b[6]="xxxxx";

    //把a的字符串复制到b去。
    strcpy(b,a);

    return 0;
}

结果是b存的字符串变为"abcdx\0"。

这里我们需要注意的是:

  • b数组的size必须比a数组的大,否则会报错。
  • a与b中的字符串必须以\0结尾,否者复制时可能会造成越界访问。
  • a可以是常量字符串,但b必须为字符数组或其他合法的地址。

以下为strcpy的模拟实现:

char* my_strcpy(char* dest, const char* src)
{
	while (*dest++ = *src++)
	{
		;
	}
}

 

2.3 strcmp

strcmp是比较两个字符串的函数,通常不会比较字符串的长度,strcmp的参数是两个字符串的起始地址,从该地址开始比较每个字符的ASCII码,到\0结束,途中若某一个字符比较出了大小,那么将根据情况返回整数或者负数代表大于或小于。若两个字符串相同那么返回0;

下面是strcmp的模拟实现:

int my_strcmp(const char* p1,const char* p2)
{
	while (*p1 == *p2)
	{
		if (*p1 == '\0')
		{
			return 0;
		}
        p1++,p2++;
	}
	return *p1 - *p2;
}

与原strcmp对比,一般在VS上面*p1>*p2会返回1,换了不同的编译器会有不同的值。

总之*p1>*p2会返回正数,否则返回负数,相同返回0;


 

 2.4 strcat

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

 strcat函数的作用是在dest字符串的末端接上src字符串。所以需要两个参数,1是目标字符串的地址,以及另一个字符串的地址。

strcat的模拟实现:

char* my_strcat(char* dest,const char* src)
{
	char* ret = dest;
	while (*dest)
	{
		dest++;
	}
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}

可以看到,strcat是先找到字符串dest的\0位置,然后将src的首字符覆盖\0,然后进行strcopy。

那么这时,我们就需要注意:

strcat的两个参数不可以是同一个地址的字符串,也就是该字符串不可以自己给自己追加字符。因为strcat会将原字符串的\0覆盖,两个指针同时移动,永远不可能停下。

 一旦开始追加,那么src永远找不到\0所以追加永远不会结束。


2.5 strstr

char* strstr(const char* p1,const char* p2);

我曾经在KMP算法中讲过这个函数,在字符串p1中寻找p2子串。其实我目前了解到的找子串的算法有两种KMP算法、字符串哈希、暴力算法。

如果是暴力算法那思路很简单,从p1每一个字符开始,与p2进行比对,如果比对失败,则进行下一个字符的比对。这样的算法时间复杂度为O(N^2)。

KMP算法在我的另一文中有所介绍(适用于strstr)。这里就直接模拟暴力解法了。

char* my_strstr(const char* p1,const char* p2)
{
	while (*p1)
	{
		if (*p1 == *p2)
		{
			const char* cur1 = p1, * cur2 = p2;
			while (*cur1 == *cur2 || *cur2 == '\0')
			{
				if (*cur2 == '\0')
				{
					return p1;
				}
				cur1++, cur2++;
			}
		}
		p1++;
	}
	return NULL;
}

 那么字符串函数我们就先讲到这里,接下来看看内存函数。

 3. 内存函数


基本概念:

内存函数是对内存(16进制数)进行直接操作的函数,通常用来进行初始化,无差别修改等操作。

 假如我们想交换a,b的值,那么我仅需要把a中每一个十六进制数与b中的每一个十六进制数进行交换即可。有了这个基本概念,我们就可以进行内存函数的操作了。


3.1memcpy

void* memcpy(void* dest ,void* src, size_t size);

这个函数其实很像我们的strncpy函数,后面会有有讲到。

由于是进行内存上的操作,那么无论何种类型的参数,内存函数都应该能接收,通过计算好的size来对size个字节的内存进行修改,这里便是对size个字节的空间进行copy操作。

先上模拟的函数:

void* my_memcpy(void* dest ,void* src, size_t size)
{
	void* ret = dest;
	while (size--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return ret;
}

这里把类型强制转换成char* 就是为了能够以字节为单位进行操作,同时需要注意一下dest与src指针递增的方式,这是比较标准的,因为void指针不能进行++操作,因为不同的编译器可能会存在语法上不支持的操作。为了代码的兼容性更高,我们采用这种方式进行。


3.2 memmove

这个函数是加强版的memcpy函数,如果想要把数组本身的内容复制给本身,那么我们就要用到memmove函数,比如 12345678,想要修改成 12567878,仅需把2作为目的地,把5678copy到2345即可。

那有人可能会问,这样memcpy就不能实现了吗?其实memcpy也可以实现,但是memmove与memcopy本身定位的功能就是这么分的而已。

下面是memmove的模拟实现:

void* my_memmove(void* dest,void* src,size_t size)
{
	assert(dest && src);
	void* ret = dest;
	if (dest <= src)
	{
		while (size--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	else
	{
		while (size--)
		{
			*((char*)dest + size) = *((char*)src + size);
		}
	}
	return ret;
}

 


3.3 (补充)memset

void* memset(char* dest, int val, size_t size);

memset函数是将size个字节的十六进制数修改成val值,val常用0,-1,ff等,根据需要来初始化内存空间,一般独立使用不接收返回的指针。


4. 其他好用的字符串函数


4.1 strn系列

strncpy复制n个字符
strncmp比较n个字符
strncat追加n个字符

需要3个参数,前两个与原本的相同,第三个需要一个整形n。不再赘述。


4.2 strtok

假如我需要把一个邮箱分为3个部分,比如 123456@qq.com 分为123456 、qq、com。

这时候就需要用到字符串分割函数strtok。

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

sep中是一个只含分隔符标志的字符串,比如“@.”。str就是需要被分割的字符串。这个函数会把第一个遇到的分隔符变成\0,返回起始地址。这个函数有记忆功能,需要用到循环配合使用。

str1[] = "123456@qq.com";
seq[] = "@.";

char* p1 = strtok(str1,seq);
p1拿到的是1的地址。

char* p2 = strtok(NULL,seq);
p2拿到的是q的地址。

char* p3 = strtok(NULL,seq);
p3拿到的是c的地址

后面再用strtok将不起作用。

4.3 strerror

char * strerror ( int errnum );

strerror需要搭配errno变量来实现,这个变量是C语言自带的,使用时需要#include <errno,h>

errno是错误信息码,比如我们遇到的404 NOT FOUND 中的404也是错误码。

而strerror(errno)则能够返回错误码所对应的错误信息。可以以%s的方式打印出错误信息。

 printf ("Error opening file unexist.ent: %s\n",strerror(errno));

而perror则是printf + strerror(errno)的结合体。

perror("自定义信息");

4.4 字符分类函数

函数如果他的参数符合下列条件就返回真
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
salnum字母或者数字,a~z,A~Z,0~9
ispunct标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph任何图形字符
isprint任何可打印字符,包括图形字符和空白字符

 

5. 结语

以上便是字符函数与内存函数的库函数介绍,因为经常用到,我们这里就进行了还算详细的介绍以及模拟实现。希望能帮得上大家。

  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Laxinues

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值