目录
连接字符函数——>将源字符串放到目标空间末尾;将str追加到dest末尾;(指的是字符串,字符的末尾,而不是在\0后面)
一、字符串函数:
这类函数都用到了统一的 头文件 :
#include<string.h>
1、stlren函数:
求字符串长度;
关于定义-——>
//size_t 代表的是无符号整形;
size_t strlen(const char *s);
传上一个字符串给指针s,计算到\0之前的字符个数;即不包括\0;传回的长度一直是正整数;
关于模拟实现——>https://blog.youkuaiyun.com/SWsunlight/article/details/136563759
2、strcpy函数:
复制字符串(拷贝字符串);
//返回类型为char*
char* stecpy(char*dest,const char*str);
其中dest是目标空间,str是源空间,将str的内容复制到dest中;
注意:传给str的字符串必须有\0;且目标空间足够大,能够装下需要复制的字符串;
3、strncpy函数:
有限制的复制字符串:
char* strncpy(char* dest, const char* str, size_t n);
相较于strcpy,多一个参数n,代表要复制str中字符的个数;
满足如下规则:当字符串str的长度小于n时,因为后面的字符不够复制到dest中,所以会补\0;
当字符串str的长度大于n时,复制到那个字符就是那个字符,后面不会补\0;
模拟实现》》》》》》》》
#include<assert.h>
char* my_strncpy(char* dest, const char* str, size_t n)
{
//断言,防止传参,传的空指针;
assert(dest && str);
char* p = dest;
while (n--)
{
*dest++ = *str++;
}
return p;
}
int main()
{
char arr1[20] = { "xx" };
char arr2[] = { "abcdefghj" };
char* p = my_strncpy(arr1,arr2,4);
printf("%s\n", p);
return 0;
}
为了更好观察,将arr1[20]的数组中赋值很多x,看下图,会发现复制到达字符d后,后面没有补\0;
4、strcat函数:
连接字符函数——>将源字符串放到目标空间末尾;将str追加到dest末尾;(指的是字符串,字符的末尾,而不是在\0后面)
char* strcat(char* dest, const char* str);
5、strncat函数:
有限制的追加函数,
char* strcat(char* dest, const char* str,size_t n);
n表示将str中n个字符追加到dest末尾;
若是 str >n;截掉超出部分字符;
若是 str <n;则追加并且补\0;
char* my_strncat(char* dest, const char* str,size_t n)
{
//断言
assert(dest && str);
char *p = dest;
//将dest的位置(地址)移动到\0处;
while (*dest)
dest++;
while (n--)
{
if(*str == '\0')
*dest++ = '\0';
else
*dest++ = *str++;
}
return p;
}
int main()
{
char arr1[20] = { "xxxxx" };
char arr2[] = { "abcdefghj" };
char* p = my_strncat(arr1,arr2,4);
printf("%s\n", p);
return 0;
}
6、strcmp函数:
字符串比较函数》》 对字符串进行比较,从第一个字符开始,对应的字符进行比较,第一次遇到不同的字符时,谁大,则说明那个字符串大;(本质上是ASCII码值进行比较)
int strcmp(const char*dest,const char*str);
若 dest > str 则 返回正整数;
若 dest = str 则 返回 0 ;
若 dest < str 则 返回负整数;
模拟实现:
#include<stdio.h>
#include<assert.h>
//模拟实现字符串大小比较
int my_strcmp(const char* s1, const char* s2)
{
//断言
assert(s1 && s2);
//相等则进入循环,直到找到不相等的
while (*s1 == *s2)
{
//相等就返回0;因为进入循环前提条件为*s1==*s2无论是谁等于\0都行的,所以写一个就够了;
if (*s1 == '\0')
{
return 0;
}
*s1++;
*s2++;
}
if (*s1 - *s2 > 0)
{
return 1;
}
else
{
return -1;
}
}
6、strstr函数:
字符串查找函数
返回第一次相同的字符以及后面的内容(即第一次找到时的首元素地址)
没找到则返回空指针;
char* strstr(const char* dest, const char* str);
两个空间都不需要进行改变,所以const锁死他两个;
模拟实现
/指针str1是目标空间,str2是源字符串
char* my_strstr(const char* str1, const char* str2)
{
const char* dest1 = NULL;
const char* dest2 = NULL;
const char* stu = str1;//将空间指针地址放入stu
while (*stu)
{
dest1 = stu;
dest2 = str2;
while(*dest1 != '\0' && *dest2 != '\0' && *dest1 == *dest2)
{
dest1++;
dest2++;
}
//找到以后则此时*dest2为\0;
if (*dest2 == '\0')
{
//返回的就是第一次找到的时首元素首地址
return stu;
}
stu++;
}
return NULL;
}
int main()
{
char* sz = my_strstr("abbcde", "bcd");
printf("%s\n", sz);
return 0;
}
尽量不去改变传上来的参数;因此设置了临时变量来分别接受目标空间和源空间;还有一个变量stu则是存放当遇到需要查找的字符串时的地址;
7、strtok函数:
分割字符串:
char* strtok(char* str, const char* sep);
sep里面为分割符(标记),可以是一个或者多个,str则是指定一个字符串,里面可以包含0个或者多个sep里面的分割符,
将str从首元素地址开始遍历,若是遇到分割符(标记)将str中这个字符标记,并将这个标记改成\0并返回指向这个标记的指针;strtok会改变被操作的字符串,所以使用它进行切分的字符串一般都是临时拷贝的内容且可以修改,也就是单独创建了一个指针ret来接受返回的字符串
注意:
strtok第一个传参在对同一个字符串使用时第一次不能为NULL,strtok函数找到str中第一个标记,并会保存改位置,接下来,想要继续后面的进行标记,就传NULL,会在上次分割的位置继续进行下去,当遇到标记时,再次分割出新的字符串,拷贝到ret里面 如下显示!!
当不存在更多标记时,返回NULL
不需要标记符时,也不要给sep传NULL,而是传 “ ” (空字符)因为本身传的参数是字符串(字符)!!!!
牛客网实战!!!!!!

