字符函数与字符串函数
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之前可以先了解BF。
BF & KMP视频讲解