C语言进阶之字符串函数和内存函数的介绍及部分函数的模拟实现

在这里插入图片描述

字符串函数和内存函数

  • 1.字符串函数介绍
    • 1.1 strlen
    • 1.2 strcpy
    • 1.3 strcat
    • 1.4 strcmp
    • 1.5 strncpy
    • 1.6 strncat
    • 1.7 strncpy
    • 1.8 strstr
    • 1.9 strtok
    • 1.10 strerror
    • 1.11 字符分类函数
  • 2.内存函数
    • 2.1 memcpy
    • 2.2 memmove
    • 2.3 memcmp
  • 3.函数的模拟实现
    • 3.1 模拟实现strlen
    • 3.2 模拟实现strcpy
    • 3.3 模拟实现strcat
    • 3.4 模拟实现strstr
    • 3.5 模拟实现strcmp
    • 3.6 模拟实现memcpy
    • 3.7 模拟实现memmove
  • 结语

1.字符串函数介绍

1.1 strlen

头文件<string.h>

获取字符串长度

返回 C 字符串 str 的长度。

C 字符串的长度由终止空字符确定:C 字符串的长度与字符串开头和终止空字符之间的字符数一样长(不包括终止空字符本身)。

这不应与保存字符串的数组的大小混淆。例如:

int main()
{char mystr[100] = "test string";printf("%d", strlen(mystr));return 0;
}

在这里插入图片描述

定义一个大小为 100 个字符的字符数组,但初始化 mystr 时使用的 C 字符串的长度仅为 11 个字符。因此,当 sizeof(mystr) 的计算结果为 100 时,strlen(mystr) 返回 11。

库中的声明如下

_Check_return_
size_t __cdecl strlen(_In_z_ char const* _Str);
  1. 字符串已经 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中 ‘\0’ 前面出现的字符个数(不包含 ‘\0’ )。
  2. 参数指向的字符串必须要以 ‘\0’ 结束。
  3. 注意函数的返回值为size_t,是无符号的( 易错 )

1.2 strcpy

头文件<string.h>

复制字符串

将源指向的 C 字符串复制到目标指向的数组中,包括终止的 null 字符(并在该点停止)。

为避免溢出,目标指向的数组的大小应足够长,以包含与源相同的 C 字符串(包括终止空字符),并且不应在内存中与源重叠。

int main()
{char str1[] = "Sample string";char str2[40];char str3[40];strcpy(str2, str1);strcpy(str3, "copy successful");printf("str1: %s\nstr2: %s\nstr3: %s\n", str1, str2, str3);return 0;
}

在这里插入图片描述
库中声明如下

__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_1(char*, __RETURN_POLICY_DST, __EMPTY_DECLSPEC, strcpy,_Out_writes_z_(_String_length_(_Source) + 1), char,        _Destination,_In_z_                                        char const*, _Source)
  1. 源字符串必须以 ‘\0’ 结束。
  2. 会将源字符串中的 ‘\0’ 拷贝到目标空间。
  3. 目标空间必须足够大,以确保能存放源字符串。
  4. 目标空间必须可变。

1.3 strcat

头文件<string.h>

追加字符串

将源字符串的副本追加到目标字符串。目标中的终止空字符被源的第一个字符覆盖,并且在目标中由两者串联形成的新字符串的末尾包含一个空字符。

目标字符串和源字符串不得重叠。

int main()
{char str[80];strcpy(str, "these ");strcat(str, "strings ");strcat(str, "are ");strcat(str, "concatenated.");puts(str);return 0;
}

在这里插入图片描述
库中声明如下

__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_1(char*, __RETURN_POLICY_DST, __EMPTY_DECLSPEC, strcat,_Inout_updates_z_(_String_length_(_Destination) + _String_length_(_Source) + 1), char,        _Destination,_In_z_                                                                           char const*, _Source)
  1. 源字符串必须以 ‘\0’ 结束。
  2. 目标空间必须有足够的大,能容纳下源字符串的内容。
  3. 目标空间必须可修改。

1.4 strcmp

头文件<string.h>

比较两个字符串

将 C 字符串 str1 与 C 字符串 str2 进行比较。

此函数开始比较每个字符串的第一个字符。如果它们彼此相等,则继续以下对,直到字符不同或达到终止空字符。

此函数执行字符的二进制比较。

返回一个整数值,该值指示字符串之间的关系:
在这里插入图片描述

int main()
{char key[] = "apple";char buffer[80];do {printf("Guess my favorite fruit? ");fflush(stdout);scanf("%79s", buffer);} while (strcmp(key, buffer) != 0);puts("Correct answer!");return 0;
}

