【C字符串函数】——对C语言中有关字符串库函数的实现及模拟

简介: 【C字符串函数】——对C语言中有关字符串库函数的实现及模拟

目录


字符串函数

前言

1.0 strlen的实现及模拟(求字符串长度)

1.1 strcmp的实现及模拟 (字符串比较)

strncmp的实现及模拟

1.2 strcpy的实现及模拟(字符串拷贝)

strncpy的使用及模拟实现

1.3 strcat的实现及模拟(字符串追加)

strncat的使用及模拟实现

1.4 strstr的实现及模拟(字符串查找)

1.5 strtok函数(在字符串中定义用作分隔符的集合)

1.6 strerror函数 (错误码转换错误信息)

字符串函数


前言

我们已经了解了C语言中很多数据类型,比如int(整数类型)、char(字符类型)、以及浮点型的double(双精度)、float(单精度),但是有一点就是我们发现这里并没有提到我们常见的有关字符串的类型。其实在C语言中,字符串通常是放在 常量字符串 中或者 字符数组 中的。(常量字符串是不可被修改的)


1.0 strlen的实现及模拟(求字符串长度)


字符串是双引号" "引起来的,以 ‘\0’ 作为结束标志,strlen是专门用来求字符串长度的,strlen函数返回的是在字符串中 ‘\0’ 前面出现的字符个数(不包含 ‘\0’ )。并且返回值必须是正数,因为字符个数不存在负数情况!


1.jpeg


使用以及模拟实现


#include<stdio.h>//printf头文件
#include<string.h>//strlen头文件
#include<assert.h>//assert头文件
//返回值为无符号类型(正数)
//模拟实现,这里尽量做到还原原函数的形式
size_t my_strlen(const char* str)
{
  assert(str);//断言,如果str是NULL,编译器报错
  const char* start = str;//首字符地址赋给start以及end
  const char* end = str;
  while (*end != '\0')//当end指向'\0'时结束
  {
  end++;
  }
  return end - start;//指针相减得到两者之间元素个数
}
int main()
{
  char arr[] = "abcdef";
  printf("%d\n", strlen(arr));//arr是数组名,表示数组首元素地址,即a的地址,返回值为\0之前的字符个数,即6
  int len=my_strlen(arr);
  printf("%d\n", len);//6
  return 0;
}


这里我是采用指针-指针=两指针之间元素个数的方式,还有很多方式都可以模拟实现,比如创建一个变量计数的方式等,如下:


size_t my_strlen(const char * str)
{
 assert(str);//断言
 size_t count = 0;//无符号
 while(*str)
 {
  count++;//指针从起始位置开始往前走,每走一次count++一次
  str++;
 }
 return count;//最后返回count
}


1.1 strcmp的实现及模拟 (字符串比较)

strcmp是用来比较两个字符串的。两个字符串比较的其实是各字符所对应的ASCII码值,如下:


2.jpeg


使用以及模拟实现


#include<string.h>//strcpy头文件包含
#include<stdio.h>//printf头文件包含
#include<assert.h>//断言
int my_strcmp(const char* s1, const char* s2)
{
  assert(s1 && s2);//断言
  while (*s1 == *s2)//如果两个字符串中对应的字符相等,则继续往后找
  {
  if (*s1 == '\0')//当s1到\0时,说明s2也是\0,字符串直到结束,两者都相等
    return 0;//返回0
  //如果没到\0,继续往后走,进行下一个字符比较  
  s1++;
  s2++;
  }
  //当跳出循环时,说明s1与s2指向的字符不相等,直接返回两者之差即可
  return *s1 - *s2;
  //*s1 -*s2>0  即 *s1>*s2,即字符数组arr1中的字符串>arr2中的
               //反之则小于
}
int main()
{
    //字符串存放在字符数组
  char arr1[] = "abcdeaf";
  char arr2[] = "abcdef";
  //模拟实现
  int len=my_strcmp(arr1, arr2);
    //strcmp两个参数,arr1,arr2数组名,即首元素地址,即字符串首字符地址
  printf("%d\n", strcmp(arr1, arr2));//-5
  printf("%d\n", len);//-5         a对应ASCII:97  f:102
                                   //   97-102 ==-5
  return 0;
}


