本篇介绍C语言提供的这些库函数:内存块和字符串的拷贝,字符串连接、内存块和字符串比较
文章目录
一、Copy
1. memcpy
函数原型: void * memcpy ( void * destination, const void * source, size_t num ); |
函数功能:从 source 指向的内存空间拷贝 num 个字节的内容到 destination 指向的内存空间
- 函数可以拷贝任意类型的数据
- 函数会精确的拷贝 num 个字节,应该保证目标空间大于等于 num 个字节
- 当内存重叠时建议使用 memmove
参数列表:
- destination:拷贝目标起始空间的地址
- source:拷贝源头起始空间的地址
const :函数中 source 指向的空间内容不应该被修改 - num:拷贝的字节数
size_t :unsigned int 的重命名
返回值:destination 的起始地址
接收返回值,转化为特定类型后,可以实现链式访问等
例子:
#include <stdio.h>
//打印数组内容
void print(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
int main()
{
int arr[5] = { 1,2,3,4,5 };
int arr_copy[5] = { 0 };
int sz = sizeof(arr) / sizeof(arr[0]);
printf("拷贝前:");
print(arr_copy, sz);
//拷贝 arr数组的前 12 个字节(3 个整形)到 arr_copy 数组中
memcpy(arr_copy, arr, 12);
printf("拷贝后:");
print(arr_copy, sz);
return 0;
}
输出:
拷贝前:0 0 0 0 0
拷贝后:1 2 3 0 0
memcpy 的模拟实现:
void* 类型的指针可以接收任意类型的地址,但 void* 的指针却不能进行整数加减,以及间接访问操作,所以这里强转为 char* 后逐字节拷贝即可
#include <assert.h>
void* my_memcpy(void* dest, void* src, int num)
{
//需要对指针进行间接访问,指针不能为空指针
assert(dest != NULL && src != NULL);
void* ret = dest;
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return ret;
}
2. memmove
函数原型:void * memmove ( void * destination, const void * source, size_t num ); |
函数功能、参数列表、返回值与 memcpy 相同,点击即可跳转,但 memmove 可以实现重叠内存拷贝
例子:
#include <stdio.h>
//打印数组
void print(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int sz = sizeof(arr) / sizeof(arr[0]);
printf("移动前:");
print(arr, sz);
//重叠拷贝:
//将数组 arr 中的 3 4 5 6 7 拷贝到数组 arr 中 5 6 7 8 9 的位置
memmove(arr + 4, arr + 2, 20);
printf("移动后:");
print(arr, sz);
return 0;
}
输出:
移动前:1 2 3 4 5 6 7 8 9 10
移动后:1 2 3 4 3 4 5 6 7 10
memmove 模拟实现:
与 memcpy 的模拟实现相同:需要转换为 char* 指针进行操作
对于重叠拷贝的内存做出如下分析
#include <assert.h>
void* my_memmove(void* dest, void* src, int num)
{
//需要对指针进行间接访问,指针不能为空指针
assert(dest != NULL && src != NULL);
void* ret = dest;
if (dest < src)
{
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;
}
3. strcpy
函数原型:char * strcpy ( char * destination, const char * source ); |
函数功能:将 source 指向的字符串拷贝到 destination 指向的字符数组中
函数拷贝时遇到 source 的 ‘\0’ 时拷贝结束,‘\0’ 也会拷贝到 destination 指向的数组中
参数列表:
- destination:指向拷贝到的目标数组的起始地址
- source:指向拷贝源头的字符串起始地址
const :函数中 source 指向的字符串不应该被修改
返回值:destination 的起始地址
将返回值作为其他函数的参数,可以实现链式访问等
例子:
#include <stdio.h>
#include <string.h>
int main()
{
char arr[20] = "Hello!";
char* p = "Hello reader!";
//拷贝前
printf("%s\n", arr);
//拷贝后
strcpy(arr, p);
printf("%s\n", arr);
return 0;
}
输出:
Hello!
Hello reader!
strcpy 模拟实现:
逐字节拷贝即可,遇到 '\0’时停止
注意:‘\0’ 也需要拷贝
#include <assert.h>
char* my_strcpy(char* dest, const char* src)
{
//需要对指针进行间接访问,指针不能为空指针
assert(dest != NULL && src != NULL);
//'\0' ASCII码为 0,可以做为循环的结束条件
char* ret = dest;
while (*dest++ = *src++)
;
return ret;
}
4. strncpy
函数原型:char * strncpy ( char * destination, const char * source, size_t num ); |
函数功能:拷贝 source 指向的字符串中前 num 个字符到 destination 指向的字符数组中
source 指向的字符串不足 num 个字符时,以 ‘\0’ 补充
参数列表:
- destination:指向拷贝目标数组的起始地址
- source:指向拷贝源头的字符串起始地址
const :函数中 source 指向的字符串不应该被修改 - num:拷贝的字节个数
size_t :unsigned int 的重命名
返回值:destination 的起始地址
将返回值做为其他函数参数,可以实现链式访问
例子:
#include <stdio.h>
#include <string.h>
int main()
{
char arr[20] = "Hello! Hello!";
char* p = "reader";
//拷贝前
printf("%s\n", arr);
//拷贝后
strncpy(arr, p, 6);
printf("%s\n", arr);
return 0;
}
输出:
Hello! Hello!
reader Hello!
strncpy 模拟实现:
拷贝 num 个字节,不足以 ‘\0’ 补充
#include <assert.h>
char* my_strncpy(char* dest, const char* src, int num)
{
//需要对指针进行间接访问,指针不能为空指针
assert(dest != NULL && src != NULL);
char* ret = dest;
while (num && (*dest++ = *src++))
num--;
while (num--)
*dest++ = '\0';
return ret;
}
二、Concatenation
1. strcat
函数原型:char * strcat ( char * destination, const char * source ); |
函数功能:source 指向的字符串追加到 destination 字符数组之后
从 destination 字符数组中的 ‘\0’ 开始追加,追加到 source 指向的字符串遇到 ‘\0’ 时停止
- source 指向的字符串应该包含 ‘\0’
- destination 指向的字符串应该包含 ‘\0’
- destination 空间保证可以容纳自身和追加后的字符串及 ‘\0’
- 重叠追加因使用 strncat
参数列表:
- destination:指向追加到的目标数组的起始地址
- source:指向追加源头的字符串起始地址
const :函数中 source 指向的字符串不应该被修改
返回值:destination 的起始地址
将返回值做为其他函数参数,可以实现链式访问
例子:
#include <string.h>
int main()
{
char arr[20] = "Hello ";
char* p = "readers!";
//追加前
printf("%s\n", arr);
//追加后
strcat(arr, p);
printf("%s\n", arr);
return 0;
}
输出:
Hello
Hello readers!
strcat 模拟实现:
先找到 destination 字符数组中的 ‘\0’,然后与 strcpy 模拟实现相同
逐字节拷贝,遇到 '\0’时停止 ‘\0’ 也需要拷贝
#include <assert.h>
char* my_strcat(char* dest, const char* src)
{
//需要对指针进行间接访问,指针不能为空指针
assert(dest != NULL && src != NULL);
char* ret = dest;
while (*dest != '\0')
dest++;
//'\0' ASCII码为 0,可以做为循环的结束条件
while (*dest++ = *src++)
;
return ret;
}
2. strncat
函数原型:char * strncat ( char * destination, const char * source, size_t num ); |
函数功能:source 指向的字符串前 num 个字符追加到 destination 字符数组之后
从 destination 字符数组中的 ‘\0’ 开始追加
source 指向的字符串长度大于 num 则追加 num 个字符后,加上 ‘\0’
小于 num 则追加到 source 指向的字符串 ‘\0’ 处
- destination 指向的字符串应该包含 ‘\0’
- destination 空间保证可以容纳追加后的字符串
参数列表:
- destination:指向追加目标数组的起始地址
- source:指向追加源头字符串的起始地址
const :函数中 source 指向的字符串不应该被修改 - num:追加的字节个数
size_t :unsigned int 的重命名
返回值:destination 的起始地址
将返回值做为其他函数参数,可以实现链式访问
例子:
#include <stdio.h>
#include <string.h>
int main()
{
char arr[20] = "Hello! ";
//追加前
printf("%s\n", arr);
//追击后
strncat(arr, arr, 6);
printf("%s\n", arr);
return 0;
}
输出:
Hello!
Hello! Hello!
strncat 模拟实现:
先找到 destination 字符数组中的 ‘\0’,然后与 strncpy 模拟实现相似
注意:追加 num 个字节,未超时不处理,超过时末尾需要加上 ‘\0’
#include <assert.h>
char* my_strncat(char* dest, const char* src, int num)
{
//需要对指针进行间接访问,指针不能为空指针
assert(dest != NULL && src != NULL);
char* ret = dest;
while (*dest != '\0')
dest++;
while (num && (*dest++ = *src++))
num--;
*dest = '\0';
return ret;
}
三、Comparison
1. memcmp
函数原型:int memcmp ( const void * ptr1, const void * ptr2, size_t num ); |
函数功能:ptr1 指向的内存块前 num 个字节内容和 ptr2 指向的内存块前 num 个字节内容比较
可以比较任意类型的数据
从第一个字节内容开始比较,相等比较下一个字节内容
- 如果 ptr1 指向的字节内容大于 ptr2 指向的字节内容,返回大于 0 的数字
- ptr1 指向的字节内容小于 ptr2 指向的字节内容,返回小于 0 的数字
- 比较的 num 个字节内容都相等时返回 0
参数列表:
- ptr1 指向比较的内存块
const :函数中 ptr1 指向的内存块内容不应该被修改 - ptr2 指向比较的内存块
const :函数中 ptr2 指向的内存块内容不应该被修改 - num:比较的字节个数
size_t :unsigned int 的重命名
返回值:
- ptr1 大于 ptr2 返回大于 0 的数字
- ptr1 等于 ptr2 返回等于 0 的数字
- ptr1 小于 ptr2 返回小于 0 的数字
例子:
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[5] = { 1,2,3,4,4 };
int arr2[5] = { 1,2,3,4,5 };
int arr3[5] = { 1,2,3,4,5 };
int arr4[5] = { 1,2,3,4,5 };
int arr5[5] = { 1,2,3,4,5 };
int arr6[5] = { 1,2,3,5,5 };
printf("%d\n", memcmp(arr1, arr2, 20));
printf("%d\n", memcmp(arr3, arr4, 20));
printf("%d\n", memcmp(arr5, arr6, 20));
return 0;
}
输出结果:
-1
0
-1
memcmp 模拟实现:
void* 类型的指针可以接收任意类型的地址,但 void* 的指针却不能进行整数加减,以及间接访问操作,所以这里强转为 char* 后逐字节内容比较即可
#include <assert.h>
int my_memcmp(const void* ptr1, const void* ptr2, int num)
{
//需要对指针进行间接访问,指针不能为空指针
assert(ptr1 != NULL && ptr2 != NULL);
while (num && (*(char*)ptr1 == *(char*)ptr2))
{
ptr1 = (char*)ptr1 + 1;
ptr2 = (char*)ptr2 + 1;
num--;
}
if (num == 0)
return 0;
else
return *(char*)ptr1 - *(char*)ptr2;
}
2. strcmp
函数原型:int strcmp ( const char * str1, const char * str2 ); |
函数功能:str1 指向的字符串和 str2 指向的字符串比较
从第一个字符开始比较,相等则比较下一个字符
- 如果 str1 指向的字符大于 str2 指向的字符,返回大于 0 的数字
- str1 指向的字符小于 str2 指向的字符,返回小于 0 的数字
- str1 和 str2 都指向 ‘\0’ 时,返回0
参数列表:
- str1 指向比较的字符串
const :函数中 str1 指向的字符串不应该被修改 - str2 指向比较的字符串
const :函数中 str2 指向的字符串不应该被修改
返回值:
- str1 大于 str2 返回大于 0 的数字
- str1 等于 str2 返回等于 0 的数字
- str1 小于 str2 返回小于 0 的数字
例子:
#include <stdio.h>
#include <string.h>
int main()
{
char* p1 = "good";
char* p2 = "gooD";
char* p3 = "good";
char* p4 = "good";
char* p5 = "gooD";
char* p6 = "good";
printf("%d\n", strcmp(p1, p2));
printf("%d\n", strcmp(p3, p4));
printf("%d\n", strcmp(p5, p6));
return 0;
}
输出结果:
-1
0
-1
strcmp 模拟实现:
逐字符比较即可
#include <assert.h>
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 != NULL && str2 != NULL);
while (*str1 == *str2)
{
if (*str1 == '\0')
return 0;
str1++;
str2++;
}
return *str1 - *str2;
}
3. strncmp
函数原型:int strncmp(const char* str1, const char* str2, size_t num); |
函数功能:str1 指向的字符串前 num 个字符和 str2 指向的字符串前 num 个字符比较
从第一个字符开始比较,相等比较下一个字符
- 如果 ptr1 指向的字符大于 ptr2 指向的字符,返回大于 0 的数字
- ptr1 指向的字符小于 ptr2 指向的字符,返回小于 0 的数字
- 比较的 num 个字符都相等时 返回 0
参数列表:
- str1 指向比较的字符串
const :函数中 str1 指向的字符串不应该被修改 - str2 指向比较的字符串
const :函数中 str2 指向的字符串不应该被修改 - num:比较的字符个数
size_t :unsigned int 的重命名
返回值:
- ptr1 大于 ptr2 返回大于 0 的数字
- ptr1 等于 ptr2 返回等于 0 的数字
- ptr1 小于 ptr2 返回小于 0 的数字
例子:
#include <stdio.h>
#include <string.h>
int main()
{
char* p1 = "HeLLo!";
char* p2 = "HEllo!";
char* p3 = "HeLLO!";
char* p4 = "Hello!";
char* p5 = "HELLO!";
char* p6 = "Hello!";
printf("%d\n", strncmp(p1, p2, 2));
printf("%d\n", strncmp(p3, p4, 2));
printf("%d\n", strncmp(p5, p6, 2));
return 0;
}
输出
-1
0
-1
strncmp 模拟实现:
逐字符比较
若中途 str1 指针和 str2 指针都遇到 ‘\0’ 时也表示字符串相等
#include <assert.h>
int my_strncmp(const char* str1, const char* str2, int num)
{
assert(str1 != NULL && str2 != NULL);
while (num && *str1 == *str2)
{
if (*str1 == '\0')
return 0;
str1++;
str2++;
num--;
}
if (num == 0)
return 0;
else
return *str1 - *str2;
}