【C语言】字符串和内存函数的介绍 -- 详解

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

一、求字符串长度⚪strlen 

  • 字符串以 '\0' 作为结束标志,strlen 函数返回的是在字符串中 '\0' 前面出现的字符个数不包含 '\0' )
  • 参数指向的字符串必须要以 '\0' 结束。
  • 易错点):注意函数的返回值为 size_t,是无符号(unsigned)的。

strlen - C++ Reference (cplusplus.com)

⚪【模拟实现】(三种不同方法)

#include <stdio.h>// 1、计数器方式
size_t my_strlen(const char* str)
{size_t count = 0;while (*str){count++;str++;}return count;
}// 2、不能创建临时变量计数器
size_t my_strlen(const char* str) 
{ if(*str == '\0'){return 0;}else {return 1 + my_strlen(str+1);}
}// 3、指针-指针的方式
size_t my_strlen(char* str) 
{char *p = str; while(*p != '\0' ) {p++;} return p-str; 
}int main()
{char arr[] = "hello world";size_t count = my_strlen(arr);printf("%zu\n", count);return 0;
}


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

1、strcpy

Copies the C string pointed by source into the array pointed by destination, including the terminating null character (and stopping at that point).

  • 源字符串 src 必须'\0' 结束
  • 会将源字符串 src 中的 '\0' 拷贝到目标空间 dest。
  • 目标空间必须足够大,以确保能存放源字符串。
  • 目标空间必须可变,即目标空间 dest 不可以被 const 声明

strcpy - C++ Reference (cplusplus.com)

⚪【模拟实现】

#include <stdio.h>
#include <assert.h>char* my_strcpy(char* str2, const char* str1)
{assert(str1 && str2);char* ret = str2;while (*str2++ = *str1++){;}return ret;
}int main()
{char s1[] = "hello world";char s2[20] = { 0 };char* ret = my_strcpy(s2, s1);printf("%s\n", ret);return 0;
}


2、strcat

Appends a copy of the source string to the destination string. The terminating null character in destination is overwritten by the fifirst character of source, and a null-character is included at the end of the new string formed by the concatenation of both in destination.
  • 源字符串 src 必须'\0' 结束
  • 将源字符串 src 中的 '\0' 一同拷贝到目标空间 dest ,并删除 *dest 原来末尾的 '\0'
  • 目标空间必须有足够大,能容纳下源字符串的内容。
  • 目标空间必须可修改,即目标空间 dest 不可以被 const 声明

strcat - C++ Reference (cplusplus.com)

🔺字符串自己给自己追加,会如何?

        当使用 strcat 函数将一个字符串追加到自身时,可能会导致未定义的行为。strcat 函数的工作原理是在源字符串的结尾处追加目标字符串的内容,并在结尾加上空字符 '\0'。当源字符串和目标字符串是同一个字符串时,追加操作会导致源字符串的内容被破坏,因为在追加过程中,源字符串的内容会被覆盖,最终结果会是一个不可预测的字符串。因为根据不同的编译器和库的版本,strcat 函数在某些情况下可能不会导致未定义行为。但是将一个字符串追加到自身仍然是一个不好的编程实践,因为它容易引发错误和混乱。

        因此,不推荐使用 strcat 函数将字符串追加到自身。如果需要将一个字符串复制到另一个字符串末尾,可以使用 strcpy 函数进行复制操作。

【模拟实现】

#include <stdio.h>
#include <assert.h>char* my_strcat(char* dest, const char* src)
{char* tmp = dest;assert(dest && src);while (*dest){dest++;}while (*dest = *src){dest++;src++;}return tmp;
}int main()
{char s1[20] = "hello";char s2[] = " world";char* ret = my_strcat(s1, s2);printf("%s\n", ret);return 0;
}


3、strcmp

This function starts comparing the fifirst character of each string. If they are equal to each other, it continues with the following pairs until the characters diffffer or until a terminating null-character is reached.