在这里插入图片描述
库中声明如下

_Check_return_
int __cdecl strcmp(_In_z_ char const* _Str1,_In_z_ char const* _Str2);
  1. 第一个字符串大于第二个字符串,则返回大于0的数字(vs中为固定值1)
  2. 第一个字符串等于第二个字符串,则返回0
  3. 第一个字符串小于第二个字符串,则返回小于0的数字(vs中为固定值-1)

1.5 strncpy

头文件<string.h>

从字符串中复制字符

将源的第一个字符数复制到目标。如果在复制 num 个字符之前找到源 C 字符串的末尾(由 null 字符表示),则目标将填充零,直到总共写入 num 个字符为止。

如果源长度超过 num,则不会在目标末尾隐式附加空字符。因此,在这种情况下,不应将目标视为以空结尾的 C 字符串(这样读取它会溢出)。

目标字符串和源字符串不得重叠

int main()
{char str1[] = "To be or not to be";char str2[40];char str3[40];strncpy(str2, str1, sizeof(str2));strncpy(str3, str2, 5);str3[5] = '\0'; puts(str1);puts(str2);puts(str3);return 0;
}

在这里插入图片描述
库中声明如下

__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_2_EX(char*, __RETURN_POLICY_DST, _ACRTIMP, strncpy, strncpy_s,_Out_writes_z_(_Size)               char,_Out_writes_(_Count) _Post_maybez_, char,        _Destination,_In_reads_or_z_(_Count)             char const*, _Source,_In_                                size_t,      _Count)
  1. 拷贝num个字符从源字符串到目标空间。
  2. 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。

1.6 strncat

头文件<string.h>

从字符串追加字符

将源的第一个数字字符追加到目标,外加一个终止空字符。

如果源中 C 字符串的长度小于 num,则仅复制终止空字符之前的内容。

int main()
{char str1[20];char str2[20];strcpy(str1, "To be ");strcpy(str2, "or not to be");strncat(str1, str2, 6);puts(str1);return 0;
}

在这里插入图片描述
库中声明如下

__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_2_EX(char*, __RETURN_POLICY_DST, _ACRTIMP, strncat, strncat_s,_Inout_updates_z_(_Size)   char,_Inout_updates_z_(_Count), char,        _Destination,_In_reads_or_z_(_Count)    char const*, _Source,_In_                       size_t,      _Count)

1.7 strncpy

头文件<string.h>

从字符串中复制字符

将源的第一个字符数复制到目标。如果在复制 num 个字符之前找到源 C 字符串的末尾(由 null 字符表示),则目标将填充零,直到总共写入 num 个字符为止。

如果源长度超过 num,则不会在目标末尾隐式附加空字符。因此,在这种情况下,不应将目标视为以空结尾的 C 字符串(这样读取它会溢出)。

目标字符串和源字符串不得重叠

int main()
{char str1[] = "To be or not to be";char str2[40];char str3[40];strncpy(str2, str1, sizeof(str2));strncpy(str3, str2, 5);str3[5] = '\0'; puts(str1);puts(str2);puts(str3);return 0;
}

在这里插入图片描述
库中声明如下

__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_2_EX(char*, __RETURN_POLICY_DST, _ACRTIMP, strncpy, strncpy_s,_Out_writes_z_(_Size)               char,_Out_writes_(_Count) _Post_maybez_, char,        _Destination,_In_reads_or_z_(_Count)             char const*, _Source,_In_                                size_t,      _Count)

1.8 strstr

头文件<string.h>

查找子字符串

返回指向 str2 中第一次出现的 str1 的指针,如果 str2 不是 str1 的一部分,则返回一个空指针。

匹配过程不包括终止空字符,但它到此为止。

int main()
{char str[] = "This is a simple string";char* pch;pch = strstr(str, "simple");puts(pch);if (pch != NULL)strncpy(pch, "sample", 6);puts(str);return 0;
}

在这里插入图片描述
库中声明如下

_VCRTIMP char _CONST_RETURN* __cdecl strstr(_In_z_ char const* _Str,_In_z_ char const* _SubStr);

1.9 strtok

头文件<string.h>

将字符串拆分为标记

对此函数的一系列调用将 str 拆分为标记,这些标记是由分隔符中的任何字符分隔的连续字符序列。

在第一次调用时,该函数需要一个 C 字符串作为 str 的参数,其第一个字符用作扫描令牌的起始位置。在后续调用中,该函数需要一个空指针,并使用最后一个令牌末尾之后的位置作为扫描的新起始位置。