strncmp的实现及模拟

strncmp与strcmp很类似,也是用来比较两个字符串的,唯一的区别就是strcmp是比较整个字符串,而strncmp可以指定比较的第多少个字符,比如说比较abcdef与abcdfg两个字符串的前三个字符,结果返回值就是0。因为仅仅比较的是前三个字符,即abc。


模拟实现


#include<string.h>
#include<stdio.h>
#include<assert.h>
int my_strncmp(const char* str1, const char* str2, size_t num)
{
  assert(str1 && str2);//断言
  //比较前num个字符,两者相等时,进入循环,继续往后比,num限制比较字符数
  while (--num && (*str1) && (*str1 == *str2))
  {
  str1++;
  str2++;
  }
  //跳出循环,说明num比完了,或者有一方遇到\0,或者两字符不相等
  //直接返回两者之差
  return *str1 - *str2;
}
int main()
{
  char arr1[] = "abcdef";
  char arr2[] = "abcbefgg";
  int num = 0;
  scanf("%d", &num);
  //比较字符串arr1与arr2的前num个字符
  int len=my_strncmp(arr1, arr2,num);
  //printf("%d\n", strncmp(arr1, arr2,3));//0
  printf("%d\n", len);// num=3,len=0;num=4,len>0
  return 0;
}


1.2 strcpy的实现及模拟(字符串拷贝)

strcpy是用来拷贝字符串的


3.png


注意事项


!!! 源字符串必须以 ‘\0’ 结束。

!!! 会将源字符串中的 ‘\0’ 拷贝到目标空间。

!!! 目标空间必须足够大,以确保能存放源字符串。

!!! 目标空间必须可变。


4.png


使用及模拟实现


#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strcpy(char*dest,char*src)
{
  //断言
  assert(dest && src);
  char* ret = dest;//用来记住arr的起始地址
  while (*dest++ = *src++)//不断地赋值,arr不断地被覆盖
  {
  ;
  }
  //返回起始地址
  return ret;
}
int main()
{
  char arr[20] = "hello";
  char* p = "abcdef";
  //strcpy(arr, p);
  //模拟实现
  my_strcpy(arr, p);
  printf("%s", arr);//abcdef
  return 0;
}


strncpy的使用及模拟实现

strncpy与strcpy的使用相类似,但是strcpy是拷贝整个字符串,而strncpy是可以指定拷贝,即只拷贝num个字符,如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。




使用及模拟实现


#include<stdio.h>
#include<assert.h>
char* my_strncpy(char* dest, const char* sor, size_t num)
{
  //断言
  assert(dest && sor);
  char* s = dest;//起始地址记住
  int i = 0;
  for (i = 0; i < (int)num; i++)
  {
  //开始拷贝,num次,每次拷贝一个字符,拷贝完后,两指针往后走,继续拷贝
  *dest++ = *sor++;
  }
  //返回起始地址
  return s;
}
int main()
{
  char arr1[20] = "abcdefghi";
  char arr2[20] = "xxxx";
  size_t n = 0;
  scanf("%d", &n);
  my_strncpy(arr1, arr2, n);
  //从arr2中拷贝n个字符到arr1去
  printf("%s", arr1);//n=4,xxxxefghi\0 ; n=5,xxxx\0fghi\0,但是打印的是xxxx,因为遇到\0字符串停止
  return 0;
}


1.3 strcat的实现及模拟(字符串追加)

strcat是字符串追加函数,顾名思义就是在一个字符串后面再增加另外一个字符串。

注意事项

源字符串必须以 ‘\0’ 结束。

目标空间必须有足够的大,能容纳下源字符串的内容。

目标空间必须可修改。

不能自己给自己追加


4.png


实现及模拟


#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strcat(char* dest, const char* src)
{
  //断言
  assert(dest && src);
  char* sur = dest;//记住起始地址
  while (*sur != '\0')
  {
  sur++;
  }
  //走到这里,sur已经指向了arr1的\0处,从这里开始,把src指向的字符即arr2的字符赋给sur
  while (*sur++ = *src++)
  {
  ;
  }
  //最后返回arr1追加后的起始空间地址
  return dest;
}
int main()
{
  char arr1[20] = "hello ";
  char arr2[] = "world";
  //在arr1后面增加arr2
  //strcat(arr1, arr2);
  my_strcat(arr1, arr2);
  printf("%s\n", arr1);//hello world
  return 0;
}


