C语言字符串函数和内存函数的介绍与模拟实现

news2025/1/10 2:59:31

0.前言

C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在 常量字符串 中或者 字符数组 中。 字符串常量 适用于那些对它不做修改的字符串函数.

1.字符串函数介绍与模拟实现

  • C语言本身就带有一些库函数,所以看见别人不认识的函数可以在这个网站查一查,使用库函数记得引相应的头文件

    [Reference - C++ Reference]:

1.1 strlen

求字符串的个数的函数

例如

int main()
{
    char arr1[] = "abcdef";
    int ret = strlen(arr1);
    printf("%d\n", ret);
    return 0;
}

运行结果

 

size_t strlen ( const char * str );

  • 字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包 含 '\0' )。

  • size_t strlen ( const char * str );

  • 参数指向的字符串必须要以 '\0' 结束。

  • 注意函数的返回值为size_t,是无符号的

  • 自己模拟实现

// 计算器的方式实现
size_t my_strlen(const char* str)
{
    int count = 0;
    while (*str != '\0')
    {
        count++;// 统计'\0'前面的字符的个数
        str++;
    }
    return count;
}
// 递归的方式实现
size_t my_strlen(const char* str)
{
    if (*str == '\0')
        return 0;
    else
        return 1 + my_strlen2(str + 1);
}

1.2 strcpy

char* strcpy(char * destination, const char * source );

字符串拷贝函数,给你两个字符串可以,可以把一个字符串放到另外一个空间里

例如

int main()
{
​
    char arr1[] = "hello world!";
    char arr2[20] = { 0 };
    strcpy(arr2, arr1);
    printf("%s\n", arr2);
    return 0;
}
​

运行结果

 

  • 源字符串必须以'\0',作为字符结束标志

  • 会将源字符串的'\0'拷贝放到目标空间中

  • 目标空间必须是可变的

  • 目标空间的空间必须足够大,以确保能放到目标空间中

  • 自己模拟实现

strcpy函数的模拟实现

#include <assert.h>
char* my_strcpy(char* dest, const char* src)
{
    assert(dest && src);
​
    char* ret = dest;
    while (*dest != *src)
    {
        *dest = *src; // 把src的字符赋给dest
        dest++;
        src++;
    }
​
    return ret;
}

其实我们理解了while循环的判断还可以把代码变得更简单,直接在while循环的判断部分把src的值直接赋值给dest.

当src的值找到'\0'了并且赋给了dest后,while循环就会停下来,就把src里的全部字符拷贝放到dest里面

char* my_strcpy(char* dest, const char* src)
{
    assert(dest && src);
​
    char* ret = dest;
    while (*dest++ = *src++)
    {
        ;
    }
​
    return ret;
}

1.3 strcat

char * strcat ( char * destination, const char * source );

字符串追加函数,可以在一个字符串里在追加另外一个字符串

例如

int main()
{
    char arr1[20] = "hello ";
    char arr2[] = "world!";
    strcat(arr1, arr2);
    printf("%s\n", arr1);
​
    return 0;
}

运行结果

  • 源字符串必须以 '\0' 结束。

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

  • 目标空间必须可修改。

  • 字符串自己给自己追加可以吗?

  • 答案是不可以的,因为自己给自己追加会把'\0'覆盖,'\0'是字符串结束的标志,把'\0'覆盖追加就停不下来了

strcat的模拟实现

char* my_strcat(char* dest, const char* src)
{
    assert(dest && src);
    char* ret = dest;
    // 找到目标的'\0'
    while (*dest != '\0')
    {
        dest++;
    }
    // 拷贝到目标空间
    while (*dest++ = *src++)
    {
        ;
    }
    
    return ret;
}

1.4 strcmp

int strcmp ( const char * str1, const char * str2 );

字符串比较函数,如果arr1里的字符大于arr2里的字符返回>0的数,如果arr1里的字符小于arr2里的字符返回<0的数,

如果arr1里的字符等于arr2里的字符返回=0的数.

例如

