C语言 <string.h> 常用库函数一览(上)

本篇介绍C语言提供的这些库函数:内存块和字符串的拷贝字符串连接内存块和字符串比较

一、Copy

1. memcpy

memcpy

函数原型: void * memcpy ( void * destination, const void * source, size_t num );

函数功能:从 source 指向的内存空间拷贝 num 个字节的内容到 destination 指向的内存空间

  • 函数可以拷贝任意类型的数据
  • 函数会精确的拷贝 num 个字节,应该保证目标空间大于等于 num 个字节
  • 当内存重叠时建议使用 memmove

参数列表:

  • destination:拷贝目标起始空间的地址
  • source:拷贝源头起始空间的地址
    const :函数中 source 指向的空间内容不应该被修改
  • num:拷贝的字节
    size_tunsigned 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

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

strcpy

函数原型:char * strcpy ( char * destination, const char * source );

函数功能:将 source 指向的字符串拷贝到 destination 指向的字符数组中

函数拷贝时遇到 source‘\0’ 时拷贝结束,‘\0’ 也会拷贝到 destination 指向的数组中

  • source 指向的字符串需要包含 ‘\0’
  • 目标空间足够大,能保证可以容纳拷贝的字符串以及 ‘\0’
  • 重叠拷贝请使用 memmove

参数列表:

  • 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

strncpy

函数原型:char * strncpy ( char * destination, const char * source, size_t num );

函数功能:拷贝 source 指向的字符串中前 num 个字符到 destination 指向的字符数组中

source 指向的字符串不足 num 个字符时,以 ‘\0’ 补充

  • 目标空间足够大,能保证可以容纳拷贝的 n 个字符
  • 拷贝 num 个字符之后,不会主动加上 ‘\0’
  • 重叠拷贝请使用 memmove

参数列表:

  • destination:指向拷贝目标数组的起始地址
  • source:指向拷贝源头的字符串起始地址
    const :函数中 source 指向的字符串不应该被修改
  • num:拷贝的字节个数
    size_tunsigned 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

strcat

函数原型:char * strcat ( char * destination, const char * source );

函数功能:source 指向的字符串追加到 destination 字符数组之后

destination 字符数组中的 ‘\0’ 开始追加,追加到 source 指向的字符串遇到 ‘\0’ 时停止

参数列表:

  • 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

strncat

函数原型:char * strncat ( char * destination, const char * source, size_t num );

函数功能:source 指向的字符串前 num 个字符追加到 destination 字符数组之后

destination 字符数组中的 ‘\0’ 开始追加
source 指向的字符串长度大于 num 则追加 num 个字符后,加上 ‘\0’
小于 num 则追加到 source 指向的字符串 ‘\0’

参数列表:

  • destination:指向追加目标数组的起始地址
  • source:指向追加源头字符串的起始地址
    const :函数中 source 指向的字符串不应该被修改
  • num:追加的字节个数
    size_tunsigned 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

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_tunsigned 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

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

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_tunsigned 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;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值