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

写在最后


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


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

相关文章
|
1月前
|
C语言 C++
【C语言】解决不同场景字符串问题:巧妙运用字符串函数
【C语言】解决不同场景字符串问题:巧妙运用字符串函数
|
23天前
|
存储 C语言
【c语言】字符串函数和内存函数
本文介绍了C语言中常用的字符串函数和内存函数,包括`strlen`、`strcpy`、`strcat`、`strcmp`、`strstr`、`strncpy`、`strncat`、`strncmp`、`strtok`、`memcpy`、`memmove`和`memset`等函数的使用方法及模拟实现。文章详细讲解了每个函数的功能、参数、返回值,并提供了具体的代码示例,帮助读者更好地理解和掌握这些函数的应用。
19 0
|
1月前
|
存储 安全 编译器
深入C语言库:字符与字符串函数模拟实现
深入C语言库:字符与字符串函数模拟实现
|
1月前
|
C语言
C语言常见字符函数和字符串函数精讲
C语言常见字符函数和字符串函数精讲
|
1月前
|
程序员 编译器 数据处理
【C语言】深度解析:动态内存管理的机制与实践
【C语言】深度解析:动态内存管理的机制与实践
|
1月前
|
C语言
【C语言】模拟实现深入了解:字符串函数
【C语言】模拟实现深入了解:字符串函数
|
1月前
|
Serverless 编译器 C语言
【C语言】指针篇- 深度解析Sizeof和Strlen:热门面试题探究(5/5)
【C语言】指针篇- 深度解析Sizeof和Strlen:热门面试题探究(5/5)
|
3月前
|
安全 程序员 C语言
【C语言】字符串函数及其模拟实现
【C语言】字符串函数及其模拟实现
|
3月前
|
程序员 C语言
位操作在C语言中的解析与应用
位操作在C语言中的解析与应用
90 0
|
3月前
|
C语言
【C语言篇】字符和字符串以及内存函数详细介绍与模拟实现(下篇)
perror函数打印完参数部分的字符串后,再打印⼀个冒号和⼀个空格,再打印错误信息。
61 0

推荐镜像

更多