C语言常用的字符串函数及模拟实现详解!

前言:

掌握常用的字符串函数,会有很多好处。

可以更方便地处理字符串操作,大大提高代码的可读性和可维护性,避免自己编写复杂的字符串处理逻辑,从而减少错误的可能性。另外,一些字符串函数还可以提高程序的运行效率,比如使用 strstr 函数来查找字符串中的子串,比自己遍历字符串要快得多。

总之,掌握字符串函数可以让C 语言编程更加高效、准确。

那么本篇文章围绕常用的字符串函数进行运用及模拟实现,帮大家深入理解这些函数,以便更灵活地运用。

1.strlen的使用和模拟实现

  • 函数声明

size_t strlen ( const char * str );

可以看到,strlen的返回值为 size_t(简单理解为 unsigned int,即无符号整型),函数参数为 

const char * str

  • 使用方法

1.strlen的使⽤需要包含头文件
#include <stdio.h>
2.字符串以 '\0' 作为结束标志

strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包 含 '\0' ) 。如果字符串中没有'\0', strlen函数也会超出字符串外去查找,直到遇到'\0'才会停止

那么参数指向的字符串必须要以 '\0' 结束。否则就计算不出字符串正确的长度

我们以下面的代码为例

#include <stdio.h>
#include <string.h>

int main()
{
	const char* str1 = "abcd";//结尾有'\0'
	char str2[] = {'a','b','c','d'};//结尾无'\0'
	printf("%zd\n", strlen(str1));
	printf("%zd\n", strlen(str2));
	return 0;
}

输出结果为

而且vs会有警告

3.注意函数的返回值为 size_t,是无符号的。

这里容易掉坑里,我们来看个例子:

#include <stdio.h>
#include <string.h>
int main()
{ 
    const char* str1 = "abcdef";
	const char* str2 = "abcd"; 
	if (strlen(str2) - strlen(str1) > 0)
	{
	    printf("str2>str1\n");
    }
    else
    {
	    printf("srt1>str2\n");
	}
    return 0;
}

结果是:

str2="abcd",  str1="abcdef",  那么str2的长度为4,str1长度为6

 strlen(str2) - strlen(str1) > 0 

例题解析

1.我们知道,有符号整数在二进制表示中,有符号位和数值位两部分,最高位的1位是被当作符号位,剩余的都是数值位。符号位用0表示“正”,用1表示“负”。(下图为-1和1的二进制原码表示形式。)

而在无符号数中,最高位不是符号位,就是它的数值位。所以下图中的int i=-1的二进制码值,如果表示的是unsigned int,那就是相当大的数字了!

2.计算机内整型类的数据以二进制的补码形式进行存储,我们输入与计算机为我们输出的整数是原码,它们有这样的关系:

需要注意,无符号整数和正整数的原码、反码和补码是相同的,不进行上图的转换计算

3.解这道题的关键,就在于需要注意,strlen函数返回值为 size_t,是无符号的,那么strlen(str2) - strlen(str1)的结果也是无符号的。strlen(str2) - strlen(str1)=4-6=-2,负二作为无符号的数字被表示出来会是一个相当大的数。

  • 模拟实现

1.计数器方式

思路:遇到‘\0’停止,没遇到就让计数器+1

int my_strlen(const char* str)
{
    int count = 0;
    assert(str);
    while(*str) //*str为'\0'时停止循环
    {
     count++;
     str++;
    }
    return count;
}
2.指针-指针方式

思路:两个指针相减得到的是它们之间的元素个数(就像日期和日期相减得到天数一样),让'\0'对应的指针减去第一个字符对应的指针就能得到整个字符串长度。

int my_strlen(const char* str)
{
    assert(str);
    char *p = str;
    while(*p)
        p++;
    return p-s;
}
3.递归

如果题目有要求,不能创建临时变量,那么我们就要用递归。

int my_strlen(const char* str)
{
    assert(str);
    if(*str == '\0')
        return 0;
    else
        return 1 + my_strlen(str+1);
}