int main()
{
    char arr1[] = "abcd";
    char arr2[] = "abc";
    int ret = strcmp(arr1, arr2);
    printf("%d\n", ret);
    return 0;
}

执行结果

 

 

标准规定

  • 第一个字符串大于第二个字符串,则返回大于0的数字

  • 第一个字符串等于第二个字符串,则返回0

  • 第一个字符串小于第二个字符串,则返回小于0的数字

strcmp的模拟实现

int my_strcmp(const char* str1, const char* str2)
{
    assert(str1 && str2);
    while (*str1 == *str2)
    {
        if (*str1 == '\0')
           return 0;
        str1++;
        str2++;
    }
    
    if (*str1 > *str2)
        return 1;
    else
        return -1;
​
    return *str1 - *str2;
}

1.5 strncpy

char * strncpy ( char * destination, const char * source, size_t num );

跟strcpy函数的功能类似,n表示的是num,可以指定拷贝字符串的个数

例如

int main()
{
​
    char arr1[] = "hello world!";
    char arr2[20] = { 0 };
    strncpy(arr2, arr1,5);
    printf("%s\n", arr2);
    return 0;
}

执行结果

 

char * strncat ( char * destination, const char * source, size_t num );
  • 拷贝num个字符从源字符串到目标空间中

  • 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。

1.6 strncat

char * strncat ( char * destination, const char * source, size_t num );

跟strcat的功能类似,n也表示num,追加num个字符到目标空间

例如

int main()
{
    char arr1[20] = "hello ";
    char arr2[] = "world!";
    strncat(arr1, arr2,6);
    printf("%s\n", arr1);
​
    return 0;
}

执行结果

 

1.7 strncmp

int strncmp ( const char * str1, const char * str2, size_t num );

跟strcmp的功能类似,可以指定比较num个字符

例如

int main()
{
    char arr1[] = "abcd";
    char arr2[] = "abce";
    int ret = strncmp(arr1, arr2,4);
    printf("%d\n", ret);
    return 0;
}

执行结果

 

int strncmp ( const char * str1, const char * str2, size_t num );
  • 比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。

1.8 strstr

  char * strstr (const char * str1, const char * str2 );

查找子串的函数,如果存在子串会把整个字符串全部打印出来

例如

int main()
{
    char arr1[] = "abcdefg";
    char arr2[] = "abc";
​
    char* p = strstr(arr1, arr2);
    if (*p == NULL)
    {
        printf("找不到\n");
    }
    else
    {
        printf("%s\n", p);
    }
​
    return 0;
}

执行结果

 

strstr函数的模拟实现

char* my_strstr(const char* str1, const char* str2)
{
    assert(str1 && str2);
    char* s1 = NULL;
    char* s2 = NULL;
    char* cp = str1;
    while (*cp)
    {
        s1 = cp;
        s2 = (char*)str2;
        while (*s1 && *s2 && *s1 == *s2)
        {
            s1++;
            s2++;
        }
        if (*s2 == '\0')
        {
            return cp;
        }
        cp++;
    }
    return NULL;
}

1.9 strtok

char * strtok ( char * str, const char * delimiters );
  • delimiters是分割符,把长的字符串按照分隔符分成多段短的字符串

  • delimiters的参数含有0个或者多个分割符

  • strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。

  • (注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容 并且可修改。)

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

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

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

将长的字符串按照分割符分成多个子串

例如

int main()
{
    char arr[] = "192.168.222.33";
    char buf[30] = { 0 };
    strcpy(buf, arr);
    const char* p = ".";
    char* str = strtok(buf, p);
    printf("%s\n", str);
​
    str = strtok(NULL, p);
    printf("%s\n", str);
​
    str = strtok(NULL, p);
    printf("%s\n", str);
​
    str = strtok(NULL, p);
    printf("%s\n", str);
    return 0;
}

执行结果

 

2.0 strerror

错误码返回信息函数,这个函数只要你给他一个错误码,它就能返回一个错误信息

例如