为了确定标记的开头和结尾,该函数首先从起始位置扫描分隔符中未包含的第一个字符(该字符将成为标记的开头)。然后从令牌的开头开始扫描分隔符中包含的第一个字符,该字符将成为令牌的末尾。如果找到终止空字符,扫描也会停止。

令牌的此结尾将自动替换为空字符,并且令牌的开头由函数返回。

一旦在对 strtok 的调用中找到 str 的终止空字符,则对此函数的所有后续调用(以空指针作为第一个参数)都将返回空指针。

找到最后一个令牌的点由要在下一次调用中使用的函数在内部保留(不需要特定的库实现来避免数据争用)。

int main()
{char str[] = "- This, a sample string.";char* pch;printf("Splitting string \"%s\" into tokens:\n", str);pch = strtok(str, " ,.-");while (pch != NULL){printf("%s\n", pch);pch = strtok(NULL, " ,.-");}return 0;
}

在这里插入图片描述
库中声明如下

_Check_return_ _CRT_INSECURE_DEPRECATE(strtok_s)
_ACRTIMP char* __cdecl strtok(_Inout_opt_z_ char*       _String,_In_z_        char const* _Delimiter);

返回值

  1. 如果找到令牌,则指向令牌开头的指针。
  2. 否则为空指针。
  3. 当在正在扫描的字符串中到达字符串的末尾(即空字符)时,始终返回空指针。

1.10 strerror

头文件<string.h>

获取指向错误消息字符串的指针

解释 errnum 的值,生成一个字符串,其中包含描述错误条件的消息,就像由库的函数设置为 errno 一样。

返回的指针指向静态分配的字符串,程序不应修改该字符串。对此函数的进一步调用可能会覆盖其内容(不需要特定的库实现来避免数据争用)。

strerror 生成的错误字符串可能特定于每个系统和库实现。

int main()
{FILE* pFile;pFile = fopen("unexist.ent", "r");if (pFile == NULL)printf("Error opening file unexist.ent: %s\n", strerror(errno));return 0;
}

在这里插入图片描述
我们可以给下面这段代码来看看vs的库中前20个错误信息提示是什么

int main()
{for (int i = 0; i < 20; i++){printf("%d:%s\n", i,strerror(i));}return 0;
}

在这里插入图片描述

1.11 字符分类函数

函数如果他的参数符合下列条件就返回真
iscntrl任何控制字符
isspace空白字符:空格‘ ’,换页‘\f’,换行’\n’,回车‘\r’,制表符’\t’或者垂直制表符’\v’
isdigit十进制数字 0~9
isxdigit十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F
islower小写字母a~z
isupper大写字母A~Z
isalpha字母a~z或A~Z
isalnum字母或者数字,a~z,A~Z,0~9
ispunct标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph任何图形字符
isprint任何可打印字符,包括图形字符和空白字符

字符转换示例

#include <stdio.h>
#include <ctype.h>
int main()
{int i = 0;char str[] = "Test String.\n";char c;while (str[i]){c = str[i];if (isupper(c))c = tolower(c);putchar(c);i++;}return 0;
}

在这里插入图片描述

2.内存函数

2.1 memcpy

头文件<string.h>

复制内存块

将字节数的值从源指向的位置直接复制到目标指向的内存块。

源指针和目标指针指向的对象的基础类型与此函数无关;结果是数据的二进制副本。

该函数不检查源中的任何终止空字符 - 它总是准确地复制字节数。

为避免溢出,目标和源参数指向的数组大小应至少为字节数,并且不应重叠

struct {char name[40];int age;
} person, person_copy;int main()
{char myname[] = "Pierre de Fermat";/* using memcpy to copy string: */memcpy(person.name, myname, strlen(myname) + 1);person.age = 46;/* using memcpy to copy structure: */memcpy(&person_copy, &person, sizeof(person));printf("person_copy: %s, %d \n", person_copy.name, person_copy.age);return 0;
}

在这里插入图片描述
库中声明如下

void* __cdecl memcpy(_Out_writes_bytes_all_(_Size) void* _Dst,_In_reads_bytes_(_Size)       void const* _Src,_In_                          size_t      _Size);
  1. 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
  2. 这个函数在遇到 ‘\0’ 的时候并不会停下来。
  3. 如果source和destination有任何的重叠,复制的结果都是未定义的。

2.2 memmove

头文件<string.h>

移动内存块

