目录
strlen
模拟实现
长度不受限字符串函数
strcpy
模拟实现
编辑 strcat
模拟实现
strcmp
模拟实现
长度受限字符串函数
strncpy
模拟实现
strncat
strncmp
strstr
模拟实现
strtok
strerror
perror
字符分类函数
字符转换
示例:
编辑内存函数
memcpy
模拟实现
memmove
模拟实现
memcmp
memset
strlen
功能:统计字符串中\0之前的字符个数。
注意:如果没有\0则返回一个随机值。
易错:
strlen的返回值为size_t,也就是无符号整形,使用时要注意这点。
模拟实现
#include <assert.h>
size_t my_strlen(const char* str)
{assert(str);const char* start = str;const char* end = str;while (*end != '\0'){end++;}return end - start;
}
长度不受限字符串函数
strcpy
功能:将一个源头字符串拷贝到一个目标字符串中
注意:如果源头字符串没有\0则会报错。目标空间要足够大,且可变。
易错:
char* p = "hello";//常量字符串char arr[] = "world";strcpy(p, arr);
模拟实现
char* my_strcpy(char* dest, const char* src)
{assert(dest);assert(src);char* str = dest;while (*dest++ = *src++)\\取巧写法,注意后置++不会对程序产生影响{;}return str;//能够被接收
}
测试结果:
strcat
功能:在一个字符串后面追加另一个字符串。
注意:目标空间要足够大,且可变。都需要包含\0。
易错:
追加位置在\0处:
模拟实现
char* my_strcat(char* dest, const char* src)
{assert(dest);assert(src);char* str = dest;while (*dest)//\0处追加,循环体内++{dest++;}while (*dest++ = *src++){;}return str;
}
注意不要利用strcat去追加自身,否则会造成死循环!
strcmp
功能:比较字符串对应位置的ascii码大小
注意:都要包含\0。
模拟实现
//写法1
int my_strcmp(const char* s1, const char* s2)
{assert(s1 && s2);while (*s1 == *s2){if (*s1 == '\0')//全等{return 0;}s1++;s2++;}if (*s1 > *s2)return 1;else return -1;
}
//写法2
int my_strcmp(const char* s1, const char* s2)
{assert(s1 && s2);while (*s1 == *s2){if (*s1 == '\0'){return 0;}s1++;s2++;}return *s1 - *s2;
}
长度受限字符串函数
strncpy
模拟实现
模拟实现一次,后续不再模拟实现。
具体逻辑是用一个无符号整数加入循环条件,如果需要拷贝的源头字符串超过了自身长度可以考虑给多余的地方拷贝\0。
char* my_strncpy(char* dest, const char* src,size_t size)
{assert(dest);assert(src);char* str = dest;while (size && (*dest++ = *src++))//=优先级最低,所以加(){size--;}if(size) //超出源头字符串长度while (--size){*dest++ = '\0';size--;}return str;
}
strncat
将n个子串拷贝到目标字符串后,自动补\0,可以自己增添自己。
strncmp
比较n个子串的大小,大于返回正数,小于返回负数,等于返回0。
strstr
查找一个字符串里的子串,返回首次匹配所有子串的目标字符串中相应字符串的首地址。
模拟实现
法一:我的逻辑是能不能用strncmp负责比较字符串,用一个指针去遍历目标数组,注意结束条件为\0。但这样做有个漏洞,一次性比较多个字符串势必有越界的情况,我们以\0为突破口,能不能用它们的地址进行比较?于是思路就出现了。
char* my_strstr(const char* str1, const char* str2)
{assert(str1);assert(str2);const char* ptr = str1;int len1 = strlen(str1);int len2 = strlen(str2);while (strncmp(str1, str2, len2)!=0 && str1){if (str1 + len2 > ptr + len1){return NULL;}str1++;}return (char*)str1;
}
法二:四指针遍历法。两指针负责移动,两指针负责记录。
char* my_strstr(const char* str1, const char* str2)
{assert(str1);assert(str2);const char* p = str1;const char* src = str2;while (*p){str1 = p;str2 = src;while (*str1 != '\0' && *str2 != '\0' && * str1 == *str2)//注意\0{str1++;str2++;}if (*str2 == '\0'){return (char*)p;}p++;}return NULL;
}
strtok
功能:分隔字符串,strtok函数找到str的下一个标记,并将其用\0替换,返回一个指向这个标记的指针。
注意:strtok会改变原数据的内容!一般修改临时拷贝的内容。
使用方法:
- 第一个参数传字符串,第二个参数传字符串里分隔符
- 第一个参数首次传递不为空,通过strtok函数找到指定分隔符位置后,将其替换成\0并记录下它的地址再返回回去,后面第一个参数一律为NULL(否则重置),从被保存的位置开始查找下一个分隔符所在位置。
- 如果本次找不到分隔符,就返回NULL。
测试:
发现确实实现了分割并返回,但并不完全,我们可以写个循环:
strerror
功能:当函数调用失败时,将错误码(errno)转换成对应的错误信息或指向对应的地址。
perror
等价于printf + strerror函数,如需打印可以使用这个函数。
字符分类函数
int isalnum(int c):检查字符是否为数字或字母;(0~9,a~z,A~Z)
int isalpha(int c):检查字符是否为字母;(a~z, A~Z)
int iscntrl(int c):检查字符是否为控制字符;(八进制000~037以及177的字符)
int isdigit(int c):检查字符是否为十进制数字;(0~9)
int isgraph(int c):检查字符是否为图形表示,依赖于使用语言的环境;0~9,a~z,A~Z,以及标点符号)
int islower(int c):检查字符是否为小写的字母;(a~z)
int isprint(int c):检查字符是否为可打印的;(数字、字母、标点符号、空白字符)
int ispunct(int c):检查字符是否为标点符号;(! ” # $ % & ’ ( ) * + , - . / : ; < = > ? @ [ ] ^ _ ` { | } ~等)
int isspace(int c):检查字符是否为空白字符;(TAB、换行、垂直TAB、换页、回车、空格)
int isupper(int c):检查字符是否为大写字母;(A~Z)
int isxdigit(int c):检查字符是否为十六进制数字;(0 1 2 3 4 5 6 7 8 9 A B C D E F a b c d e f)
字符转换
int tolower(int c):转化字符为小写字母;
int toupper(int c):转化字符为大写字母;
示例:
内存函数
memcpy
功能: 将一个内存空间的数据按字节复制到另一块空间。
模拟实现
void* my_memcpy(void* dest, void* src, size_t num)
{void* ret = dest;assert(dest);assert(src);while(num--){*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;}return ret;
}
memmove
使用memcpy拷贝空间出现重叠时分两种情况:
memcpy显然只能从一方进行拷贝,如果出现另一种情况,则会存在内存覆盖现象。而memmove的作用就是对重叠空间进行拷贝。(有些编译器memcpy可以实现重叠拷贝)
字符串重叠空间拷贝用memmove代替strncpy。
模拟实现
void* my_memmove(void* dest, void* src, size_t num)
{assert(dest);assert(src);//(char*)dest++;不能这样写,强制暂时改变了类型,void*无法自增void* ret = dest;//从前往后if (src > dest){while (num--){*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;}}//从后往前else{while (num--){*((char*)dest+num) = *((char*)src+num);}}return ret;
}
测试:
memcmp
内存比较函数,不多赘述。
memset
按字节初始化内存空间。
测试:
由于小端的存储方式,我们修改前9个字节实则将前3个整形变量修改成了0。