int main()
{
    char* str = strerror(0);
    printf("%s\n", str);
​
    str = strerror(1);
    printf("%s\n", str);
​
    str = strerror(2);
    printf("%s\n", str);
​
    str = strerror(3);
    printf("%s\n", str);
​
    str = strerror(4);
    printf("%s\n", str);
    return 0;
}

执行结果

 

2.1 memcpy

void * memcpy ( void * destination, const void * source, size_t num );
  • memcpy函数从source的位置向后复制num字节的数据放到destination的位置

  • 这个函数在遇到'\0'的时候不会停下来

  • 如果source和destination有任何重叠,复制的结果是不可知的

内存拷贝函数,有两块空间,可以把一块的空间里的内容放到另外一块空间里.

例如

int main()
{
    int arr1[] = { 1,2,3,4,5,6,7,8 };
    int arr2[10] = { 0 };
    memcpy(arr2, arr1,32);
    int i = 0;
    int sz = sizeof(arr1) / sizeof(arr1[0]);
    for (i = 0; i < sz; i++)
    {
        printf("%d ", arr2[i]);
    }
​
    return 0;
}

执行结果

 

这里把arr1数组的内容放到arr2数组里面

memcpy函数的模拟实现

void* my_memcpy(void* dest, const void* src, size_t num)
{
    assert(dest && src);
    void* ret = dest;
​
    while (num--)
    {
        *(char*)dest = *(char*)src;// 把dest强转(char*)然后进行解引用,同理src也是
        dest = (char*)dest + 1;// 把dest原来的位置往后挪
        src = (char*)src + 1; //  把src原来的位置往后挪
    }
    return ret;
}

2.2 memmove

void * memmove ( void * destination, const void * source, size_t num );
  • memcpy和memmove的区别在于memmove函数处理源内存块和目标空间内存块可以重叠

  • 如果源内存块和目标空间内存块重叠,就需要用memmove函数

例如

int main()
{
    int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
    memmove(arr1, arr1+2, 12);
    int i = 0;
    int sz = sizeof(arr1) / sizeof(arr1[0]);
    for (i = 0; i < sz; i++)
    {
        printf("%d ", arr1[i]);
    }
    return 0;
}

把arr1向后12字节的内容,放到arr1里,就是把4,5,6覆盖1,2,3,然后后面的内容不变

执行结果

 

memmove函数的模拟实现

void* my_memmove(void* dest, const void* src, size_t num)
{
    assert(dest && src);
    void* ret = dest;
    if (dest < src)
    {
        while (num--)
        {
            // 前往后
            *(char*)dest = *(char*)src;
            dest = (char*)dest + 1;
            src = (char*)src + 1;
        }
    }
    else
    {
        // 后往前
        while (num--)
        {
            *((char*)dest + num) = *((char*)src + num);
        }
    }
    return ret;
}

这个分两种情况,当dest<src时,是前->后进行拷贝,当dest>src时,是后->前。这里比较的是位置,通过画图可以看得出

dest<src

 

dest>src

 

2.3 memcmp

int memcmp ( const void * ptr1, const void * ptr2, size_t num );
  • 比较从ptr1和ptr2指针开始的num个字节

  • 返回值如下

 

使用例子

int main()
{
    int arr1[] = { 1,2,3,4,6 };
    int arr2[] = { 1,2,3,4,5 };
    int ret = memcmp(arr1, arr2, 17);
    printf("%d\n", ret);
​
}

执行结果

 

这里就不带大家模拟实现的,感兴趣的可以自己研究。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/564820.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

研发工程师玩转Kubernetes——CPU配额

在一个Pod中&#xff0c;可以有多个容器&#xff0c;比如一个主要业务容器和若干辅助业务容器。如果辅助业务容器内程序有问题&#xff0c;导致占用了大量的CPU资源&#xff0c;进而影响了主要业务容器的执行效率&#xff0c;那就需要进行干涉了。本节我们将使用“资源配额”来…

Edgedetect

