C语言中对字符和字符串的处理很是频繁,但是,C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。

常量字符串适用于那些对它不做修改的字符串函数。

1.字符串函数的介绍及其模拟实现

1.strlen

size_t strlen (const char* str);

1.字符串是用'\0'作为结束标志的,strlen函数返回是的从给定位置开始到'\0'之间的字符个数,不包括'\0';

2.参数指向的字符串必须要以'\0'结束,否则可能会出现越界访问等情况;

3.strln函数的返回值是size_t,是无符号整型;

使用:
#include <stdio.h> #include <string.h> 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

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

模拟实现:

1.计数器的方法:
#include <assert.h> size_t my_strlen(const char* str) { assert(str); size_t
count = 0; while (*str++) { count++; } return count; }
2.递归的方法:
#include <assert.h> size_t my_strlen(const char* str) { assert(str); if (*str
== '\0') { return 0; } return 1 + my_strlen(++str); }
3.指针-指针的方法:
#include <assert.h> 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所指向的字符串是不能被改变的

使用:

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.目标空间必须可以被改变;

使用:

模拟实现:

从目标字符串的'\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的数字;

使用:

在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个为止;

使用:

模拟实现:
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);

使用:

 

 模拟实现:
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);

使用:

 模拟实现:
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,那么返回一个空指针。

使用:

 模拟实现:

思路:用一个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参数是个字符串,定义了用作分割符的字符集合

2.第一个参数str是个字符串,它包含了0个或者多个在sep字符串中存在的作为分割符的字符作为标记

3.strtok函数会在str中找到下一个标记,并将其用'\0'结尾,返回一个指向这个标记的指针。strtok函数会改变被操作的字符串,所以在用strtok对字符串进行分割时一般用临时拷贝的字符串,并且字符串可以被改变。

4.strtok函数的第一个标志不为NULL,函数将找到str中的第一个标记,strtok函数将会保存这个标记在字符串中的位置。

5.strtok函数的第一个参数为NULL,函数将从同一个字符串中保存的位置开始,查找下一个标记。

6.如果字符串中不存在更多的标记,则返回空指针

使用:

strtok函数将会返回分割出来的字符串的首字符地址,我们借助一个循环,就能把想要分割的字符串依次打印出来了
#include <stdio.h> #include <string.h> 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就是错误码。 

使用:

 

strerror会返回对应错误信息的首字符地址

11.memcpy

void* memcpy(void* destination, const void* source, size_t num);

1.memcpy会从source的起始位置开始向后复制num个字节的数据到memcpy的内存位置

2.这个函数在遇到'\0'的时候并不会停下来

3.如果source和destination由任何的重叠,复制的结果都是未定义的

使用:

 模拟实现:
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 = (char*)src + 1; } return ret; }
而如果在同一块空间进行拷贝可能会出现这种情况:

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

 12.memmove

void* memmove(void* destination, const void* source, size_t num); 

1.和memcpy的差别是源内存块和目标内存块是可以重叠的,如果源空间和目标空间出现了重叠,就可以通过memcpy函数来进行处理

使用:

 模拟实现:

思路:

两块内存空间重叠分为两种情况:

 第一种,如果src在dest的前面的话,那么就将数据从后往前依次拷贝,先将5拷到8的位置,再将4拷到7的位置,依次类推。

 第二种,当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 = (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三种。

使用:

模拟实现:
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; }

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

技术
©2019-2020 Toolsou All rights reserved,
TypeScript:函数类型接口8道大厂指针笔试题让你秒杀指针!!!MySQL 日期时间加减mysql 查询条件之外的数据_mysql 查询符合条件的数据查linux的操作系统版本,如何查看Linux操作系统版本?将String类型转换成Map数据类型使用uuid做MySQL主键,被老板,爆怼一顿C语言中的字符串函数和字符函数linux服务器中毒排查--基础篇C# ASCII码字符转换