高阶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,否则…...
国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...
深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
反向工程与模型迁移:打造未来商品详情API的可持续创新体系
在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...
基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...
汽车生产虚拟实训中的技能提升与生产优化
在制造业蓬勃发展的大背景下,虚拟教学实训宛如一颗璀璨的新星,正发挥着不可或缺且日益凸显的关键作用,源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例,汽车生产线上各类…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
前端中slice和splic的区别
1. slice slice 用于从数组中提取一部分元素,返回一个新的数组。 特点: 不修改原数组:slice 不会改变原数组,而是返回一个新的数组。提取数组的部分:slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...
Vue 模板语句的数据来源
🧩 Vue 模板语句的数据来源:全方位解析 Vue 模板(<template> 部分)中的表达式、指令绑定(如 v-bind, v-on)和插值({{ }})都在一个特定的作用域内求值。这个作用域由当前 组件…...
沙箱虚拟化技术虚拟机容器之间的关系详解
问题 沙箱、虚拟化、容器三者分开一一介绍的话我知道他们各自都是什么东西,但是如果把三者放在一起,它们之间到底什么关系?又有什么联系呢?我不是很明白!!! 就比如说: 沙箱&#…...
从物理机到云原生:全面解析计算虚拟化技术的演进与应用
前言:我的虚拟化技术探索之旅 我最早接触"虚拟机"的概念是从Java开始的——JVM(Java Virtual Machine)让"一次编写,到处运行"成为可能。这个软件层面的虚拟化让我着迷,但直到后来接触VMware和Doc…...
