c语言|C语言中的字符串函数和字符函数

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

size_t strlen (const char* str);
1.字符串是用'\0'作为结束标志的,strlen函数返回是的从给定位置开始到'\0'之间的字符个数,不包括'\0';
2.参数指向的字符串必须要以'\0'结束,否则可能会出现越界访问等情况;
3.strln函数的返回值是size_t,是无符号整型;
使用:
#include #include int main() { char* arr = "abcdef"; char arr2[] = "abcdefgh"; int len = strlen(arr); int len2 = strlen(arr2); printf("%d\n", len); printf("%d\n", len2); return 0; }

记得要引头文件
下面这种情况也要注意,因为strlen的返回值类型是size_t的,所以最后得到的是一个很大的数,而不是-2
c语言|C语言中的字符串函数和字符函数
文章图片

接下里来模拟实现一下strlen函数

模拟实现:
1.计数器的方法:
#include size_t my_strlen(const char* str) { assert(str); size_t count = 0; while (*str++) { count++; } return count; }

2.递归的方法:
#include size_t my_strlen(const char* str) { assert(str); if (*str == '\0') { return 0; } return 1 + my_strlen(++str); }

3.指针-指针的方法:
#include size_t my_strlen(const char* str) { assert(str); const char* p = str; while (*str) { str++; } return str - p; }

2.strcpy
char* strcpy(char* destination, const char* source);
1.源字符串必须以'\0'结束;
2. strcpy函数会将源字符串中的'\0'拷贝到目标空间;
3.目标空间必须足够大,以确保放得下源字符串,否则可能会越界访问;
4.目标空间必须可以被改变,如果是常量字符串则不能被改变;
int main() { char* str1 = "abcdef"; char* str2 = "abcde"; char* ret = strcpy(str1, str2); printf("%s\n", ret); return 0; }

像这种情况,str1所指向的字符串是不能被改变的
使用:
c语言|C语言中的字符串函数和字符函数
文章图片

strcpy返回值是个char*类型的,是为了能够实现链式访问,即一个函数的返回值作为另一个函数的参数。
如:
int main() { char arr1[] = "qwertyuiop"; char arr2[] = "abc"; printf("%s\n", strcpy(arr1, arr2)); return 0; }

模拟实现:
char* my_strcpy(char* dest, const char* src) { assert(dest && src); char* cur = dest; while (*src) { *dest = *src; dest++; src++; } *dest = *src; return cur; }

可以对代码进行简化:
char* my_strcpy(char* dest, const char* src) { assert(dest && src); char* cur = dest; while (*dest++ = *src++) { ; } return cur; }

3.strcat
char* strcat(char* destination, const char* source);
1.源字符串必须以'\0'结束;
2.目标空间必须足够大,可以容纳得下源字符串的内容‘
3.目标空间必须可以被改变;
使用:
c语言|C语言中的字符串函数和字符函数
文章图片

模拟实现:
从目标字符串的'\0'位置上开始追加源字符串
char* my_strcat(char* dest, const char* src) { assert(dest && src); char* cur = dest; while (*dest) { dest++; } while (*dest++ = *src++) { ; } return cur; }

4.strcmp
int strcmp(const char* str1, const char* str2);
从两个字符串的给定字符位置开始进行比较,如果对应位置上的字符相同,则继续比较下一对字符,直到遇到'\0'或者两个字符不同为止;
标准规定:
1.第一个字符串大于第二个字符串,则返回大于0的数字;
2.第一个字符串等于第二个字符串,则返回0;
3.第一个字符串小于第二个字符串,则返回小于0的数字;
使用:
c语言|C语言中的字符串函数和字符函数
文章图片

在VS编译器中,strcmp只会返回-1,0,1三个数字。
模拟实现:
int my_strcmp(const char* str1, const char* str2) { assert(str1 && str2); while (*str1 == *str2 && *str1 != '\0') { str1++; str2++; } if (*str1 > *str2) { return 1; } else if (*str1 < *str2) { return -1; } else { return 0; } }

5.strncpy
char* strncpy(char* destination, const char* source, size_t num);
1.从源字符串拷贝num个字符到目标空间;
2.如果源字符串的个数小于num,则在拷贝完源字符串后,在目标的后面追加'\0',直到字符总数达到num个为止;
使用:
c语言|C语言中的字符串函数和字符函数
文章图片

c语言|C语言中的字符串函数和字符函数
文章图片

模拟实现:
char* my_strncpy(char* dest, const char* src, size_t num) { assert(dest && src); size_t len = strlen(src); if (num <= len) { int i = 0; for (i = 0; i < num; i++) { *(dest + i) = *(src + i); } } else { int i = 0; for (i = 0; i < len; i++) { *(dest + i) = *(src + i); } for (; i < num; i++) { *(dest + i) = '\0'; } } return dest; }

