C语言—CH10—字符函数与字符串函数

字符函数与字符串函数

1、相关函数介绍

1.1strlen()

专门用来求一个字符串的长度的函数

#include<string.h>

int main()
{
	char arr[] = "abcdef"; //abcdef\0
	int len = strlen(arr);
	printf("%d\n",len);
	return 0;
}

output:

6

字符串以’\0’为结束标志,strlen计算的是’\0’之前出现的字符个数。如果arr没有’\0’结尾,则输出的是随机值。

#include<string.h>

int main()
{
	char arr[] = {'a','b','c','d'};
	int len = strlen(arr);
	printf("%d\n",len);
	return 0;
}

output:

随机值

只有在遇到’\0’的时候才会停下来,只是在内存中什么时候遇到’\0’我们无法得知,所以是随机值。

strlen()返回的类型是size_t,即无符号整型unsigned int。

在这里插入图片描述
在cplusplus.com可以看到strlen的返回类型是size_t,之后在vs2022中,我们可以查询size_t:
在这里插入图片描述
可以看到typedef unsigned_int64 size_t。
可以编写一个程序来证明strlen返回的是无符号整型:

#include<string.h>

int main()
{
	if(strlen("abc") - strlen("abcd") > 0)
	{
		printf("大于\n");
	}
	else
	{
		printf("小于\n");
	}

	return 0;

}

output:

大于

strlen()可以模拟实现:

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

size_t my_strlen(const char* str)//因为该函数只求长度,不改变字符串内容,所以可以加一个const
{
	size_t count = 0;//计数器
	assert(str);
	while(*str != '\0')//看指针指向的位置是不是终止条件
	{
		count++;
		str++;
	}

	return count;
}

int main()
{
	char arr[] = "abcdef";
	size_t n = my_strlen(arr);
	printf("%u\n",n);
	return 0;
}

这里用到的assert.h库的assert()函数,是把条件语句放入assert中,如果为真,则继续程序。如果为假则终止程序。这里把str放入assert中是为了防止传入的参数首字母地址内为空。

1.2strcpy()

字符串拷贝

在这里插入图片描述
注意到他的两个参数,一个是source,source的类型时 const char*,因为我们不会修改出发地的内容,而仅仅只是拷贝它,所以加个const。一个是char* destination,目的地。粘贴的对象。

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

int main()
{
	char arr[20] = { 0 };
	strcpy(arr, "Helloworld");
	printf("%s\n", arr);
	return 0;
}

为什么不能这么写:

arr = "Helloworld";

这样写是错的,因为arr是数组的首元素地址,是一个地址,而不是一个变量。地址是一个常量值,不能被赋值。
另外,strcpy只会把’\0’之前的内容拷贝过来:

strcpy(arr,"abcd\0efg");

output:

abcd

并且,本来char arr[]里面放的是"Helloworld"也会被清空,不会出现:abcdoworld的情况。

注意点:

1、同样的,没有'\0'会报错,一个只申请了3个字符的数组,
被拷贝1000个字符,会打印出前三个字符,但是程序会崩溃。

2、目标空间必须许可变
char* p = "abcdef";
strcpy(p,arr);
return 0;

char* p 指向的常量字符串不可以改这样程序会崩溃。

  • 模拟实现:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<assert.h>

char* my_strcpy(char* destination, const char* source)
{
	assert(destination);
	assert(source);//判断非空

	char* ret = destination;
	while (*destination++ = *source++)
	{
		;
	}
	return ret;
}

int main()
{
	char arr1[] = "abcdef";
	char arr2[20] = { 0 };
	my_strcpy(arr2, arr1);
	printf("%s\n", arr2);

}

1.3strcat()

字符串追加,在一个字符串后面追加另一个字符串。
在这里插入图片描述

1.3.1代码实例:
int main()
{
	char arr1[20] = "hello ";
	strcat(arr1,"world");
	printf("%s\n",arr1);
	return 0;
}

op:

hello world
1.3.2注意事项

1、源字符串必须以’\0’结尾,两个字符串都是以’\0’结尾。
2、目标空间必须足够大
3、目标空间可修改(参数没有const)

1.3.3自己实现

步骤:
1、找到目标字符串的结尾
2、拷贝–类似strcpy

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

char* my_strcat(char* dest,const char* source)
{
	assert(dest&&source);
	char* ret = dest;
	
	while(*dest != '\0')
	{
		dest++;
	}
	while(*dest++ = *source++)
	{
		;
	}
	return ret;
}
1.3.4可否自己给自己追加

不可以:
因为’\0’在之前被覆盖掉了,之后要复制过来发现没有’\0’了,不能结束。

1.4strcmp()

比较两个字符串是否相等。
在这里插入图片描述

为什么需要这样的一个函数呢,直接等号不香吗?注意看这个代码:

int main()
{
	char arr1[20] = "hello world";
	char arr2[20] = "hello world";
	
	if(arr1 = arr2){
		printf(" = ")} 
	else
	{
		printf("!=");
	}
	
	return 0;
}

