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

简介: C语言常见字符串函数解析(上)

前言


  • 常见的字符串函数在一定程度上可以让我们在写代码,或者是在刷某些有关字符串的题目时事半功倍,并且常见字符串函数的功能非常常用,因此我们应该熟练使用这些字符串函数,以及部分函数要能自我实现。
  • 字符串函数都要引入一个库函数:string(#include <string.h>)


1.长度不受限制的常见字符串函数


strlen


strlen函数是求字符串长度的,遇到 \0 停止(计算\0之前有多少个字符),如果有多个\0,则只计算第一个\0前面的字符个数。


a0cc65abf82146f793825c7a577559fa.png


strlen的使用:

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



运行结果:6


arr有6个字符,所以所得为6,值得注意的是,strlen函数的返回值是 size_t(unsigned int),size_t表示无符号整型,但是这里我们用%d形式打印也是没有问题的。


为什么strlen函数的返回值要弄size_t 呢?因为长度是没有负数之说的,所以size_t符合实际,但是size_t又难免会出现一些问题,例如:

#include <stdio.h>
#include <string.h>
int main()
{
  char a1[] = "abc"; // 3
  char a2[] = "abcdef";  // 6
  if (strlen(a1) - strlen(a2) < 0)
  {
    printf("a1 < a2\n");
  }
  else
  {
    printf("a1 > a2\n");
  }
  return 0;
}


猜这里输出的结果是什么呢?


正常来说应该输出a1 < a2才对,可是这里的输出是a1 > a2,那么就说明strlen(a1) - strlen(a2) > 0,这是为什么呢?


strlen(a1)返回一个size_t的数3,strlen(a2)返回一个size_t的数6,3 - 6 = -3,此时-3也是一个size_t类型,所以当-3作为一个无符号数来看待的话,那将是一个很大的整数,自然也就大于零输出第一个printf了。


所以库函数中strlen返回值为size_t可以说有利也有弊,需细心使用,接下来我对字符串函数的实现,如果是返回整型的话,我都会采用返回int的。


strlen的自我实现


这里我的strlen实现有三种方式:计数,指针减指针,递归,他们分别对应my_strlen1, my_strlen2, my_strlen3

#include <stdio.h>
#include <assert.h>
int my_strlen1(const char* s)
{
  assert(s);
  int count = 0;
  while (*s)
  {
    ++count;   // *s 不是 \0 就加一
    ++s;
  }
  return count;
}
int my_strlen2(const char* s)
{
  assert(s);
  const char* cur = s;
  while (*cur)
  {
    cur++;
  }
  return (int)(cur - s); // 用 cur 指针找到 \0 ,再用 cur 减去 s 得到之间字符的个数 6
}
int my_strlen3(const char* s)
{
  assert(s);
  if (*s != '\0')
    return 1 + my_strlen3(s + 1);
  else
    return 0;
}
int main()
{
  char a[] = "abcdef";
  printf("%d\n", my_strlen1(a));  // 6
  printf("%d\n", my_strlen2(a));  // 6
  printf("%d\n", my_strlen3(a));  // 6
  return 0;
}


strcpy


strcpy的功能是字符串拷贝,将源头(src)字符串拷贝到目的地(dest)字符串当中,并且是从头开始拷贝,src中的\0也要拷贝过去。

注意:dest 的字符串长度要大于等于 src ,不然 src 拷贝过去会出现非法访问的错误。


image.png


strcpy函数返回目的地字符串(被拷贝后)首元素地址。

strcpy的使用

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



运行结果为:abcdef


41b189aa1395415ea2d9bb16db1f08d7.png

strcpy的自我实现


上图实际上就是整个拷贝的过程,*dest++ = *src++是整个代码实现核心。

实现代码如下:


#include <stdio.h>
#include <assert.h>
char* my_strcpy(char* dest, const char* src)
{
  assert(dest && src);
  char* ret = dest;  // 先要记住dest的起始位置
  while (*dest++ = *src++)  // 先运算*dest = *src,再判断*dest,再分别++
  {
    ;
  }
  return ret; // 返回dest起始位置
}
int main()
{
  char arr1[] = "xxxxxxxxxx";
  char arr2[] = "abcdef";
  printf("%s\n", my_strcpy(arr1, arr2));
  return 0;
}


strcat


该函数的功能是在目的地字符串末尾追加源字符串(连接),目的地字符串的末尾不包括\0,也就是说\0将会被追加的字符串覆盖。

  • 注意:
    源字符串必须以 \0 结束。
    目标空间必须足够的大,能容纳下追加后目的地字符串的所有内容。
    目标空间必须可修改。


dca2c8d60d964f28a78be5dcdeeabf67.png


strcat的使用

1.正常的追加

42cf21965c16468aac933644766282ad.png

#include <stdio.h>
#include <string.h>
int main()
{
  char arr1[20] = "xxxxx";
  char arr2[] = "abc";
  printf("%s\n", strcat(arr1, arr2));
  return 0;
}

运行结果为:xxxxxabc

2.目的地字符串中存在\0


b88641f38d244492a89ff7127fd79dca.png


#include <stdio.h>
#include <string.h>
int main()
{
  char arr1[20] = "xxx\0xxxxxxxx";
  char arr2[] = "abcdef";
  printf("%s\n", strcat(arr1, arr2));
  return 0;
}

运行结果为:xxxabcdef

要注意:


strcat不能追加自己,因为再追加自己的同时,末尾的\0在追加的时候被修改了,这时就会死循环,因为要追加的字符串也找不到\0了,此时程序会崩溃。如果要追加自己,可以用下面要讲解的strnpy函数。


strcat的自我实现

通过上面的解析可以知道,我们首先要让一个指针找到dest目的地字符串)的\0,再进行追加(连接),而追加的功能类似于拷贝(*dest++ = *src++)。