6.strncat
char* strncat(char* destination, const char* source, size_t num);
使用:
c语言|C语言中的字符串函数和字符函数
文章图片

模拟实现:
char* my_strncat(char* dest, const char* src, size_t num) { assert(dest && src); char* cur = dest; while (*dest) { dest++; } size_t len = strlen(src); if (num < len) { int i = 0; for (i = 0; i < num; i++) { *(dest + i) = *(src + i); } *(dest + i) = '\0'; } else { while (*dest++ = *src++) { ; } } return cur; }

7.strncmp
int strncmp(const char* str1, const char* str2, size_t num);
使用:
c语言|C语言中的字符串函数和字符函数
文章图片


模拟实现:
int my_strncmp(const char* str1, const char* str2, size_t num) { assert(str1 && str2); size_t i = 0; while (*str1 == *str2 && *str1 != '\0' && i < num - 1) { str1++; str2++; i++; } if (*str1 > *str2) { return 1; } else if (*str1 < *str2) { return -1; } else { return 0; } }

函数内部实现方法是比较两个字符的大小,相同的话指针就向后走一个字符大小,如果num个字符中前面都相同的话,最后是要比较第num对字符的大小的,所以判断语句while (*str1 == *str2 && *str1 != '\0' && i < num - 1)中num要-1.
8.strstr
char* strstr(const char* str1, const char* str2);
str1为被搜索的字符串,str2为要检索出来的子字符串,如果在str1中找到了str2,那么就返回str1中找到str2时的起始位置的地址,如果str1中找不到str2,那么返回一个空指针。
使用:
c语言|C语言中的字符串函数和字符函数
文章图片

模拟实现:
思路:用一个cur指针指向str1的第一个字符,然后再拿一个s1指针去跟随着cur指针,用s2指针去指向str2的第一个字符,当cur指向第一个字符的时候,拿s1指向的字符去和s2指针指向的字符进行一个比较,要是不同,那么就让cur指针+1,s1和cur一样,当*s1和*s2相同的时候,让s1和s2同时往后走,继续比较字符,要是一直相同,那么就会出现3种情况,第一种就是str1字符串已经走到'\0'的位置了,但是str2还没有走完,那么此时退出s1和s2不断增加的循环,cur+1,s1回退到此时cur+1的位置,s2也重新回退到str2其实字符的位置,重新开始匹配相同的字符,不过要是出现这种情况的话,大概率后面的也会不相同,第2种情况,就是s1和s2一直往后走,直到s2指向的字符是'\0',那么此时,str2已经遍历完了,说明str2是str1的子字符串,那么也会退出循环,下一步再判断*s2是否是'\0',是的话就把此时cur作为返回值返回。第3中情况是前面的字符串相同,中间的不同,那么也会退出循环,让cur+1,然后把s1的位置回退到cur的位置,s2的位置也回退到str2起始字符串的位置,重新开始匹配。
char* my_strstr(const char* str1, const char* str2) { assert(str1 && str2); const char* s1 = str1; const char* s2 = str2; const char* cur = str1; while (*cur) { s1 = cur; s2 = str2; while (*s1 && *s2 && (* s1 == *s2)) { s1++; s2++; } if (*s2 == '\0') { return (char*)cur; } cur++; } return NULL; }

9.strtok
char* strtok(char* str, const char* sep);
1.sep参数是个字符串,定义了用作分割符的字符集合
【c语言|C语言中的字符串函数和字符函数】2.第一个参数str是个字符串,它包含了0个或者多个在sep字符串中存在的作为分割符的字符作为标记
3.strtok函数会在str中找到下一个标记,并将其用'\0'结尾,返回一个指向这个标记的指针。strtok函数会改变被操作的字符串,所以在用strtok对字符串进行分割时一般用临时拷贝的字符串,并且字符串可以被改变。
4.strtok函数的第一个标志不为NULL,函数将找到str中的第一个标记,strtok函数将会保存这个标记在字符串中的位置。
5.strtok函数的第一个参数为NULL,函数将从同一个字符串中保存的位置开始,查找下一个标记。
6.如果字符串中不存在更多的标记,则返回空指针
使用:
strtok函数将会返回分割出来的字符串的首字符地址,我们借助一个循环,就能把想要分割的字符串依次打印出来了
#include #include int main() { char* arr = "0zhangsan@new.net."; char buff[30]; strcpy(buff, arr); char* sep = "@.0"; char* ret = NULL; for (ret = strtok(buff, sep); ret != NULL; ret = strtok(NULL, sep)) { printf("%s\n", ret); } return 0; }