边缘检测&#xff0c;检测上升沿 对于 8 位矢量中的每个位&#xff0c;检测输入信号何时从一个时钟周期中的 0 变为下一个时钟周期的 1&#xff08;类似于正边沿检测&#xff09;。输出位应在发生 0 到 1 转换后设置周期。 以下是一些示例。为清楚起见&#xff0c;in[1] 和 pe…

Jenkins+GitLab+Docker搭建前端自动化构建镜像容器部署(无本地证书,映射版本)

前言 &#x1f680; 需提前安装环境及知识点&#xff1a; 1、Docker搭建及基础操作 2、DockerFile文件描述 3、Jenkins搭建及基础点 &#x1f680; 目的&#xff1a; 将我们的前端项目打包成一个镜像容器并自动发布部署&#xff0c;可供随时pull访问 一、手动部署镜像及容器 1…

【Linux】线程分离 | 线程库 | C++调用线程 | 线程局部存储

文章目录 1. 线程分离1. 为什么要线程分离&#xff1f;2. 具体使用3. 为什么有时候分离在调用join 会正常运行&#xff1f; 2. 如何理解线程库&#xff1f;如何理解 先描述 在组织&#xff1f; 3. C中使用多线程4. 线程局部存储局部变量全局变量 1. 线程分离 1. 为什么要线程分…

服务器虚拟化部署

服务器虚拟化部署 1、背景2、目的3、环境4、部署4.1、部署VMware ESXi4.1.1、准备工作4.1.2、部署ESXi4.1.3、配置ESXi4.1.4 、部署虚拟机 1、背景 项目上利旧9台服务器&#xff0c;项目需要使用15台服务器&#xff0c;外购已经没有项目硬件采购预算&#xff0c;只能从目前的…

自定义HikariCP连接池

文章目录 一、简介1、概述2、地址 二、配置参数1、Hikari原生参数2、Springboot中参数 三、springboot中使用四、自定义数据源1、各模块2、完整代码3、多数据源 五、多数据源dynamic中使用1、简介2、引入依赖3、参数配置 六、XMind整理 一、简介 1、概述 官方解释&#xff1a…

zabbix 自动发现与自动注册、部署 zabbix 代理服务器及部署 Zabbix 高可用集群

目录 一、zabbix 自动发现二、zabbix 自动注册&#xff08;对于 agent2 是主动模式&#xff09;三、部署 zabbix 代理服务器四、部署 Zabbix 高可用集群五、Zabbix 监控 Windows 系统六、Zabbix 监控 java 应用七、Zabbix 监控 SNMP 一、zabbix 自动发现 zabbix 自动发现&…

Nautilus Chain开启全球行,普及Layer3概念加速其采用

在去年&#xff0c;在 2022 年&#xff0c;Vitalik 进一步提出了 Layer3 的概念与早期形态&#xff0c;期盼弥补目前链体系存在的不足&#xff0c;并为 Layer3 提出了三大目标&#xff0c;即Layer2 用于扩展&#xff0c;Layer3 用于定制功能&#xff0c;如隐私&#xff1b;Laye…

【C++】哈希表特性总结及unordered_map和unordered_set的模拟实现

✍作者&#xff1a;阿润菜菜 &#x1f4d6;专栏&#xff1a;C 文章目录 前言一、哈希表的特性 - 哈希函数和哈希冲突1 哈希函数2. 哈希冲突 二、闭散列的实现 -- 开放地址法1. 定义数据结构2.insert()3.Find()4. Erase()5.仿函数处理key值不能取模无法映射 --- BKDRHash 三、开…

【Linux系列P4】Linux需要什么?编辑器?软件包?一文帮你了解掌握 [yum][vim]———基础开发工具篇

前言 大家好&#xff0c;这里是YY的Linux系列part4&#xff1b;本章主要内容面向接触过Linux的老铁&#xff0c;主要内容含【学习yum工具&#xff0c;进行软件安装】【拓展yum源安装】【掌握vim编辑器使用&#xff0c;基本命令】【命令集】【懒人配置文件安装教程】 在下一章节…

