string.h头文件中strcpy、memset等常见函数的使用介绍与模拟实现


------每日一图"地球情绪"

文章目录

目录

文章目录

前言

一、字符串操作函数

strlen

strcpy与strncpy

[strcpy( )](#strcpy( ))

[strncpy( )](#strncpy( ))

strcat与strncat

[strcat( )](#strcat( ))

[strncat( )](#strncat( ))

strcmp与strncmp

[strcmp( )](#strcmp( ))

[strncmp( )](#strncmp( ))

[strstr( )](#strstr( ))

[strtok( )](#strtok( ))

二、内存操作函数

memset

memcpy与memmove

memcpy

memmove

memcmp

memchr

总结



前言

即使我们已经进入了C++时代,但 string.h 中的一些函数仍然非常重要,尤其是当我们需要处理操作系统底层代码或与C语言交互时,尤为明显。

在string.h中,除了与字符串操作相关的函数(如strlen, strcpy, strcat, strcmp, strstr, strchr等)之外,还有一些内存操作函数,这些函数在C++中仍然非常有用,因为它们可以操作任意类型的内存块,而不仅仅是字符串。

本文总结string.h头文件中,经常用到的函数,主要包括:字符串操作函数与内存操作函数。


一、字符串操作函数

strlen

函数原型:

cpp 复制代码
#include <string.h>
size_t strlen(const char *str);

函数作用: 计算传入字符串的长度,即字符的个数,不包括字符串末尾的空字符\0(包括\n)。

**函数参数:**str,需要计算的字符串。

**函数返回值:**该字符串的长度。

使用注意:如果传入的字符串指针不是以 '\0' 结尾,函数的行为是未定义的,可能会一直访问内存直到遇到 '\0',导致程序错误。

使用示例:

cpp 复制代码
void test_strlen()
{
	const char* str = "hello strlen";
	cout << strlen(str) << endl;
    char str1[] = "hello world\n";
    cout << strlen(str1) << endl;
}

模拟实现:

cpp 复制代码
size_t my_strlen(const char* str = NULL)
{
	if (!str)return 0;

	size_t ret = 0;
	while (*str != '\0')
	{
		++ret;
		++str;
	}
	return ret;
}

strcpy与strncpy

strcpy( )

函数原型:

cpp 复制代码
#include <string.h>
char *strcpy(char *dest, const char *src);

函数作用: 将源字符串src (包括结束符\0)复制到目标字符串dest中。

函数参数:dest 目标字符串;src源字符串。

函数返回值: 指向目标字符串dest的指针。

使用注意:使用strcpy( ) 时,需要注意目标数组dest 的空间大小是否足够大,否则**如果源字符串长度过长,可能会导致缓冲区溢出。源字符串与目标内存区域不能重叠,**如果重叠,行为未定义。

使用示例:

cpp 复制代码
void test_strcpy()
{
	const char* str1 = "hello strcpy\n";
	char buffer[24];
	cout << strcpy(buffer, str1);
	cout << buffer;
}

模拟实现:

cpp 复制代码
char* my_strcpy(char* dest, const char* src)
{
	if (src == NULL || dest == NULL)return NULL;
	char* temp = dest;
	// 赋值表达式的结果就是所赋值的字符,即赋值后左边的值,也就是原来的*src的值
	while ((*dest++ = *src++)) { ; }
	return temp;
}

strncpy( )

函数原型:

cpp 复制代码
#include <string.h>
char *strncpy(char *dest, const char *src, size_t n);

函数作用: 从源字符串src 复制最多n 个字符到目标字符dest 中。strcpy的升级版,指定拷贝字节数量,可预防strcpy可能导致的缓冲区溢出的缺陷,。

函数参数: dest ,目标字符串;src ,源字符串(被复制字符串);n,要复制的最大字符数。

函数返回值: 指向目标字符串dest的指针。

使用注意:与 strcpy相比,它增加了安全性,但不保证目标字符串以 '\0' 结尾 (例如当 n 小于或等于 src的长度时)。使用者需要自己确保目标字符串的正确终止。

使用示例:

cpp 复制代码
void test_strncpy()
{
		const char* str1 = "hello strcpy\n";
		char buffer[24];
		cout << strcpy(buffer, str1);
		cout << strncpy(buffer,str1,strlen(str1)+1);//将\0也拷贝了
}

模拟实现:

cpp 复制代码
char* my_strncpy(char* dest, const char* src, size_t n)
{
	if (!dest || !src || n == 0)return dest;

	char* ret = dest;
	for (size_t i = 0; i < n; ++i)
	{
		*dest++ = *src;
		if (*src != '\0') src++;
	}
	return ret;
}

strcat与strncat

strcat( )

函数原型:

cpp 复制代码
#include <string.h>
char *strcat(char *dest, const char *src);

函数作用: 将源字符串src 追加到目标字符串dest的末尾。

函数参数: dest ,目标字符串;src,要追加的源字符串。

函数返回值: 指向目标字符串dest的指针。

使用注意:C标准库中dest与src同源时会发生未定义行为。 目标数组必须足够大,以容纳拼接后的两个字符串(包括 '\0'

使用示例:

cpp 复制代码
void test_strcat()
{
	char buffer[64] = "hello world";
	const char* str = "hello strcat\n";
	if (!strcat(buffer, buffer))cout << "NO" << endl;
	cout << buffer;
}

模拟实现:

cpp 复制代码
char* my_strcat(char* dest, const char* src)
{
	if (src == NULL || dest == NULL)return NULL;
	//C标准库中dest与src同源时会发生未定义行为
	if (dest == src)return NULL;
	char* temp = dest;
	while (*dest != '\0')dest++;

	while ((*dest++ = *src++)) { ; }
	return temp;
}

strncat( )

函数原型:

cpp 复制代码
#include <string.h>
char *strncat(char *dest, const char *src, size_t n);

函数作用: 将源字符串src 的最多前n个字符追加到目标字符串dest的末尾。实际复制的字节数 = min(sizeof (src), n),也就是说当n大于src的字节数时,只会拷贝strlen( src)个字节数。

函数参数: dest ,目标字符串;src ,要追加的源字符串;n,要追加的最大字符数。

函数返回值: 指向目标字符串dest的指针。

使用注意:比 strcat 更安全,因为它限制了追加的字符数。它会保证结果字符串以 '\0' 结尾。

使用示例:

cpp 复制代码
void test_strncat()
{
		char buffer[64] = "hello world";
		const char* str = ",hello strcat\n";
		strncat(buffer, str, 100);
		cout << buffer;
}

模拟实现:

cpp 复制代码
char* my_strncat(char* dest, const char* src, size_t n)
{
	if (!dest || !src || n == 0)return dest;

	char* ret = dest;
	while (*dest)++dest;

	for (size_t i = 0; i < n && src; ++i)
	{
		*dest++ = *src++;
	}
	*dest='\0';

	return ret;
}

strcmp与strncmp

strcmp( )

函数原型:

cpp 复制代码
#include <string.h>
int strcmp(const char *str1, const char *str2);

函数作用: 比较两个字符串**str1** 和**str2**。

函数参数: str1 ,第一个要比较的字符串;str2,第二个要比较的字符串。

函数返回值:

  • 小于0,如果**str1** 小于**str2**

  • 等于0,如果**str1** 等于**str2**

  • 大于0,如果**str1** 大于**str2**

使用注意:比较是基于字符的 ASCII 值进行的。

使用示范:

cpp 复制代码
void test_strcmp()
{
	const char* str1 = "abcdef";
	const char* str2 = "agb";

	cout << strcmp(str1, str2) << endl;
}

模拟实现:

cpp 复制代码
//更符合标准库风格的实现方式
int my_strcmp(const char* str1, const char* str2)
{
	if (str1 == NULL && str2 == NULL)return 0;
	if (str1 == NULL)return -1; 
	if (str2 == NULL)return 1;

	while (*str1 == *str2)
	{
		if (*str1 == '\0')return 0;
		++str1;
		++str2;
	}

	return (*str1 - *str2);
}

strncmp( )

函数原型:

cpp 复制代码
#include <string.h>
int strncmp(const char *str1, const char *str2, size_t n);

函数作用: 比较两个字符串**str1** 和**str2** 的最多前n个字符。

函数参数: str1 ,第一个要比较的字符串;str2 ,第二个要比较的字符串;n,要比较的最大字符数。

函数返回值:

  • 小于0,如果str1小于str2

  • 等于0,如果str1等于str2

  • 大于0,如果str1大于str2

使用注意:如果在比较完 n 个字符前遇到任一个字符串的 '\0',比较也会停止。

使用示例:

cpp 复制代码
void test_strncmp()
{
		const char* str1 = "abcdef";
		const char* str2 = "abc";
		cout << strncmp(str1, str2, strlen(str1)) << endl;
}

模拟实现:

cpp 复制代码
int my_strncmp(const char* dest, const char* src, size_t n)
{
	if (!dest || !src) 
	{
		// 更简洁的空指针处理
		if (!dest && !src) return 0;
		return (!dest) ? -1 : 1;
	}

	size_t i = 0;
	//这样写,当*dest!=*src时,src还会++,导致越界
	//while (i < n && *dest++ == *src++)
	while (i < n && *dest == *src)
	{
		++i;
		dest++;
		src++;
	}
	
	if (i == n)return 0;
	return (*(unsigned char*)dest - *(unsigned char*)src);
}

strstr( )

函数原型:

cpp 复制代码
#include <string.h>
char *strstr(const char *str1, const char *str2);

函数作用: 在字符串str1中查找子字符串**str2**的第一次出现。

函数参数: str1,要搜索的主字符串;str2,要查找的子字符串。

函数返回值: 指向子字符串第一次出现的指针,如果未找到则返回NULL。

使用注意:搜索包括终止空字符在内的所有字符,但通常用于查找非空子串。

使用示例:

cpp 复制代码
void test_strstr()
{
	char str[] = "This is a simple string";
	char* pch;
	pch = strstr(str, "simple");
	strncpy(pch, "sample",6);
	printf("%s\n", str);
	printf("%s\n", pch);
}

模拟实现:

cpp 复制代码
char* my_strstr(const char* str1, const char* str2)
{
	if (!str1 || !str2)return NULL;
	char* temp = (char*)str1;
	while (*temp)
	{
		//在里面定义防止误触while循环
		char* s1 = temp;
		char* s2 = (char*)str2;

		char* ret = s1;
		while (*s1 && *s2 && !(*s1 - *s2))
		{
			++s1;
			++s2;
		}
		if (!*s2)return ret;
		++temp;
	}
	return NULL;
}

strtok( )

函数原型:

cpp 复制代码
#include<string.h>
char *strtok(char *str, const char *sep)

函数作用 :将字符串 str 分割成一系列以\0结束的子串。在第一次调用时,str 指定要分割的字符串,后续调用应传入 NULL,函数会继续从上次的位置进行分割。
函数参数str :要分割的字符串,在第一次调用时指定,后续调用应为 NULLsep:包含分隔符的字符串。

函数返回值 :返回指向下一个找到的令牌的指针。如果没有更多令牌,则返回 NULL

使用注意:此函数会修改原始字符串,它在找到的分隔符处写入 '\0'

使用示例:

cpp 复制代码
void test_strtok()
{
	char temp[] = "hello world,hello strtok.hello XXX\n";
	char s[] = "[=";
	char* ret = strtok(temp, s);
	while (ret)
	{
		cout << ret << endl;
		ret = strtok(NULL, s);
	}
}

二、内存操作函数

这些内存操作函数在C++中仍然非常有用,因为它们可以操作任意类型的内存块,而不仅仅是字符串。

memset

函数原型:

cpp 复制代码
#include <string.h>
void *memset(void *ptr, int value, size_t num);

函数作用: 将内存区域**ptr** 的前n个字节设置为指定的值value

函数参数: ptr,要设置的内存区域;value,要设置的值;n,要设置的字节数。

函数返回值: 指向内存区域ptr的指针。

注意事项:常用于内存初始化(如清零)或设置特定模式。按字节进行设置。

使用示例:

cpp 复制代码
void test_memset()
{
	char buffer[64];
    //方便strcpy或其他操作之后直接打印字符串
	memset(buffer, '\0', sizeof(buffer));

	const char* str = "hello memset\n";
	strncpy(buffer, str, strlen(str));
	cout << buffer;
}

memcpy与memmove

memcpy

函数原型:

cpp 复制代码
#include <string.h>
void *memcpy(void *dest, const void *src, size_t n);

函数作用: 从源内存地址src复制n个字节到目标内存地址dest

函数参数: dest,目标内存地址;src,源内存地址;n,要复制的字节数。

函数返回值: 指向目标内存地址dest的指针。

注意事项:不处理内存重叠。如果源和目标内存区域有重叠,应使用 memmove 函数。它不关心数据的类型(void*),只进行纯粹的字节拷贝。

使用示例:

cpp 复制代码
struct Point
{
	int _x;
	int _y;
	Point(int x = 0, int y = 0)
		:_x(x),_y(y)
	{ }
};

void test_memcpy()
{
	const char* str = "hello memcpy\n";
	char buffer[24];
	cout << (char*)memcpy(buffer, str, strlen(str) + 1);

	Point p1(12, 12);
	Point p2;
	memcpy(&p2, &p1, sizeof(p2));
	cout << p2._x << ' ' << p2._y << endl;
}

memmove

函数原型:

cpp 复制代码
#include <string.h>
void *memmove(void *dest, const void *src, size_t n);

函数作用: 从源内存地址src复制n个字节到目标内存地址dest,正确处理内存重叠区域。

函数参数: dest,目标内存地址;src,源内存地址;n,要复制的字节数。

函数返回值: 指向目标内存地址dest的指针。

注意事项:与 memcpy 功能类似,但能够正确处理内存区域重叠的情况,可能比 memcpy 效率稍低。

使用示例:

cpp 复制代码
//它能除了内存重叠的情况:自己复制自己
void test_memmove()
{
	char str[24] = "aaaabbbb";
	cout << str << endl;
	memmove(str + 4, str, 4);
	cout << str << endl;
}

memcmp

函数原型:

cpp 复制代码
#include <string.h>
int memcmp(const void *ptr1, const void *ptr2, size_t n);

函数作用: 比较两个内存区域ptr1ptr2的前n个字节。

函数参数: ptr1,第一个内存区域;ptr2,第二个内存区域;n,要比较的字节数。

函数返回值:

  • 小于0,如果ptr1小于ptr2

  • 等于0,如果ptr1等于ptr2

  • 大于0,如果ptr1大于ptr2

注意事项:比较是按字节进行的,不关心数据的类型,也不在乎是否有 '\0' 字符。

使用示例:

cpp 复制代码
struct Point
{
	int _x;
	int _y;
	Point(int x = 0, int y = 0)
		:_x(x),_y(y)
	{ }
};
void test_memcmp()
{
	const char* str1 = "hello a";
	const char* str2 = "hello b";
	cout << memcmp(str1, str2, strlen(str1)) << endl;

	Point p1(1, 1);
	Point p2(1, 1);
	cout << memcmp(&p1, &p2, sizeof(p1));
}

memchr

函数原型:

cpp 复制代码
#include <string.h>
void *memchr(const void *ptr, int value, size_t n);

函数作用: 在内存区域ptr的前n个字节中搜索第一次出现值value的位置。

函数参数: ptr,要搜索的内存区域;value,要搜索的值;n,要搜索的字节数。

函数返回值: 指向第一次出现值value的指针,如果未找到则返回NULL。

使用示例:

cpp 复制代码
void test_memchr()
{
	char str[] = "hello memchr";
	cout << sizeof(str);
    //注意是字符型,因为memchr的形参二是int型
	char* pos = (char*)memchr(str, 'o', strlen(str));
	cout << pos << endl;
}

总结

本文总结了C语言string.h头文件中的常用字符串和内存操作函数。

字符串函数包括:strlen计算长度,strcpy/strncpy复制字符串,strcat/strncat连接字符串,strcmp/strncmp比较字符串,strstr查找子串,strtok分割字符串。

内存操作函数包括:memset内存设置,memcpy/memmove内存复制,memcmp内存比较,memchr内存查找。

这些函数在C++中仍然重要,特别是处理底层代码或与C交互时。本文详细说明了每个函数的原型、作用、参数、返回值和使用注意事项,并提供了模拟实现代码示例。

整理不易,读完点赞,手留余香~

相关推荐
dangdang___go43 分钟前
动态内存管理||malloc和free.realloc和calloc
c语言·开发语言·算法·动态内存管理
('-')1 小时前
《从根上理解MySQL是怎样运行的》第十三章笔记
数据库·笔记·mysql
cpp_25011 小时前
P5412 [YNOI2019] 排队
数据结构·c++·算法·题解·洛谷
LO嘉嘉VE1 小时前
学习笔记二十一:深度学习
笔记·深度学习·学习
kingmax542120081 小时前
图论核心算法(C++):包括存储结构、核心思路、速记口诀以及学习方法, 一站式上机考试学习【附PKU百练,相关练习题单】
c++·算法·图论·信奥赛·上机考试·百练·pku
罗湖老棍子1 小时前
【例9.15】潜水员(信息学奥赛一本通- P1271)
c++·算法·动态规划·二维费用背包
代码游侠2 小时前
学习笔记——数据结构学习
linux·开发语言·数据结构·笔记·学习
摇滚侠2 小时前
零基础小白自学 Git_Github 教程,发现工具寻找灵感,笔记04
笔记·github
xuanzdhc2 小时前
Gitgit
java·linux·运维·服务器·c++·git