8、总结:
//求字符串长度
size_t strlen(const char* s);
//进行字符串复制(拷贝)
char* stecpy(char* dest, const char* str);
//有条件限制的复制
char* strncpy(char* dest, const char* str, size_t n);
//字符串的追加
char* strcat(char* dest, const char* str);
//有条件的追加
char* strcat(char* dest, const char* str,size_t n);
//字符串大小的比较
int strcmp(const char* dest, const char* str);
//字符串的查找
char* strstr(const char* dest, const char* str);
//字符串分割
char* strtok(char* str, const char* sep);
二、内存操作函数:
针对内存进行操作的函数,在内存中以字节为单位进行操作,对于任何数据类型都能进行操作;头文件同上是一个;(dest目标空间,str源空间,不再作解释)
1、memcpy函数:
内存拷贝:
针对内存块进行拷贝,将源空间的拷贝到目标空间,接受任何类型数据
void* memcpy(void* dest, const void* str, size_t num);
注意:目标空间与源空间不能冲重叠(这里的变量num 表示的是长度,即字节个数)
模拟实现:
void* my_memcpy(void* s1, const void* s2,size_t num)
{
//断言
assert(s1 && s2);
//将源空间存放在一个void*指针中,保证传参回去时,地址为s1的首地址
void* ret = s1;
while (num--)
{
*(char*)s1 = *(char*)s2;
//不能写(char*)s1++;因为()强制性转换具有临时性,会变成(char*)s1 == s1+1;所以就得((char*)s1)++;
s1 = (char*)s1 + 1;
s2 = (char*)s2 + 1;
}
return ret;
}
需要注意的就是将void*指针转变为char*,这样才能进行访问并且访问的字节数是以1为单位的;
将每个字节的内容进行解引用拷贝到s1中,如此就能完成拷贝了;相当于把每个字节的代表的地址拷贝到进去
2、memmove函数:
空间发生重叠的拷贝:
若是空间发生重叠,也需要拷贝,就可以用到如下函数了
void* memmove(void* dest, void* str, size_t num);
关于模拟实现:
---->这个函数模拟实现有点难度;就是在交换时,要怎么保证拷贝能按理想情况进行?让我用粗糙的画图技巧来说简单讲讲,以int型数组进行讲解🐉🐉🐉🐉🐉
绿色框 (^///^) 就是要拷贝的内容·
1号图——第一种情况;
因为地址是有低地址向高地址建立的;将2 3 4拷贝到1 2 3 中,直接从前往后进行拷贝 (dest < str的情况),看图可以发现,重叠了,但是每次拷贝时,却没有覆盖我们需要的数 就是说,当 str 的 3 拷贝到 dest 的2时,2变成3,当 4 去替换dest的 3 时,都能替换;
2号图——第二种情况;
(str < dest) 讲2 3 4 拷贝到 3 4 5 ,若是从前面往后拷贝,则 2 放到 3 的位置 ,当要将 3 放到 dest 的 4时,3 在第一次拷贝2的时候就被踢掉了,所以这样拷贝就会出现问题 —— 那么试下 从后往前 :先将 str 的 4 拷贝到 dest 的 5 ,然后接着 3 到 dest的4 ,最后就是 2 到dest 的 3 ;
3号图——第三种情况;
没有相交的部分;无论从那边进行都行;
总结:
dest>str 时就是后往前进行拷贝;
dest<str 时就是前往后进行拷贝;
由此可以编写程序:
void* my_memmove(void* s1, void* s2, size_t num)
{
assert(s1 && s2);
//存放首元素地址
void* p = s1;
//s1<s2,从前往后;
if (s1< s2)
{
while (num--)
{
//类型转换
*(char*)s1 = *(char*)s2;
s1 = (char*)s1 + 1;
s2 = (char*)s2 + 1;
}
}
//否则,从后往前
else
{
while (num--)
{
//将void*指针转换成char*,加字节数,就是最后一个元素地址,然后解引用
*((char*)s1 + num) = *((char*)s2 + num);
}
}
return p;
}
3、memset函数:
设置内存函数:
将内存块设置成想要的内容:
void* memset(void* ptr, int value, size_t num);
ptr指向的要填充要设置的内存块,value就是要设置的内容(可以传字符的,因为字符以ASCII码值进行存放的);num则是字节个数,不同类型的字节个数不一样
比如: int型的数组,你想要将一个元素设置为 1 ,则要 4 个字节;
4、memcmp函数:
内存块比较函数:
内存块间进行比较:
int memcmp(const void* ptr1, const void* ptr2, size_t num);
ptr1和ptr2指针指向的内容进行比较,到num个字节为止;
ptr1 > ptr2 | 返回 1 |
ptr1 = ptr2 | 返回 0 |
ptr1 < ptr2 | 返回-1 |
5、总结:
void* memcpy(void* dest, const void* str, size_t n);
//重叠空间下的内存拷贝
void* memmove(void* dest, void* str, size_t num);
//内存填充
void* memset(void* ptr, void* value, size_t num);
//内存块的比较
void* memcmp(const void* ptr1, const void* ptr2, size_t num);
注意:我们不是对解引用的值进行操作,而是对地址进行操作,因为内存函数是对字节进行操作,存储在内存中的数据可以是char int 型等等,所以这里是对目标和源空间 字节的地址进行操作
并没有用到 *
三、综上所诉
其实无论时那个函数的模拟实现,只要知道每个参数的任务,经过作图进行思考,明白其中的原理,注意容易出错的地方,模拟实现就很容易了,虽然对于这些函数只要有一定了解,但是仍然建议大家试着自己模拟实现,这样更深刻;加油!各位观众老爷们!!!!