标准规定
  • 第一个字符串大于第二个字符串,则返回大于 的数字
  • 第一个字符串等于第二个字符串,则返回 0
  • 第一个字符串小于第二个字符串,则返回小于 的数字

strcmp - C++ Reference (cplusplus.com)

🔺那么如何判断两个字符串?

返回值只需要满足要求即可,比如大于 0 的数字,不一定是1,只需满足条件即可。

strcmp 函数的比较是基于字符的 ASCII 码进行的。它从两个字符串的第一个字符开始逐个比较,直至找到不相等的字符或者其中一个字符串的结束符 '\0'。在比较的时候,它会将两个字符的 ASCII 码进行减法运算,返回结果作为比较的结果。

        需要注意的是,strcmp 函数是区分大小写的。也就是说,大写字母和小写字母被认为是不同的字符。如果需要不区分大小写的字符串比较,可以使用 strcasecmp 函数(在某些编程环境中可能被称为_stricmp)。

⚪【模拟实现】

#include <stdio.h>
#include <assert.h>int my_strcmp(const char* str1, const char* str2)
{assert(str1 && str2);while (*str1 == *str2){if (*str1 == '\0'){return 0;}str1++;str2++;}return (*str1 - *str2);
}int main()
{char s1[] = "abcdef";char s2[] = "abcq";int ret = my_strcmp(s1, s2);if (ret > 0){printf(">\n");}else if (ret == 0){printf("=\n");}else{printf("<\n");}return 0;
}

⚠ 注意:根据编译器的不同,返回的结果也不同。

VS2019 中,大于返回 1,等于返回 0,小于返回 -1。但在 Linux-gcc 中,大于返回正数,等于返回0,小于返回负数。

// 推荐
if(strcmp(p1, p2) > 0)
{printf("p1 > p2");
}
else if(strcmp(p1, p2 == 0))
{printf("p1 == p2");
}
else if(strcmp(p1, p2) < -1)
{printf("p1 < p2");
}// 不推荐
if(strcmp(p1, p2) == 1)
{printf("p1 > p2");
}
else if(strcmp(p1, p2 == 0))
{printf("p1 == p2");
} else if(strcmp(p1, p2) == -1)
{printf("p1 < p2");
}

三、长度受限制的字符串函数介绍

1、strncpy

Copies the fifirst num characters of source to destination. If the end of the source C string (which is signaled by a null-character) is found before num characters have been copied, destination is padded with zeros until a total of num characters have been written to it.
  • 拷贝 count 个字符从源字符串到目标空间。
  • 如果源字符串的长度小于 count,则拷贝完源字符串之后,在目标的后边追加 0直到 count 
  • dest 和 src 不应该重叠(重叠时可以用更安全的 memmove 替代)。

  • 目标空间 dest 必须足够大,以确保能够存放源字符串。

  • 目标空间 dest 必须可变,即目标空间 dest 不可以被 const 声明

strncpy - C++ Reference (cplusplus.com)

⚪【模拟实现】

#include <stdio.h>
#include <assert.h>char* my_strncpy(char* dest, const char* src, size_t count)
{assert(dest && src);char* cur = dest;while (count--){if ((*dest++ = *src++) == '\0'){break;}}if (count != 0){while (count--){*dest++ = '\0';}}return cur;
}int main()
{char s1[20] = { 0 };char s2[] = "hello world";int sz = sizeof(s2) / sizeof(s2[0]);printf("%s\n", my_strncpy(s1, s2, sz));return 0;
}


2、strncat

Appends the fifirst num characters of source to destination, plus a terminating null-character. If the length of the C string in source is less than num, only the content up to the terminating null-character is copied.
  • 如果源字符串的长度小于 count,则只复制 '\0' 之前的内容。
strncat - C++ Reference (cplusplus.com)

⚪【模拟实现】

#include <stdio.h>
#include <assert.h>char* my_strncat(char* dest, const char* src, size_t count)
{assert(dest && src);char* cur = dest;while (*dest){dest++;}while (count--){if ((*dest++ = *src++) == '\0'){return cur;}}*dest = '\0';return cur;
}int main()
{char s1[20] = "hello";char s2[] = " world";size_t sz = sizeof(s2) / sizeof(s2[0]);printf("%s\n", my_strncat(s1, s2, sz)); // 从s2中取sz个追加到s1中return 0;
}