strncat的使用及模拟实现

strncat与strcat很类似,只不过strcat是追加整个字符串,而strncat是追加指定的字符串,比如说可以给arr1追加4个字符


5.png


模拟实现及使用


#include<stdio.h>
#include<assert.h>
char* my_strncat(char* dest, const char* sor, size_t num)
{
  assert(dest && sor);
  char* p = dest;
  while (*p != '\0')
  {
  p++;
  }
  //此时已经指向arr1中的\0
  //把num个字符赋给*p
  while (num--)
  {
  *p++ = *sor++;
  }
  return dest;
}
int main()
{
  char arr1[20] = "hello ";
  char arr2[20] = "world!!!!!";
  size_t n = 0;
  scanf("%d", &n);
  my_strncat(arr1, arr2, n);
  printf("%s", arr1);//n=5,hello world
  return 0;
}


1.4 strstr的实现及模拟(字符串查找)

strstr是实现字符串查找的一个函数,即在一个字符串中查找该字符串是不是含有另一个字符串


6.png


使用及模拟


#include<stdio.h>
#include<string.h>
#include<assert.h>
//char* strstr(const char*str1,const char*str2)
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 && *s2 && *s1 == *s2)
  {
    s1++;
    s2++;
  }
  if (*s2 == '\0')
    return (char*)p;
  p++;
  }
  return NULL;
}
int main()
{
  char arr1[] = "abbbcdef";
  char arr2[] = "bbpc";
  //char* p = strstr(arr1, arr2);
  char* p = my_strstr(arr1, arr2);
  if (p == NULL)
  printf("不存在");
  else
  printf("%s\n", p);//不包含,输出结果为不存在
  return 0;
}


1.5 strtok函数(在字符串中定义用作分隔符的集合)


7.png


注意事项

1、strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)


2、strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。


3、strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。


4、如果字符串中不存在更多的标记,则返回 NULL 指针。


举例使用


#include<stdio.h>
#include<string.h>
int main()
{
  char arr[] = "zinuo@qq.com.qdy";
  char buf[200] = { 0 };
  //p是用作分隔符的字符串首字符地址
  const char* p = "@..";
  //由于strtok函数会改变原字符串的内容,所以一般都会拷贝一个用来使用
  strcpy(buf, arr);
  char* str = NULL;
  for (str=strtok(buf,p); str != NULL; str=strtok(NULL,p) )
  {
  //用来做分隔符的字符都被改成了\0,下一次调用函数时,会从这个\0开始继续往后查找分隔符,再次改为\0,一直到找完所有分隔符,返回NULL
  printf("%s\n", str);//zinuo
                      //qq
                   //com
                  //qdy
  }
  return 0;
}


1.6 strerror函数 (错误码转换错误信息)

#include<stdio.h>
#include<errno.h>//errno包含头文件
#include<string.h>//strerror包含头文件
int main()
{
  //打开文件操作
  FILE* pf = fopen("test.txt", "r");
  //打开文件失败,返回空指针,strerror可以将错误码转换成让我们看得懂的错误信息
  if (pf == NULL)
  {
  perror("lzn");//相当于printf+strerror  
  //lzn: No such file or directory
  //没有这个文件,错误码转换为错误信息打印出来
  printf("%s\n", strerror(errno));
  //No such file or directory
  return 1;
  }
  //关闭文件
  fclose(pf);
  pf = NULL;
  return 0;
}


end

生活原本沉闷,但跑起来就会有风!