10.strerror
char* strerror(int errnum);
strerror是用来返回错误码所对应的错误信息,平常上网有时会出现的404就是错误码。
使用:
c语言|C语言中的字符串函数和字符函数
文章图片

strerror会返回对应错误信息的首字符地址
11.memcpy
void* memcpy(void* destination, const void* source, size_t num);
1.memcpy会从source的起始位置开始向后复制num个字节的数据到memcpy的内存位置
2.这个函数在遇到'\0'的时候并不会停下来
3.如果source和destination由任何的重叠,复制的结果都是未定义的
使用:
c语言|C语言中的字符串函数和字符函数
文章图片

模拟实现:
void* my_memcpy(void* dest, const void* src, size_t num) { assert(dest && src); void* ret = dest; while (num--) { *(char*)dest = *(char*)src; dest = (char*)dest + 1; src = https://www.it610.com/article/(char*)src + 1; } return ret; }

而如果在同一块空间进行拷贝可能会出现这种情况:
c语言|C语言中的字符串函数和字符函数
文章图片

这是因为数据是从前往后一个字节一个字节拷贝过去的,后面的将要拷贝的数据会被前面的数据给覆盖了,导致拷贝的数据发生了变化,而visual studio这款编译器将memcpy的功能已经实现到和memmove的功能差不多了,因此如果用库函数提供的memcpy去在同一块空间进行拷贝的话,不会发生这种情况。
c语言|C语言中的字符串函数和字符函数
文章图片

12.memmove
void* memmove(void* destination, const void* source, size_t num);
1.和memcpy的差别是源内存块和目标内存块是可以重叠的,如果源空间和目标空间出现了重叠,就可以通过memcpy函数来进行处理
使用:
c语言|C语言中的字符串函数和字符函数
文章图片

模拟实现:
思路:
两块内存空间重叠分为两种情况:
c语言|C语言中的字符串函数和字符函数
文章图片

第一种,如果src在dest的前面的话,那么就将数据从后往前依次拷贝,先将5拷到8的位置,再将4拷到7的位置,依次类推。
c语言|C语言中的字符串函数和字符函数
文章图片

第二种,当src再dest的后面,那么就将数据从前往后依次拷贝,先将6拷到4的位置,再将7拷到5的位置,依次类推。
而如果两块内存空间没有重叠,则从前往后或者从后往前都可以拷贝。
代码:
void* my_memmove(void* dest, const void* src, size_t num) { //stc在dest的前面 if (src < dest) { while (num--) { *((char*)dest + num) = *((char*)src + num); } return dest; } //src在dest的后面 else { void* ret = dest; while (num--) { *(char*)dest = *(char*)src; dest = (char*)dest + 1; src = https://www.it610.com/article/(char*)src + 1; } return ret; } }

13.memcmp
int memcmp(const void* ptr1, const void* ptr2, size_t num);
1.比较从ptr1指针和ptr2指针开始的num个字节的数据,和strcmp有点相似,返回值也是分为<0、==0和 >0三种。
使用:
c语言|C语言中的字符串函数和字符函数
文章图片

模拟实现:
int my_memcmp(const void* ptr1, const void* ptr2, size_t num) { num -= 1; while (num-- && (*(char*)ptr1 == *(char*)ptr2)) { ptr1 = (char*)ptr1 + 1; ptr2 = (char*)ptr2 + 1; } if ((*(char*)ptr1 == *(char*)ptr2)) { return 0; } else if (*(char*)ptr1 > *(char*)ptr2) { return 1; } else { return -1; } }

因为指针+num-1就到要比较最后一个字节大小空间的位置了了,所以一开始num就要-1.
2.字符函数的介绍
函数 如果它的参数符合下列条件就返回真(非0表示真)
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 任何可打印字符,包括图形字符和空白字符
使用:
注意:参数会被转换成ASCII码
int main() { printf("%d\n", iscntrl(1)); printf("%d\n", isspace(3)); printf("%d\n", isdigit(48)); printf("%d\n", isxdigit(49)); printf("%d\n", islower('A')); printf("%d\n", isupper('A')); printf("%d\n", isalpha('a')); printf("%d\n", isalnum('a')); printf("%d\n", ispunct('.')); printf("%d\n", isgraph('#')); printf("%d\n", isprint('3')); return 0; }

字符转换:
int toupper(int c);
int tolower(int c);
使用:
int main() { putchar(toupper('c')); printf("\n"); putchar(tolower('C')); return 0; }


关于字符串函数和字符函数的内容就到这里了,今后也会不定期更新

    推荐阅读