1 字符串函数
首先是长度无限制的strcpy,strcmp,strcat;
这几个函数从左至右功能依次是 字符串复制,字符串比较,字符串追加,
但是这几个字符串函数并不是很安全,因为有可能接受的字符串的大小不够,因此又有一种字符串有限制的函数
strncpy,strncmp,stcncat;
这几个函数会限制只能复制,比较或者追加前n个字符,相对安全。
重点来啦:strstr函数
strstr函数的作用是查找子串,比如在 "abbbcdedf"中查找"bbc"是否存在,并且返回匹配的位置。
接下来是模拟strstr写出的my_strstr函数
代码如下:
#include<stdio.h>
const char* my_strstr(const char* str1,const char* str2)
{
char* p1 = str1;
char* p2 = str2;
while (*p1)
{
str1 = p1;
str2 = p2;
while (*str1 == *str2)
{
str1++;
str2++;
if (*str2 == '\0')
return p1;//返回匹配开始的位置
}
p1++;
}
return NULL;//若是p1都是'\0'则表示匹配失败,输出NULL;
}
int main()
{
char s[] = "abbbcddef";
char d[] = "bbc";
char e[] = "qeww";
char* ret = my_strstr(s, d);
printf("%s\n", ret);
ret = my_strstr(s, e);
printf("%s", ret);
return 0;
}
我们先后将s字符串先后和d字符串,e字符串比较,发现d字符串和s字符串是能够匹配的,并且返回了s字符串和d字符串匹配的位置,而e字符串则不能和s字符串匹配,因此返回null;
重点2 strtok函数
strtok函数能够切割字符串
具体示例如下:
#include<stdio.h>
#include<string.h>
int main()
{
const char* sep = "@.";
char email[] = "3327618203@qq.com";
char str[20];
strcpy(str, email);
for (char* ret = strtok(str, sep); ret != NULL; ret = strtok(NULL, sep))
{
printf("%s\n", ret);
}
printf("%s", email);
return 0;
}
这个函数会在email字符串中依次寻找有没有和sep里面对应的字符,若是有,则在email字符串中改为'\0',因为这个函数会对email进行修改,因此此处我先将email的内容复制在strcpy中再进行分割,表现如下;
可以看到,分割后分别打印出了str的三部分字符串,使用这个函数有一个要注意的就是,只有第一次需要传str函数的数组名,后面只用传NULL就可以了,因为每次这个函数都会在str字符串中有分隔符号的位置留下一个标记在堆区中,而这个标记就是NULL,因此只有第一次使用需要传字符串首元素地址作为传参,后面只用传NULL,编译器自己会根据这个标记找到对应的修改位置。
内存函数
前面讲过strcpy是用来复制字符串的函数,那么有没有一种函数什么数组都能够复制呢?答案是肯定的,比如接下来的memcpy,顾名思义,这函数就是用来复制内存的,我们都知道数据在编译器中是以字节为单位进行存储的,而这个函数就是一个一个字节复制,因此无论什么类型都能够复制,下面是我对这个函数的模拟
memcpy函数模拟
#include<stdio.h>
void* my_memcpy(void* ret, const void* src, size_t num)
{
void* p = ret;
while (num--)
{
*(char*)ret = *(char*)src;
ret = (char*)ret + 1;
src = (char*)src + 1;
}
return ret;
}
int main()
{
int a[5] = { 1,2,3,4,5 };
int b[5] = { 0 };
char c[20] = "abcdef";
char d[20] = { 0 };
my_memcpy(b, a,20);
my_memcpy(d, c,6);
for (int i = 0; i < 5; i++)
printf("%d ", b[i]);
printf("\n%s", d);
return 0;
}
我们可以看到这个函数不管是int类型的数组还是char类型的数组都能够使用,那么我们来深究一下代码:
void* my_memcpy(void* ret, const void* src, size_t num)
{
void* p = ret;
while (num--)
{
*(char*)ret = *(char*)src;
ret = (char*)ret + 1;
src = (char*)src + 1;
}
return ret;
}
这个函数一共有三个形参,ret表示用来接受数据的数组,src表示有数据的数组,而num则表示需要复制的内存的字节数,但是前两个为什么都是void*和const void * 类型呢?
由于这个函数是不管什么类型都能够复制的,因此我们接受的形参应该使用void * 类型的,因为这个类型是万能类型,无论什么都能接受,但是为什么我们在复制的时候,会使用*(char *)ret = *(char *)src呢?为什么一定要将他们强制类型转化为char* 类型之后才解引用呢?这是因为char *类型是一个字节,只有这样,我们才能逐个字节的复制保证自己不出错。
讲完memcpy我们再来讲讲memmove函数。
memmove函数
这个函数实际上和memcpy函数差不多,但是这个函数是专门用来实现内存重叠拷贝的,因为memcpy若是对于一个数组内部进行拷贝,会发生错误,而这个函数就不会出现这种状况。
函数模拟
#include<stdio.h>
#include<assert.h>
void* my_memmove(void* des, const void * src, size_t num)
{
assert(des && src);
void* ret = des;
if (des > src)
{
//
while (num--)
{
*((char*)des + num) = *((char*)src + num);
}
}
else
{
while (num--)
{
*(char*)des = *(char*)src;
des = (char*)des + 1;
src = (char*)src + 1;
}
}
return ret;
}
int main()
{
int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
my_memmove(a, a + 2, 20);
for (int i = 0; i< 10; i++)
printf("%d ", a[i]);
printf("\n");
int b[10] = { 1,2,3,4,5,6,7,8,9,10 };
my_memmove(b + 2, b, 20);
for (int i = 0; i < 10; i++)
printf("%d ", b[i]);
return 0;
}
这个函数的形参和memcpy类似,而代码实现形式却不一样,这个会根据des和src在内存的前后顺序不同来决定是从前向后复制还是从后向前复制,具体实现就是上面。
示例如下:
这就是本人新学的几个小函数,谢谢大家。