将字节数的值从源指向的位置复制到目标指向的内存块。复制就像使用了中间缓冲区一样,允许目标和源重叠。

源指针和目标指针指向的对象的基础类型与此函数无关;结果是数据的二进制副本。

该函数不检查源中的任何终止空字符 - 它总是准确地复制字节数。

为避免溢出,目标参数和源参数指向的数组的大小应至少为字节数。

int main()
{char str[] = "memmove can be very useful......";memmove(str + 20, str + 15, 11);puts(str);return 0;
}

在这里插入图片描述
库中声明如下

_VCRTIMP void* __cdecl memmove(_Out_writes_bytes_all_opt_(_Size) void*       _Dst,_In_reads_bytes_opt_(_Size)       void const* _Src,_In_                              size_t      _Size);
  1. 和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
  2. 如果源空间和目标空间出现重叠,就得使用memmove函数处理。

2.3 memcmp

头文件<string.h>

比较两个内存块

将 ptr1 指向的内存块的前 num 字节数与 ptr2 指向的第一个字节数进行比较,如果它们都匹配,则返回零,如果不匹配,则返回一个不同于零的值,表示哪个值更大。 请注意,与 strcmp 不同,该函数在找到空字符后不会停止比较。

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

在这里插入图片描述
库中声明如下

int __cdecl memcmp(_In_reads_bytes_(_Size) void const* _Buf1,_In_reads_bytes_(_Size) void const* _Buf2,_In_                    size_t      _Size);
  1. 比较从ptr1和ptr2指针开始的num个字节
  2. 返回值
    返回一个整数值,该值指示内存块内容之间的关系:
    在这里插入图片描述

3.函数的模拟实现

3.1 模拟实现strlen

三种方式:
方式1:

//计数器方式
int my_strlen(const char * str)
{int count = 0;while(*str){count++;str++;}return count;
}

方式2:

//不能创建临时变量计数器
int my_strlen(const char * str)
{if(*str == '\0')return 0;elsereturn 1+my_strlen(str+1);
}

方式3:

//指针-指针的方式
int my_strlen(char *s)
{char *p = s;while(*p != ‘\0)p++;return p-s;
}

3.2 模拟实现strcpy

参考代码:

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

3.3 模拟实现strcat

参考代码:

char *my_strcat(char *dest, const char*src)
{char *ret = dest;assert(dest != NULL);assert(src != NULL);while(*dest){dest++;}while((*dest++ = *src++)){;}return ret;
}

3.4 模拟实现strstr

char* strstr(const char* str1, const char* str2)
{char* cp = (char*)str1;char* s1, * s2;if (!*str2)return((char*)str1);while (*cp){s1 = cp;s2 = (char*)str2;while (*s1 && *s2 && !(*s1 - *s2))s1++, s2++;if (!*s2)return(cp);cp++;}return(NULL);
}

3.5 模拟实现strcmp

参考代码:

int my_strcmp(const char* src, const char* dest)
{int ret = 0;assert(src != NULL);assert(dest != NULL);while (!(ret = *(unsigned char*)src - *(unsigned char*)dest) && *dest)++src, ++dest;if (ret < 0)ret = -1;else if (ret > 0)ret = 1;return(ret);
}

3.6 模拟实现memcpy

参考代码:

void* my_memcpy(void* dst, const void* src, size_t count)
{void* ret = dst;assert(dst);assert(src);while (count--) {*(char*)dst = *(char*)src;dst = (char*)dst + 1;src = (char*)src + 1;}return(ret);
}

3.7 模拟实现memmove

参考代码:

void* my_memmove(void* dst, const void* src, size_t count)
{void* ret = dst;if (dst <= src || (char*)dst >= ((char*)src + count)) {while (count--) {*(char*)dst = *(char*)src;dst = (char*)dst + 1;src = (char*)src + 1;}}else {dst = (char*)dst + count - 1;src = (char*)src + count - 1;while (count--) {*(char*)dst = *(char*)src;dst = (char*)dst - 1;src = (char*)src - 1;}}return(ret);
}

结语

有兴趣的小伙伴可以关注作者,如果觉得内容不错,请给个一键三连吧,蟹蟹你哟!!!
制作不易,如有不正之处敬请指出
感谢大家的来访,UU们的观看是我坚持下去的动力
在时间的催化剂下,让我们彼此都成为更优秀的人吧!!!
在这里插入图片描述

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

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

相关文章

单片机第一季:零基础4——LED点阵

