C语言常见字符串函数解析(下)

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: C语言常见字符串函数解析(下)

strncpy的自我实现


#include <stdio.h>
#include <assert.h>
#include <string.h>
char* my_strncpy(char* dest, const char* src, int n)
{
  assert(dest && src);
  char* ret = dest;
  // 这里求src的长度是为了解决拷贝个数大于源字符串的长度 + 1的情况
  // 每拷贝一个lenSrc--,当 lenSrc == 0,而 n != 0,此时应该在后面拷贝\0直至n == 0;
  // 因为字符串后面还有一个\0,要把他算在内,所以lenSrc要等于长度 + 1;
  int lenSrc = (int)strlen(src) + 1;
  while (n--)
  {
    if (!lenSrc) // 当lenSrc为0时,!lenSrc为真,后面拷贝\0直至 n == 0;
    {
      *dest++ = '\0';
    }
    else
    {
      *dest++ = *src++;
      lenSrc--;  // 每拷贝一次源字符串的内容,lenSrc--;
    }
  }
  return ret;
}
int main()
{
  char arr1[20] = "xxxxxxxxxxxxxxxx";
  char arr2[] = "abcdef";
  printf("%s\n", my_strncpy(arr1, arr2, 5));
  return 0;
}


strncat


该函数的功能就是指定在目的地字符串后面追加(连接)源字符串的几个字符,其注意事项与strcat差不多,由于长度受限制,所以追加的形式与strcat也会不同。

4e677dc54d6b44f888c88c2b16ca5628.png


strncat的使用

1.目的地字符串只有末尾有\0

#include <stdio.h>
#include <string.h>
int main()
{
  char a1[20] = "xxxxxxxxxxx";
  char a2[] = "abcdef";
  printf("%s\n", strncat(a1, a2, 4));
  return 0;
}

运行结果为:xxxxxxxxxxxabcd

2.目的地字符串中间也有\0

#include <stdio.h>
#include <string.h>
int main()
{
  char a1[20] = "xxx\0xxxxxxx";
  char a2[] = "abcdef";
  printf("%s\n", strncat(a1, a2, 5));
  return 0;
}


运行结果为:xxxabcde

可以发现,当追加完所定个数的字符后,会在此的后面自动追加一个\0(打印的时候遇到\0停止可确定)。

3.当追加的个数大于源字符串的长度时,将源字符串追加过去后便停止追加:


#include <stdio.h>
#include <string.h>
int main()
{
  char a1[20] = "xxxx";
  char a2[] = "abcdef";
  printf("%s\n", strncat(a1, a2, 8));
  return 0;
}

6e8bbf98afc3416aaf1701a61fb2a255.png

运行结果为:xxxxabcdef


strncat的自我实现


了解到上面函数的功能后,接下来就将这些功能实现成strncat函数

  • 核心功能:
    1.追加完后自动再追加一个\0;
    2.目的地字符串先找\0;
    3.源字符串整个追加完后停止追加;
    4.追加的代码;
#include <stdio.h>
#include <assert.h>
#include <string.h>
char* my_strncat(char* dest, const char* src, int n)
{
  assert(dest && src);
  char* ret = dest;
  int lenSrc = (int)strlen(src);
  // 目的地字符串先找\0
  while (*dest)
  {
    dest++;
  }
  while (n)
  {
    // 如果整个源字符串追加完了此时 n != 0,在后面追加一个\0,停止追加
    if (!lenSrc)
    {
      *dest++ = '\0';
      break;
    }
    else
    {
      *dest++ = *src++;
      lenSrc--;
    }
    n--;
  }
  // 如果n == 0也就是说要追加的追加完了,在后面再追加一个\0
  if (n == 0)
    *dest = '\0';
  return ret;
}
int main()
{
  char a1[20] = "xxxxxxx";
  char a2[] = "abcdef";
  printf("%s\n", my_strncat(a1, a2, 8));
  return 0;
}


strncmp

该函数功能是指定两个字符串多少对字符进行对比,对比的方式与strcmp函数相同。


0b90faf864ca4ce4bbd98e8516d6238c.png


3959cc4d70594fff84b64d5987dab611.png

strncmp的使用

#include <stdio.h>
#include <string.h>
int main()
{
  char a1[] = "abcdef";
  char a2[] = "abcdq";
  printf("%d\n", strncmp(a1, a2, 5));
  return 0;
}


当比对到eq时 e的ASCLL码值小于qASCLL码值,所以打印小于零的数,如果第三个参数为4,则返回0,也就要打印0


strncmp的自我实现


这里是按照vs的标准来实现的,也就是比对小于返回-1,比对大于返回1,等于返回0;


#include <stdio.h>
#include <assert.h>
int my_strncmp(const char* str1, const char* str2, int n)
{
  assert(str1 && str2);
  while (n--)
  {
    if (*str1 - *str2)
    {
      if (*str1 < *str2)
        return -1;
      else
        return 1;
    }
    str1++;
    str2++;
  }
    // n个都比对完了,而前面没有返回(比对都相同),说明这n个相等
  return 0;
}
int main()
{
  char a1[20] = "abc";
  char a2[] = "abcgdef";
  printf("%d\n", my_strncmp(a1, a2, 3));
  // 1:a1 > a2
  // 0:a1 == a2
  // -1:a1 < a2
  return 0;
}