3、strncmp

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

strncmp - C++ Reference (cplusplus.com)

【代码演示】 

#include <stdio.h>
#include <string.h>int main()
{const char* p1 = "abczdef";const char* p2 = "abcqwer";int ret1 = strncmp(p1, p2, 1);int ret2 = strncmp(p1, p2, 4);printf("%d %d\n", ret1, ret2);return 0;
}


四、字符串查找

1、strstr


Returns a pointer to the fifirst occurrence of str2 in str1, or a null pointer if str2 is not part of str1.
  • 返回字符串中首次出现子串的地址。若 str2 是 str1 的子串,则返回 str2 在 str1 中首次出现的地址。如果 str2 不是 str1 的子串,则返回 NULL 。
strstr - C++ Reference (cplusplus.com)

⚪【模拟实现】

#include <stdio.h>
#include <assert.h>char* my_strstr(const char* str1, const char* str2)
{assert(str1 && str2);if (*str2 == '\0'){return (char*)str1;}char* cur = (char*)str1;char* s1, *s2;while (*cur != '\0'){s1 = cur;s2 = (char*)str2;while (*s1 && *s2 && (*s1 == *s2)){s1++;s2++;}if (*s2 == '\0'){return cur;}cur++;}return NULL;
}int main()
{char s1[] = "abbcde";char s2[] = "bcd";char s3[] = "abcd";char* ret1 = my_strstr(s1, s2);char* ret2 = my_strstr(s1, s3);if (ret1 == NULL){printf("未找到匹配的子串!\n");}else{printf("%s\n", ret1);}if (ret2 == NULL){printf("未找到匹配的子串!\n");}else{printf("%s\n", ret2);}return 0;
}


2、strtok

  • sep(delimit) 参数是个字符串,定义了用作分隔符的字符集合
  • 第一个参数指定一个字符串,它包含了 个或者多个由 sep 字符串中一个或者多个分隔符分割的标记。
  • strtok 函数找到 str 中的下一个标记,并将其用 '\0' 结尾,返回一个指向这个标记的指针。(注:strtok 函数会改变被操作的字符串,所以在使用 strtok 函数切分的字符串一般都是临时拷贝的内容并且可修改。)
  • strtok 函数的第一个参数不为 NULL ,函数将找到 str 中第一个标记,strtok 函数将保存它在字符串中的位置。
  • strtok 函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
  • 如果字符串中不存在更多的标记,则返回 NULL 指针。

strtok - C++ Reference (cplusplus.com)

注意strtok 会破坏原字符串,分割后原字符串保留第一个分割符前的字符。 

【代码演示】

#include <stdio.h>
#include <string.h>int main()
{char arr[] = "3031899646@qq.com";printf("原字符串: %s\n", arr);const char* sep = "@."; // 创建sepchar arr1[30];char* ret = NULL;strcpy(arr1, arr); // 将数据拷贝一份,保留arr数组的内容// 分行打印切割内容for (ret = strtok(arr, sep); ret != NULL; ret = strtok(NULL, sep)){printf("%s\n", ret);}printf("分割后原字符串被破坏: %s\n", arr); // 分割后原字符串保留第一个分割符前的字符return 0;
}


五、错误信息报告⚪strerror

  • 返回错误码,所对应的错误信息。
  • errno 是一个全局的错误码变量。当 C 语言的库函数在执行过程中,发生了错误后就会把对应的错误码赋值到errno中。

strerror - C++ Reference (cplusplus.com)

⚪【模拟实现】 

#include <stdio.h>
#include <string.h>
#include <errno.h>// 错误码   错误信息
// 0 -      No error
// 1 -      Operation not permitted
// 2 -      No such file or directory
//int main()
{char* str = strerror(errno);printf("%s\n", str);return 0;
}


六、字符操作

1、字符分类函数

