字符串函数详解

今天我们来了解一下字符串,在c语言中,我们有时会碰到要对字符串进行操作的情况,要是我们一个个去自己操作的话可能会非常麻烦,就比如要判断一个字符串中是否含有另一个字符串,要是我们自己去实现这个功能的话,可能会比较复杂,但如果我们借助字符串函数strstr的话,就可以轻松判断。所以我们今天来介绍一下各种字符串函数。

strlen函数

str表示字符串,len也显而易见,是指长度,所以这个函数的功能就是求一个字符串的长度

我们知道,每一个字符串的结尾都有一个 \0 ,strlen的作用是求 \0 前的字符长度

我们来看这个函数的原型

size_t strlen ( const char * str );

如图所示,它的参数是一个const char * str 是指,你想要求的字符串起始地址,从这个地址往后直到\0前的字符长度。

使用const修饰这个地址,因为我们的目的就只是计算它的长度,而不能修改他,所以加一个const来避免原来的字符串被修改。

它的返回值是size_t 实际上就是一个正整数。之所以是size_t就是因为你这个字符串的长度不可能是负数吧,所以返回值用一个正整数来接收。

那么我们能不能来通过代码来实现一下这个函数呢?答案是肯定的,而且还是比较简单的。

int my_strlen(const char * str)
{
    int count = 0;
    assert(str);
    while(*str)
    {
        count++;
        str++;
    }
    return count;
}

当然这只是一种方式,还可以使用递归以及指针-指针的方式等来实现,这里就不做演示了,感兴趣的同学可以下来自己试试看。

strcpy函数

str是指字符串,cpy很容易联想到copy,确实,strcpy就是字符串复制的功能

我们先来看它的函数原型

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

如图所示,它有两个参数,一个是char* dest,另一个是const char* src。返回值是char*

dest实际上是destination目的地的缩写,也就是要接收你要复制字符串的地址。

src是source来源的缩写,也就是你要从哪开始复制字符串,用const修饰的也是我避免这个字符串被修改,因为我们想要修改的是目的地,而不是来源,所以要限制一下,不让它修改。

返回值是目的地的起始地址,其实就是dest,但是有人可能会疑惑,我们之前使用的时候都不知道这个还有返回地址的啊,没有这个也可以实现啊。确实,我们平时都没有用过这个返回地址,但是它确实是存在的,但是我们不用也可以,返回值这东西,可以接收,也可以不接收。

它的功能是将src中的字符串复制到dest中,并且包括src最后的\0。

我们可以看到str2中的x的数量明显比str1中的字符串长,但是最后却只打印出来了str1,我们都知道以 %s 的方式打印字符串是遇到 \0 停止打印,这不就说明strcpy将 \0一起复制到了str2中。 

那么我们在使用是需要注意一点什么呢,首先是你传入的地址不能是一个空指针吧,如果是空指针,那它怎么去复制呢,然后就是目的地应该有足够的内存来储存要复制的字符串吧,不然存不下不就造成了越界了,最后就是你要复制的字符串要含有 \0,它结束复制的标志就是\0,如果没有这个字符串,那么复制的字符串什么时候停下了就是一个问题了。

那么我们这个函数我们能不能自己来实现一下?答案当然是可以的。

char* my_strcpy(char* p1,const char* p2)
{
    //保存返回值
	char* ret = p1;
	//*p2最后的值是\0,\0的ASCII码值是0,会自动退出循环
	while (*p1++ = *p2++)
	{
		;
	}
	return ret;
}

strncpy函数

粗心一点的同学可能会觉得这个函数跟上一个函数一样,认真看看,实际上比上一个函数多了一个n。

我们都知道在数学里面很多未知数都用n来表示,这里的n实际上也是未知数的意思。我们来看它的函数原型

char * strncat ( char * dest, const char * src, size_t num );

它前两个参数跟strcpy函数一样,问题是多了一个参数size_t num,指的是要复制的字符的个数。

这个函数的功能实际上跟上一个一样,就是多了一个指定长度的功能,通过num可以指定从src中复制多少个字符到dest中。

我们知道在上一个函数中,复制的结尾会添加一个\0,那这个函数会不会也添加一个\0呢?

 

通过上面的例子我们可以看到实际上是没有添加\0的,所以在使用是要注意一下这个点

但假如我想要从src中复制的长度比src本身都要长呢?那他还会不会帮我实现任务,还是报错之类的?

 

通过上面的例子我们看到,它确实帮我们完成了任务,在超出了它自生的长度的部分它的做法是添加\0,看的出来它很看重这个数字,就算我自己不够,我也要给你凑出来。

要想去模拟实现它也很简单,无非就是以num来实现循环就行。

char* my_strncpy(char* s1, const char* s2, size_t num)
{
	assert(s1 && s2);
	char* ret = s1;
	while (num--)
	{
		if (*s2 == '\0')
			*s1++ = '\0';
		else
			*s1++ = *s2++;
	}
	return s1;
}

strcat函数

那有时候我们不想要动dest中的字符串,想要src中的字符串能够复制到dest的后边呢?这时候就要用到strcat函数。

我们来看它的函数原型

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

老生常谈的两个参数dest与src,依旧是目的地首地址以及来源的首地址。还有返回值char *,返回的是dest。

它的功能是将src中的字符串拼接到dest的末尾。

使用是要注意的点是dest和src的末尾都必须要有\0,其次是dest必须要存的下src。

下面是它的模拟实现