2.strcpy的使用和模拟实现

  • 函数声明

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

strcpy函数会将source指向的字符串拷贝到destination指向的字符串,并返回destination指向字符串的首地址。

需要包含头文件

 #include <string.h>
  • 使用方法

1.源字符串必须以‘\0’结束,会将源字符串的'\0'也拷贝到目标空间。
2.目标空间必须足够大,能够存放下源字符串。
3.目标空间必须可修改。

这里也容易出现小错误,我们看一个例子。

#include <stdio.h>
#include <string.h>
int main()
{
	char* dest = "xxxxxxxxxxxxxxx";//常量字符串,不可更改!!
	char* src = "i love you!";
	strcpy(dest, src);
	return 0;
}

这样写就不会有输出,因为dest是常量字符串,不能够被修改!

正确写法:

#include <stdio.h>
#include <string.h>
int main()
{
	char dest[] = "xxxxxxxxxxxxxxx";//写成数组的形式就可以被修改了
	char* src = "i love you!";
	strcpy(dest, src);
	printf(dest);
	return 0;
}

结果是:(因为"i love you!"后面有‘\0’,strcpy把‘\0’,也给复制到了dest里,所以dest后面多的几个xx就不会被打印出来)

  • 模拟实现

思路:过程很简单,就是复制过去一个,指针指向下一个字符,直到source指向‘\0’。如下图

用代码来实现一下

char* my_strcpy(char* dest, const char* src)
{
    assert(dest && src);
    char* ret = dest;//用ret存起始地址,之后方便return
    while((*dest++=*src++)) //两边先使用,后++
    {
        ;
    }
    return ret;
}

根据优先级顺序,中间的while部分的实现过程是这样的:

while(*src!='\0')
{
   *dest=*src;
    dest++;
    src++; 
}

但是写成上面的形式会更加巧妙简洁。

3.strcat的使用和模拟实现

  • 函数声明

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

strcat和strcpy很像,只是前者是在destination指向的字符串后追加source指向的字符串,而后者直接将source指向字符串复制到destination指向的字符串中。

需要包含头文件

#include <string.h>
  • 使用方法

1.源字符串必须以‘\0’结束,会将源字符串的'\0'也拷贝到目标空间。

2.目标字符串必须以'\0'结束,否则没办法知道从哪里开始追加。

3.目标空间必须足够大,能够存放下源字符串。
4.目标空间必须可修改。

举个栗子:

#include <stdio.h>
#include <string.h>
int main()
{
	char dest[20] = "xxx ";
	char* src = "i love you!";
	strcat(dest, src);
	printf(dest);
	return 0;
}

运行结果:

  • 模拟实现

思路:和strcpy函数差不多,只不过这次要先找到destination字符串的‘\0’,从‘\0’开始追加source字符串,所以只需要先找到‘\0’,后面的过程和strcpy就是一样的了。

char* my_strcat(char* dest, const char* src)
{
	char* ret = dest;//存dest的首地址
	assert(dest && src);
	while (*dest)//让dest指向'\0’,从'\0'开始连接字符串
		dest++;
	while ((*dest++ = *src++))
		;
	return ret;//返回目标字符串的首地址
}

!需要注意:strcat是可以实现字符串自己给自己追加的,但我们写的my_strcat是没办法实现的!

4.strcmp的使用和模拟实现

  • 函数声明

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

通过依次比较字符的ASCII码值来比较两个字符串大小。

str1 > str2 返回大于0的数字; str1 = str2 返回0; str1 < str2 返回小于0的数字。

需要使用头文件:

#include <string.h>
  • 函数使用

1.两个字符串比较大小,不能够直接用大于号或小于号来比较,必须要用strcmp函数。

2.只要有一个字符能够比出大小就结束。

举个栗子:

#include <stdio.h>
#include <string.h>
int main()
{
	printf("%d\n", strcmp("abcd", "abcd")); //0
	printf("%d\n", strcmp("abcd", "abc"));  //1
	printf("%d\n", strcmp("abcd", "abcde"));//-1
	printf("%d\n", strcmp("abcd", "acd"));  //-1
	return;
}

结果:

在vs里,返回的大于0的数就是1,小于0的数是-1。

  • 模拟实现

思路:从字符串首地址开始比较,字符相同,两个字符串就继续比较下一个字符;

字符不同,就比较大小,判断返回值比0大还是比0小;

当两个字符串都同时比较到了'\0',说明两个字符串大小相同。

int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);
	while (*str1 == *str2)//字符相等,进入循环,继续比较下一个
	{
		if (*str1 == '\0')//因为两个字符是相等的,一个是'\0'那两个都是'\0'
			return 0; //说明两个字符都同时比较到了字符串结尾,字符串相等
		str1++;
		str2++;
	}
	return *str1 - *str2;//如果*str1>*str2,返回正数,反之
}

这样写最后的返回值是*str1和*str2的ASCII码值的差值,就不一定是-1或1了,如果想要是-1或1,需要用if再判断一下。

int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);
	while (*str1 == *str2)//字符相等,进入循环,继续比较下一个
	{
		if (*str1 == '\0')//因为两个字符是相等的,一个是'\0'那两个都是'\0'
			return 0; //说明两个字符都同时比较到了字符串结尾,字符串相等
		str1++;
		str2++;
	}
    if(*str1 > *str2)
        return 1;
    if(*str1 < *str2)
        return -1;
}

5.strstr的使用和模拟实现

  • 函数声明

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

需要包含头文件

#include <string.h>
  • 函数使用

1.函数返回字符串str2在str1中第一次出现的位置。

2.找不到返回NULL。

3.不比较'\0',以'\0'作为结束标志。

举个例子:

#include <stdio.h>
#include <string.h>
int main()
{
	char str[] = "This is a simple string";
	char* pch;
	pch = strstr(str, "simple");
	printf("%s\n", pch);
	return 0;
}

结果是:

  • 模拟实现

思路:必然需要一个字符一个字符地查找,那么会出现下面三种情况:

第一种情况,查找到str1的'\0'字符还没有找到,就结束查找,找不到需要返回NULL;

第二种情况,查找到第一个相同的字符之后,判断后续字符是否相等,相等了返回第一次出现的位置,所以我们需要一个指针来存str1中第一次出现与str2相同字符位置的地址,在这里就是str1的'b'字符。

第三种情况,第一个字符相同,但第二个字符不同,需重新查找,所以str2的地址不能改变,需要再用一个指针来进行加减

综上,为了模拟实现strstr函数,除了str1指针和str2指针外,我们还需要一个指针存储字符串str2在str1中第一次出现的位置【char* cp】,一个指针来对str2指向的字符串进行加减【char* s2】,也可以再加一个指针来对str1指向的字符串进行加减【char* s1】。

代码:

char* my_strstr(const char* str1, const char* str2)
{
    assert(str1 != NULL);
    assert(str2 != NULL);
	char* cp = (char*)str1;//str1是const char* 类型,需要强制转换
	char* s1 = NULL;
	char* s2 = NULL;
	if (!*str2)//当str2是空字符'\0'时,返回str1的地址
		return (char*)str1;
	while (*cp) //当cp指向'\0',就是找到了str1的结尾都没有找到,跳出循环,返回NULL
	{
		s1 = cp;
		s2 = (char*)str2;
		while (*s1 && *s2 && (*s1 == *s2)) {//s1和s2都不能是'\0'
			s1++;            //s1和s2相同,就继续向后查找,
			s2++;
		}
		if (!*s2)//s2是'\0'时意味着已经在str1里找到了str2字符串
			return cp;
		cp++;
	}
	return (NULL);
}

6.strtok函数的使用

  • 函数声明

char * strtok ( char * str, const char * delimiters );//delimiter 分隔符

