目录
目录
1.求字符串函数
strlen
第1,2点的小坑,观察下面的代码:
第3点小坑(关于strlen的返回值),观察下面的代码:
模拟实现strlen(3种方法)
2.长度不受限制的字符串函数
strcpy
第1点
第2点:
第3点:
第4点:
第5点:
模拟实现strcpy
strcat
第1点:调用演示
第2,3,4点参考strcpy
第5点:字符串无法自己给自己追加
模拟实现strcat
strcmp
第1点:
模拟实现strcmp
长度受限制的字符串
strncpy
用法:
编辑 第3点:
模拟实现strncpy
strncat
第1点: 追加字符串时,strncat末尾会补\0代码:
模拟实现strncat
strncmp
字符串查找:
strstr
用法:
模拟实现strstr
编辑
strtok
用法演示:
字符操作
错误信息报告
strerror
用法:
内存操作函数
memcpy
模拟实现memcpy
memmove
模拟实现memmove
memcmp
memset
一个小坑:
这篇文章的大纲:
我会按照图片讲解和模拟实现部分函数
作用 :求字符串长度(不包含\0)
注意点:
#include
#include
int main()
{
char arr1[] = { 'b', 'i', 't' };
char arr2[] = { 'b', 'i', 't','\0'};
char arr3[] = "bit";
int len1 = strlen(arr1);
int len2 = strlen(arr2);
int len3 = strlen(arr3);
printf("%d\n", len1);
printf("%d\n", len2);
printf("%d\n", len3);
return 0;
}
输出结果:
同样求"bit"的长度,三个结果不同的原因:
arr1,arr2原本是字符数组,arr2在arr1的基础上加上'\0'可以当成字符串,用strlen计算字符串长度时直接计算'\0'之前出现的元素个数即可;arr3本身就是字符串,所以arr2和arr3结果相同都是3。arr1后并没有跟'\0'依然是字符数组,它在内存中的存储:
用strlen来求取长度时,strlen要去寻找'\0', 但它在内存中前后位置的元素是不可知的,所以什么时候找到'\0'也是未知的,所以strlen求出的长度是随机值。(在不同的机器上可能结果不同)
int main()
{
if (strlen("abc") - strlen("abcdef") > 0)
{
printf(">\n");
}
else
{
printf("<\n");
}
return 0;
}
输出结果:
第一眼看觉得输出结果毋庸置疑是"<",其实这就是很多新手没有理解strlen的返回值,strlen返回值是size_t,size_t是由type_def重定义unsigned int,所以size_t其实是无符号整型,计算strlen("abc") - strlen("abcdef")的结果是-3,-3以无符号整型出现时会被当成一个很大的正数,所以两个相减的结果>0会输出">"
首先观察这个函数的返回值和参数:
方法1: 计数器实现
size_t my_strlen(const char* str)
{
assert(str);
int count = 0;
while (*str != '\0')
{
count++;
str++;
}
return count;
}
int main()
{
char arr[] = "abcdef";
int len = my_strlen(arr);
printf("%d\n", len);
return 0;
}
方法2:递归实现
size_t my_strlen(const char* str)
{
assert(str);
if(*str != '\0')
{
return 1 + my_strlen(str + 1);
}
else
{
return 0;
}
}
int main()
{
char arr[] = "abcdef";
int len = my_strlen(arr);
printf("%d\n", len);
return 0;
}
关于法1,法2具体介绍:
方法3: 指针-指针实现
size_t my_strlen(const char* str)
{
assert(str);
const char* ptr = str;
while (*ptr != '\0')
{
ptr++;
}
return (ptr-str);
}
int main()
{
char arr[] = "abcdef";
int len = my_strlen(arr);
printf("%d\n", len);
return 0;
}
简单介绍:指针-指针返回的是2个指针之间的元素个数,所以用一个指针记录首元素的位置,另一个指针通过循环找到'\0'之前那个元素的位置,二者相减即可得到字符串的长度
作用:拷贝字符串
注意点:
可能新手是以赋值的方式来拷贝字符串的,那就错了,观察下面的代码:
int main()
{
char name[30] = { 0 };
//"zhangsan"
//string cpoy
//name = "zhangsan"; //err, name数组名是地址,不是空间,地址是一个常量值,不能被赋值
strcpy(name, "zhangsan");
printf("%s\n", name);
return 0;
}
输出结果:
以上注释内容就是错误拷贝方法,错误原因见注释,正确拷贝字符串调用strcpy,strcpy函数用法:
将source的内容拷贝到destination中
错误示例:
int main()
{
char name[30] = "xxxxxxx";
char arr[] = { 'b','i','t'};
strcpy(name, arr);
printf("%s\n", name);
return 0;
}
输出结果:
bit后没有跟'\0',什么时候出现'\0'是未知的,所以就将'\0'之前位置的未知元素也拷贝过来了
正确写法:
int main()
{
char name[30] = "xxxxxxx";
char arr[] = { 'b','i','t', '\0'};
strcpy(name, arr);
printf("%s\n", name);
return 0;
}
输出结果:
int main()
{
char name[30] = "xxxxxxx";
strcpy(name, "zhang\0san");
printf("%s\n", name); //打印出来zhang
return 0;
}
输出结果:
调试观察:
当目标空间小于源字符串长度时,程序会出现崩溃,下面的代码就出现了程序崩溃
int main()
{
char name[3] ={0};
char arr[] = "abcdef";
strcpy(name, arr);
printf("%s\n", name);
return 0;
}
当目标空间是常量字符串时,是不可以拷贝的,观察下面代码:
int main()
{
const char* p = "abcdef"; //常量字符串,不可修改
char arr[] = "bit";
strcpy(p, arr); //目标区域不可修改
return 0;
}
char* my_strcpy(char*dest,const char* src)
{
assert(dest && src);
const char* ret = src;
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char arr1[] = "abcdef";
char arr2[20] = { 0 };
my_strcpy(arr2,arr1);
printf("%s\n", arr2);
return 0;
}
简单介绍:就是通过循环将源字符串的元素一一拷贝到目标空间中,直至遇到'\0'为止,最后返回源字符串首元素的地址
作用:字符串追加
注意点:
调用: 将源字符串追加到目标空间之后
代码:
int main()
{
char arr1[20] = "hello ";
strcat(arr1, "world");
printf("%s\n", arr1);
return 0;
}
输出结果:
原因:正如下面模拟实现strcat一样,如果目标空间和源字符串都是同一个字符串时,那么dest++找到'\0'时,src的内容也改变了,无法自己给自己追加
错误示例:
char* my_strcat(char* dest, const char* src)
{
assert(dest && src);
char* ret = dest;
while (*dest != '\0')
{
dest++;
}
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char arr2[20] = "hello ";
my_strcat(arr2, arr2);
printf("%s\n", arr2);
return 0;
}
输出结果:
思路:分2步, 1. 找到目标字符串的末尾 2. 将源字符串拷贝到目标字符串末尾的后面
char* my_strcat(char* dest, char* src)
{
assert(dest && src);
char* ret = dest;
//1. 找到目标空间空间的末尾
while (*dest != '\0')
{
dest++;
}
//2. 拷贝字符串
while(*dest++ = *src++)
{
;
}
return dest;
}
int main()
{
char arr1[20] = "hello ";
char arr2[] = "world";
my_strcat(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
作用:比较字符串
注意点:
两个字符串是如何比较大小的:
新手可能会犯的错误,如下:
int main()
{
char arr1[20] = "zhangsan";
char arr2[] = "zhangsanfeng";
//比较两个字符串是否相等
//arr1是数组名,数组名是数组首元素的地址
//arr2是数组名,数组名是数组首元素的地址
//这里是在比较2个地址,而不是2个字符串的内容,2个字符串的地址是不相同的
if (arr1 == arr2)
{
printf("==\n");
}
else
{
printf("!=\n");
}
return 0;
}
输出结果: 解释见代码中的注释
正确比较字符串的方法,如下:
int main()
{
char arr1[] = "abcdp";
char arr2[] = "abcw";
int ret = strcmp(arr1, arr2);
if (ret < 0)
{
printf("<\n");
}
else if (ret == 0)
{
printf("=\n");
}
else
{
printf(">\n");
}
return 0;
}
输出结果:
简单思路:就是通过循环让str1和str2中的元素一一去比较,一旦相等继续比较,一旦不相等返回
(*str1 - *str2)的值,中间的优化是一旦在循环中某个字符串遇到'\0'就return 0,表示两个字符串相等
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
while (*str1 == *str2)
{
if (*str1 == '\0')
{
return 0; //相等
}
str1++;
str2++;
}
return (*str1 - *str2);
}
int main()
{
char arr1[] = "abc";
char arr2[] = "abcq";
int ret = my_strcmp(arr1, arr2);
if (ret < 0)
{
printf("<\n");
}
else if (ret == 0)
{
printf("=\n");
}
else
{
printf(">\n");
}
return 0;
}
注意点:
观察代码:
int main()
{
char arr1[] = "abcdef";
char arr2[] = "bit";
strncpy(arr1, arr2, 5);
printf("%s\n", arr1);
return 0;
}
输出结果:
调试观察:
char*my_strncpy(char* dest, const char* src, size_t num)
{
assert(dest && src);
char* ret = src;
while (num--)
{
*dest++ = *src++;
}
*dest = '\0';
return ret;
}
int main()
{
char arr1[] = "abcdefgh";
char arr2[] = "hello";
my_strncpy(arr1, arr2, 5);
printf("%s\n", arr1);
return 0;
}
注意点:
用法:
int main()
{
char arr1[20] = "hello ";
char arr2[] = "world";
strncat(arr1, arr2, 3);
printf("%s\n", arr1);
return 0;
}
输出结果:
int main()
{
char arr[20] = "hello\0xxxx";
char arr2[] = "bit";
strncat(arr, arr2, 3);
printf("%s\n", arr);
return 0;
}
输出结果:
调试观察:
char* my_strncat(char* dest, const char* src, size_t num)
{
assert(dest && src);
char* ret = dest;
while (*dest != '\0')
{
dest++;
}
while (num--)
{
*dest++ = *src++;
}
return dest;
}
int main()
{
char arr1[15] = "hello ";
char arr2[] = "world";
my_strncat(arr1, arr2, 5);
printf("%s\n", arr1);
return 0;
}
注意点:
用法如下:
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abcq";
int ret = strncmp(arr1, arr2, 3);
printf("%d\n", ret);
if (ret < 0)
{
printf("<\n");
}
else if (ret == 0)
{
printf("=\n");
}
else
{
printf(">\n");
}
return 0;
}
输出结果:
作用:查找子串
int main()
{
char email[] = "[email protected]";
char substr[] = "bitejiuyeke";
char* ret = strstr(email, substr);
if (ret == NULL)
{
printf("子串不存在\n");
}
else
{
printf("%s\n", ret);
}
return 0;
}
输出结果:
思路讲解:
代码:
char* my_strstr(char* str1, char* str2)
{
assert(str1 && str2);
char* s1 = str1;
char* s2 = str2;
char* p = str1;
while (*p)
{
s1 = p;
s2 = str2;
while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
{
s1++;
s2++;
}
if (*s2 == '\0')
{
return (char*)p;
}
p++;
}
return NULL;
}
int main()
{
char email[] = "[email protected]";
char substr[] = "bitejiuyeke";
char* ret = my_strstr(email, substr);
if (ret == NULL)
{
printf("子串不存在\n");
}
else
{
printf("%s\n", ret);
}
return 0;
}
作用:切割字符串
注意点:
麻烦写法:
int main()
{
const char* sep = "@.";
char arr1[] = "[email protected]";
char arr2[50] = { 0 };
strcpy(arr2, arr1);
char*ret=strtok(arr2, sep);
if(ret != NULL)
printf("%s\n", ret);
ret = strtok(NULL, sep);
if (ret != NULL)
printf("%s\n", ret);
ret = strtok(NULL, sep);
if (ret != NULL)
printf("%s\n", ret);
ret = strtok(NULL, sep);
if (ret != NULL)
printf("%s\n", ret);
return 0;
}
简洁写法
int main()
{
char* sep = "@.";
char arr1[] = "[email protected]";
char arr2[50] = { 0 };
strcpy(arr2, arr1);
char* ret = NULL;
for (ret = strtok(arr2, sep); ret != NULL; ret = strtok(NULL, sep))
{
printf("%s\n", ret);
}
;
return 0;
}
输出结果:
作用:返回错误码所对应的错误信息
int main()
{
//errno - C语言设置的一个全局的错误码存放的变量
FILE* pf = fopen("C:\\Users\\86189\\Desktop\\test.txt", "r");
if (pf == NULL)
{
printf("%s\n", strerror(errno));
return 1;
}
else
{
//
}
return 0;
}
输出:
作用:负责拷贝两块独立空间中的数据
区别于strcpy,strcpy是拷贝字符串的
注意点:
调用演示:
int main()
{
int arr1[] = {0,1,2,3,4,5,6,7,8,9};
int arr2[10] = { 0 };
memcpy(arr2, arr1, 40);
float arr3[5] = { 1.0, 2.5,3.0,5.0,6.0 };
float arr4[10] = { 0.0 };
memcpy(arr4, arr3, 20);
return 0;
}
调试观察:
简单思路:
通过循环将src中的元素拷贝到dest中,因为memcpy拷贝时是不限制数据类型的,所以要用void*的指针作为形式参数,赋值时也不能直接赋值首先要将void*的指针强制类型转换成char*在赋值,用char*指针的原因是char*指针每次访问一个字节,而如果用其他类型的指针如int,每次访问4个字节,想拷贝19个字节是没有办法的;而对void*指针是没法直接++的,要先强制类型转换成char*再++
代码:
void* my_memcpy(void* dest, const void* src, size_t num)
{
assetr(dest && src);
const void* ret = src;
while (num--)
{
*(char*)dest = *(char*)src;
//err:
//dest++;
//src++;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return ret;
}
int main()
{
int arr1[] = {0,1,2,3,4,5,6,7,8,9};
int arr2[10] = { 0 };
my_memcpy(arr2, arr1, 40);
float arr3[5] = { 1.0, 2.5,3.0,5.0,6.0 };
float arr4[10] = { 0.0 };
my_memcpy(arr4, arr3, 20);
return 0;
}
作用: 负责拷贝重叠内存之间的数据
注意点:
调用演示:
int main()
{
int arr[] = { 0,1,2,3,4,5,6,7,8,9 };
memmove(arr, arr + 2, 12);
//2 3 4 3 4 5 6 7 8 9
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
输出结果:
思路讲解:
代码:
void* my_memmove(void* dest, const void* src, size_t num)
{
assert(dest && src);
void* ret = src;
if(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;
}
int main()
{
int arr[] = { 0,1,2,3,4,5,6,7,8,9 };
my_memmove(arr, arr + 2, 12);
//2 3 4 3 4 5 6 7 8 9
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
作用:比较内存中的数据
注意点:
调用演示:
int main()
{
int arr1[] = { 1,2,3,4}; //01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00
int arr2[] = { 1,3,2}; //01 00 00 00 03 00 00 00 02 00 00 00
int ret = memcmp(arr1, arr2, 12);
printf("%d\n", ret);
return 0;
}
输出结果:
作用:内存设置
调用演示:
int main()
{
char arr[] = "hello bit";
memset(arr+6, 'x', 3);
printf("%s\n", arr);
return 0;
}
输出结果:
需要将一个数组全部初识化为1时,观察下面代码:
int main()
{
//把arr初始化为全1
int arr[10] = { 0 };
memset(arr, 1, 40); //一个字节一个字节初始化
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d\n", arr[i]);
}
return 0;
}
输出结果:
原因分析:
每个字节都设置为1