前言
C语言内置了相当多的函数供用户使用,这些函数被集成封装在标准库里,用户只需要调用即可完成相应特定的功能,这些函数称为库函数。
下面是百度百科中给出的相关定义:
? 库函数一般是指编译器提供的可在C源程序中调用的函数。由于版权原因,库函数的源代码一般是不可见的,但在头文件中你可以看到它对外的接口。
? ------百度百科
本文的主要内容是向读者介绍常用的与字符串和内存相关的系列库函数的使用及其功能的自实现,使读者对库函数的内部实现机理有较深入的了解。
与字符串有关的库函数
库函数strlen
strlen函数是一个用来计算一个字符串的长度(单位是字节)的。以下是C和C++官网给出的相关函数信息:
strlen函数的使用
下面通过一个小案例来调用该函数。
从运行的结果可以看出,该库函数计算的字符串长度是6个字节,也就是abcdef这6个字符所占的大小。下面再看一个例子,与其进行对比:
? 这次的结果令人感到震惊,这个16是怎么来的?这个数组就只放了6个字符啊。该函数的使用应注意以下几点:
-
strlen计算字符串的长度,返回该字符串的长度; -
实际参数必须是首元素的地址,以指针接收; -
指针从前往后遍历,直到找到\0为止,计算出字符串的大小。 由以上分析,结合案例,我们发现案例2中的数组没有\0,也就是说计算的结果是随机值,返回值是没有参考意义的。
strlen函数的模拟实现
通过前面的使用和分析,我们可以自己编写代码来模拟实现strlen函数的基本功能。
#include <stdio.h>
size_t my_strlen(char* str)
{
size_t count = 0;
while (*str)
{
count++;
str++;
}
return count;
}
int main()
{
char arr[] = "abcdefg";
int r = my_strlen(arr);
printf("字符串arr的长度为:%u\n", r);
return 0;
}
下图是其实现细节:
从首元素地址处开始,如果其内容不是字符\0,记数器的值自增1,指针向后移动1个字节,直到指针指向地址处保存的值是\0结束循环,当前计数器的值就代表了字符串的总长度。
库函数strcpy
strcpy是一个实现字符串拷贝功能的库函数。以下是C和C++官网给出的信息:
strcpy函数的使用
下面通过一个小案例来调用该函数。
从调用该函数的结果来看,arr1的内容发生了变化,其值变成了arr2的内容。下面是该函数的一些特点:
- strcpy函数的功能是将源字符串拷贝到目标字符串;
- 源字符串的结束标志\0也会被拷贝到目标字符串中;
- 拷贝完成后,目标字符串的长度会发生改变,等于源字符串长度;
- 目标字符串的空间应该足够大。
strcpy函数的模拟实现
通过前面的使用和分析,我们可以自己编写代码来模拟实现strcpy函数的基本功能。
#include <stdio.h>
char* my_strcpy(char* dest, char* src)
{
char* ret = dest;
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char arr1[] = "abcd";
char arr2[] = "ijk";
printf("拷贝前: %s", arr1);
my_strcpy(arr1, arr2);
printf("\n拷贝后: %s", arr1);
return 0;
}
下图是其实现细节:
dest指针指向目标字符串的起始位置,src指针指向源字符串的起始位置,每拷贝完1个字符后指针都后移1字节。将源字符串中所有内容拷贝到目标字符串中,当\0拷贝完后,结束整个拷贝过程。
库函数strcmp
strcpy是一个实现字符串比较功能的库函数。以下是C和C++官网给出的信息:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8Dp2wraL-1632012230846)(D:\typora文件\有关字符串和内存的库函数的自实现图片\8.png)]
strcmp的使用
下面通过一个小案例来调用该函数。
下面是该函数的特点:
- strcmp函数实现两字符串的比较功能;
- 从第一个字符开始比较,当第一个字符串对应位置的值和第二个字符串相等时,两字符串相等,否则不相等;
- 该函数返回值为等于0、大于0和小于0这三种情况之一。
strcmp函数的模拟实现
通过前面的使用和分析,我们可以自己编写代码来模拟实现strcmp函数的基本功能。
#include <stdio.h>
int my_strcmp(char* str1, char* str2)
{
while (*str1 == *str2 && *str1 != '\0')
{
str1++;
str2++;
}
if (*str1 == '\0' && *str2 == '\0')
{
return 0;
}
return (*str1 - *str2);
}
int main()
{
char arr1[] = "abc";
char arr2[] = "abd";
int r = my_strcmp(arr1, arr2);
if (r == 0)
{
printf("arr1 等于 arr2\n");
}
else
{
printf("arr1 不等于 arr2\n");
}
}
下图是其实现细节:
str1指向第一个字符串的首元素地址,str2指向第二个字符串的首元素地址,如果对应位置数据相同则将两指针后移,一旦发现对应数据不相等随即结束循环,返回此时ASCII码之差;如果从开头一直遍历到字符串末尾时,对应数据仍是相等的,说明两字符串是相等的,返回0。
库函数strcat
strcat是一个实现字符串追加功能的库函数。以下是C和C++官网给出的信息:
strcat函数的使用
下面通过一个小案例来调用该函数。
下面是该函数的特点:
- strcat函数在目标字符串发末尾处追加一个字符串;
- 追加的起始位置为目标字符串的\0处;
- 源字符串的\0也会追加到目标字符串中,成为目标字符串的结束标志;
- 目标字符串的空间应足够大。
strcat函数的模拟实现
通过前面的使用和分析,我们可以自己编写代码来模拟实现strcat函数的基本功能。
#include <stdio.h>
char* my_strcat(char* dest, char* src)
{
char* ret = dest;
while (*dest)
{
dest++;
}
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char arr1[6] = "ab";
char arr2[] = "cd";
printf("追加前字符串:%s",arr1);
my_strcat(arr1, arr2);
printf("\n追加后字符串:%s", arr1);
return 0;
}
下图是其实现细节:
库函数strstr
strstr是一个判断某字符串是否是另一个字符串的子串的库函数。以下是C和C++官网给出的信息:
strstr函数的使用
下面通过一个小案例来调用该函数。
下面是该函数的主要特点:
- strstr函数实现的功能是判断源字符串是否是目标字符串的子串;
- 如果源字符串是目标字符串的子串,则返回目标字符串中第一次出现该子串的起始地址;
- 如果源字符串不是目标字符串的子串,则返回空指针NULL。
strstr函数的模拟实现
通过前面的使用和分析,我们可以自己编写代码来模拟实现strstr函数的基本功能。
#include <stdio.h>
char* my_strstr(char* dest, char* src)
{
char* pc = dest;
char* s1 = dest;
char* s2 = src;
if (*src == '\0')
{
return dest;
}
while (*pc)
{
s1 = pc;
s2 = src;
while ((*s1 != '\0') && (*s2 != '\0') && (*s1 == *s2))
{
s1++;
s2++;
}
if (*s2 == '\0')
{
return pc;
}
pc++;
}
return NULL;
}
int main()
{
char arr1[] = "abc";
char arr2[] = "ac";
char* r = my_strstr(arr1, arr2);
if (r == null)
{
printf("arr2 不是 arr1 的子串\n");
}
else
{
printf("arr2 是 arr1 的子串\n");
}
return 0;
}
下图是其实现细节:
如果源字符串是目标字符串的子串,则pc不需要遍历完目标字符串所有位置,反之,pc需一直移动直到目标字符串结尾。
与内存拷贝有关的函数
库函数memcpy
memcpy库函数具备内存拷贝的功能。以下是C和C++官网给出的信息:
memcpy函数的使用
下面通过一个小案例来调用该函数。
下面是该函数的主要特点:
- memcpy函数可以将特定位置的数据拷贝到另一个位置处;
- 参数接收的地址处存放的数据类型不限定;
- 每次拷贝一个字节的内容,直到拷贝完设定的大小;
- 不能拷贝具有重叠空间的数据。
memcpy函数的模拟实现
通过前面的使用和分析,我们可以自己编写代码来模拟实现memcpy函数的基本功能。
#include <stdio.h>
void* my_memcpy(void* dest, void* src, size_t count)
{
void* ret = dest;
while (count--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return ret;
}
int main()
{
int arr1[] = { 1, 2, 3, 4, 5};
int arr2[] = { 2, 3, 4, 5, 6};
printf("拷贝前的数组: ");
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d ", arr2[i]);
}
my_memcpy(arr2, arr1, 3);
printf("\n拷贝后的数组: ");
for (i = 0; i < 5; i++)
{
printf("%d ", arr2[i]);
}
return 0;
}
下图是其实现细节:
从该函数的内部实现可以看出,每次拷贝1个字节,所有无论所接收的数据为何种类型,只需要控制好需拷贝的字节数即可实现多类型数据拷贝,故其通用性较强。
库函数memmove
memmove函数可以实现重叠内存空间的数据拷贝。以下是C和C++官网给出的信息:
memmove函数的使用
下面通过一个小案例来调用该函数。
该函数的特点如下:
- memmove函数可以将特定位置的数据拷贝到另一个位置处;
- 参数接收的地址处存放的数据类型不限定;
- 每次拷贝一个字节的内容,直到拷贝完设定的大小;
- 能拷贝具有重叠空间的数据。
memmove函数的模拟实现
通过前面的使用和分析,我们可以自己编写代码来模拟实现memmove函数的基本功能。
#include <stdio.h>
void* my_memmove(void* dest, void* src, size_t count)
{
void* ret = dest;
if (dest < src)
{
while (count--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
else
{
while (count--)
{
*((char*)dest + count) = *((char*)src + count);
}
}
return ret;
}
int main()
{
int arr[] = { 0, 1, 2};
printf("拷贝前数组:");
int i = 0;
for (i = 0; i < 3; i++)
{
printf("%d ", arr[i]);
}
my_memmove(arr + 1, arr, 4);
printf("\n拷贝后数组:");
for (i = 0; i < 3; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
下图是其实现细节:
拷贝顺序取决与源空间地址与目标空间地址的相对位置,因为拷贝顺序选择不当会导致后续没来得及拷贝的数据被覆盖,从而后续拷贝出错。
结语
C语言标准提供的各类库函数之间既相互区别又相互联系,合理的选择库函数能达到所需的目的。对于针对字符串及字符数组相关的操作,选择字符串相关族的函数;对于其他类型的数组的有关数据的拷贝等操作应尽量选择内存相关函数来达到所需目的。
这就是今天文章的所有内容咯,希望各位小伙伴们都能有所收获,拜拜溜~~
|