char* my_strcat(char* s1, char* s2)
{
	assert(s1 != NULL);
	assert(s2 != NULL);
	char* ret = s1;
	while (*s1)
	{
		s1++;
	}
	while (*s1++ = *s2++)
	{
		;
	}
	return ret;
}

strncat函数

同样是多了一个n,这个函数的功能也是比strcat多了一个指定长度的功能。我们来看它的函数原型

char * strncat ( char * dest, const char * src, size_t num );

熟悉的dest与src,还有一个num,返回值char *,返回的是dest。

同样的num是可以指定从src中复制到dest中的字符的长度。

但是这个函数会不会在拼接完后给拼接的字符串中添加一个\0呢?函数的拼接是从找到dest中的\0后开始的,那么我们就可以利用这个特性来验证那个问题。

在这里我设置了两个字符串,一个是我们要拼接的字符串str1,另一个中我们用多个x来表示,这样便于观察,同时在第三个x后面提前设置\0,这样就能做到让它提前开始拼接的效果。

我们可以看到,在拼接完后字符串后,拼接的字符串后面被加上了\0,这一点就跟strncpy函数不一样了,使用时也要注意一下。

那么要是要拼接的长度超过了src的长度呢?是会像strncpy一样在超过的部分全部加上\0还是别的呢?

通过上面例子我们可以看到,在拼接完后,只在最后加上了一个\0,并没有多加。就像是我有的的都给你了,再想要更多的真没有了。这里跟strncpy也有区别,要注意区分两者的区别。

下面是模拟实现

char* my_strncat(char* s1, const char* s2, size_t num)
{
	assert(s1 && s2);
	char* ret = s1;
	//找到第一个字符串为\0的位置,从这个位置开始拼接
	while (*s1)
	{
		s1++;
	}
	while (num--)
	{
		*s1++ = *s2++;
		//如果碰到s2的最后一个字符,直接退出
		if (*s2 == '\0')
		{
			*s1 = *s2;
			return ret;
		}
	}
	*s1 = '\0';
	return ret;
}

strcmp函数

有时我们会遇到要将两个字符串进行比较的情况,是不能像整形那样直接去比较的,这时要就要用到strcmp这个函数。

先看函数原型

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

两个 char *类型的参数,str1和str2,分别指向要比较的字符串的起始位置,都用const来修饰了,是因为我们想做的只是比较他们的大小,而不是改变他们,所以用const限制。

最后是一个int类型的返回值,当str1大于str2时,返回正数,当str1小于str2时,返回负数,如果相等,则返回0。

比较的原理是比较相同的位置,比较同一位的ASCII码值,当遇到第一个不一样的字符时,比较™的大小,随后返回值。

在比较str1和str2时,前面两个字符一样,所以直接跳过,知道第三字符,这时str1中是 ‘c’, str2中是 'q',很明显q的ACSII码值大,所以就是str2大,注意,它只比较遇到的不同的第一个字符,无论后面怎么样,这里虽然str1比str2长,但是第一个不一样的字符是str2大,那么依旧是str2大。

那还有一种情况怎么办呢?就是如果两个字符串前面都一样,但是一个字符串长,另一个短呢?

 

这里我们看到的是str1大于str2,所以说明,在字符串前面都相同的情况下,谁更加长,那么就是谁更大。

这里是模拟实现:

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

我这里是返回的两个字符的差值,但是在VS中,这个返回值是只有0,1, -1的,但是我们这种返回值也是没有错的。

strncmp函数

这个函数跟前一个函数很相似,也是比较,就是多了一个长度而已

先看函数原型

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

所有参数我们都很熟悉,两个要比较的字符串的地址str1和str2,以及长度num。

这个函数唯一要注意的是这个num是要比较的字符串的最大长度,而不是一定要比较那么长,假设在num的长度内就已经能够分出大小了,那么就不需要一直将整个num比较完。剩下的跟strcmp是一样的。

这个函数的模拟实现也很简单,这里我就不写了。

strstr函数

这个函数的作用就是判断一个字符串中是否含有另一个字符串

我们来看函数原型

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

函数的具体功能就是判断str1中是否含有str2。

如果str1中含有str2,那么就会返回str1中str2的地址,如果str1中不含str2,那么返回值是一个空指针。

可以看到,我用一个指针接收它的返回值,打印出来的是bcdef,它的返回值实际上是b的地址,所以这个返回值可以理解了吧。

这个函数想要模拟实现还是稍微有点难度的,由于作者有点懒,所以就只放代码,不讲解思路了

//首先要有一个来记录主字符串开始比较的位置
//还要有一个字符串来记录第二个字符串起始的位置
//还有两个比较的位置
char* my_strstr(const char* str1, const char* str2)
{
	const char* s1 = NULL;
	const char* s2 = NULL;
	//记录主字符串开始比较的位置
	const char* cur = str1;
	if (*str2 == '\0')
	{
		return (char*)str1;
	}
	while (*cur)
	{
		s1 = cur;
		s2 = str2;
		//判断从cur位置开始,s2是不是相等。
		//如果s2遇到了\0,那么说明s1中含有s2
		//并且如果s1到达\0且s2不是\0的情况下也可以直接说明不包含
		while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
		{
			s1++;
			s2++;
		}
		//在上面三个条件中,如果是s2为0,那么说明包含
		if (*s2 == '\0')
			return (char *)cur;
		cur++;
	}
	//到最后都没有返回,那么说明没有找到,直接返回null
	return NULL;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值