Java高并发编程—可见性与有序性原理

原子性、可见性和有序性是并发编程所面临的三大问题。 Java通过CAS操作已解决了并发编程中的原子性问题&#xff0c;本章为大家介绍Java如何解决剩余的另外两个问题——可见性和有序性。 CPU物理缓存结构 由于CPU的运算速度比主存&#xff08;物理内存&#xff09;的存取速度…

Android系统原理性问题分析 - 多路并发情况下的C/S模型

声明 在Android系统中经常会遇到一些系统原理性的问题&#xff0c;在此专栏中集中来讨论下。Android系统中很多地方都采用了I/O多路复用的机制&#xff0c;为了引出I/O多路复用机制&#xff0c;先来分析多路并发情况下的C/S模型。此篇参考一些博客和书籍&#xff0c;代码基于A…

C++条件变量condition_variable

一、问题 假设没有条件变量&#xff0c;对于一个生产者消费者问题&#xff0c;消费线程在得知队列中没有产品时&#xff0c;将阻塞自己。生产者线程可以给队列中放入产品&#xff0c;但是没有办法激活消费者线程&#xff0c;而消费者线程处于阻塞状态也没有办法自己激活自己。…

RocketMQ 领域模型概述

本文为您介绍 Apache RocketMQ 的领域模型。 Apache RocketMQ 是一款典型的分布式架构下的中间件产品&#xff0c;使用异步通信方式和发布订阅的消息传输模型。通信方式和传输模型的具体说明&#xff0c;请参见下文通信方式介绍和消息传输模型介绍。 Apache RocketMQ 产品具备…

IOS开发指南之自定义TableViewCell使用

演示效果: 1.自定义TableViewCell创建 File->new->File... 在iOS模板中选择Empty来创建一个空的XIB文件,然后点击下一步 输入XIB文件名Cell,然后点击Create创建 创建XIB文件成功后如下: 同时按钮Shift+command+L弹出库,然后输入 table筛选,选择Table View Cell 拖到下…

一文通透spring的初始化

简述 今天重点分析ApplicationContext初始化时做的事情&#xff0c;我们都只到spring是个IOC和AOP容器&#xff0c;那再我们new一个ApplicationContext&#xff0c;spring内部都做了什么&#xff1f;怎么实现的IOC和AOP&#xff1f; 比如说下面这段代码 Configuration Compon…

计组 第二章 数据的表示与运算 2.1 数制与编码 知识点整理

2.1 数制与编码 二进制转八进制&#xff1a;3位一组&#xff0c;高位补0 二进制转十六进制&#xff1a;4位一组&#xff0c;高位补0 任意进制转十进制&#xff08;按权展开法&#xff09;&#xff1a;数码与权值相乘&#xff0c;再相加 十进制转化为任意进制数&#xff08;基…

全面接入:ChatGPT杀进10个商业应用,让AI替你打工

ChatGPT狂飙160天&#xff0c;世界已经不是两个月前的样子。 新建了一个网站 https://ai.weoknow.com/ 每天给大家更新可用的国内可用chatGPT资源 ChatGPT API已开放60多天。世界已经不是两个月前的样子了。 微软联合创始人比尔盖茨&#xff08;BillGates&#xff09;将GPT称…

一、预约挂号详情

文章目录 一、预约挂号详情1、需求分析 2、api接口2.1 添加service接口2.2 添加service接口实现2.2.1 在ScheduleServiceImpl类实现接口2.2.2 在获取科室信息 2.3 添加controller方法 3、前端3.1封装api请求3.2 页面展示 二、预约确认1、api接口1.1 添加service接口1.2 添加con…

FastRcnn理论合集

FastRcnn理论合集 Rcnn 论文原著 Rich feature hierarchies for accurate object detection and semantic segmentation R-CNN可以说是利用深度学习进行目标检测的开山之作。作者Ross Girshick多次在PASCAL VOC的目标检测竞赛中折桂&#xff0c;曾在2010年带领团队获得终身成就…