【C进阶】字符串函数
创始人
2024-06-03 10:54:50
0

文章目录

  • 前言
  • 字符串函数
    • strlen
      • 模拟实现strlen
    • strcpy
      • 模拟实现strcpy
    • strcat
      • 模拟实现strcat
    • strcmp
      • 模拟实现strcmp
    • strncpy
      • 模拟实现strncpy
    • strncat
      • 模拟实现strncat
    • strncmp
      • 模拟实现strncmp
    • strstr
      • 模拟实现strstr
    • strchr
      • 示例
      • 模拟实现strchr
    • strrchr
      • 示例
      • 模拟实现strrchr
    • strtok
      • 示例
    • strerror
      • 示例
    • perror
      • 示例

前言

C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者 字符数组中。
字符串常量适用于那些对它不做修改的字符串函数
本章的主要内容为介绍常见字符串函数以及模拟实现

  • strlen
  • strcpy
  • strcat
  • strcmp
  • strncpy
  • strncmp
  • strncat
  • strstr
  • strchr
  • strrchr
  • strtok
  • strerror

字符串函数

strlen

在这里插入图片描述

  1. strlen函数是求字符串的长度,结束符标志着字符串的终止,并且字符串长度不包括结束符
int main()
{char str[] = "abc\0ab";printf("%zd\n", strlen(str));//统计'\0'前面的字符个数
}
  1. 返回值为unsigned int类型,与整形比较时整形会进行算术转换提升为unsigned int
    在这里插入图片描述
    i被当作uint类型处理,因此-1的补码被解读为无符号数

模拟实现strlen

1.计数器

size_t my_strlen(const char* pstr)
{int cnt = 0;while (*pstr++) cnt++;return cnt;
}

2.指针-指针

size_t my_strlen(const char* pstr)
{
assert(pstr);
char* cur = pstr;
while (*cur++);return cur - 1 -pstr;
}

3.递归

size_t my_strlen(const char* pstr){assert(pstr);if (*pstr) return 1 + my_strlen(pstr + 1);else return 0;
}

strcpy

在这里插入图片描述

  • 源字符串必须以 ‘\0’ 结束。
  • 会将源字符串中的 ‘\0’ 拷贝到目标空间。
  • 目标空间必须足够大,以确保能存放源字符串。
  • 目标空间必须可变
    在这里插入图片描述看见将src数组中的‘\0’之前包括’\0’都拷贝进dest数组中

目标空间必须可变
在这里插入图片描述
pdest指向的是数据段上的字符串常量,字符串常量不可以修改,因此目标空间是不可变得,无法使用strcpy函数

目标空间必须足够大
在这里插入图片描述
dest得空间只有7个字节,但是src得空间有9个字节,拷贝时会提示非法访问dest周围的空间

模拟实现strcpy

char* my_strcpy(char* dst, const char* src)
{char* cur = dst;assert(dst && src);while (*dst++ = *src++);return cur;
}

strcat

在这里插入图片描述

  • 源字符串必须以 ‘\0’ 结束。
  • 目标空间必须有足够的大,能容纳下源字符串的内容。
  • 目标空间必须可修改。
  • 不可以自己追加自己(使用strncat追加自己)

在这里插入图片描述

原字符串必须以’\0’结束
在这里插入图片描述
strcpy函数需要将src数组结束符前所有字符包括结束符拼接到dest数组中,所以需要给src数组定义结束符

目标空间必须足够大
同strcpy,目标空间不足够容纳时会发生非法访问

目标空间必须可以修改
同strcpy,目标空间指向常量字符串时不可以使用strcat函数

不可以自己追加自己
在这里插入图片描述
在这里插入图片描述
当追加第一个dest元素’a’时,‘a’会覆盖dest的结束符’\0’,后续dest数组中不在存在结束符’\0’,因此strcat函数会重复追加a b c d e f再dest数组后面,始终没有结束符,最后会导致越界非法访问

在某些编译器上,可能使用strcat函数可以实现自己追加自己(如MSVC),但是标准C没有规定可以这么做,所以取决于编译器自己,这样的程序可能在不同编译器上的结果不同,可移植性不高

模拟实现strcat

char* my_strcat(char* dest, const char* src)
{assert(dest && src);char* cur = dst;while (*dest++);dest--;while (*dest++ = *src++);return cur;
}

strcmp

在这里插入图片描述
在这里插入图片描述
标准规定:

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

strcmp比较的是2个字符串的大小,不是字符串的长度
标准只规定str1>str2时返回>0的数,str1,并没有规定返回具体的数,所以再判断字符串大小时应当用函数的返回值是否>0或者<0带判断而不是返回值是否等于某个具体的数

在这里插入图片描述

模拟实现strcmp

int my_strcmp(const char* str1, const char* str2)
{assert(str1 && str2);while (*str1 && *str2){if (*str1 != *str2)return *str1 - *str2;else{str1++;str2++;}}return *str1 - *str2;
}

上面介绍的函数strcat,strcpy都有越界的风险,因此在很多情况下,认为它们是不安全的,我们下面来介绍几个相对来说更加安全的函数

strncpy

在这里插入图片描述

  • 拷贝num个字符从源字符串到目标空间。
  • 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。
  • 如果源字符串的长度大于num,则destination的默认不会默认添加结束符

num>src的大小,不足用’\0’补
在这里插入图片描述
num 在这里插入图片描述

模拟实现strncpy

char* my_strncpy(char* dest, const char* src, size_t num)
{assert(dest && src);char* cur = dest;size_t len = strlen(src);//num大于src的大小if (num > len){for (size_t i = 0; i < num && *src != 0; i++){*dest++ = *src++;}for (size_t i = 0; i < num - len; i++) *dest++ = '\0';}//num小于src的大小else{for (size_t i = 0; i < num; i++){*dest++ = *src++;}}return cur;
}