通俗的讲,这个函数用来去掉字符串里指定的分隔符。

  • 使用方法

1.delimiters参数指向⼀个字符串,定义了⽤作分隔符的字符集合。

2.str指定⼀个字符串,它包含了0个或者多个由delimiters字符串中⼀个或者多个分隔符分割的标

记。
举个例子:
3.  strtok函数找到str中的⼀个标记,并将其用 '\0'   结尾,返回⼀个初始指针,并记住这个标记的指针。如下图:

要注意两点:
首先,这个函数会直接改变字符串,所以我们应该 拷贝一下再用,以免原数据被改变。
其次,我们 不能直接利用这个函数打印出去掉分隔符后的字符,如上面的例子,打印出 iamyu就结束了,用代码来试验一下。
#include <stdio.h>
#include <string.h>
int main()
{
	char str1[] = "iamyu@qq.com";
	char str2[20] = { 0 };
	strcpy(str2, str1);//拷贝一份使用
	char* ret = NULL;
	ret = strtok(str2, "@.");
	printf("%s", ret);
	return 0;
}

结果:

4.在上面的例子中,函数第一个参数即str不为NULL,说明是第一次在此字符串里查找分隔符,函数会找到第一个分隔符在的位置,将它改为'\0'后还会记住它的位置(如上面的例子)。

想要函数继续查找,第一个参数必须为NULL。

#include <stdio.h>
#include <string.h>
int main()
{
	char str1[] = "iamyu@qq.com";
	char str2[20] = { 0 };
	strcpy(str2, str1);
	char* ret = NULL;
	//第一次查找
	ret = strtok(str2, "@.");
	printf("%s\n", ret);
	//第二次查找
	ret = strtok(NULL, "@.");
	printf("%s\n", ret);

	return 0;
}

我们在第二次查找时,给函数的第一个参数改为NULL,它继而就能找到被分隔的第二个字符串"qq"。看一下代码执行结果:

以此类推,想要输出"com"这个字符串,我们需要第三次查找!

#include <stdio.h>
#include <string.h>
int main()
{
	char str1[] = "iamyu@qq.com";
	char str2[20] = { 0 };
	strcpy(str2, str1);
	char* ret = NULL;
	//第一次查找
	ret = strtok(str2, "@.");
	printf("%s\n", ret);
	//第二次查找
	ret = strtok(NULL, "@.");
	printf("%s\n", ret);

	//第三次查找
	ret = strtok(NULL, "@.");
	printf("%s\n", ret);

	return 0;
}

结果:

5.至此,我们已经查找完毕,但是如果载查找一次呢?函数就会返回NULL指针

最终代码:上面的代码过于啰嗦,再来简化一下
#include <stdio.h>
#include <string.h>
int main()
{
	char str1[] = "iamyu@qq.com";
	char str2[20] = { 0 };
	strcpy(str2, str1);//拷贝一下使用
	char* ret = NULL;
	for (ret = strtok(str1, "@."); ret != NULL; ret = strtok(NULL, "@.")) {
		printf("%s\n", ret);
	}
	return 0;
}

结果:

解释一下for循环的意思:

7.strncpy,strncat,strncmp函数的使用

这三个函数我就一起说了

  • strncpy的使用

函数声明

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

1.strcpy是把源字符串整体都拷贝到目标空间中,那strncpy就是只拷贝源字符串中num个字符到目标空间。

2.如果源字符串比num长,不会在目标后再去追加'\0'。

3.如果源字符串长度小于num,拷贝完字符串后,在目标后面追加'\0',直到num个。

  • strncat的使用

函数声明

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

1.strncat只追加num个源字符串中的字符到目标空间,并且会在后面再追加'\0'字符。

2.如果源字符串长度小于num,只会追加到源字符串的'\0',不会再向后补'\0'。

  • strcmp

函数声明

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

strncmp最多比较str1和str2中num个字符,比较规则和strcmp是相同的,这里不多赘述。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值