🍁🍁🍁 猛戳订阅 👉 详解数据结构专栏 👈 深度解析 🍁🍁🍁
📋个人简介
💬大家好,我是_奇奇,一名C/C++博主。河牧院大一在读。
🔔欢迎一起交流学习
💬我会将大一学的数据结构和C语言深度解析写成笔记记录下来。后期会慢慢推进。感兴趣可以订阅以下专栏。
📌详解数据结构专栏🍁
📌深度理解C语言专栏🍁
🔑 个人主页🌳
💡编程的前途是光明的,道路是曲折的。
这不是目录
📋个人简介
❄️前言
字符串函数
📌strlen
函数模拟
📌strcpy
函数模拟
📌strcat
函数模拟
📌strcmp
函数模拟
📌strncpy
📌strncat
📌strstr
函数模拟
📌strtok
📌streeor
📌字符分类函数
内存函数
📌memcpy
函数模拟实现
📌memmove
函数模拟
📌memcmp
❄️前言
C语言中对字符和字符串的处理很是频繁,不仅如此在以后的校招面试中字符串的题目也会涉及不少,这点在LeetCode中就能体现到,在刷题时经常会遇到处理字符串的内容,字符串还和指针相关联,对字符串的深入理解也会增加对指针的理解。
字符串函数
📌strlen
❄️功能:求字符串长度
❄️函数原型
size_t strlen ( const char* str );
🍁注意:
- 字符串以 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中 ‘\0’ 前面出现的字符个数(不包含 ‘\0’ )。
- 参数指向的字符串必须要以 ‘\0’ 结束。
- 注意函数的返回值为
size_t
,是无符号整型,也就是不是负数。(易错!!!
)
函数模拟
1.指针-指针的方式
注意:这种方式只适用于同一个数组
int my_strlen(char *s) { char *p = s; while(*p != ‘\0’ ) p++; return p-s; }
2.计数器方法
int my_strlen(const char* str) { int count = 0; while(*str) { count++; str++; } return count; }
3.递归方法(面试官最爱考
)
//不能创建临时变量计数器 int my_strlen(const char * str) { if(*str == '\0') return 0; else return 1+my_strlen(str+1); }
📌strcpy
❄️函数功能:拷贝源字符串到目的地空间,包括"\0"也拷贝过去
。
❄️函数原型
char *strcpy( char *strDestination, const char *strSource );
🍁注意:
- 源字符串必须以 ‘\0’ 结束。
- 会将源字符串中的 ‘\0’ 拷贝到目标空间。
- 目标空间必须足够大,以确保能存放源字符串。
- 目标空间必须可变,
原字符串不可变!
函数模拟
注意点:
- assert断言
- const修饰
char *my_strcpy(char *dest, const char*src) { char *ret = dest; assert(dest != NULL); assert(src != NULL); while((*dest++ = *src++)) { ; } return ret; }
📌strcat
❄️函数功能:
- 将源字符串和目标字符串连接起来。
❄️函数原型
char * strcat ( char * destination, const char * source );
🍁注意:
- 源字符串必须以 ‘\0’ 结束。
- 目标空间必须有足够的大,能容纳下源字符串的内容。
- 目标空间必须可修改。
- 返回的是目标字符串的首元素的地址。
函数模拟
注意:
后置++是先使用后++。
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; }
📌strcmp
❄️函数功能:
比较两个字符串的大小,按照每个字符的ASCII码值依次往后比较,一个字符一个字符的比较,只要遇到字符不同,就返回。
❄️函数原型
char * strncpy ( char * destination, const char * source, size_t num );
🍁注意:
并不是大于返回1,小于返回-1,没有这个规定
规定如下
- 第一个字符串大于第二个字符串,则返回大于0的数字
- 第一个字符串等于第二个字符串,则返回0
- 第一个字符串小于第二个字符串,则返回小于0的数字
函数模拟
🍁注意;
while的循环条件&&操作符,左操作数为假时,有操作数就不用执行了。
int my_strcmp (const char * src, const char * dst) { int ret = 0 ; assert(src != NULL); assert(dest != NULL); while( ! (ret = *(unsigned char *)src- *(unsigned char *)dst) && *dst) { ++src; ++dst; } if ( ret < 0 ) ret = -1 ; else if ( ret > 0 ) ret = 1 ; return( ret ); }
📌strncpy
❄️函数功能:
拷贝num个字符从源字符串到目标空间。
❄️函数原型
char * strncpy ( char * destination, const char * source, size_t num );
🍁注意:
- 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。
- 目标空间必须足够大。
- 和strcpy函数唯一区别就是拷贝的个数受限制。
📌strncat
❄️函数功能:
将num个源字符串和目标字符串连接起来。
❄️函数原型
char * strncat ( char * destination, const char * source, size_t num );
🍁注意:
- 目标字符串足够大
📌strstr
函数功能:
在目标字符串str1里找到源字符串,并返回这个源字符串的首元素的地址。
函数原型
char * strstr ( const char *str1, const char * str2);
🍁注意:
- 重点在函数模拟的实现
函数模拟
- 如果str2为空,不用照了。直接返回str1。
如图包含了找字符串的所有情况。
思考问题要考虑全面。
我认为下面代码是最优的代码了。但是需要自己细看
。描述的话太麻烦了。
#include<stdio.h> #include<assert.h> char* my_strstr(const char* str1, const char* str2) { assert(str1 && str2); if (!*str2) { return((char*)str1); } char* cp = str1; char* s1 = 0; char* s2 = 0; while (*cp != '\0') { s1 = cp; s2 = str2; while (*s2 != '\0' && *s1 != '\0' && *s2 == *s1) { s1++; s2++; if (!*s2) { return cp; } } cp++; } return NULL; } int main() { char arr[] = "hworld"; char arr1[] = "world"; char* p = my_strstr(arr, arr1); printf("%s", p); return 0; }
📌strtok
❄️函数功能:将字符串分割。
❄️函数原型
char * strtok ( char * str, const char * sep );
🍁
注意
sep参数是个字符串,定义了用作分隔符的字符集合
第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记
strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
如果字符串中不存在更多的标记,则返回 NULL 指针。
如图例子
📌streeor
❄️函数功能:返回错误码所对应的错误信息。
❄️函数原型
char * strerror ( int errnum );
例子
/* strerror example : error list */ #include <stdio.h> #include <string.h> #include <errno.h>//必须包含的头文件 int main () { FILE * pFile; pFile = fopen ("unexist.ent","r"); if (pFile == NULL) printf ("Error opening file unexist.ent: %s\n",strerror(errno)); //errno: Last error number return 0; } Edit & Run
📌字符分类函数
内存函数
📌memcpy
❄️函数功能:
- 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置
❄️函数原型
void * memcpy ( void * destination, const void * source, size_t num );
🍁注意:
注意是以字节为单位的拷贝。
这个函数在遇到 ‘\0’ 的时候并不会停下来。
如果source和destination有任何的重叠,复制的结果都是未定义的。
函数模拟实现
- 因为不知道数据是哪种类型所以需要
进行强制类型转换
。
void * 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); }
📌memmove
❄️函数功能:
和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
❄️函数原型
void * memmove ( void * destination, const void * source, size_t num );
🍁注意:
- 如果源空间和目标空间出现
重叠
,就得使用memmove函数处理。
函数模拟
🍁注意
- 如果src > dest ,或者det > src+ count 从
前往后拷贝
就可以了 - 否则
从后往前拷贝
- 不遵循这个规则的话就会覆盖,导致拷贝错误。
void* 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); }
📌memcmp
❄️函数功能:
- 比较从ptr1和ptr2指针开始的num个字节
- 和strcmp函数功能相同。
❄️函数原型
int memcmp ( const void * ptr1,
const void * ptr2,
size_t num );
函数返回值