strcpy memcpy strcmp strlen strcat strstr strchr实现及辨析

本文深入探讨了C语言中常见的字符串操作函数,包括strcpy、memcpy、strcmp、strlen、strcat、strstr和strchr的实现原理及注意事项。通过对比strcpy与memcpy,解析了它们在复制内容、方法和用途上的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.strcpy实现

以下是C标准库写法,其最大隐患是有可能 dst 的长度小于 strlen(src) + 1 造成越界访问,或是类似 strcpy(ptr + 1, ptr) 发生重叠造成无限循环。

#include <assert.h>

char* strcpy(char* dst, const char* src)
{
	assert(dst);
	assert(src);
	char* ret = dst;
	while ((*dst++ = *src++) != '\0');
	return ret;
}

以下为改进后的版本,不会因为 dst 处于区间 (src, src + len) 而导致错误

#include <assert.h>

char* strcpy_s(char* dst, const char* src) {
	assert(dst);
	assert(src);

	char* ret = dst;
	int size = strlen(src) + 1;

	if (dst == src) {
		return ret;
	}

	/* 以下表示 dst 处于区间 (src, src + size) 而发生重叠,此时需要从后面开始拷贝 */
	if (dst > src && dst < src + size) {
		dst = dst + size - 1;
		src = src + size - 1;
		while (size--) {
			*dst-- = *src--;
		}
	}
	else {		//这里表示正常拷贝
		while (size--) {
			*dst++ = *src++;
		}
	}
	return ret;
}

2.memcpy实现

#include <assert.h>

void* memcpy(void* dst, const void* src, size_t size) {
	assert(dst);
	assert(src);

	void* ret = dst;
	char* pdst = (char*)dst;
	char* psrc = (char*)src;

	if (pdst == psrc) {
		return ret;
	}

	/* 如果 pdst 处于区间 (psrc, psrc + size) 则发生重叠 */
	if (pdst > psrc && pdst < psrc + size) {
		pdst = pdst + size - 1;
		psrc = psrc + size - 1;
		while (size--) {
			*pdst-- = *psrc--;
		}
	}
	else {	//无重叠
		while (size--) {
			*pdst++ = *psrc++;
		}
	}

	return ret;
}

 3.strcpy与memcpy的区别

1、复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。

2、复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。

3、用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy。

 4.strcmp实现

函数原型:extern int strcmp(const char* s1, const char* s2);

  • 当s1 < s2时,返回值 < 0
  • 当s1 = s2时,返回值 = 0
  • 当s1 > s2时,返回值 > 0

 注意:该函数是按字典序逐个字符进行比较,直到遇上不相同的字符或是'\0'为止,和字符串长度没有关系。

#include <assert.h>

int strcmp(const char *str1, const char *str2)
{
	assert(str1);
	assert(str2);

	int ret = 0;
	while (!(ret = *str1 - *str2) && *str1) {
		++str1;
		++str2;
	}

	if (ret < 0) {
		ret = -1;
	}
	else if (ret > 0) {
		ret = 1;
	}
	
	return ret;
}

5.strlen实现

#include <assert.h>
 
size_t strlen(const char* str)
{
	assert(str);
	unsigned int len = 0;

	while(*str++ != '\0')
		++len;
	return len;
}

 注意:strlen的结果不包含末尾的结束符'\0',而sizeof的结果包含'\0',并且sizeof作用于数组时是返回数组的大小。

6.strcat实现

 注意没有处理出现重叠的情况,如果两个指针相同会造成无限循环

char* strcat(char* str1, const char* str2) {
	assert(str1);
	assert(str2);

	char* p = str1;
	while (*p)
		++p;
	while (*p++ = *str2++);
	return str1;
}

7.strstr 实现

函数原型:extern char *strstr(char *str1, const char *str2);

str1: 被查找的对象

str2: 要查找的目标

返回值:若str2是str1的子串,则返回str2在str1的首次出现的地址;如果str2不是str1的子串,则返回NULL

char *strstr(const char *str1, const char *str2) {
	char *cp = (char*)str1;
	char *s1, *s2;

	if (!*str2)
		return((char *)str1);

	while (*cp) {
		s1 = cp;
		s2 = (char *)str2;

		while (*s1 && *s2 && !(*s1 - *s2))
			s1++, s2++;
		if (!*s2)
			return(cp);

		cp++;
	}
	return(NULL);
}

 8.strchr实现

函数功能:返回首次出现ch的位置的指针,返回的地址是被查找字符串指针开始的第一个与ch相同字符的指针,如果s中不存在ch则返回NULL 

char* strchr(const char *s, char ch)
{
	assert(s);
 
	while(*s != '\0' && *s != ch)
        ++s;

	return *s ? s : NULL;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值