代码实现:

#include <stdio.h>
#include <assert.h>
char* my_strcat(char* dest, const char* src)
{
  assert(dest && src);
  char* ret = dest; 
  // 先找到 dest 的第一个 \0
  while (*dest)
  {
    dest++;
  }
  while (*dest++ = *src++) // 追加
  {
    ;
  }
  return ret;
}
int main()
{
  char arr1[20] = "xxxxx";  // 大小为 20 ,为了能够承受住 arr2 的追加
  char arr2[] = "abcdef";
  printf("%s\n", my_strcat(arr1, arr2));
  return 0;
}


运行结果为:xxxxxabcdef

strcmp

该函数的功能是比较两个字符串,看相等,小于,还是大于,是小于还是大于是根据字符的ASCLL码值来比较的。而字符的比较是两个字符串一对一对字符的比。


55ddedca99f84ae2b332ea2f61d9d69a.png

ce77c77560aa46318c398335d05a9790.png


  • 如果str1 < str2 返回一个小于0的数,如果str1 == str2返回0,如果str1 > str2返回一个大于0的数。
  • 例如”abc““ac”比较,a == ab != c,又bASCLL码值``小于``c的ASCLL码值,所以返回一个小于零的数。


strcmp的使用

#include <stdio.h>
#include <string.h>
int main()
{
  char arr1[] = "abcdef";
  char arr2[] = "abcdq";
  char arr3[] = "abcd";
  printf("%d\n", strcmp(arr1, arr2));
  printf("%d\n", strcmp(arr1, arr3));
  return 0;
}


运行结果为:-1 1

这是因为vs的strcmp如果小返回-1,大返回1,相等返回0,而标准就是上面所说。


7c6a42ce40ba4b7397bd88fcf4e5bffd.png


strcmp的自我实现

这里按标准的返回值来实现

#include <stdio.h>
#include <assert.h>
int my_strcmp(const char* str1, const char* str2)
{
  assert(str1 && str2);
  while (*str1 != '\0' || *str2 != '\0')
  {
    if (*str1 - *str2)
    {
      return *str1 - *str2;
    }
    str1++;
    str2++;
  }
  return 0;
}
int main()
{
  char arr1[] = "abcdef";
  char arr2[] = "abcdq";
  printf("%d\n", my_strcmp(arr1, arr2));
  return 0;
}


运行结果为:-12

2.长度受限制的常见字符串函数

strncpy


该函数的功能是指定拷贝几个字符,与strcpy不同的是,strncpy多了一个确定拷贝字符个数的参数,这也就限制了长度,让使用者更能精确的拷贝自己想要的字符。


同样要注意的是:

1.源字符串拷贝到目的地字符串时不能超出目的地字符串的空间大小;

2.如果拷贝个数小于源字符串的长度,这时不会拷贝\0,也就是“abcdef”,如果拷贝4个,则只拷贝“abcd”过去;

3.如果拷贝个数大于源字符串的长度 + 1(因为后面还有一个\0),则多出来的拷贝放\0。


该函数的函数参数:


9237c4425b554e7d9b0f7580266261aa.png

strncpy的使用

1.正常拷贝:

#include <stdio.h>
#include <string.h>
int main()
{
  char arr1[] = "xxxxxxxxxxxxxxxx";
  char arr2[] = "abcdef";
  printf("%s\n", strncpy(arr1, arr2, 5));  // 拷贝5个
  return 0;
}


运行结果为:abcdexxxxxxxxxxx

2.拷贝个数等于源字符串的长度 + 1

#include <stdio.h>
#include <string.h>
int main()
{
  char arr1[] = "xxxxxxxxxxxxxxxx";
  char arr2[] = "abcdef";
  printf("%s\n", strncpy(arr1, arr2, 7)); // 拷贝7个,该字符串的长度为6
  return 0;
}


运行结果为:abcdef

3.拷贝个数大于源字符串的长度 + 1:

#include <stdio.h>
#include <string.h>
int main()
{
  char arr1[] = "xxxxxxxxxxxxxxxx";
  char arr2[] = "abcdef";
  printf("%s\n", strncpy(arr1, arr2, 10)); // 拷贝10个,arr2不够,后面拷贝\0
  return 0;
}


76898c38b3094fa2b2da74d9bf2ab6ed.png


运行结果为:abcdef


相关文章
|
2月前
|
安全 C语言
C语言中的字符、字符串及内存操作函数详细讲解
通过这些函数的正确使用,可以有效管理字符串和内存操作,它们是C语言编程中不可或缺的工具。
239 15
|
9月前
|
存储 网络协议 编译器
【C语言】深入解析C语言结构体:定义、声明与高级应用实践
通过根据需求合理选择结构体定义和声明的放置位置,并灵活结合动态内存分配、内存优化和数据结构设计,可以显著提高代码的可维护性和运行效率。在实际开发中,建议遵循以下原则: - **模块化设计**:尽可能封装实现细节,减少模块间的耦合。 - **内存管理**:明确动态分配与释放的责任,防止资源泄漏。 - **优化顺序**:合理排列结构体成员以减少内存占用。
693 14
|
9月前
|
存储 编译器 C语言
【C语言】数据类型全解析:编程效率提升的秘诀
在C语言中,合理选择和使用数据类型是编程的关键。通过深入理解基本数据类型和派生数据类型,掌握类型限定符和扩展技巧,可以编写出高效、稳定、可维护的代码。无论是在普通应用还是嵌入式系统中,数据类型的合理使用都能显著提升程序的性能和可靠性。
400 8
|
9月前
|
存储 算法 C语言
【C语言】深入浅出:C语言链表的全面解析
链表是一种重要的基础数据结构,适用于频繁的插入和删除操作。通过本篇详细讲解了单链表、双向链表和循环链表的概念和实现,以及各类常用操作的示例代码。掌握链表的使用对于理解更复杂的数据结构和算法具有重要意义。
2899 6
|
9月前
|
存储 网络协议 算法
【C语言】进制转换无难事:二进制、十进制、八进制与十六进制的全解析与实例
进制转换是计算机编程中常见的操作。在C语言中,了解如何在不同进制之间转换数据对于处理和显示数据非常重要。本文将详细介绍如何在二进制、十进制、八进制和十六进制之间进行转换。
812 5
|
9月前
|
C语言 开发者
【C语言】断言函数 -《深入解析C语言调试利器 !》
断言(assert)是一种调试工具,用于在程序运行时检查某些条件是否成立。如果条件不成立,断言会触发错误,并通常会终止程序的执行。断言有助于在开发和测试阶段捕捉逻辑错误。
192 5
|
5天前
|
存储 C语言
`scanf`是C语言中用于按格式读取标准输入的函数
`scanf`是C语言中用于按格式读取标准输入的函数,通过格式字符串解析输入并存入指定变量。需注意输入格式严格匹配,并建议检查返回值以确保读取成功,提升程序健壮性。
226 0
|
8月前
|
存储 算法 C语言
【C语言程序设计——函数】素数判定(头歌实践教学平台习题)【合集】
本内容介绍了编写一个判断素数的子函数的任务,涵盖循环控制与跳转语句、算术运算符(%)、以及素数的概念。任务要求在主函数中输入整数并输出是否为素数的信息。相关知识包括 `for` 和 `while` 循环、`break` 和 `continue` 语句、取余运算符 `%` 的使用及素数定义、分布规律和应用场景。编程要求根据提示补充代码,测试说明提供了输入输出示例,最后给出通关代码和测试结果。 任务核心:编写判断素数的子函数并在主函数中调用,涉及循环结构和条件判断。
373 23
|
7月前
|
人工智能 Java 程序员
一文彻底搞清楚C语言的函数
本文介绍C语言函数:函数是程序模块化的工具,由函数头和函数体组成,涵盖定义、调用、参数传递及声明等内容。值传递确保实参不受影响,函数声明增强代码可读性。君志所向,一往无前!
187 1
一文彻底搞清楚C语言的函数
|
8月前
|
算法 C语言
【C语言程序设计——函数】利用函数求解最大公约数和最小公倍数(头歌实践教学平台习题)【合集】
本文档介绍了如何编写两个子函数,分别求任意两个整数的最大公约数和最小公倍数。内容涵盖循环控制与跳转语句的使用、最大公约数的求法(包括辗转相除法和更相减损术),以及基于最大公约数求最小公倍数的方法。通过示例代码和测试说明,帮助读者理解和实现相关算法。最终提供了完整的通关代码及测试结果,确保编程任务的成功完成。
309 15
【C语言程序设计——函数】利用函数求解最大公约数和最小公倍数(头歌实践教学平台习题)【合集】