⚪【代码演示】

#include <stdio.h>
#include <ctype.h>int main()
{char ch1 = 'a';int ret = islower(ch1); // 判断ch1是否为小写 -- 非0为真printf("%d\n", ret);char ch2 = 'B';int res = islower(ch2); // 判断ch2是否为小写 -- 0为假printf("%d\n", res);return 0;
}

注意:需引入头文件 ctype.h 头文件。 


2、字符转换

int tolower ( int c ); 
int toupper ( int c );

⚪【代码演示】 

#include <stdio.h>int main()
{char ch = tolower('Q'); // 大写转小写putchar(ch);return 0;
}


七、内存操作函数

1、memcpy

 

  • 函数 memcpy 从 src 的位置开始向后复制 count 个字节的数据到 dest 的内存位置。
  • 这个函数在遇到 '\0' 的时候并不会停下来
  • 如果 src 和 dest 有任何的重叠,复制的结果都是未定义的。

 memcpy - C++ Reference (cplusplus.com)

⚪【模拟实现】 

#include <stdio.h>
#include <assert.h>void* my_memcpy(void* dest, const void* src, size_t count)
{assert(dest && src);void* ret = dest;while (count--){*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;}return ret;
}int main()
{char s1[] = "abcdefgh";char s2[20] = "xxxxxxxxxx";my_memcpy(s2, s1, 5);printf("%s\n", s2);return 0;
}

⚪【代码演示】 

// 拷贝结构体
#include <stdio.h>
#include <string.h>struct S
{char name[20];int age;
};int main()
{struct S arr3[] = { {"张三", 20}, {"李四", 30} };struct S arr4[3] = { 0 };memcpy(arr4, arr3, sizeof(arr3));return 0;
}

 


2、memmove

  • 和 memcpy 的差别就是 memmove 函数处理的源内存块和目标内存块是可以重叠的
  • 如果源空间和目标空间出现重叠,就得使用 memmove 函数处理。

C语言标准要求:

memcpy 用来处理不重叠的内存拷贝,而 memmove 用来处理重叠内存的拷贝

memmove - C++ Reference (cplusplus.com)

⚪【模拟实现】

#include <stdio.h>
#include <assert.h>void* my_memmove(void* dest, const void* src, size_t count)
{assert(dest && src);void* ret = dest;//从前->后if (dest <= src){while (count--){*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;}}//从后->前else{dest = (char*)dest + count - 1;src = (char*)src + count - 1;while (count--){*(char*)dest = *(char*)src;dest = (char*)dest - 1;src = (char*)src - 1;}}return ret;
}int main()
{char arr[] = "abcdefgh";my_memmove(arr, arr + 3, 2);printf("%s\n", arr);return 0;
}


3、memset

 

  • memset 是以字节为单位设置内存的

memset - C++ Reference (cplusplus.com)

⚪【代码实现】 

#include <stdio.h>
#include <string.h>int main()
{int arr[10] = { 0 };memset(arr, 1, 20); // 将前20个字节全部设置为1return 0;
}


4、memcmp

  • 比较从 buf和 buf指针开始的 count 个字节。

注意memcmp 不同于 strcmp,memcmp 遇到 '\0' 不会停止比较。

memcmp - C++ Reference (cplusplus.com)

⚪【代码演示】

#include <stdio.h>
#include <string.h>int main()
{float arr1[] = { 1.0, 2.0, 3.0, 4.0 };float arr2[] = { 1.0, 3.0 };int ret = memcmp(arr1, arr2, 8); // arr1是否比arr2大,比较8个字节printf("%d\n", ret);return 0;
}

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

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

相关文章

【Rust】Rust学习 第十四章进一步认识 Cargo 和 Crates.io

本章会讨论 Cargo 其他一些更为高级的功能&#xff0c;我们将展示如何&#xff1a; 使用发布配置来自定义构建将库发布到 crates.io使用工作空间来组织更大的项目从 crates.io 安装二进制文件使用自定义的命令来扩展 Cargo Cargo 的功能不止本章所介绍的&#xff0c;关于其全…