strncat

在这里插入图片描述

  • 在dest数组后面追加num个src里的元素,并且在追加的最后一个元素后面加上终止符
  • 如果src的个数小于num个,那么只会追加src大小的长度

会在追加最后一个元素的后面加上结束符
在这里插入图片描述
src的个数不足num个,只追加src个
在这里插入图片描述

因为strncat会根据num的个数在最后追加的字符后面加上结束符,所以用strncat追加自己并不会因为dest数组中的结束符被覆盖而导致程序崩溃,自己追加自己时应使用strncat函数

模拟实现strncat

char* my_strncat(char* dest, const char* src, size_t num)
{assert(dest && src && num > 0);char* cur = dest;while (*dest++);dest--;while (num-- && (*src)){*dest++ = *src++;}*dest = '\0';return cur;
}

strncmp

在这里插入图片描述
在这里插入图片描述
函数和功能strcmp类似,只不过比较的是前num个元素

//strncmp example
//寻找具有相同的前缀
int main()
{char str[][5] = { "R2D2", "C3PO", "R2A6" };int n;puts("Looking for R2 astromech droids...");for (n = 0; n < 3; n++){if (strncmp(str[n], "R2xx", 2) == 0){printf("Found %s\n", str[n]);}}return 0;
}

模拟实现strncmp

int my_strncmp(const char* str1, const char* str2, size_t num)
{assert(str1 && str2);while (num--){if (*str1 == '\0' || *str2 == 0)return *str1 - *str2;else if (*str1 != *str2){return *str1 - *str2;}else{str1++;str2++;}}return *--str1 - *--str2;
}

strstr

在这里插入图片描述
strstr在C语言中是第二种形式,第一种形式是C++的重载

  • strstr返回str2在str1中第一次出现的位置
  • 如果找不到str1中的str2则返回NULL
/* strstr example */
/*此示例在 str 中搜索“simple”子字符串并将该词替换为“sample”*/
int main()
{char str[] = "This is a simple string";char* pch;pch = strstr(str, "simple");if (pch != NULL)strncpy(pch, "sample", 6);puts(pch);return 0;
}

模拟实现strstr

//方法1:借助strncmp
char* my_strstr(const char* str, const char* find)
{assert(str && find);while (*str){if (strncmp(str, find, strlen(find)) == 0){return str;}else str++;}return NULL;
}//方法2
char* my_strstr(const char* str, const char* find)
{assert(str && find);char* str1 = (char*)str;char* str2 = (char*)find;char* cur = (char*)str;while (*str1){//有可能匹配if (*str1 == *str2){cur = str1;//记录可能的返回位置while (*str1 == *str2){str1++;str2++;if (*(str2 + 1) == '\0')//结束符之前的位置匹配成功return cur;}//匹配失败str1 = cur + 1;//str1回到前面的位置str2 = find;//str2回到前面的位置}//没有可能匹配else{str1++;}}return NULL;
}

strchr

在这里插入图片描述
C语言中只支持第二种函数原型

  • strchr返回的是第一次出现的位置

示例

/*strchr example*/
/*用来寻找字符串中所有s出现的位置*/
int main()
{char str[] = "This is a sample string";char* pch;printf("Looking for the 's' character in \"%s\"...\n", str);pch = strchr(str, 's');while (pch != NULL){printf("found at %d\n", pch - str + 1);pch = strchr(pch + 1, 's');}return 0;
}

模拟实现strchr

char* my_strchr(const char* str, int character)
{while (*str){char* cur = str;if (*str == character){return cur;}str++;}if (character == '\0')return str;return NULL;
}

strrchr

在这里插入图片描述
strrstr在C语言中是第二种形式

  • strrchr返回的是字符在数组中最后一次出现的位置

示例

/* strrchr example */
//寻找字符最后一次出现的位置
int main()
{char str[] = "This is a sample string";char* pch;pch = strrchr(str, 's');printf("Last occurence of 's' found at %d \n", pch - str + 1);return 0;
}

模拟实现strrchr

char* my_strrchr(const char* str, int character)
{int len = strlen(str);char* cur = str + len;while (cur - len >= 0){if (*cur == character)return cur;cur--;}return NULL;
}

strtok

在这里插入图片描述

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

示例

在这里插入图片描述

函数较复杂,这里不再模拟,以后有机会的话在出文章专门实现strtok

strerror

在这里插入图片描述

  • 函数获取指向错误消息字符串的指针
  • 根据错误码的值生成一个带有描述错误条件的消息的字符串,就好像由库的函数设置为 errno 一样
  • strerror 产生的错误字符串可能特定于每个系统和库实现。
    在这里插入图片描述
  • errno是定义在头文件errno.h中的变量,用于接收库函数的错误码
  • errno的值在程序开始时默认为0(因此错误码为0代表没有错误),后续由库函数修改
  • errno的值总是最后一个错误码的值

示例

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

在这里插入图片描述

错误的原因是当前路径地下没有对应的文件,因此库函数fopen打开文件失败,此时会生成一个错误码,错误码的值赋给errno,这是系统完成的,然后通过strerrorerrno的值解读为错误的原因

perror

在这里插入图片描述

  • 如果参数 str 不是空指针,则打印 str 后跟一个冒号和一个空格。然后,无论 str 是否为空指针,都会打印生成的错误描述,后跟换行符 (‘\n’)。

可以认为perrorprintf函数加上strerror函数

示例

在这里插入图片描述

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...