高阶C语言|深入理解字符串函数和内存函数

文章目录

  • 前言
    • 1.求字符串长度
      • 1.1 字符串长度函数:`strlen`
        • 模拟实现
    • 长度不受限制的字符串函数
      • 1.2 字符串拷贝函数:`strcpy`
        • 模拟实现
      • 1.3 字符串连接函数:`strcat`
        • 模拟实现
      • 1.4 字符串比较函数:`strcmp`
        • 模拟实现
    • 长度受限制的字符串函数
      • 2.1`strncpy`
      • 2.2`strncat`
      • 2.3`strncmp`
    • 字符串查找
      • 3.1字符串查找函数:`strstr`
        • 模拟实现
      • 3.2字符串分割函数:`strtok`
    • 错误信息报告
      • 错误信息报告函数:`strerror`
    • 内存操作函数
      • 4.1 内存操作函数:`memcpy` 和 `memmove`
        • `memcpy`模拟实现
        • `memmove`模拟实现
      • 4.2 内存操作函数:`memset`
      • 注意事项:
      • 4.3 `memset` 的应用场景
      • 4.4 内存比较函数:`memcmp`
      • 使用示例:
      • 输出:
        • 注意事项:
        • 应用场景:

前言

在C语言中,字符和字符串是常用的数据类型。然而,C语言并没有专门的字符串类型,所有字符串都是通过字符数组或字符串常量来表示。为了处理这些字符串,C语言提供了许多强大的库函数。本文将详细介绍这些常用的字符和字符串处理函数,以及它们的使用方法和注意事项。

1.求字符串长度

1.1 字符串长度函数:strlen

strlen 函数用来计算字符串的长度。它返回字符串中不包含终止字符 '\0' 的字符数量。

size_t strlen(const char *str);

使用示例:

#include <stdio.h>
#include <string.h>int main() {const char *str = "Hello, world!";printf("Length of the string: %zu\n", strlen(str)); // 输出:13return 0;
}
模拟实现
#include<stdio.h>
#include<assert.h>
//#include<string.h>int my_strlen(const char* str)
{assert(str);int count = 0;while (*str++) {count++;}return count;
}
int main()
{char arr[] = "abcdef";int ret = my_strlen(arr);printf("%d", ret);return 0;
}

长度不受限制的字符串函数

1.2 字符串拷贝函数:strcpy

strcpy 函数将源字符串拷贝到目标字符串,包括终止符 '\0'

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

注意事项:

  • 源字符串必须以 '\0' 结束。
  • 目标空间必须足够大,以容纳源字符串。

使用示例:

#include <stdio.h>
#include <string.h>int main() {char dest[20];strcpy(dest, "Hello");printf("Destination string: %s\n", dest); // 输出:Helloreturn 0;
}
模拟实现
#include<stdio.h>
//#include<string.h>
#include<assert.h>void my_strcpy(char* dest,const char* source)
{assert(dest && source);while (*dest++=*source++) {;}
}int main()
{char arr1[] = "abcdefg";char arr2[] = "hijklmn";my_strcpy(arr1, arr2);printf("%s", arr1);return 0;
}

1.3 字符串连接函数:strcat

strcat 函数将源字符串追加到目标字符串的末尾。它会覆盖目标字符串末尾的 '\0',并将新的字符串结束符 '\0' 放置在新字符串的末尾。

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

使用示例:

#include <stdio.h>
#include <string.h>int main() {char dest[20] = "Hello, ";strcat(dest, "world!");printf("Concatenated string: %s\n", dest); // 输出:Hello, world!return 0;
}
模拟实现
#include<stdio.h>
//#include<string.h>
#include<assert.h>char* my_strcat(char* dest, const char* source)
{char* ret = dest;while (*dest) {dest++;}while (*dest++ = *source++) {;}return ret;
}int main()
{char arr1[20] = "abcdf";char arr2[10] = "ghijk";my_strcat(arr1, arr2);printf("%s", arr1);return 0;
}

1.4 字符串比较函数:strcmp

strcmp 函数比较两个字符串的大小。它返回一个整数,根据两个字符串的字典顺序进行比较。