3.字符串查找


strstr

该函数功能实际上是判断一个字符串是否是另一个字符串的子串,例如字符串s1 = “iou”,字符串s2 = “youioume?”,s2中间出现了iou这样的子串,所以该函数返回i的地址,打印出来也就是ioume?。

42edf1b82f73443da739df9163978c12.png

ca0fa57403d94ff697b68daaa4be43d7.png


返回指向 str2 中指定的整个字符序列在 str1首次出现的指针,如果字符序列str2str1 中不存在,则返回 null 指针。

strstr的使用

#include <stdio.h>
#include <string.h>
int main()
{
  char arr1[] = "abcdef";
  char arr2[] = "bcde";
  char arr3[] = "bcb";
  printf("%s\n", strstr(arr1, arr2));
  printf("%s\n", strstr(arr1, arr3));
  return 0;
}


运行结果为: bcdef (null)

06738fe604d944b188c12f13d0f2c314.png

strstr的自我实现


strstr的自我实现相对较难,这里我用暴力解法。

定义三个指针cur ,s1,s2,cur为开始匹配的位置(匹配成功好返回),s1与s2是比对指针。

什么时候停止匹配返回NULL呢?

1.当*cur为\0时停止

2.当从cur位置开始后面的字符个数小于str2的字符个数时停止(strlen(cur)< strlen(str2));

8da6f8327b6e4690b6f25835fa314152.png


代码实现:

#include <stdio.h>
#include <string.h>
#include <assert.h>
char* my_strstr(const char* str1, const char* str2)
{
  assert(str1 && str2);
  if (*str2 == '\0')
    return (char*)str1;
  const char* s1 = NULL;
  const char* s2 = NULL;
  const char* cur = str1;
  while (*cur)
  {
    s1 = cur;
    s2 = str2;
    while (!(*s1 - *s2) && *s1 && *s2)
    {
      s1++;
      s2++;
    }
    if (!*s2)  // 如果上面的循环停止是因为*s2 == \0,那么匹配成功
      return (char*)cur;
    if ((int)strlen(cur) < (int)strlen(str2))
      return NULL;
    cur++;
  }
  return NULL;
}
int main()
{
  char arr1[] = "abbbce";
  char arr2[] = "bbc";
  printf("%s\n", my_strstr(arr1, arr2));
  return 0;
}



运行结果为:bbce


strtok


这个函数的功能通俗来说是分割字符串,在一个字符串内输入需要在此字符分割的字符,例如“@.”,也就是说在另一个字符串里,要找到@.并在此字符的位置对该字符串进行分割。


函数参数介绍:


97ee2e63c43f4b68b3292d64b55f32c0.png


3cc5bc7cf29345bfa6cdef99da90679e.png

strtok的使用

  • 当我们第一次传参时,将要分割的字符串和确定分割的字符的字符串传过去,第一次分割好后,strtok函数会自动记住分割的位置,下一次我们要继续调用这个函数,不过函数第一个参数要传NULL(因为strtok自动记住了第一次分割的位置),直到要分割的字符串中没有要分割的字符,此时返回一个 NULL,停止分割。
  • 这里我们可以用一个fou循环来进行分割,当最后返回一个NULL的时候表示分割结束,循环结束。
#include <stdio.h>
#include <string.h>
int main()
{
  char a1[] = "I@love.you";
  char a2[] = "@.";
  char* ret = NULL;
  for (ret = strtok(a1, a2); ret != NULL; ret = strtok(NULL, a2))
  {
    printf("%s\n", ret);
  }
  return 0;
}

5a200ba8feb14f60b24707a172f653b4.png

4.错误信息报告


strerror

  • 该函数的功能相当于是报错误,如果我们在实现某个功能的时候,怕这个程序出现问题,这时我们可以if(如果怎么怎么样),就报一个错误(用strerror),当然学习这个函数还要认识另一个函数,那就是errno(对应头文件为(errno.h))errno:C语言的库函数在运行的时候,如果发生错误,就会将错误码存在一个变量中,这个变量> 是:errno,这时我们用strerror将其输出,就可以得到错误信息。
  • 当然有一些错误码是一些数字:1 2 3 4 5
  • 我们也可以将这些数字作为strerror函数的参数输出,这时需要将错误码翻译成错误信息然后再输出。


strerror的使用

1.数字错误码:

#include <stdio.h>
#include <string.h>
int main()
{
  printf("%s\n", strerror(0));
  printf("%s\n", strerror(1));
  printf("%s\n", strerror(2));
  printf("%s\n", strerror(3));
  printf("%s\n", strerror(4));
  printf("%s\n", strerror(5));
  return 0;
}


a27874c9954744d7ab06602708bebcb6.png