1&#xff0c;第八章-LED点阵 如何驱动LED点阵&#xff1a; (1)单片机端口直接驱动。要驱动8*8的点阵需要2个IO端口&#xff08;16个IO口&#xff09;、要驱动16*16的点阵需要4个IO端口&#xff08;32个IO口&#xff09;。 (2)使用串转并移位锁存器驱动。要驱动16*16点阵只需要…

网安学习经历小记

明明自觉学会了不少知识&#xff0c;可真正开始做题时&#xff0c;却还是出现了“一支笔&#xff0c;一双手&#xff0c;一道力扣&#xff08;Leetcode&#xff09;做一宿”的窘境&#xff1f;你是否也有过这样的经历&#xff0c;题型不算很难&#xff0c;看题解也能弄明白&…

漏洞攻击 --- TCP -- 半开攻击、RST攻击

TCP半开攻击&#xff08;半连接攻击&#xff09; --- syn攻击 &#xff08;1&#xff09;定义&#xff1a; sys 攻击数据是DOS攻击的一种&#xff0c;利用TCP协议缺陷&#xff0c;发送大量的半连接请求&#xff0c;耗费CPU和内存资源&#xff0c;发生在TCP三次握手中。 A向B…

已解决 BrokenPipeError: [Errno 32] Broken pipe

作者主页&#xff1a;爱笑的男孩。的博客_CSDN博客-深度学习,活动,python领域博主爱笑的男孩。擅长深度学习,活动,python,等方面的知识,爱笑的男孩。关注算法,python,计算机视觉,图像处理,深度学习,pytorch,神经网络,opencv领域.https://blog.csdn.net/Code_and516?typeblog个…

zabbix server is not running错误解决方法

1.错误&#xff1a;zabbix server is not running 打开zabbix server的时候&#xff0c;底部飘着一行黄色的警告字 2.解决方法 (1)关闭selinux (2)查看日志文件 #tail -f /var/log/zabbix/zabbix_server.log 发现内存溢出了 __zbx_mem_realloc(): out of memory 那…

【小沐学C++】libcurl实现HTTP/HTTPS请求

文章目录 1、简介2、下载和编译2.1 下载2.2 编译2.3 使用 3、命令行测试3.1 获取文件头Headers3.2 请求内容Request Content3.3 响应内容Response Content3.4 GET请求3.5 POST请求3.6 其他 4、代码测试3.1 simple.c3.2 url2file.c3.3 simplepost.c3.4 resolve.c3.5 progressfun…

uniapp 集成 Android Studio 使用原生插件

uniapp 集成 Android Studio 使用原生插件 前期工作 下载 Android Studio下载 HbuilderX 对应的 App离线SDK 准备集成 打开选中项目选中其中的模块文件夹在该文件夹下的libs目录下添加需要使用的jar包&#xff08;一般是第三方设备平台提供&#xff09;在该文件夹下的src\m…

二、OAuth2 client对接Spring Authorization Server

这里用的是授权码模式 搭建&#xff1a;Spring Authorization Server 代码结构如下&#xff1a; 代码实现 添加依赖 <dependency> <groupId>org.springframework.boot</groupId> …

Spring WebFlux 实现原理与架构图

启动原理与架构图 通过spring-boot-autoconfigure中的spring.factories文件&#xff0c;通过Spring Boot自动初始化下列类&#xff1a;HttpHandlerAutoConfiguration、ReactiveWebServerFactoryAutoConfiguration、WebFluxAutoConfiguration、ErrorWebFluxAutoConfiguration、…

ARM day9 (按键中断控制led亮灭)

key.h #ifndef __KEY_H__ #define __KEY_H__#include "stm32mp1xx_gpio.h" #include "stm32mp1xx_rcc.h" #include "stm32mp1xx_uart.h" #include "stm32mp1xx_exti.h" #include "stm32mp1xx_gic.h"//事件号 #define EXTI_…

学无止境·MySQL④(多表查询)

多表查询试题 试题一1、创建表2、表中添加数据3、查询每个部门的所属员工4、查询研发部门的所属员工5、查询研发部和销售部的所属员工6、查询每个部门的员工数,并升序排序7、查询人数大于等于3的部门&#xff0c;并按照人数降序排序 试题一 1、创建表 use mydb3; – 创建部门…

NAT介绍

目录 NAT NAT的配置——配置位置都是在边界路由器的出接口上进行配置 静态NAT 动态NAT——多对多的NAT NAPT——easy IP 多对多的NAPT 端口映射——高级用法 NAT——网络地址转换 IPV4地址不够用 NAT ABC——三类地址中截取了一部分地址&#xff08;并且让这一部分地址可以重复…