int strcmp(const char *str1, const char *str2);
  • 如果 str1 大于 str2,返回大于 0 的值。
  • 如果 str1 等于 str2,返回 0。
  • 如果 str1 小于 str2,返回小于 0 的值。

使用示例:

#include <stdio.h>
#include <string.h>int main() {const char *str1 = "abc";const char *str2 = "abd";int result = strcmp(str1, str2);if (result < 0) {printf("str1 is less than str2\n");} else if (result > 0) {printf("str1 is greater than str2\n");} else {printf("str1 is equal to str2\n");}return 0;
}
模拟实现
#include<stdio.h>
//#include<string.h>
#include<assert.h>int my_strcmp(const char* str1,const char* str2)
{assert(str1 && str2);while (*str1== *str2) {str1++;str2++;}return *str1 - *str2;
}int main()
{char arr1[] = "abcd";char arr2[] = "abcf";int ret = my_strcmp(arr1, arr2);if (ret == 0){printf("=\n");}else if (ret > 0) {printf(">\n");}elseprintf("<\n");return 0;
}

长度受限制的字符串函数

2.1strncpy

char * strncpy ( char * destination, const char * source, size_t num );
  • 拷贝num个字符从源字符串到目标空间。
  • 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。

2.2strncat

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

在目标字符串结尾追加源字符串前num个字符


2.3strncmp

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

比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。


字符串查找

3.1字符串查找函数:strstr

strstr 函数查找一个字符串在另一个字符串中首次出现的位置。如果找到,则返回该位置的指针;如果未找到,则返回 NULL

char *strstr(const char *haystack, const char *needle);

使用示例:

#include <stdio.h>
#include <string.h>int main() {const char *str = "This is a simple string";const char *substr = "simple";char *result = strstr(str, substr);if (result != NULL) {printf("Found substring: %s\n", result); // 输出:simple string}return 0;
}
模拟实现
#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* cp = str1;while (cp) {s1 = cp;s2 = str2;while (*s1 !='\0' && *s2 != '\0' && * s1 == *s2) {s1++;s2++;}if (*s2 == '\0')return (char*)cp;cp++;}return NULL;
}int main()
{char* p1 = "abbbcdef";char* p2 = "ba";char* ret = my_strstr(p1, p2);if (ret = NULL) {printf("Ҳ\n");}else {printf("%s", ret);}return 0;
}

3.2字符串分割函数:strtok

strtok 函数将字符串分割成多个标记,每次调用 strtok 返回下一个标记。当没有更多标记时,返回 NULL

char *strtok(char *str, const char *delim);

注意事项:

  • strtok 会修改原始字符串,因此一般会拷贝字符串后再进行分割。
  • 每次调用 strtok 返回字符串中的下一个标记,后续调用需要将第一个参数设为 NULL

使用示例:

#include <stdio.h>
#include <string.h>int main() {char str[] = "Hello, world! C programming";char *token = strtok(str, " ,!");while (token != NULL) {printf("Token: %s\n", token);token = strtok(NULL, " ,!");}return 0;
}

错误信息报告

错误信息报告函数:strerror

strerror 函数根据错误码返回相应的错误信息。

char *strerror(int errnum);

使用示例:

#include <stdio.h>
#include <string.h>
#include <errno.h>int main() {FILE *file = fopen("nonexistent.txt", "r");if (file == NULL) {printf("Error: %s\n", strerror(errno));}return 0;
}

内存操作函数

4.1 内存操作函数:memcpymemmove

  • memcpy 用于从源内存区域复制指定字节的数据到目标内存区域。
  • memmovememcpy 类似,但它处理内存重叠的情况。
void *memcpy(void *dest, const void *src, size_t n);
void *memmove(void *dest, const void *src, size_t n);

使用示例:

#include <stdio.h>
#include <string.h>int main() {char src[] = "This is a test.";char dest[20];memcpy(dest, src, strlen(src) + 1);printf("Copied string: %s\n", dest);return 0;
}
memcpy模拟实现
#include<stdio.h>
#include<string.h>
#include<assert.h>void* my_memcpy(void* dest, const void* source, size_t num)
{assert(dest && source);void* ret = dest;while (num--){*(char*)dest = *(char*)source;dest = (char*)dest + 1;source = (char*)source + 1;}return ret;
}void test1()
{int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };int arr2[10] = { 0 };my_memcpy(arr1, arr2, 20);
}int main()
{test1();return 0;
}
memmove模拟实现
#include<stdio.h>
#include<string.h>
#include<assert.h>void* my_memmove(void* dest, const void* source, size_t num)
{assert(dest && source);void* ret = dest;if (dest < source) {while (num--) {*(char*)dest = *(char*)source;dest = (char*)dest + 1;source = (char*)source + 1;}}else {while (num--) {*((char*)dest + num) = *((char*)source + num);}}return ret;
}void test1()
{int arr[10] = {1,2,3,4,5,6,7,8,9,10};my_memmove(arr+3, arr, 20);
}int main()
{test1();//test2();return 0;
}

4.2 内存操作函数:memset

memset 函数用于将一块内存区域的所有字节设置为指定的值。通常用于初始化内存或将内存区域清零。

void *memset(void *s, int c, size_t n);
  • s:指向内存区域的指针。
  • c:要设置的值(以无符号字符形式)。
  • n:要设置的字节数。

注意事项:

  • memset 函数将指定的字节值填充到内存区域中,可以用于初始化数组或结构体的值。
  • 由于是按字节处理,因此它适用于任何类型的内存块。

使用示例:

#include <stdio.h>
#include <string.h>int main() {char str[20];memset(str, 'A', sizeof(str) - 1); // 将前19个字节设置为'A'str[19] = '\0';  // 确保字符串以'\0'结束printf("The string is: %s\n", str); // 输出:AAAAAAAAAAAAAAAAAAAreturn 0;
}

在上面的例子中,memset 将数组 str 的前 19 个字节设置为字符 'A',并在最后设置为字符串的终止符 '\0',确保它成为一个有效的字符串。

4.3 memset 的应用场景

  1. 初始化数组或结构体:在动态分配内存或创建数据结构时,使用 memset 可以方便地初始化内存,将内存区域填充为特定的值。例如,常见的做法是使用 memset 来清零结构体或数组中的内容。

  2. 清空敏感数据:当处理涉及敏感信息(如密码、密钥)的程序时,使用 memset 可以确保这些数据在使用后被立即清除,以减少安全隐患。

  3. 内存清零:在程序需要清除缓存或重置数据时,memset 可以帮助快速设置内存区域为零。


4.4 内存比较函数:memcmp

memcmp 函数用于比较两个内存区域的内容。它按字节逐一比较两个内存块,并根据比较结果返回一个整数值。

int memcmp(const void *ptr1, const void *ptr2, size_t num);
  • ptr1:指向第一个内存区域的指针。
  • ptr2:指向第二个内存区域的指针。
  • num:要比较的字节数。

返回值

  • 如果两个内存区域的内容完全相同,则返回 0
  • 如果 ptr1 指向的内存块大于 ptr2,则返回大于 0 的值。
  • 如果 ptr1 指向的内存块小于 ptr2,则返回小于 0 的值。

使用示例:

#include <stdio.h>
#include <string.h>int main() {char buffer1[] = "DWgaOtP12df0";char buffer2[] = "DWGAOTP12DF0";int result = memcmp(buffer1, buffer2, sizeof(buffer1));if (result > 0) {printf("'%s' is greater than '%s'.\n", buffer1, buffer2);} else if (result < 0) {printf("'%s' is less than '%s'.\n", buffer1, buffer2);} else {printf("'%s' is the same as '%s'.\n", buffer1, buffer2);}return 0;
}

输出:

'DWgaOtP12df0' is greater than 'DWGAOTP12DF0'.

在这个示例中,memcmp 比较了两个字符数组 buffer1buffer2 的内容。由于在比较过程中,DWgaOtP12df0 的字节值大于 DWGAOTP12DF0,所以返回的值大于 0。

注意事项:
  1. 按字节比较memcmp 是按字节逐一比较内存区域的内容,因此它不会考虑数据的类型。如果要比较更复杂的数据类型(例如结构体),需要保证结构体成员的字节表示没有问题。
  2. 内存重叠问题:与 memcpy 不同,memcmp 不会处理内存重叠的情况,它只比较内存的内容,而不考虑是否存在重叠。因此在使用 memcmp 时要确保比较的内存区域不会重叠。