2.errno对应的使用:

这里开开辟一个巨大的空间,如果开辟失败,就会返回NULL,然后就会输出对应的错误信息(为什么开辟失败)。

#include <stdio.h>
#include <string.h>
#include <errno.h> // 使用errno所需头文件
#include <stdlib.h>
int main()
{
  int* tmp = (int*)malloc(sizeof(int) * 12345678910);
  if (tmp == NULL)
  {
    printf("%s\n", strerror(errno));
  }
  else
  {
    printf("开辟成功!\n");
  }
  free(tmp);
  tmp = NULL;
  return 0;
}


f2b2cd93831f4d27a9997f190e03b5da.png

写在最后


常用字符串函数的熟练使用可以在某些地方大大提升写代码效率,因此一定要好好掌握,最好能够自我实现,吃透这些函数的运行逻辑,以便更精确更融洽的使用。


感谢阅读本小白的博客,错误的地方请严厉指出噢!

相关文章
|
15天前
|
存储 网络协议 编译器
【C语言】深入解析C语言结构体:定义、声明与高级应用实践
通过根据需求合理选择结构体定义和声明的放置位置,并灵活结合动态内存分配、内存优化和数据结构设计,可以显著提高代码的可维护性和运行效率。在实际开发中,建议遵循以下原则: - **模块化设计**:尽可能封装实现细节,减少模块间的耦合。 - **内存管理**:明确动态分配与释放的责任,防止资源泄漏。 - **优化顺序**:合理排列结构体成员以减少内存占用。
84 14
|
19天前
|
存储 编译器 C语言
【C语言】数据类型全解析:编程效率提升的秘诀
在C语言中,合理选择和使用数据类型是编程的关键。通过深入理解基本数据类型和派生数据类型,掌握类型限定符和扩展技巧,可以编写出高效、稳定、可维护的代码。无论是在普通应用还是嵌入式系统中,数据类型的合理使用都能显著提升程序的性能和可靠性。
40 8
|
19天前
|
存储 算法 C语言
【C语言】深入浅出:C语言链表的全面解析
链表是一种重要的基础数据结构,适用于频繁的插入和删除操作。通过本篇详细讲解了单链表、双向链表和循环链表的概念和实现,以及各类常用操作的示例代码。掌握链表的使用对于理解更复杂的数据结构和算法具有重要意义。
145 6
|
19天前
|
存储 网络协议 算法
【C语言】进制转换无难事:二进制、十进制、八进制与十六进制的全解析与实例
进制转换是计算机编程中常见的操作。在C语言中,了解如何在不同进制之间转换数据对于处理和显示数据非常重要。本文将详细介绍如何在二进制、十进制、八进制和十六进制之间进行转换。
29 5
|
19天前
|
C语言 开发者
【C语言】断言函数 -《深入解析C语言调试利器 !》
断言(assert)是一种调试工具,用于在程序运行时检查某些条件是否成立。如果条件不成立,断言会触发错误,并通常会终止程序的执行。断言有助于在开发和测试阶段捕捉逻辑错误。
27 5
|
19天前
|
存储 C语言 开发者
【C语言】字符串操作函数详解
这些字符串操作函数在C语言中提供了强大的功能,帮助开发者有效地处理字符串数据。通过对每个函数的详细讲解、示例代码和表格说明,可以更好地理解如何使用这些函数进行各种字符串操作。如果在实际编程中遇到特定的字符串处理需求,可以参考这些函数和示例,灵活运用。
39 10
|
19天前
|
存储 程序员 C语言
【C语言】文件操作函数详解
C语言提供了一组标准库函数来处理文件操作,这些函数定义在 `<stdio.h>` 头文件中。文件操作包括文件的打开、读写、关闭以及文件属性的查询等。以下是常用文件操作函数的详细讲解,包括函数原型、参数说明、返回值说明、示例代码和表格汇总。
42 9
|
19天前
|
存储 Unix Serverless
【C语言】常用函数汇总表
本文总结了C语言中常用的函数,涵盖输入/输出、字符串操作、内存管理、数学运算、时间处理、文件操作及布尔类型等多个方面。每类函数均以表格形式列出其功能和使用示例,便于快速查阅和学习。通过综合示例代码,展示了这些函数的实际应用,帮助读者更好地理解和掌握C语言的基本功能和标准库函数的使用方法。感谢阅读,希望对你有所帮助!
31 8
|
19天前
|
C语言 开发者
【C语言】数学函数详解
在C语言中,数学函数是由标准库 `math.h` 提供的。使用这些函数时,需要包含 `#include <math.h>` 头文件。以下是一些常用的数学函数的详细讲解,包括函数原型、参数说明、返回值说明以及示例代码和表格汇总。
40 6
|
19天前
|
存储 C语言
【C语言】输入/输出函数详解
在C语言中,输入/输出操作是通过标准库函数来实现的。这些函数分为两类:标准输入输出函数和文件输入输出函数。
108 6

推荐镜像

更多
下一篇
DataWorks