高阶C语言|深入理解字符串函数和内存函数
文章目录
- 前言
- 1.求字符串长度
- 1.1 字符串长度函数:`strlen`
- 模拟实现
- 2.长度不受限制的字符串函数
- 2.1 字符串拷贝函数:`strcpy`
- 模拟实现
- 2.2 字符串连接函数:`strcat`
- 模拟实现
- 2.3 字符串比较函数:`strcmp`
- 模拟实现
- 3.长度受限制的字符串函数
- 3.1`strncpy`
- 3.2`strncat`
- 3.3`strncmp`
- 4.字符串查找
- 4.1字符串查找函数:`strstr`
- 模拟实现
- 4.2字符串分割函数:`strtok`
- 5.错误信息报告
- 5.1错误信息报告函数:`strerror`
- 6.内存操作函数
- 6.1 内存操作函数:`memcpy` 和 `memmove`
- `memcpy`模拟实现
- `memmove`模拟实现
- 6.2 内存操作函数:`memset`
- 6.3 内存比较函数:`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;
}
2.长度不受限制的字符串函数
2.1 字符串拷贝函数: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;
}
2.2 字符串连接函数: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;
}
2.3 字符串比较函数: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;
}
3.长度受限制的字符串函数
3.1strncpy
char * strncpy ( char * destination, const char * source, size_t num );
- 拷贝num个字符从源字符串到目标空间。
- 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。
3.2strncat
char * strncat ( char * destination, const char * source, size_t num );
在目标字符串结尾追加源字符串前num个字符
3.3strncmp
int strncmp ( const char * str1, const char * str2, size_t num );
比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。
4.字符串查找
4.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;
}
4.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;
}
5.错误信息报告
5.1错误信息报告函数: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;
}
6.内存操作函数
6.1 内存操作函数:memcpy
和 memmove
memcpy
用于从源内存区域复制指定字节的数据到目标内存区域。memmove
与memcpy
类似,但它处理内存重叠的情况。
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;
}
6.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'
,确保它成为一个有效的字符串。
应用场景
-
初始化数组或结构体:在动态分配内存或创建数据结构时,使用
memset
可以方便地初始化内存,将内存区域填充为特定的值。例如,常见的做法是使用memset
来清零结构体或数组中的内容。 -
清空敏感数据:当处理涉及敏感信息(如密码、密钥)的程序时,使用
memset
可以确保这些数据在使用后被立即清除,以减少安全隐患。 -
内存清零:在程序需要清除缓存或重置数据时,
memset
可以帮助快速设置内存区域为零。
6.3 内存比较函数: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
比较了两个字符数组 buffer1
和 buffer2
的内容。由于在比较过程中,DWgaOtP12df0
的字节值大于 DWGAOTP12DF0
,所以返回的值大于 0。
注意事项:
- 按字节比较:
memcmp
是按字节逐一比较内存区域的内容,因此它不会考虑数据的类型。如果要比较更复杂的数据类型(例如结构体),需要保证结构体成员的字节表示没有问题。 - 内存重叠问题:与
memcpy
不同,memcmp
不会处理内存重叠的情况,它只比较内存的内容,而不考虑是否存在重叠。因此在使用memcmp
时要确保比较的内存区域不会重叠。
应用场景:
- 内存区域比较:
memcmp
适用于需要比较两个内存区域是否相等的场景,例如在数据校验或加密算法中经常使用它来比较数据块的内容。 - 查找差异:当需要查找两个内存块中不同的位置时,可以使用
memcmp
来快速发现差异。
相关文章:
高阶C语言|深入理解字符串函数和内存函数
文章目录 前言1.求字符串长度1.1 字符串长度函数:strlen模拟实现 2.长度不受限制的字符串函数2.1 字符串拷贝函数:strcpy模拟实现 2.2 字符串连接函数:strcat模拟实现 2.3 字符串比较函数:strcmp模拟实现 3.长度受限制的字符串函数…...
UE学习日志#17 C++笔记#3 基础复习3
19.2 [[maybe_unused]] 禁止编译器在未使用某些内容时发出警告 19.3 [[noreturn]] 永远不会把控制权返回给调用点 19.4 [[deprecated]] 标记为已弃用,仍然可以使用但是不鼓励使用 可以加参数表示弃用的原因[[deprecated("")]] 19.5 [[likely]]和[[un…...

团体程序设计天梯赛-练习集——L1-028 判断素数
前言 一道10分的题目,相对来说比较简单,思考的时候要仔细且活跃,有时候在写代码的时候一些代码的出现很多余,并且会影响最后的结果 L1-028 判断素数 本题的目标很简单,就是判断一个给定的正整数是否素数。 输入格式…...
从0开始使用面对对象C语言搭建一个基于OLED的图形显示框架(基础图形库实现)
目录 基础图形库的抽象 抽象图形 抽象点 设计我们的抽象 实现我们的抽象 测试 抽象线 设计我们的抽象 实现我们的抽象 绘制垂直的和水平的线 使用Bresenham算法完成任意斜率的绘制 绘制三角形和矩形 矩形 三角形 实现 绘制圆,圆弧和椭圆 继续我们的…...

创新创业计划书|建筑垃圾资源化回收
目录 第1部分 公司概况........................................................................ 1 第2部分 产品/服务...................................................................... 3 第3部分 研究与开发.................................................…...

反射、枚举以及lambda表达式
一.反射 1.概念:Java的反射(reflection)机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,既然能拿到那么&am…...

ROS应用之IMU碰撞检测与接触事件识别
前言 碰撞检测与接触事件识别是机器人与环境交互中的重要任务,尤其是在复杂的动态环境中。IMU(惯性测量单元)作为一种高频率、低延迟的传感器,因其能够检测加速度、角速度等动态变化而成为实现碰撞检测的核心手段之一。结合先进的…...

docker安装MySQL8:docker离线安装MySQL、docker在线安装MySQL、MySQL镜像下载、MySQL配置、MySQL命令
一、镜像下载 1、在线下载 在一台能连外网的linux上执行docker镜像拉取命令 docker pull mysql:8.0.41 2、离线包下载 两种方式: 方式一: -)在一台能连外网的linux上安装docker执行第一步的命令下载镜像 -)导出 # 导出镜…...

android安卓用Rime
之前 [1] 在 iOS 配置用上自改方案 [2],现想在安卓也用上。Rime 主页推荐了两个安卓平台支持 rime 的输入法 [3]: 同文 Tongwen Rime Input Method Editor,但在我的 Realme X2 Pro 上似乎有 bug:弹出的虚拟键盘只有几个 switcher…...

每日一博 - 三高系统架构设计:高性能、高并发、高可用性解析
文章目录 引言一、高性能篇1.1 高性能的核心意义 1.2 影响系统性能的因素1.3 高性能优化方法论1.3.1 读优化:缓存与数据库的结合1.3.2 写优化:异步化处理 1.4 高性能优化实践1.4.1 本地缓存 vs 分布式缓存1.4.2 数据库优化 二、高并发篇2.1 高并发的核心…...
C++ 中的引用(Reference)
在 C 中,引用(Reference)是一种特殊的变量类型,它提供了一个已存在变量的别名。引用在很多场景下都非常有用,比如函数参数传递、返回值等。下面将详细介绍 C 引用的相关知识。 1. 引用的基本概念和语法 引用是已存在…...
负荷预测算法模型
1. 时间序列分析方法 时间序列分析方法是最早被用来进行电力负荷预测的方法之一,它基于历史数据来构建数学模型,以描述时间与负荷值之间的关系。这种方法通常只考虑时间变量,不需要大量的输入数据,因此计算速度快。然而ÿ…...
【C语言】内存管理
【C语言】内存管理 文章目录 【C语言】内存管理1.概念2.库函数3.动态分配内存malloccalloc 4.重新调整内存的大小和释放内存reallocfree 1.概念 C 语言为内存的分配和管理提供了几个函数。这些函数可以在 <stdlib.h> 头文件中找到。 在 C 语言中,内存是通过…...

deepseek大模型本机部署
2024年1月20日晚,中国DeepSeek发布了最新推理模型DeepSeek-R1,引发广泛关注。这款模型不仅在性能上与OpenAI的GPT-4相媲美,更以开源和创新训练方法,为AI发展带来了新的可能性。 本文讲解如何在本地部署deepseek r1模型。deepseek官…...
动态规划DP 最长上升子序列模型 拦截导弹(题目分析+C++完整代码)
概览检索 动态规划DP 最长上升子序列模型 拦截导弹 原题链接 AcWiing 1010. 拦截导弹 题目描述 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。 但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每…...
缩位求和——蓝桥杯
1.题目描述 在电子计算机普及以前,人们经常用一个粗略的方法来验算四则运算是否正确。 比如:248153720248153720 把乘数和被乘数分别逐位求和,如果是多位数再逐位求和,直到是 1 位数,得 24814>145 156 56 而…...

Baklib赋能企业实现高效数字化内容管理提升竞争力
内容概要 在数字经济的浪潮下,企业面临着前所未有的机遇与挑战。随着信息技术的迅猛发展,各行业都在加速推进数字化转型,以保持竞争力。在这个过程中,数字化内容管理成为不可或缺的一环。高效的内容管理不仅能够优化内部流程&…...

【视频+图文讲解】HTML基础2-html骨架与基本语法
图文教程 基本骨架 举个例子,下图所展示的为html的源代码 -!DOCTYPE:表示文档类型(后边写的html表示文档类型是html);其中“!”表示声明 只要是加这个声明标签的,浏览器就会把下边的源代码当…...

消息队列篇--原理篇--常见消息队列总结(RabbitMQ,Kafka,ActiveMQ,RocketMQ,Pulsar)
1、RabbitMQ 特点: AMQP协议:RabbitMQ是基于AMQP(高级消息队列协议)构建的,支持多种消息传递模式,如发布/订阅、路由、RPC等。多语言支持:支持多种编程语言的客户端库,包括Java、P…...
【力扣每日一题】存在重复元素 II 解题思路
219. 存在重复元素 II 解题思路 问题描述 给定一个整数数组 nums 和一个整数 k,要求判断数组中是否存在两个 不同的索引 i 和 j,使得: nums[i] nums[j]且满足 abs(i - j) < k 如果满足上述条件,返回 true,否则…...
Ubuntu系统下交叉编译openssl
一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机:Ubuntu 20.04.6 LTSHost:ARM32位交叉编译器:arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...

visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...
oracle与MySQL数据库之间数据同步的技术要点
Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异,它们的数据同步要求既要保持数据的准确性和一致性,又要处理好性能问题。以下是一些主要的技术要点: 数据结构差异 数据类型差异ÿ…...

相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...
uniapp中使用aixos 报错
问题: 在uniapp中使用aixos,运行后报如下错误: AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...

html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...

android RelativeLayout布局
<?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"android:gravity&…...