应用场景:
  1. 内存区域比较memcmp 适用于需要比较两个内存区域是否相等的场景,例如在数据校验或加密算法中经常使用它来比较数据块的内容。
  2. 查找差异:当需要查找两个内存块中不同的位置时,可以使用 memcmp 来快速发现差异。

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

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

相关文章

Golang Ticker Reset异常的坑

前言 延迟执行的场景我们通常会使用time.NewTimer(…)来实现&#xff0c;当一些场合可能需要使用timer.Reset(…)方法修改超时时间&#xff0c;这时使用要多注意&#xff0c; 使用不当会导致Reset失败&#xff0c;或是重复执行两次的情况。 复现 下面这段代码我们是希望&…

Chameleon(变色龙) 跨平台编译C文件,并一次性生成多个平台的可执行文件

地址:https://github.com/MartinxMax/Chameleon Chameleon 跨平台编译C文件&#xff0c;并一次性生成多个平台的可执行文件。可以通过编译Chameleon自带的.C文件反向Shell生成不同平台攻击载荷。 登录 & 代理设置 按照以下步骤设置 Docker 的代理&#xff1a; 创建配置目…

DFFormer实战:使用DFFormer实现图像分类任务(二)

文章目录 训练部分导入项目使用的库设置随机因子设置全局参数图像预处理与增强读取数据设置Loss设置模型设置优化器和学习率调整策略设置混合精度&#xff0c;DP多卡&#xff0c;EMA定义训练和验证函数训练函数验证函数调用训练和验证方法 运行以及结果查看测试完整的代码 在上…

几种K8s运维管理平台对比说明

目录 深入体验**结论**对比分析表格**1. 功能对比****2. 用户界面****3. 多租户支持****4. DevOps支持** 细对比分析1. **Kuboard**2. **xkube**3. **KubeSphere**4. **Dashboard****对比总结** 深入体验 KuboardxkubeKubeSphereDashboard 结论 如果您需要一个功能全面且适合…

DeepSeek API 的获取与对话示例

代码文件下载&#xff1a;Code 在线链接&#xff1a;Kaggle | Colab 文章目录 注册并获取API环境依赖设置 API单轮对话多轮对话流式输出更换模型 注册并获取API 访问 https://platform.deepseek.com/sign_in 进行注册并登录&#xff1a; 新用户注册后将赠送 10 块钱余额&#…

Vue 3 + TypeScript 实现父子组件协同工作案例解析

引言 在现代的前端开发中&#xff0c;Vue.js 作为一款流行的渐进式 JavaScript 框架&#xff0c;为我们构建交互式用户界面提供了强大的支持。Vue 3 的推出带来了许多新特性&#xff0c;尤其是组合式 API 的引入&#xff0c;让代码的组织和复用更加灵活。同时&#xff0c;TypeS…

基于STM32的循迹小车设计与实现

1 系统方案设计 根据系统设计功能&#xff0c;展开基于STM32的循迹小车设计&#xff0c;整体设计框图如图2.1所示。系统采用STM32单片机作为控制器,通过L298驱动器控制两个直流电机实现对小车的运动控制&#xff0c;两路红外模块实现黑线的检测&#xff0c;HC-SR04超声波模块实…

14.模型,纹理,着色器

模型、纹理和着色器是计算机图形学中的三个核心概念&#xff0c;用通俗易懂的方式来解释&#xff1a; 1. 模型&#xff1a;3D物体的骨架 通俗解释&#xff1a; 模型就像3D物体的骨架&#xff0c;定义了物体的形状和结构。 比如&#xff0c;一个房子的模型包括墙、屋顶、窗户等…

Docker/K8S

文章目录 项目地址一、Docker1.1 创建一个Node服务image1.2 volume1.3 网络1.4 docker compose 二、K8S2.1 集群组成2.2 Pod1. 如何使用Pod(1) 运行一个pod(2) 运行多个pod 项目地址 教程作者&#xff1a;教程地址&#xff1a; https://www.bilibili.com/video/BV1Zn4y1X7AZ?…

Ubuntu 20.04安装Protocol Buffers 2.5.0