【JavaScript】使用js实现滑块验证码功能与浏览器打印

滑块验证码 效果图&#xff1a; 实现思路&#xff1a; 根据滑块的最左侧点跟最右侧点&#xff0c; 是否在规定的距离内【页面最左侧为原点】&#xff0c;来判断是否通过 html代码&#xff1a; <!DOCTYPE html> <html><head><title>滑动图片验证码&…

Python爬虫常用:谷歌浏览器驱动——Chromedriver 插件安装教程

前言 大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 我们在做爬虫的时候经常要使用谷歌浏览器驱动&#xff0c;今天分享下这个Chromedriver 插件的安装方法。 话不多说&#xff0c;直接开搞&#xff0c;如果有什么疑惑/资料需要的可以点击文章末尾名片领取源码 第一步、打开谷…

使用Dockker创建vwas容器时报错的解决方法

执行命令 docker run -it -d -p 13443:3443 --cap-add LINUX_IMMUTABLE secfa/docker-awvs没有详细看报错之前找了各种各样的解决办法&#xff0c;都无法解决。因此以后在看报错提示的时候耐心一点看关键词Error 后来才发现启动vwas时docker报了这个错&#xff1a; OSError: …

CANoe自动化工程的搭建

基于XMLCAPL建立自动化工程 1、导入ini文件2、新建 Test Environment3、报告类型4、代码编写 1、导入ini文件 工程的配置的文件&#xff0c;配置DUT相关信息&#xff0c;具体视工程而编写内容。 2、新建 Test Environment 1、新建XML测试用例环境 2、导入XML测试用例文件 …

Jmeter常用功能-参数化介绍

JMeter也有像LR中的参数化&#xff0c;本篇就来介绍下JMeter的参数化如何去实现。 参数化&#xff1a;录制脚本中有登录操作&#xff0c;需要输入用户名和密码&#xff0c;假如系统不允许相同的用户名和密码同时登录&#xff0c;或者想更好的模拟多个用户来登录系统。 这个时…

虚拟机/双系统Ubuntu扩容

虚拟机Ubuntu扩容 1.需要删除所有的快照 2.扩展虚拟机磁盘大小 虚拟机(M)→设置(s)→硬盘(SCSI)→扩展磁盘容量 3.Ubuntu内调整分区大小 安装gparted分区工具&#xff1a;sudo apt-get install gparted 启动gparted并resize分区 4.最后最好建一个快照&#xff0c;不然gg了…

WinPlan经营大脑垂直大模型行业报告

一、引言 在当前高度信息化的时代,企业经营管理决策的重要性已经得到了广泛的认可。然而,在实际操作中,许多企业仍然在凭经验、拍脑袋进行经营决策,缺乏数据工具与专职分析团队,导致决策难、效率低等问题。针对这一问题,近年来,一种名为“WinPlan”的经营决策产品逐渐崭…

[测试报告] 爱搜Blog 自动化测试报告

目录 项目背景 项目功能 测试详情 一、设计测试用例 二、功能测试步骤结果 1. 登录页面 2. 个人博客页面 3. 博客详情页 4. 博客编辑页 三、自动化测试及测试结果 1. 测试环境 2. 登录测试用例&#xff1a; 3. 个人详情页测试用例&#xff1a; 4. 写博客并发布测试…

Android免打包多渠道统计如何实现

摘要&#xff1a; 实际上只要完成1-2步即可实现多渠道打包&#xff0c;这也意味着&#xff0c;只要每次更新App时给出一个原始包&#xff0c;运营人员就能在后台自己进行操作管理&#xff0c;简单快捷到全程无需开发人员参与。 我们都知道&#xff0c;Android 市场被分割成几十…

使用Alien对.deb包与.rpm包相互转换

目录 1、切换到root 2、更新yum&#xff08;更新比较耗时&#xff0c;不更新没试行不&#xff0c;自行斟酌是否跳过这一步&#xff09; 3、卸载ibus 4、安装Alien及其依赖包 5、安装Alien 6、将.deb转换成.rpm包 7、安装RPM包 8、如果报错 9、将.rpm转换成.deb包 10、安…