相关文章
|
1月前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
35 3
|
15天前
|
C语言
c语言调用的函数的声明
被调用的函数的声明: 一个函数调用另一个函数需具备的条件: 首先被调用的函数必须是已经存在的函数,即头文件中存在或已经定义过; 如果使用库函数,一般应该在本文件开头用#include命令将调用有关库函数时在所需要用到的信息“包含”到本文件中。.h文件是头文件所用的后缀。 如果使用用户自己定义的函数,而且该函数与使用它的函数在同一个文件中,一般还应该在主调函数中对被调用的函数做声明。 如果被调用的函数定义出现在主调函数之前可以不必声明。 如果已在所有函数定义之前,在函数的外部已做了函数声明,则在各个主调函数中不必多所调用的函数在做声明
30 6
|
1月前
|
存储 缓存 C语言
【c语言】简单的算术操作符、输入输出函数
本文介绍了C语言中的算术操作符、赋值操作符、单目操作符以及输入输出函数 `printf` 和 `scanf` 的基本用法。算术操作符包括加、减、乘、除和求余,其中除法和求余运算有特殊规则。赋值操作符用于给变量赋值,并支持复合赋值。单目操作符包括自增自减、正负号和强制类型转换。输入输出函数 `printf` 和 `scanf` 用于格式化输入和输出,支持多种占位符和格式控制。通过示例代码详细解释了这些操作符和函数的使用方法。
40 10
|
28天前
|
存储 算法 程序员
C语言:库函数
C语言的库函数是预定义的函数,用于执行常见的编程任务,如输入输出、字符串处理、数学运算等。使用库函数可以简化编程工作,提高开发效率。C标准库提供了丰富的函数,满足各种需求。
|
1月前
|
机器学习/深度学习 C语言
【c语言】一篇文章搞懂函数递归
本文详细介绍了函数递归的概念、思想及其限制条件,并通过求阶乘、打印整数每一位和求斐波那契数等实例,展示了递归的应用。递归的核心在于将大问题分解为小问题,但需注意递归可能导致效率低下和栈溢出的问题。文章最后总结了递归的优缺点,提醒读者在实际编程中合理使用递归。
61 7
|
1月前
|
存储 编译器 程序员
【c语言】函数
本文介绍了C语言中函数的基本概念,包括库函数和自定义函数的定义、使用及示例。库函数如`printf`和`scanf`,通过包含相应的头文件即可使用。自定义函数需指定返回类型、函数名、形式参数等。文中还探讨了函数的调用、形参与实参的区别、return语句的用法、函数嵌套调用、链式访问以及static关键字对变量和函数的影响,强调了static如何改变变量的生命周期和作用域,以及函数的可见性。
30 4
|
1月前
|
存储 编译器 C语言
C语言函数的定义与函数的声明的区别
C语言中,函数的定义包含函数的实现,即具体执行的代码块;而函数的声明仅描述函数的名称、返回类型和参数列表,用于告知编译器函数的存在,但不包含实现细节。声明通常放在头文件中,定义则在源文件中。
ly~
|
1月前
|
数据可视化 BI API
除了 OpenGL,还有哪些常用的图形库可以在 C 语言中使用?
除了OpenGL,C语言中还有多个常用的图形库:SDL,适合初学者,用于2D游戏和多媒体应用;Allegro,高性能,支持2D/3D图形,广泛应用于游戏开发;Cairo,矢量图形库,支持高质量图形输出,适用于数据可视化;SFML,提供简单接口,用于2D/3D游戏及多媒体应用;GTK+,开源窗口工具包,用于创建图形用户界面。这些库各有特色,适用于不同的开发需求。
ly~
234 4
|
1月前
|
存储 C语言
【c语言】字符串函数和内存函数
本文介绍了C语言中常用的字符串函数和内存函数,包括`strlen`、`strcpy`、`strcat`、`strcmp`、`strstr`、`strncpy`、`strncat`、`strncmp`、`strtok`、`memcpy`、`memmove`和`memset`等函数的使用方法及模拟实现。文章详细讲解了每个函数的功能、参数、返回值,并提供了具体的代码示例,帮助读者更好地理解和掌握这些函数的应用。
25 0
|
1月前
|
C语言
【c语言】qsort函数及泛型冒泡排序的模拟实现
本文介绍了C语言中的`qsort`函数及其背后的回调函数概念。`qsort`函数用于对任意类型的数据进行排序,其核心在于通过函数指针调用用户自定义的比较函数。文章还详细讲解了如何实现一个泛型冒泡排序,包括比较函数、交换函数和排序函数的编写,并展示了完整的代码示例。最后,通过实际运行验证了排序的正确性,展示了泛型编程的优势。
21 0