个人博客地址&#xff1a;Ubuntu 20.04安装Protocol Buffers 2.5.0 | 一张假钞的真实世界 安装过程 Protocol Buffers 2.5.0源码下载&#xff1a;https://github.com/protocolbuffers/protobuf/tree/v2.5.0。下载并解压。 将autogen.sh文件中以下内容&#xff1a; curl htt…

算法每日双题精讲 —— 二分查找(寻找旋转排序数组中的最小值,点名)

&#x1f31f;快来参与讨论&#x1f4ac;&#xff0c;点赞&#x1f44d;、收藏⭐、分享&#x1f4e4;&#xff0c;共创活力社区。 &#x1f31f; 别再犹豫了&#xff01;快来订阅我们的算法每日双题精讲专栏&#xff0c;一起踏上算法学习的精彩之旅吧&#x1f4aa; 在算法的…

【踩坑日常,已解决】彻底修改IDEA项目的JDK版本,8改为17

三处修改彻底解决IDEA中JDK版本不对问题&#xff08;8改为17&#xff09; 文章目录 三处修改彻底解决IDEA中JDK版本不对问题&#xff08;8改为17&#xff09;第一处第二处第三处 第一处 setting -> Build, Execution, Deployment -> Java Compiler -> Target bytecod…

linux naive代理设置

naive linux客户端 Release v132.0.6834.79-2 klzgrad/naiveproxy GitHub Client setup Run ./naive with the following config.json to get a SOCKS5 proxy at local port 1080. {"listen": "socks://127.0.0.1:1080","proxy": "htt…

redis的分片集群模式

redis的分片集群模式 1 主从哨兵集群的问题和分片集群特点 主从哨兵集群可应对高并发写和高可用性&#xff0c;但是还有2个问题没有解决&#xff1a; &#xff08;1&#xff09;海量数据存储 &#xff08;2&#xff09;高并发写的问题 使用分片集群可解决&#xff0c;分片集群…

Nuxt:利用public-ip这个npm包来获取公网IP

目录 一、安装public-ip包1.在Vue组件中使用2.在Nuxt.js插件中使用public-ip 一、安装public-ip包 npm install public-ip1.在Vue组件中使用 你可以在Nuxt.js的任意组件或者插件中使用public-ip来获取公网IP。下面是在一个Vue组件中如何使用它的例子&#xff1a; <template…

AI软件栈:LLVM分析(一)

文章目录 AI 软件栈后端编译LLVM IRLLVM的相关子项目AI 软件栈后端编译 AI软件栈的后端工作通常与硬件架构直接相关,为了实现一个既能适配现代编程语言、硬件架构发展的目标,所以提出了LLVM具备多阶段优化能力提供基础后端描述,便于进行编译器开发兼容标准编译器的行为LLVM …

搭建Spring Boot开发环境

JDK&#xff08;1.8及以上版本&#xff09; Apache Maven 3.6.0 修改settings.xml 设置本地仓库位置 <localRepository>D:/repository</localRepository> 设置远程仓库镜像 <mirror><id>alimaven</id><name>aliyun maven</name&…

智慧校园在职业学校的实施与展望

随着信息技术的发展&#xff0c;智慧校园的概念逐渐走进人们的视野。智慧校园不仅是一个技术层面的概念&#xff0c;更是教育理念的一次革新。对于职业教育而言&#xff0c;智慧校园的应用更是具有重要意义。通过引入物联网、大数据等先进技术&#xff0c;可以实现教学资源的高…

【Flutter】旋转元素(Transform、RotatedBox )

这里写自定义目录标题 Transform旋转元素可以改变宽高约束的旋转 - RotatedBox Transform旋转元素 说明&#xff1a;Transform旋转操作改变了元素的方向&#xff0c;但并没有改变它的布局约束。因此&#xff0c;虽然视觉上元素看起来是旋转了&#xff0c;但它仍然遵循原始的宽…

Excel中LOOKUP函数的使用

文章目录 VLOOKUP&#xff08;垂直查找&#xff09;&#xff1a;HLOOKUP&#xff08;水平查找&#xff09;&#xff1a;LOOKUP&#xff08;基础查找&#xff09;&#xff1a;XLOOKUP&#xff08;高级查找&#xff0c;较新版本Excel提供&#xff09;&#xff1a; 在Excel中&…