MongoDB(三十九)

目录 一、概述 &#xff08;一&#xff09;相关概念 &#xff08;二&#xff09;特性 二、应用场景 三、安装 &#xff08;一&#xff09;编译安装 &#xff08;二&#xff09;yum安装 1、首先制作repo源 2、软件包名&#xff1a;mongodb-org 3、启动服务&#xff1a…

通过 Amazon SageMaker JumpStart 部署 Llama 2 快速构建专属 LLM 应用

来自 Meta 的 Llama 2 基础模型现已在 Amazon SageMaker JumpStart 中提供。我们可以通过使用 Amazon SageMaker JumpStart 快速部署 Llama 2 模型&#xff0c;并且结合开源 UI 工具 Gradio 打造专属 LLM 应用。 Llama 2 简介 Llama 2 是使用优化的 Transformer 架构的自回归语…

【JavaEE基础学习打卡04】JDBC之MySQL数据库安装

目录 前言一、JDBC与数据库二、MySQL数据库1.MySQL数据库2.MySQL服务下载安装3.MySQL服务启动停止4.MySQL命令 三、MySQL客户端安装总结 前言 &#x1f4dc; 本系列教程适用于JavaWeb初学者、爱好者&#xff0c;小白白。我们的天赋并不高&#xff0c;可贵在努力&#xff0c;坚持…

【 Cocos Creator 项目实战】益智游戏《2048》(附带完整源码工程)

本文乃Siliphen原创&#xff0c;转载请注明出处 目录 游戏介绍 概述 游戏整体流程 游戏框架设计 主要流程控制类 本文项目的代码组织结构 构建游戏世界 数字方块 地图 触摸手势识别 防触摸抖动 判断用户输入的方向 地图 任意大小的地图 初始化地图大小 地图绘制…

无涯教程-Perl - sysread函数

描述 该函数等效于C /操作系统函数read(),因为它绕过了诸如print,read和seek之类的函数所采用的缓冲系统,它仅应与相应的syswrite和sysseek函数一起使用。 它从FILEHANDLE中读取LENGTH个字节,并将输出放入SCALAR中。如果指定了OFFSET,则将数据从OFFSET字节写入SCALAR,从而有效…

IC流程中 DFT 学习笔记(2)

引言 DFT是ASIC芯片设计流程中不可或缺的环节。其主要目的是在芯片前端设计验证完成后插入一些诸如寄存器链等可供测试的逻辑&#xff0c;算是IC后端设计的范畴&#xff0c;属于结构测试而非功能测试。主要是在ASIC芯片流片完成后&#xff0c;通过这些已插入的逻辑&#xff0c…

手机照片误删怎么办,电脑照片误删怎么办怎么才能找回,EasyRecovery来帮您

手机照片误删怎么办&#xff0c;电脑照片误删怎么办怎么才能找回&#xff0c;EasyRecovery 2023来帮您&#xff01;&#xff01;&#xff01; EasyRecovery 2023是一款操作安全、价格便宜、用户自主操作的 数据恢复 方案&#xff0c;它支持从各种各样的 存储介质 恢复删除 或者…

Vue3.X 创建简单项目

一、环境安装与检查 首先&#xff0c;我们要确保我们安装了构建vue框架的环境&#xff0c;不会安装的请自行百度&#xff0c;有很多安装教程。检查环境 node -v # 如果没有安装nodejs请安装&#xff0c;安装教程自行百度 vue -V# 没有安装&#xff0c;请执行npm install -g v…

TCP服务器实现—多进程版,多线程版,线程池版

目录 前言 1.存在的问题 2.多进程版 3.多线程版 4.线程池版 总结 前言 在上一篇文章中使用TCP协议实现了一个简单的服务器&#xff0c;可以用来服务端和客户端通信&#xff0c;但是之前的服务器存在一个问题&#xff0c;就是当有多个客户端连接服务器的时候&#xff0c;服…