1 函数的种类
1.1求字符串的长度
strlen
1.2长度不受限制的字符串函数
strcpy
strcmp
strcat
1.3长度受限制的字符串函数
strncpy
strncmp
strncat
1.4字符串查找
strstr
strtok
1.5 错误信息报告
strerror
1.6
字符操作
1.7内存操作函数
memcpy
memmove
2 函数的使用
2.1.1 strlen
求字符串的长度
size_t strlen ( const char * str )
字符串是以'/0'作为结束标志,但'/0'不记作字符串的长度
strlen返回的是无符号的整形,因为长度不可能为负数
#include <stdio.h>
#include <string.h>//strlen函数的使用需要引用<string.h>的头文件
int main()
{
char arr[] = "abcdefg";
int ret = strlen(arr);// strlen要返回一个整形,所以需要一个整形变量来接收
printf("字符串的长度为%d", ret);
return 0;
}
2.1.2 模拟实现
#include <stdio.h>
size_t my_strlen(const char* pc)//用cosnt修饰,是因为在这里不需要对地址进行改动
{
int ret = 0;
while (*pc)// 当pc指向的内容为'/0'的时候就停止
{
ret++;
pc++;
}
return ret;
}
int main()
{
char arr[] = "abcdefg";
int ret = my_strlen(arr);
printf("字符串的长度是%d", ret);
return 0;
}
2.2.1 strcpy
拷贝字符串
char * strcpy ( char * destination, const char * source )
strcpy函数返回一个指针变量,所以要用指针接收
cosnt 修饰*source是因为源头函数不需要改变
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[20] = { 0 };
char arr2[] = "abcdefg";
char* pc = strcpy(arr1, arr2);//返回指针用指针接收
printf("被拷贝后的arr1-> %s\n", arr1);
return 0;
}
2.2.2 模拟实现
#include <stdio.h>
char* my_strcpy(char* dest, const char* sou)
{
char* ret = dest;//保存原地址
while (*sou)
{
*ret = *sou;//拷贝后地址各往后移动一个单位
ret++;
sou++;
}
return dest;
}
int main()
{
char arr1[20] = { 0 };
char arr2[] = "abcdefg";
char* ret = my_strcpy(arr1, arr2);
printf("被拷贝后的arr1-> %s\n", arr1);
return 0;
}
2.3.1 strcmp
int strcmp ( const char * str1, const char * str2 )
比较字符串的大小,并且在不同情况返回不同值
注:大小的比较是一个字符和一个字符的比较
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[] = "abcdefg";
char arr2[] = "abcdeff";
int ret = strcmp(arr1, arr2);
if (ret > 0)
{
printf("arr1大于arr2");
}
else if (ret < 0)
{
printf("arr1小于arr2");
}
else
{
printf("arr1等于arr2");
}
return 0;
}
2.3.2 模拟实现
#include <stdio.h>
int my_strcmp(const char* str1, const char* str2)
{
while (*str1)
{
if (*str1 > *str2)
{
return 1;
}
else if (*str1 < *str2)
{
return -1;
}
str1++;
str2++;
}
if ((*str1 == '\0') && (*str2 != '\0'))
return -1;//当arr1的长度小于arr2长度时
return 0;//跳出循环时,代表两组字符串包含的内容相同
}
int main()
{
char arr1[] = "abcd";
char arr2[] = "abcdeff";
int ret = my_strcmp(arr1, arr2);
if (ret > 0)
{
printf("arr1大于arr2");
}
else if (ret < 0)
{
printf("arr1小于arr2");
}
else
{
printf("arr1等于arr2");
}
return 0;
}
2.4.1 strcat
char * strcat ( char * dest, const char * sou)
将sou字符串追加到dest的结尾
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[20] = "abcd";
char arr2[] = "efgh";
char* ret = strcat(arr1, arr2);
printf("添加后的arr1-> %s\n", ret);
return 0;
}
2.4.2 模拟实现
#include <stdio.h>
char* my_strcat(char* dest, const char* sou)
{
char* pc = dest;//保存dest的地址
//找到arr1字符串结束的地址
while (*pc)
{
pc++;
}
//当到arr2字符串'/0'位置时结束
while (*sou)
{
*pc = *sou;
pc++;
sou++;
}
return dest;
}
int main()
{
char arr1[20] = "abcd";
char arr2[] = "efgh";
char* ret = my_strcat(arr1, arr2);
printf("添加后的arr1-> %s\n", ret);
return 0;
}
2.5.1 strncpy
char * strncpy ( char * destination, const char * source, size_t num )
在strcpy的基础上增加了可选择拷贝字符串长度的功能
num为选择拷贝字符串长度的参数,用无符号整形修饰
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[20] = { 0 };
char arr2[] = "abcdefg";
char* ret = strncpy(arr1, arr2, 4);//4为拷贝几个字符的参数
printf("拷贝4个字节后的arr1-> %s\n", ret);
return 0;
}
2.5.2 模拟实现
#include <stdio.h>
char* my_strncpy(char* dest, const char* sou, size_t num)
{
char* ret = dest;//保留dest的地址
while (num--)//循环执行的次数
{
*ret = *sou;
ret++;
sou++;
}
return dest;
}
int main()
{
char arr1[20] = { 0 };
char arr2[] = "abcdefg";
char* ret = my_strncpy(arr1, arr2, 4);
printf("拷贝4个字符后的arr1-> %s\n", ret);
return 0;
}
2.6.1 strncmp
int strncmp ( const char * str1, const char * str2, size_t num )
在strcmp的基础上增加可选择比较字符的个数
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[] = "abcdefg";
char arr2[] = "abcfeff";
int ret = strncmp(arr1, arr2, 4);
if (ret > 0)
{
printf("前4个字符 arr1 比 arr2 大\n");
}
else if (ret < 0)
{
printf("前4个字符 arr1 比 arr2 小\n");
}
else
{
printf("前4个字符 arr1 和 arr2 相等\n");
}
return 0;
}
2.6.2 模拟实现
#include <stdio.h>
int my_strncmp(char* str1, char* str2, size_t num)
{
while (num--)
{
if (*str1 > *str2)
{
return 1;
}
else if (*str1 < *str2)
{
return -1;
}
else// 当相等时往后移动一个字符
{
str1++;
str2++;
}
}
return 0;//退出循环时,表示选择长度的字符串相等
}
int main()
{
char arr1[] = "abcdefg";
char arr2[] = "abcfeff";
int ret = my_strncmp(arr1, arr2, 4);
if (ret > 0)
{
printf("前4个字符 arr1 比 arr2 大\n");
}
else if (ret < 0)
{
printf("前4个字符 arr1 比 arr2 小\n");
}
else
{
printf("前4个字符 arr1 和 arr2 相等\n");
}
return 0;
}
2.7.1 strncat
char * strncat ( char * destination, const char * source, size_t num )
在strcat的基础上可以选择在destination出现第一个'\0'位置后加上source字符的个数
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[20] = "abcdef\0qqqqqq";//判断是否在第一个出现'\0'位置添加字符
char arr2[] = "xyz";
char* ret = strncat(arr1, arr2, 2);
printf("%s\n", ret);
return 0;
}
2.7.2 模拟实现
#include <stdio.h>
char* my_strncat(char* dest, const char* sou, int num)
{
char* ret = dest;
int length = sizeof(sou);
while (*ret)
{
ret++;
}
int i = 0;
for (i = 0; i < num; i++)
{
*ret = *sou;
ret++;
sou++;
}
//判断添加的字符串长度是否大于原字符串长度
//如果小于需添加'\0'
if (num < length)
{
int len = sizeof(dest);//求添加字符后目标字符串的长度
dest[len] = '\0';
}
return dest;
}
int main()
{
char arr1[20] = "abcdef\0qqqqqq";//判断是否在第一个出现'\0'位置添加字符
char arr2[] = "xyz";
char* ret = my_strncat(arr1, arr2, 2);
printf("%s\n", ret);
return 0;
}
2.8.1 strstr
char * strstr ( char * str1, const char * str2 )
strstr实现str2是否在str1中存在,如果存在,返回从存在字符开始后面的字符串,即返回存在字符开始的地址,如果不存在返回Null。
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[] = "abcdefabcdef";
char arr2[] = "cd";
char* ret = strstr(arr1, arr2);
printf("%s", ret);
return 0;
}
2.8.2 模拟实现
#include <stdio.h>
char* my_strstr(const char* dest, const char* sou)
{
if (*sou == '\0')//如果源头字符串为空字符串,表明所有所有字符串都能与其对应
return dest;
char* pc = dest;
while (*pc)
{
char* s1 = pc;
char* s2 = sou;
while ((*s1 == *s2) && *s1 != '\0' && *s2 != '\0')
{
s1++;
s2++;
}
if (*s2 == '\0')//说明sou出现在了dest中
return pc;
pc++;
}
if (*pc == '\0')
return NULL;//退出循环表示在dest中未找到sou
}
int main()
{
char arr1[] = "abcdefabcdef";
char arr2[] = "cde";
char* ret = my_strstr(arr1, arr2);
if (ret == NULL)
printf("不存在");
else
printf("%s", ret);
return 0;
}
2.9.1 strtok
char * strtok ( char * str, const char * delimiters )
delimiters参数是字符串,内容是用作分隔符的字符的集合
str是指定一个字符串,包含了0个或多个由sep字符串中一个或多个分隔符分割的标记
strtok函数会找到str中的下一个标记,并将其用'\0'结尾,返回一个指向这个标记的指针(注意:strtok函数会改变str字符串的内容,所以在使用strtok函数切分的字符串一般用的是临时拷贝的内容并可以修改)
strtok函数的第一个参数不为NULL,函数将找到str中第一个标记,strtok函数将保存它在字符串
中的位置
strtok函数的第一个参数为NULL,函数将在同一个字符串中被保存的位置开始,查找下一个标
记。
如果字符串中不存在更多的标记,则返回NULL指针。
上述为strtok函数的使用功能和注意事项
简单使用strtok
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[] = "abcd!efgh@ijkl";
const char arr2[] = "!@";
char buf[100] = { 0 };//拷贝arr1
strcpy(buf, arr1);
char* ret = strtok(buf, arr2);
printf("%s\n", ret);
//由于第一次使用strtok时将分隔符赋值为'\0'
//起始位置就变为了空值
//需要分割几次就调用几次strtok
ret = strtok(NULL, arr2);
printf("%s\n", ret);
ret = strtok(NULL, arr2);
printf("%s\n", ret);
return 0;
}
优化代码
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[] = "abcd!efgh@ijkl";
char arr2[] = "!@";
char buf[100] = { 0 };
strcpy(buf, arr1);
char* str = NULL;
//把起始位置赋值成strtok使用后的地址
//判断条件是当strtok返回NULL值时结束
for (str = strtok(buf, arr2); str != NULL; str = strtok(NULL, arr2))
{
printf("%s\n", str);
}
return 0;
}
2.10.1 strerror
char * strerror ( int errnum )
errnum 是C语言中提供的一个全局变量,表示错误信息所使用的数字
strerror函数实现将errnum转换位我们能读取错误信息的字段
#include <stdio.h>
#include <string.h>
int main()
{
printf("%s\n", strerror(1));
printf("%s\n", strerror(2));
printf("%s\n", strerror(3));
printf("%s\n", strerror(4));
return 0;
}
strerror函数中的数字所表示的就是对应的错误信息
编译器会把相应的错误转化为相应的errnum
2.11.1 字符操作yb
函 | 符合条件返回真值 |
---|---|
iscntrl | 检查是否为控制字符 |
isspace | 空白字符:' ', ' \f ','\n','\r','\t', '\v' |
isdigit | 十进制 0~9 |
isxdigit | 十六进制 0~f |
islower | 小写字母 a~z |
isupper | 大写字母 A~Z |
isalpha | 字母 a~z A~Z |
isalnum | 字母或数字 0~9 a~z A~Z |
ispunct | 标点符号 |
isgraph | 图形字符 |
isprint | 可打印字符 包括图形字符和空白字符 |
需要引 < ctype.h > 头文件
都返回整形
#include <ctype.h>
int main()
{
char i = 'a';
printf("%d\n", isupper(i));
printf("%d\n", islower(i));
return 0;
}
非零为真
内存操作函数
2.12.1 memcpy
void * memcpy ( void * dest, const void * sou, size_t num )
比strcpy功能更强, 能够进行各种类型的拷贝
同时可选择需要拷贝的数量
其中 参数用void* 修饰是为可以接收任何类型的数据
num 的单位是字节, 在使用时,需要清楚需要几个字节
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[10] = { 0 };
int arr2[] = { 1,2,3,4,5,6,7,8,9,10 };
memcpy(arr1, arr2, 28);//28 代表sizeof(int) * 7
return 0;
}
监视
2.12.2 模拟实现
#include <stdio.h>
void* my_memcpy(void* dest, const void* sou, size_t num)
{
while (num--)
{
//因为num是以一个字节为单位
//并且dest和sou都是void*的类型
//需在运用时进行强制类型转换
//也必须转换为同样以一个字节为单位的 char* 类型
*(char*)dest = *(char*)sou;
dest = (char*)dest + 1;
sou = (char*)sou + 1;
}
}
int main()
{
int arr1[10] = { 0 };
int arr2[] = { 1,2,3,4,5,6,7,8,9,10 };
my_memcpy(arr1, arr2, 28);//28 代表sizeof(int) * 7
return 0;
}
2.13.1 memmove
void * memmove ( void * destination, const void * source, size_t num )
memmove 和 memcpy 函数功能相同, 但解决了对自身函数拷贝时,如有重叠部分时逻辑的错误。但现在的编译已经能够自身解决这种问题,即使这样,从严谨的态度上,依旧使用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, 28);//28 代表sizeof(int) * 7
return 0;
}
2.13.2 模拟实现
#include <stdio.h>
void* my_memmove(void* dest, const void* sou, size_t num)
{
char* ret = dest;
if ((char*)dest < (char*)sou)
{
while (num--)
{
*(char*)dest = *(char*)sou;
dest = (char*)dest + 1;
sou = (char*)sou + 1;
}
}
else
{
while (num--)
{
*((char*)ret + num) = *((char*)sou + num);
}
}
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
my_memmove(arr1, arr1 + 2, 28);//28 代表sizeof(int) * 7
return 0;
}
2.14. 总结
以上就是字符串函数和部分内存操作函数,在模拟实现这些函数, 以上模拟的代码还有很多地方可以进行优化,随着我们代码能力的提高,我们写的代码肯定会越来越好。