op:

!=

为什么呢?因为这里是在比较两个数组的首元素地址。注意一下这个点。

1.4.1、返回值

在这里插入图片描述

1.4.2代码实例
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<assert.h>

int main()
{
	char arr1[20] = "hello world";
	char arr2[20] = "hello world!!!";
	int ret = strcmp(arr1,arr2);
	if(ret < 0)
	{
		printf("<");
	}
	else if(ret > 0)
	{
		printf(">");
	}
	else
	{
		printf("==");
	}
	
	return 0;
}
1.4.3注意

1、strcmp不是比较字符串长度,而是比较字符串大小(ASCII 码值)。
如:abq > abcdefg

1.4.4自我实现
int my_strcmp(const char* str1,const char* str2)
{
	assert(str1 && str2);
	while(*str1 == *str2)
	{
		if(*str1 == '\0');
			return 0;//相等
		str1++;
		str2++;
	}
	return (*str1 - *str2);
}

1.5总结:

strcpy
strcat
strcmp

都是长度不受限制的字符串函数
所以有长度受限制的函数

strncpy
strncat
strncmp

都多了一个参数size_t num,即字节数。
在这里插入图片描述
其官方代码实现形式:

/***
*strncmp.c - compare first n characters of two strings
*
*       Copyright (c) Microsoft Corporation. All rights reserved.
*
*Purpose:
*       defines strncmp() - compare first n characters of two strings
*       for ordinal order.
*
*******************************************************************************/

#include <string.h>

#ifdef _M_ARM
    #pragma function(strncmp)
#endif

/***
*int strncmp(first, last, count) - compare first count chars of strings
*
*Purpose:
*       Compares two strings for ordinal order.  The comparison stops
*       after: (1) a difference between the strings is found, (2) the end
*       of the strings is reached, or (3) count characters have been
*       compared.
*
*Entry:
*       char *first, *last - strings to compare
*       unsigned count - maximum number of characters to compare
*
*Exit:
*       returns <0 if first < last
*       returns  0 if first == last
*       returns >0 if first > last
*
*Exceptions:
*
*******************************************************************************/

int __cdecl strncmp
(
    const char *first,
    const char *last,
    size_t      count
)
{
    size_t x = 0;

    if (!count)
    {
        return 0;
    }

    /*
     * This explicit guard needed to deal correctly with boundary
     * cases: strings shorter than 4 bytes and strings longer than
     * UINT_MAX-4 bytes .
     */
    if( count >= 4 )
    {
        /* unroll by four */
        for (; x < count-4; x+=4)
        {
            first+=4;
            last +=4;

            if (*(first-4) == 0 || *(first-4) != *(last-4))
            {
                return(*(unsigned char *)(first-4) - *(unsigned char *)(last-4));
            }

            if (*(first-3) == 0 || *(first-3) != *(last-3))
            {
                return(*(unsigned char *)(first-3) - *(unsigned char *)(last-3));
            }

            if (*(first-2) == 0 || *(first-2) != *(last-2))
            {
                return(*(unsigned char *)(first-2) - *(unsigned char *)(last-2));
            }

            if (*(first-1) == 0 || *(first-1) != *(last-1))
            {
                return(*(unsigned char *)(first-1) - *(unsigned char *)(last-1));
            }
        }
    }

    /* residual loop */
    for (; x < count; x++)
    {
        if (*first == 0 || *first != *last)
        {
            return(*(unsigned char *)first - *(unsigned char *)last);
        }
        first+=1;
        last+=1;
    }

    return 0;
}

1.6strstr

1.6.1应用

查找子串
在这里插入图片描述
找str1中有没有子串str2,如果有,则返回str2在str1当中,第一个字母的地址。找不到,则返回空指针。

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

int main()
{
	char email[] = "dbs@163.com";
	char sub[] = "dbs";
	char* ret = strstr(email, sub);
	if (ret == NULL)
	{
		printf("子串不存在\n");
	}
	else
	{
		printf("子串存在,字串是%s\n", ret);
	}
	return 0;
}

比如,这里sub时email的子串,sub的第一个字母在email中是d,则返回email当中的d的地址。

1.6.2模拟实现

有两种情况:
情况一:
一次匹配就能找到

abcdef
bcd

情况二:
多次匹配才能找到

abbbcdef
bbc

针对情况二,我们需要专门的一个指针,来记录str1的匹配起始位置

#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 p;
		}
		p++;
	}
	return NULL;

}

int main()
{
	char email[] = "dbs@163.com";
	char sub[] = "dbs";
	char* ret = my_strstr(email, sub);
	if (ret == NULL)
	{
		printf("子串不存在\n");
	}
	else
	{
		printf("子串存在,字串是%s\n", ret);
	}
	return 0;
}
1.6.3KMP算法

高效的查找子串的算法

KMP算法

另外在了解KMP之前可以先了解BF。
BF & KMP视频讲解

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值