字符函数和字符串函数详解
目录
- 前言
- 函数介绍及模拟
- strlen函数
- 模拟实现
- strcpy函数
- 模拟实现
- strncpy函数
- strcat函数
- 模拟实现
- strncat函数
- strcmp函数
- 模拟实现
- strncmp函数
- strstr函数
- 模拟实现
- memcpy函数
- 模拟实现
- memmove函数
- 模拟实现
- strtok函数
- strerror函数
- 字符分类函数
- 字符转换
前言
C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。
字符串常量适用于那些对它不做修改的字符串函数.
函数介绍及模拟
strlen函数
size_t strlen ( const char * str );
1.字符串以 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中’\0’ 前面出现的字符个数(不包含 ‘\0’ )。
2.参数指向的字符串必须要以 ‘\0’ 结束。
3.注意函数的返回值为size_t,是无符号的整数。
例:
#include <stdio.h>
#include<string.h>
int main()
{const char*str1 = "abcdef"; //常量字符串后面默认带了个'\0'const char*str2 = "bbb";if(strlen(str2)-strlen(str1)>0) // 6-3{printf("str2>str1\n");}else{printf("srt1>str2\n");}return 0;
}
模拟实现
size_t strlen ( const char * str );
#include<stdio.h>
size_t my_strlen(const char* str)
{const char* start = str;const char* end = str;while (*end != '\0'){end++;}return end - start; //指针相减等于两指针间的元素个数
}int main()
{char arr[] = "abcdef";int len = my_strlen(arr);printf("%d\n", len);return 0;
}
strcpy函数
char* strcpy(char * destination, const char * source );
注:前面的指针指向目标字符串,后面指针指向源字符串
1.源字符串必须以 ‘\0’ 结束。
2.将源指向的字符串复制到目标所指向的数组中,包括 ‘\0’ 字符(并在该点停止)。
3.目标空间必须足够大,以确保能存放源字符串。
4.目标空间必须可变。(常量字符串不能修改,目标空间不能是常量字符串)
#include<stdio.h>
#include<string.h>
int main()
{char arr[20] = "abcdef";char* m = "fff";strcpy(arr, m);printf("%s", arr); return 0;
}
输出:fff
模拟实现
char* strcpy(char * destination, const char * source );
函数返回值为目标字符串起始地址
char* my_strcpy(char* dest, const char* src)
{char* ret = dest;while (*dest++ = *src++); //后置加加,先解引用,再赋值,再加加,直到*dest=*src='\0',退出循环return ret;
}int main()
{char arr1[20] = "abc";char arr2[] = "hello hello";printf("%s\n", my_strcpy(arr1, arr2));return 0;
}
输出:hello hello
strncpy函数
char * strncpy ( char * destination, const char * source, size_t num );
1.拷贝num个字符从源字符串到目标空间。
2.如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。
我们可以看到,在拷贝完源字符串后又追加了3个0
模拟实现:
char* m_strncpy(char* destination, const char* source, size_t num)
{char* p = destination;int i = 1;while (*destination = *source){destination++;source++;i++;}destination++;num -= i;while (num--){*destination++ = '\0';}return p;
}
int main()
{char arr[20] = "abcdef";char* m = "abc";printf("%s", strncpy(arr, m, 6));return 0;
}
strcat函数
char * strcat ( char * destination, const char * source );
1.将源字符串的内容追加到目标字符串(包括 ‘\0’ ),目标中的 ‘\0’ 字符被源的第一个字符覆盖。
2.源字符串必须以 ‘\0’ 结束。
3.目标空间必须有足够的大,能容纳下源字符串的内容。
4.目标空间必须可修改。
例:
#include<stdio.h>
#include<string.h>
int main()
{char arr[20] = "abcdef";char* m = "abc";printf("%s", strcat(arr, m)); //函数返回值为目标字符串起始地址return 0;
}
输出:abcdefabc
模拟实现
char * strcat ( char * destination, const char * source );
#include<stdio.h>
char* m_strcat(char* destination, const char* source)
{char* p = destination;while (*destination++); destination--; //destination指向了\0后面的元素,所以--while (*destination++ = *source++);return p;
}int main()
{char arr[20] = "abcdef";char* m = "abc";printf("%s", m_strcat(arr, m)); return 0;
}
strncat函数
char * strncat ( char * destination, const char * source, size_t num );
1.将源的前 num 个字符追加到目标,外加一个终止空字符。
2.如果源中 C 字符串的长度小于 num,则仅复制到终止空字符之前的内容。
类似于前面的 strncpy 这里就不展开讲了。
strcmp函数
int strcmp ( const char * str1, const char * str2 );
1.第一个字符串大于第二个字符串,则返回大于0的数字
2.第一个字符串等于第二个字符串,则返回0
3.第一个字符串小于第二个字符串,则返回小于0的数字
如何判断两个字符串呢?
假设有如下两个字符串:
char* m = "abcdef";char* n = "abce";strcmp(m, n);
首先是两个字符串的第一个字符比较,很明显相等,再比较下一个字符,直到字符 ‘d’ 与 ‘e’ 比较,字符 ‘d’ 是小于 ‘e’ 的,所以m指向的字符串小于n指向的字符串。
#include<stdio.h>
#include<string.h>
int main()
{char* m = "abcdef";char* n = "abce";printf("%d", strcmp(m, n));return 0;
}
输出:-1
模拟实现
#include<stdio.h>
int m_strcmp(const char* str1, const char* str2)
{while (*str1 == *str2){if (*str1 == '\0')return 0;str1++;str2++;}if (*str1 > *str2)return 1;if (*str1 < *str2)return -1;
}
int main()
{char* m = "abcdef";char* n = "abca";printf("%d", m_strcmp(m, n));return 0;
}
输出:1
strncmp函数
int strncmp ( const char * str1, const char * str2, size_t num );
比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。
有兴趣可以自己实现一下。
strstr函数
char * strstr ( const char *str1, const char * str2);
1.返回指向 str1 中第一次出现的 str2 的指针,如果 str2 不是 str1 的一部分,则返回空指针。
2.匹配过程不包括终止空字符,但它在那里停止。
int main()
{char* m = "abcdef";char* n = "cde";printf("%s", strstr(m, n));return 0;
}
输出:cdef
模拟实现
char * strstr ( const char *str1, const char * str2);
char* m_strstr(const char* str1, const char* str2)
{const char* s1 = str1; //见下图,s1 标记目标字符串每次开始查找的首元素const char* s2 = str2; // s2 标记源字符串每次开始查找的首元素const char* p = str1; // p 标记每次从 s1 开始遍历的元素if (*str2 == '\0'){return str1;}while (*p){s1 = p;s2 = str2; //每次循环开始将 s1 移到后一个元素处,s2 移到首元素处while (*s1 != '\0' && *s2 != '\0' && (*s1 == *s2)){s1++;s2++;}if (*s2 == '\0') //当源字符串遍历完了后即找到了{return (char*)p; }p++;}return NULL; //找不到子串
}
int main()
{char* m = "abcdef";char* n = "cde";printf("%s", m_strstr(m, n));return 0;
}
当第一次没找到后,第二次循环 s1 、p指向如图:
若 p 指向 ‘\0’ ,则表示找不到该字符串,退出循环。
memcpy函数
void * memcpy ( void * destination, const void * source, size_t num );
1.函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
2.这个函数在遇到 ‘\0’ 的时候并不会停下来。
3.如果source和destination有任何的重叠,复制的结果都是未定义的。
int main()
{int arr[] = { 1,2,3,4,5,6,7,8,9,10 };int arr2[10] = { 0 };memcpy(arr2, arr, 20);return 0;
}
我们来看看内存分配情况:
由图可以看出memcpy函数改变了20字节的内容,也就是5个整型,改变了5个整型数,那么它是什么方式改变这20字节的呢?
从左到右一个字节一个字节的依次复制,直到20个字节。
模拟实现
void* m_memcpy(void* destination, const void* source, size_t num)
{void* p = destination;while (num--) //循环num次{*(char*)destination = *(char*)source; //char型访问一个字节,方便逐个字节打印destination = (char*)destination + 1;source = (char*)source + 1;}return p;
}
int main()
{int arr[] = { 1,2,3,4,5,6,7,8,9,10 };int arr2[10] = { 0 };m_memcpy(arr2, arr, 20);return 0;
}
memmove函数
不知道大家有没有想过,如果是自己复制自己,那memcpy为什么不能用呢?什么函数可以实现呢?
memcpy函数会改变目标数据,如果目标数据与源数据有重叠,可能导致
如下情况:
假设我要把1 2 3 4 复制到3 4 5 6 的位置上,那么从前往后就是如图,我们可以看到原来的3 4 变为了1 2 ,然后1 2 再复制到5 6 处,原来的3 4 5 6 就变为了1 2 1 2,这与我们要的结果不同,所以不能用该函数了,那么我们应该如何才能正确打印呢?
我们发现如果从后往前复制就可以很好的避免这种情况发生,即从两数据重叠处开始复制。而memmove函数就很好的解决了这点。
我们来看看memmove函数:
void * memmove ( void * destination, const void * source, size_t num );
1.和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
2.如果源空间和目标空间出现重叠,就得使用memmove函数处理
#include<stdio.h>
#include<string.h>
int main()
{int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };memmove(arr1 + 2, arr1, 20);return 0;
}
模拟实现
void* m_memmove(void* destination, const void* source, size_t num)
{void* p = destination;if (destination < source){while (num--){ //从前往后复制*(char*)destination = *(char*)source;destination = (char*)destination + 1;source = (char*)source + 1;}}else{while (num--){ //从后往前复制*((char*)destination+num) = *((char*)source+num);}}return p;
}
int main()
{int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };m_memmove(arr1 + 2, arr1, 20);return 0;
}
strtok函数
char * strtok ( char * str, const char * sep );
1.sep参数是个字符串,定义了用作分隔符的字符集合。
2.第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
3.strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
4.strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
5.strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。(该函数具有记忆功能)
6.如果字符串中不存在更多的标记,则返回 NULL 指针。
我们来具体看看:
#include<stdio.h>
#include<string.h>
int main()
{char arr[] = "zhangsan@baidu.com";char buf[200] = { 0 };strcpy(buf, arr); //将arr的内容复制到buf中const char* p = "@.";char* str = strtok(buf, p); //找到第第一个标记字符'@',并将其改为'\0',返回指向这个字符串的指针printf("%s\n", str); //打印: zhangsanstr = strtok(NULL, p); //从上次标记的'\0'开始查找下一个标记符号,重复以上操作printf("%s\n", str); //打印: baidustr = strtok(NULL, p);printf("%s\n", str); //打印: comreturn 0;
}
简化一下:
int main()
{char arr[] = "zhangsan@baidu.com";char buf[200] = { 0 };strcpy(buf, arr);const char* p = "@.";char* str = NULL;for (str=strtok(buf, p); str!=NULL; str=strtok(NULL, p)){printf("%s\n", str);}return 0;
}
strerror函数
char * strerror ( int errnum );
返回错误码,所对应的错误信息。
#include<stdio.h>
#include<string.h>
#include<errno.h>
int main()
{ //errno - C语言提供的全局的错误变量printf("%s\n", strerror(0));//printf("%s\n", strerror(1));//printf("%s\n", strerror(2));//printf("%s\n", strerror(3));//printf("%s\n", strerror(4));return 0;
}
输出:
其他的错误变量对应的错误码可以自己输出试试。
字符分类函数
返回真就是返回大于0的数,可用于条件判断。
字符转换
int tolower ( int c ); //大写转小写int toupper ( int c ); //小写转大写
我们来看下面一段小写转大写的代码:
#include<stdio.h>
#include <ctype.h>
int main()
{char arr[] = "Are you ok?";char* p = arr;while (*p){if (islower(*p)) //判断是否是小写{*p = toupper(*p); //小写转大写}p++;}printf("%s\n", arr);return 0;
}
输出:ARE YOU OK?
本希望本期内容对大家能有所帮助,下期见了~
字符函数和字符串函数详解相关推荐
- 字符数组和字符串的区别,C语言字符数组和字符串区别详解
C 语言中并不存在字符串这个数据类型,而是使用字符数组来保存字符串.那么,字符数组就一定是字符串吗? 不一定,字符数组和字符串千万不要混淆.字符串是一种特殊的字符数组,并且C语言提供了大量适用于字符串 ...
- php和c语言的字符数组中,字符数组和字符串的区别,C语言字符数组和字符串区别详解...
C 语言中并不存在字符串这个数据类型,而是使用字符数组来保存字符串.那么,字符数组就一定是字符串吗? 对于这个问题,大多教科书中的回答是"是".其实不然,字符数组和字符串是完全不相 ...
- 字符函数和字符串函数详解(二)strncpy strncat strncmp strstr strtok(及其模拟实现)
系列文章目录 字符函数和字符串函数详解(一)strlen strcpy strcat strcmp 字符函数和字符串函数详解(二)strncpy strncat strncmp strstr str ...
- C语言字符串库函数详解模拟实现(strlen、strcpy、strcat、strcmp)+字符操作函数+字符转换函数
字符串库函数详解 一.无字符串长度限制的字符串函数 1. strlen 计算字符串长度函数 2. strcpy 字符串拷贝函数 3. strcat 字符串追加函数 4. strcmp 字符串比较函数 ...
- 字符串拷贝函数:strcpy的详解及模拟实现
字符串拷贝函数:strcpy的详解及模拟实现!!! 对于字符串拷贝函数,之前在学习字符串时候,就已经学习过,但那只是片面的学习了一下,并没有经过系统的分析!只是大概的学习了一下!在关键的地方有时候还不 ...
- mysql strcmp s1 s2_MySQL函数基础——字符串函数详解
昨天,咱们对MySQL的数学函数进行了讲解,今天,咱们再来解析MySQL字符串函数. 字符串函数主要用来处理数据库中的字符串数据,MySQL中字符串函数有:计算字符串长度函数.字符串合并函数.字符串替 ...
- mysql函数编写格式_MySQL函数基础——字符串函数详解
昨天,咱们对MySQL的数学函数进行了讲解,今天,咱们再来解析MySQL字符串函数. 字符串函数主要用来处理数据库中的字符串数据,MySQL中字符串函数有:计算字符串长度函数.字符串合并函数.字符串替 ...
- php。defined,PHP defined()函数的使用图文详解
PHP defined()函数的使用图文详解 PHP defined() 函数 例子 定义和用法 defined() 函数检查某常量是否存在. 若常量存在,则返回 true,否则返回 false. 语 ...
- Delphi Format函数功能及用法详解
DELPHI中Format函数功能及用法详解 DELPHI中Format函数功能及用法详解function Format(const Format: string; const Args: array ...
- python实现排序函数_Python排序函数的使用方法详解
Python排序函数完美体现了Python语言的简洁性,对于List对象,我们可以直接调用sort()函数(这里称为"方法"更合适)来进行排序,而对于其他可迭代对象(如set,di ...
最新文章
- DoS***原理和防御方法
- Django_ORM数据表查询总结
- vim自动跳转到引用的函数
- 多进程减少多个文件的内存占用
- M理论能否成为解释一切的“万有理论”?
- 三角形垂点坐标js算法(三点定圆求圆心)
- Centos7安装32位库用来安装32位软件程序
- pmp 资料_1年 = 15300订阅 + 超100万次收听 (感恩有您,这些PMP备考资料您值得拥有!)...
- 李宏毅机器学习笔记第5周_逻辑回归
- 启动计算机管理服务,win10系统打开服务管理器的五种方法
- TextView 设置显示省略号
- python网络爬虫之淘宝订单提取
- Java中正则表达式的基本使用
- Facebook产品功能以及营销
- scratch设计跑酷游戏_我如何使用Scratch设计游戏
- OpenGL应用:天空盒子(SkyBox)
- freetype 使用解析---矢量字体
- [数学建模(四)]MATLAB神经网络工具箱的简单应用
- 【网安神器篇】——enum4linux枚举工具
- 从顶会论文看2022年推荐系统序列建模的趋势
热门文章
- API_2 安装详细流程
- Ruby 学习(六)数组Array
- Charles抓取微信小程序https请求(附注册激活码教程)
- 论文阅读:Deep Learning–Based Segmentation andQuantification in Experimental Kidney Histopathology
- sikuli python java_Sikuli 基于图形识别的自动化测试技术
- shell dd命令在bs参数太大的时候出现异常的解决方法
- js---BOM 的理解方法
- 树莓派Pi Pico套件 MicroPython编程
- android hprof,Android Hprof 分析
- 图论(十)——欧拉图和哈密尔顿图