前言:
在字符串和字符串函数(1)-CSDN博客中,已将将字符串和字符函数的使用了解,并且实现模拟了一些字符串的库函数。
接下来将继续深入学习字符串和字符串函数。并且模拟实现一些较为复杂的函数。
可控制字符长的的函数
介绍并使用strncpy函数
相同点: strncpy函数和strcpy函数的形同点是都可以实现字符串的拷贝,可以将一个字符串中的内容拷贝到另一个字符串中。
不同点:strnpy和strcpy函数的区别在于,strcpy只能将一个字符串中的所有内容全部拷贝到另一个字符串中,但是strnpy可以将一个字符串中的内容,有选择的拷贝到另一个字符串当中,参数也从2个变成了3个。
例如:我要将arr1[]中的前3个字符拷贝到arr2[]中:
可不可以这样实现,其实问题就在于传完3个字符之后,有没有将\0也传进去?
int main()
{
char arr1[] = { "ab cd ef" };
char arr2[20];
strncpy(arr2, arr1, 3);
printf("%s\n", arr2);
return 0;
}
通过调试可以看到:
在arr1中:
拷贝之后,arr2中是这样:
发现:并没有将'\0'考进去!
所以,分两种情况:
1、如果我想把一个字符串中几个字符给拿出来,这时候需要注意我们要手动将拿出来的字符串中的最后一个字符后面的一个字符加上'\0'
2、如果我只是想将一个字符串中的前几个元素做一个替换,此时就不需要手动加上'\0'。
例1:拿出arr1中的3个字符。
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[] = { "ab cd ef" };
char arr2[20];
strncpy(arr2, arr1, 3);
arr2[3] = '\0';
printf("%s\n", arr2);
return 0;
}
打印结果:
例2:替换arr2中的前三个字符
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[] = { "ab cd ef" };
char arr2[20] = {"hjklo"};
strncpy(arr2, arr1, 3);
printf("%s\n", arr2);
return 0;
}
模拟实现strncpy函数:
我们可以在strcpy的基础上再进行一些修改:
#include<stdio.h>
#include<assert.h>
char* my_strnpy( char* arr2, const char* arr1, size_t num)
{
assert(arr1 != NULL);
assert(arr2 != NULL);
char* start = arr2;
while (num)
{
*arr2++ = *arr1++;
num--;
}
return start;
}
int main()
{
char arr1[] = {"love you"};
char arr2[] = { "like me" };
my_strnpy(arr2, arr1,4);
printf("%s\n", arr2);
return 0;
}
介绍并使用strncat函数:
与strcat相比较:
相同点:都可以实现对字符串的追加。
不同点:strncat按自我需求调整所追加的字符串的长度。
strcat只能将一个字符串全部内容追加到另一个字符串当中。
追加的时候是从目的地字符数组的'\0'处开始追加。
看一组代码:
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[] = {"abcd"};
char arr2[20] = { "asdf " };
strncat(arr2, arr1, 4);
printf("%s\n", arr2);
return 0;
}
通过调试来观测一下:
初始化之后:
进入函数之后:
注意:这是后也没有主动追加'\0',可以最后手动追加。
模拟实现strncat函数:
char* my_strncat(char* arr1, const char* arr2, size_t num)
{
assert(arr1 != NULL);
assert(arr2 != NULL);
char* start = arr1;
while (*arr1)
{
arr1++;
}
while (num)
{
*arr1++ = *arr2++;
num--;
}
return start;
}
int main()
{
char arr1[20] = {"love me "};
char arr2[] = { "like you" };
my_strncat(arr1, arr2, 4);
printf("%s\n", arr1);
return 0;
}
介绍并使用strncmp函数:
相关介绍和返回值,发现和strcmp函数的返回值是一样的。
区别:除了多了一个参数num,也就是你要比较几个字符之外,其他的都是一样的。
int main()
{
char arr1[] = {"abcdef"};
char arr2[] = { "abcds" };
int a = strncmp(arr1, arr2,5);
if (a == 0)
{
printf("=");
}
else if (a < 0)
{
printf("<");
}
else
{
printf(">");
}
return 0;
}
模拟实现strncmp函数
int my_strncmp(const char* arr1, const char* arr2, size_t num)
{
assert(arr1 != NULL);
assert(arr2 != NULL);
while ( *arr1 - *arr2 == 0)
{
arr1++;
arr2++;
num--;
if (num-1 == 0)
{
break;
}
}
return *arr1-*arr2;
}
int main()
{
char arr1[] = {"abcd"};
char arr2[] = {"abfde"};
int a = my_strncmp(arr1,arr2,4);
if (a == 0)
{
printf("=");
}
else if (a < 0)
{
printf("<");
}
else
{
printf(">");
}
return 0;
}
其他类型库函数
介绍并使用strstr函数:
返回指向 str1 中首次出现的 str2 的指针,如果 str2 不是 str1 的一部分,则返回空指针.
int main() { char arr1[] = {"love you best"}; char *arr = strstr(arr1, "you"); printf("%s\n", arr); return 0; }
在arr1中找you,找到第一次出现的地方返回第一次出现的首字符的地址。
打印结果:
如果找不到,就返回NULL;
例如:
int main() { char arr1[] = {"love you best"}; char *arr = strstr(arr1, "yoo"); printf("%s\n", arr); return 0; }
打印结果:
注:这里找相同的字符串必须完全相同,差一个字符都不行!!
模拟实现strstr函数:
首先通过画图来讲解:
char* my_strstr(const char* arr1, const char* arr2)
{
char* cp = (char*)arr1;//表示arr1的红色指针
char* a;//表示arr1的绿色指针
char* b;//表示arr2的绿色指针
while (*cp)
{
if (*cp == *arr2)
{
a = cp;
b = (char*)arr2;
while (*cp == *b)
{
cp++;
b++;
if (*b == '\0')
return a;
}
}
cp++;
}
return NULL;
}
int main()
{
char arr1[] = { " welcom next new world" };
char arr2[] = {"new"};
char *str = my_strstr(arr1, arr2);
printf("%s\n", str);
return 0;
}
介绍并使用memcpy(内存拷贝函数)函数:
之前探讨了strcpy和strncpy,这两个库函数是用来拷贝字符串的,其它类型的不能拷贝,那么其他类型的数据该如何拷贝呢?
就需要用到memcpy函数,可以拷贝任意类型的数据!
例如:
int main()
{
int arr1[] = {1,2,3,4,5};
int arr2[20];
memcpy(arr2,arr1, 20);
return 0;
}
需要三个参数,目的地地址,源头地址,需要拷贝的字节数,
通过调试发现:
拷贝成功!
模拟实现memcpy函数
void* my_memcpy(void* str1, const void *str2, size_t sz)
{
void* start = str1;
assert(str1 && str2);
while (sz)
{
*(char*)str1 = *(char*)str2;
str1 = (char*)str1 + 1;
str2 = (char*)str2 + 1;
sz--;
}
return start;
}
int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[10];
my_memcpy(arr2,arr1,20);
return 0;
}
拷贝完成!
但是,这回突发奇想,想将arr1中的前4个拷贝到从第二个整形开始。可不可以呢?
void* my_memcpy(void* str1, const void *str2, size_t sz) { void* start = str1; assert(str1 && str2); while (sz) { *(char*)str1 = *(char*)str2; str1 = (char*)str1 + 1; str2 = (char*)str2 + 1; sz--; } return start; } int main() { int arr1[] = { 1,2,3,4,5,6,7,8,9 }; my_memcpy(arr1+2,arr1,16); return 0; }
发现是不可以的,那么如果我要实现重叠部分的拷贝,应该怎么办,可以使用库函数中的memmove函数!
接下来就来探究一下memmove函数!
介绍并使用memmove函数
和memcopy相比,memmove可以完成重叠部分的拷贝工作!
int main() { int arr1[] = { 1,2,3,4,5,6,7,8,9 }; memmove(arr1+2,arr1,16); return 0; }
发现可以进行重叠部分的拷贝!
模拟实现memmove函数:
那么应该怎样拷贝,才不会出现在memcpy中的情况呢,这里分为三种情况:
1、源头地址在目的地的左侧,源头低地址,目的地高地址。
此时不会再出现被覆盖的情况。
2、源头地址在目的地的左右侧,源头高地址,目的地低地址。
3、源头和目的地没有重叠部分
这种情况怎么拷贝都行,只要空间够用即可。
以上这几种情况就是memmove函数的思路!
以下是代码:
void* my_memmove(void* str1, void* str2, size_t num)
{
void* start = str1;
int sz = num;
if (str1 > str2)
{
while (sz)
{
str2 = (char*)str2+1;
str1 = (char*)str1 + 1;
sz--;
}
while (sz != num)
{
*(char*)str1 = *(char*)str2;
str1 = (char*)str1-1;
str2 = (char*)str2 - 1;
sz++;
}
}
else
{
while (sz--)
{
*(char*)str1 = *(char*)str2;
str1 = (char*)str1 + 1;
str2 = (char*)str2 + 1;
}
}
return start;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9 };
my_memmove(arr1,arr1+2,12);
return 0;
}