C语言: 字符串函数(上)

大家好,今天我们一起学习相关的字符串函数,相信有很多函数大家都知道如何去运用,但是更重要的是我们要了解并掌握其中的代码逻辑。

1. strlen函数的使用和模拟实现

strlen函数,顾名思义,string length--> 求字符串的长度

size_t strlen(const char* str);

使用strlen函数时,有几个点需要我们注意:

  • 字符串以'\0' 作为结束标志,strlen函数返回的是在字符串中'\0'前面出现的字符个数(不包含'\0')
  • 参数指向的字符串必须以'\0'结束
  • 注意函数的返回值为size_t,是无符号的(容易出错)
  • strlen函数的使用需要包含头文件 #include<string.h>

这里我们针对函数的返回值为size_t,举一个简单的小例子

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

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

	return 0;
}

str1字符串长度为6,str2字符串长度为3,进入if条件,6-3 = -3 ,哈哈,我知道啦,肯定走else,控制台上打印 str1 > str2

结果是不是这样呢?

诶,好奇怪,为啥打印大于呢?

这是因为,strlen函数返回的是size_t类型 ,无符号整型 , 3-6 = -3 相当于(无符号整型 - 无符号整型 ---------> 无符号整型), -3 转换为无符号的整型,将会是一个非常大的数,因此,会在控制台上打印"大于"。

那我们有没有办法打印 str1 > str2 呢? 办法肯定有, 就是将strlen函数返回的结果强制类型转换为int整型,控制台上就可以打印"小于等于"了。


strlen函数的使用:

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


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

执行结果: 7

接下来我们模拟实现strlen函数

方法1: 采用计数器,定义一个变量pcur,用来遍历字符串,pcur初始时指向数组str,记录的是数组首元素的地址,count计数器初始时为0,每当pcur遍历完一个元素,count自增一次,直到pcur走到'\0',此时返回count

代码如下:

int my_strlen(char arr[]) {
	int count = 0;                //计数器初始时为0
	char* pcur = arr;             //pcur指向首元素
	while (*pcur != '\0') {       //pcur进入循环,一直走到'\0'为止
		count++;                  //每当遍历一个元素,计数器自增一次
		pcur++;                   //pcur继续遍历下一个元素
	}
	return count;                 //返回计数器
}

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

方法2: 我们可以采用递归,通过不断调用自身处理字符串的下一个字符,直到遇到结束符'\0',每次递归返回当前字符长度加上剩余字符串长度,从而得到整个字符串的长度。

比如:字符串为abcdef, 现在我们调用my_strlen方法来获取字符串的长度,那么过程大致是这样的:

my_strlen("abcdef");

1+my_strlen("bcdef");

1+1+my_strlen("cdef");

1+1+1+my_strlen("def");

1+1+1+1+my_strlen("ef");

1+1+1+1+1+my_strlen("f");

1+1+1+1+1+1+my_strlen("");

1+1+1+1+1+1+0;

将结果6返回给控制台

代码如下:

int my_strlen(char arr[]) {
	char* pcur = arr;
	if (*pcur == '\0') {       
 //如果传递过来的arr(首元素的地址为'\0'),则返回0
		return 0;
	}
	else {
//否则,返回当前字符长度加上剩余字符串长度		
       return 1 + my_strlen(arr + 1);
}

int main(){
    char arr[] = "abcdef";
    int len = my_strlen(arr);
    printf("%d\n",len);

    return 0;
}

方法3: 我们可以采取指针-指针得到数组长度,用start指针指向首元素,用end指针指向最后一个元素,两个指针相减即可得到字符串的长度。

代码如下:

int my_strlen(char arr[]) {
	char* start = arr;                    //start指针指向首元素
	char* end = arr;
	while (*end != '\0') {                //end指针一直走到'\0',才退出循环
		end++;
	}
	return end - start;                   //两个指针相减得到它们之间的元素个数
}

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

另外,还有一种不需要指针的代码如下:

int str_len(char st[256]){

    int i = 0; 
    while(st[i] != '\0'){    //下标i从0开始,从第一个元素开始,一直遍历到最后一个元素
        i++;
      }
    return i;                //返回下标i即字符串的长度
    
}

2.strcpy函数的使用和模拟实现

strcpy--->string copy,一眼就看出是字符串拷贝,将源字符串的元素拷贝到目标字符串中去,我们来瞅瞅这个函数

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

使用strcpy函数时,有几个点需要我们注意:

  • 源字符串必须以 '\0' 结束
  • 会将源字符串中的 '\0' 拷贝到目标空间
  • 目标空间必须足够大,以确保能存放源字符串
  • 目标空间必须可以修改

使用strcpy函数

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

int main() {

	char arr1[100] = "";
	char arr2[] = "abcdef";
	strcpy(arr1, arr2);
	printf("%s\n", arr1);

	return 0;
}

执行结果为: abcdef


strcpy函数的模拟实现

方法一: 定义2个数组,一个为源数组,一个为目标数组,将源数组中的数据拷贝到目标数组中,定义2个指针,分别遍历源数组和目标数组,每次遍历完一个元素,2个指针都指向下一个元素,一直遍历到源数组的最后一个元素。

代码如下:

char* my_strcpy(char* dest, const char* src) {   //加上const修饰是为了防止src的内容被改变
	assert(dest && src);	//防止dest和src是空指针
	char* ret = dest;		//用ret来保存dest
	char* s1 = dest;
	char* s2 = src;
	while (*s2 != '\0') {	//拷贝除'\0'的所有内容
		*s1 = *s2;
		s1++;
		s2++;
	}
	*s1 = *s2;				//拷贝'\0'
	return ret;
}

int main(){

    char str1[100] = "#################";
    char str2[] = "abcdef";
    my_strcpy(str1,str2);
    printf("%s\n",str1);

}

emmm,代码有点复杂冗余,可否简短一些呢?

当然可以! 优化代码如下:

char* my_strcpy(char* dest,const char* src) {   //加上const修饰是为了防止src的内容被改变
	assert(dest && src);	//防止dest和src是空指针
	char* ret = dest;		//用ret来保存dest
	char* s1 = dest;
	char* s2 = src;
	while (*s2) {			//拷贝除'\0'的所有内容
	*s1++ = *s2++;
	}
	*s1 = *s2;				//拷贝'\0'
	return ret;
}

哈哈哈,我们还可以再优化:

char* my_strcpy(char* dest, const char* src) {   //加上const修饰是为了防止src的内容被改变
	assert(dest && src);	//防止dest和src是空指针
	char* ret = dest;		//用ret来保存dest
	char* s1 = dest;
	char* s2 = src;
	while (*s1++ = *s2++) {	//循环拷贝,当拷贝完'\0',判断为假,跳出循环
		;					//空语句
	}			
	return ret;
}

还有一种不用指针的解法如下:

char* str_cpy(char str1[256],char str2[50]){

    int i = 0;                    //下标的初始值为0
    while(str2[i] != '\0'){       //str2[i]的元素为'\0'时,跳出循环

        str1[i] = str2[i];        //将str2中的元素赋给str1
        i++;                      //每赋值一次,下标自增一次

    }
        str1[i] = '\0';            //str1数组最后的元素为'\0'
        return str1;               //将str1返回
}

3. strcat函数的使用和模拟实现

strcat函数(字符串拼接函数),是将一个字符串拼接到另外一个字符串,构成一个新的字符串,函数原型如下:

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

使用strcat函数时,有几个点需要我们注意:

  • 源字符串必须以'\0'结束
  • 目标字符串也得有'\0',否则没办法知道追加从哪里开始
  • 目标空间必须足够大,能容纳下源字符串的内容
  • 目标空间必须可修改

使用strcat函数 :

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

int main() {
	char arr1[100] = "abcd";
	char arr2[] = "efgh";
	strcat(arr1, arr2);        //将arr2里面的元素拼接到arr1的后面
	printf("%s\n", arr1);
	return 0;
}

执行结果为: abcdefgh

模拟实现strcat函数

思路: 1. 找到目标字符串的'\0'

2.拷贝源头字符串的数据到目标空间的'\0'位置及后面空间

代码如下:

void my_strcat(char arr1[], char arr2[]) {
    assert(arr1 && arr2);            //保证arr1 和 arr2 有效
	char* s1 = arr1;
	char* s2 = arr2;
	while (*s1 != '\0') {            //找到目标空间的'\0'
		s1++;
	}
    
    //循环将 s2 指向的字符逐个拷贝到 arr1 数组的末尾
    //当拷贝到 arr2 数组的结束符'\0'时,循环结束
    //每拷贝完一个字符, s1 和 s2 都会自增一次,直到循环完成

	while (*s2 != '\0') {            //拷贝除'\0'的部分
		*s1 = *s2;
		s1++;
		s2++;
	}
	*s1 = *s2;                        //拷贝'\0'
}

int main(){
    char arr1[100] = "hello";
    char arr2[] = "world";
    my_strcat(arr1,arr2);
    printf("%s\n",arr1);

}

另外: strcat函数不能自己给自己追加,因为会把源字符串的'\0' 覆盖掉,造成死循环。

另外一种不用指针的解法如下:

char* str_cat(char str1[256],char str2[50]){
    int i = 0;
    int j = 0;
    i = strlen(str1);            //下标i从str1的最后一个元素的下一个位置开始
    while(str2[j] != '\0'){      //遍历str2
    str1[i] = str2[j];           //将str2中的元素拷贝到str1中
    i++;
    j++;
   }
    str1[i] = '\0';              //str1数组最后一个元素为'\0'
    return str1;                 //将str1返回
}

4. strcmp函数的使用和模拟实现

strcmp函数,我们可以将其拆成2个英文单词,string compare,意思是字符串之间的比较,比较的是对应位置上ASCII码值的大小,注意: 比较的不是字符串的长度!

函数原型为:

int strcmp(const char* s1, const char* s2);

只有strcmp函数时,有以下几点需要注意:

  • 第一个字符串大于第二个字符串,则返回大于0的数字(一般返回1)
  • 第一个字符串等于第二个字符串,则返回0
  • 第一个字符串小于第二个字符串,则返回小于0的数字(一般返回-1)
  • 怎样去判断两个字符串呢? 比较两个字符串中对应位置上字符ASCII码值的大小

strcmp函数的使用:

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

int main() {
	char arr1[] = "abcdefg";
	char arr2[] = "abc";
	int ret = strcmp(arr1, arr2);    //字符'd'的ASCII码值比'\0'大,因此返回1
	printf("%d\n", ret);
	return 0;
}

字符串的比较是从左往右依次比较字符的ASCII码值,'d' 的ASCII码值比'\0'大,此时不再进行比较,直接输出结果。

执行结果: 1

怎样实现strcmp函数呢?

思路1 : 定义2个指针,分别指向2个字符串数组,当它们指向的字符的ASCII码值相同时,继续比较下一个字符,如果其中一个字符串数组的元素的ASCII码值比另外一个字符串数组的元素ASCII码值大,则跳出循环,返回1,反之,返回-1; 如果一直比较到最后的'\0'都相同,则返回0。

int my_strcmp(char arr1[], char arr2[]) {
    assert(arr1 && arr2);
	char* s1 = arr1;
	char* s2 = arr2;

     //如果s1指针指向的元素和s2相同,继续比较下一个元素
	while (*s1 == *s2) {   
 
     //如果当前字符是字符串结束符'\0',说明到目前为止,两个字符串都相同  
     //此时可以返回0,表示这两个字符串相同     
		if (*s1 == '\0') {         
			return 0;
		}
    //移动到字符串的下一个字符进行比较
		s1++;
		s2++;
	}
	if (*s1 > *s2) {
		return 1;
	}
	else {
		return -1;
	}
}

int main(){
    char arr1[] = "abcdefgh";
    char arr2[] = "abc";
    int ret = my_strcmp(arr1,arr2);    //'d'的ASCII码值比'\0'大,返回1
    printf("%d\n",ret);
    return 0;
}

另外一种不用指针的解法如下:

int str_cmp(char str1[256],char str2[50]){
    int len1 = strlen(str1);             //字符串str1的长度
    int len2 = strlen(str2);             //字符串str2的长度
    int k = len1 > len2 ? len2 : len1;   //比较两个字符串的长度,找出长度较小的那个字符串

    int t = 0;                           //t表示两个字符串之间的元素相减得到的结果
    for(int i = 0; i <= k; ){ 
      t = str1[i] - str2[i];
    if(t != 0){                //如果该结果不为0,说明两个字符串之间的元素不相同,跳出循环
       break;
     }else{
        i++;                     //如果两个字符串对应的字符恰好相等,那么继续往后遍历
      }
   }

   if(t == 0){
        return 0;          //如果结果为0,说明两个字符串相同,返回0
    }else if( t > 0){
        return 1;          //如果结果大于0,说明str1里面的字符大于str2对应的字符,返回1
    }else{
        return -1;         //如果结果小于0,说明str1里面的字符小于str2对应的字符,返回-1
    }

}

5. strstr函数的使用和模拟实现

strstr函数,是用来在一个源字符串中查找目标字符串第一次出现的位置,并且返回指向该字符串的指针,如果没有找到字符串,返回NULL。注意: 字符串的比较匹配不包含'\0'字符,以'\0'作为结束标志

函数原型如下:

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

strstr函数的使用:

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

int main() {
	char arr1[] = "Today is a nice day!";
	char* S = strstr(arr1, "nice");    //在arr1字符串数组中查找"nice"第一次出现的位置
	printf("%s\n", S);                 //找到了,返回指向该字符串的指针

	return 0;
}

执行结果: nice day!

strstr函数的模拟实现:

思路: 我们定义3个指针变量,分别为s1,s2和pcur, pcur用来标记第一次出现str2的位置;s1初始时指向源数组str1,查找在str1中是否有str2; s2初始时指向目标数组str2,将str1数组和str2数组从头到尾进行比较匹配。

代码如下:

char* my_strstr(char arr1[], char arr2[]) {
	char* pcur = arr1;         //pcur初始时指向arr1
	char* s1 = NULL;
	char* s2 = NULL;

	if (arr2 == '\0') {        //如果传递过来的arr2为空,那么直接返回arr1
		return arr1;
	}

	while (pcur != NULL) {     //pcur没有遍历完arr1,进入循环
		s1 = arr1;
		s2 = arr2;
		while (*s1 == *s2) {    //如果s1指向的元素和s2指向的元素相同,那么继续往后遍历
			s1++;
			s2++;
		}
		if (*s2 == '\0') {      //如果将字符串arr2都遍历完毕,则返回第一次出现该字符串的位置
			return pcur;
		}
		pcur++;                 //如果木有找到,继续往后找
	}
	return NULL;                //如果遍历完整个字符串都没有找到字符串arr2,返回NULL
}


int main(){
    char arr1[] = "abbbcdefg";
    char arr2[] = "bbc";
    char* S = my_strstr(arr1,arr2);
    printf("%s\n",S);

    return 0;
}

写到最后

在这篇文章中,我们学习了一些字符串函数,还有更多的字符串函数等着我们去探索,我将留在下一篇文章。

希望看到这篇文章的友友们,能点赞收藏加关注!!!

非常感谢!!!

相关推荐
yava_free7 分钟前
详解一下JVM诊断方法和其工具的使用
java·开发语言
MATLAB滤波1 小时前
MATLAB中的绘图技巧
开发语言·matlab
致宏Rex2 小时前
Rust 入门指南(零):安装及 Cargo 管理器
开发语言·rust
伏虎山真人2 小时前
R门 - rust第一课陈天 -内存知识学习笔记
开发语言·r语言
好开心332 小时前
javaScript交互补充(元素的三大系列)
开发语言·前端·javascript·ecmascript
码农多耕地呗2 小时前
刷别的学校oj—河工大oj1073-1099
开发语言·c++·算法
Envyᥫᩣ2 小时前
掌握C#中的异步编程:async和await关键字详解
开发语言·c#
sukalot3 小时前
windows C#-查询表达式基础(三)
开发语言·windows·c#
南东山人3 小时前
一文说清C++类型转换操作符(cast operator)
开发语言·c++