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

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 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语言】解决不同场景字符串问题:巧妙运用字符串函数
【C语言】解决不同场景字符串问题:巧妙运用字符串函数
|
2月前
|
存储 C语言
【c语言】字符串函数和内存函数
本文介绍了C语言中常用的字符串函数和内存函数,包括`strlen`、`strcpy`、`strcat`、`strcmp`、`strstr`、`strncpy`、`strncat`、`strncmp`、`strtok`、`memcpy`、`memmove`和`memset`等函数的使用方法及模拟实现。文章详细讲解了每个函数的功能、参数、返回值,并提供了具体的代码示例,帮助读者更好地理解和掌握这些函数的应用。
27 0
|
2月前
|
存储 安全 编译器
深入C语言库:字符与字符串函数模拟实现
深入C语言库:字符与字符串函数模拟实现
|
2月前
|
存储 C语言 数据格式
解析spritf和sscanf与模拟常用字符串函数strchr,strtok(二)
解析spritf和sscanf与模拟常用字符串函数strchr,strtok(二)
21 0
|
2月前
解析与模拟常用字符串函数strcpy,strcat,strcmp,strstr(一)
解析与模拟常用字符串函数strcpy,strcat,strcmp,strstr(一)
34 0
|
2月前
|
C语言
C语言常见字符函数和字符串函数精讲
C语言常见字符函数和字符串函数精讲
|
2月前
|
程序员 编译器 数据处理
【C语言】深度解析:动态内存管理的机制与实践
【C语言】深度解析:动态内存管理的机制与实践
|
2月前
|
C语言
【C语言】模拟实现深入了解:字符串函数
【C语言】模拟实现深入了解:字符串函数
|
2月前
|
Serverless 编译器 C语言
【C语言】指针篇- 深度解析Sizeof和Strlen:热门面试题探究(5/5)
【C语言】指针篇- 深度解析Sizeof和Strlen:热门面试题探究(5/5)
|
4月前
|
安全 程序员 C语言
【C语言】字符串函数及其模拟实现
【C语言】字符串函数及其模拟实现

推荐镜像

更多