[C] 内存操作函数 与 字符处理函数


字符处理函数

函数 参数为真条件
iscntrl 任何控制字符
isspace 空白字符(包括空格' ',换页'\f',回车'\r',换行'\n',制表符'\t'/'\v'
isdigit 十进制数字0~9
isxdigit 十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F
islower 小写字母a~z
isupper 大写字母A~Z
isalpha 字母a~zA~Z
isalnum 字母或者数字,a~z,A~Z,0~9
ispunct 标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph 任何图形字符
isprint 任何可打印字符,包括图形字符和空白字符
  • 换行符\n和回车符\r的区别:
    换行符另起一行,光标来到行首。回车符不另起新行,光标来到行首,如果再输入内容就把原内容覆盖掉了。

字符转换函数:

int tolower(int c);
int toupper(int c);

函数参数和返回值都是int类型是因为C语言都默认使用了ASCII的编码方式,用ASCII码表示了一些特定的字符。这里使用int也可以通过返回-1来标记无效值。


内存操作函数

memcpy

[memcpy的官方文档]:http://www.cplusplus.com/reference/cstring/memcpy/?kw=memcpy

函数的作用是:【从source的内存位置开始向后拷贝num个字节的数据到destination的内存位置】。

void *memcpy(void *destination, const void *source, size_t num);
  • 这里的参数num的单位是字节
  • 函数参数和返回值都是void*类型,说明拷贝的大小不固定,要视最后的num参数而定,函数最后返回了destination
  • 因为此函数不是字符串的库函数,所以不仅可以拷贝字符数组,还可以拷贝整数数组等,所以在遇到 '\0' 的时候并不会停下来。
  • 如果sourcedestination有任何的重叠,复制的结果都是未定义的,绝对不允许缓冲区重叠。

模拟实现

void *Memcpy(void * dest, const void * src, size_t num) {
	assert(dest != NULL);
	assert(src != NULL);
	char *pdest = (char*)dest;
	char *psrc = (char*)src;
	for (size_t i = 0; i < num; ++i) {
		pdest[i] = psrc[i];
	}
	return dest;
}
  • for循环中不可以像之前strncpy一样输入dest[i] = src[i];因为void*类型参数不可以解引用以及加减一个整数,这一语句就相当于进行解引用与整数加减。
    所以采取的解决方案是:对void*类型参数强转char*类型参数,再进行处理。
  • 这里对void*类型参数进行char*的强制类型转换,是想让系统按照字节来拷贝,如果强转为int*传入参数不够一个int参数的字节数,那么就无法正常处理了,所以还是按照挨个字节进行处理最为科学。
  • 程序还存在一个隐含的风险:缓冲区重合问题。就是上面所说的sourcedestination有重叠现象,复制的结果是未定义的。

什么是缓冲区重合
在这里插入图片描述
memcpy 的原理就是按照src的内存内容向dest内存逐个进行拷贝,如果出现上图中的缓冲区重叠现象,就会影响到dest内容的拷贝,造成与src内容不同的拷贝错误。
【如图就是最后一个字符的拷贝错误】
缓冲区重叠问题发生原因:dest指针处于src缓冲区之内,这是标准库认为的。所以像下图中的情况就不属于缓冲区重叠现象:
在这里插入图片描述
从图中可以看到dest指针位于src缓冲区之外,虽然拷贝影响了源内存的内容,但是根据库函数的定义,它是不属于缓冲区重叠的。

所以就引出了可以解决缓冲区重合问题的允许缓冲区重合的函数memmove


memmove

[memmove的官方文档]:http://www.cplusplus.com/reference/cstring/memmove/?kw=memmove

函数的作用与memcpy相同,都是【按字节拷贝内存空间中的内容】。

扫描二维码关注公众号,回复: 5333683 查看本文章
void *memmove ( void *destination, const void *source, size_t num );
  • memcpy的差别就是memmove函数处理的源内存块和目标内存块是允许重叠的。如果源空间和目标空间出现重叠,就得使用memmove函数处理。

模拟实现

void *Memmove(void * dest, const void * src, size_t num) {
	assert(dest != NULL);
	assert(src != NULL);
	char *pdest = (char*)dest;
	char *psrc = (char*)src;
	if (pdest >= psrc && pdest <= psrc + num) {		//是标准库认为的缓冲区重合行为
		//解决方案:从后往前拷贝
		for (int64_t i = num - 1; i >= 0; --i) {
			pdest[i] = psrc[i];
		}
	}
	else {
		for (size_t i = 0; i < num; ++i) {
			//不属于缓冲区重合,就按照原来的memcpy方式进行拷贝
			pdest[i] = psrc[i];
		}
	}
	return dest;
}

  • 从后往前的拷贝方式避免了源内存顺序拷贝所存在的问题。
  • 第一个for语句中使用int64_t的数据类型,而不使用size_t,解决了 2 个问题:
  1. i>=0 恒成立。
    如果使用size_t,那么循环对参数进行修改,到达0时,再进行--操作,就会变成一个很大的数,因为内存中负数是按照补码的形式存放的,所以看似是减为了一个负数而跳出循环,实则恒为一个正数,是死循环现象。使用有符号的int64_t类型就可以避免无符号类型带来的问题。
  2. 如果num传入为0,那么参数就从很大的数开始运算。
    同样是无符号类型的弊端,传入参数为0时,在第一个for循环中需要对num进行减一操作,所以还是变成了一个非常大的数,一定不会满足题意。同样选择有符号的int64_t类型进行规避。

memcmp

[memcmp的官方文档]:http://www.cplusplus.com/reference/cstring/memcmp/?kw=memcmp

函数的作用及参数基本与strncmp完全相同,都是进行内容比较。
memcmp是【比较两内存空间中的内容】,strncmp是比较两字符串。

int memcmp ( const void * ptr1, const void * ptr2, size_t num );

因为与strncmp函数几乎完全相同,所以不进行赘述了,读者可以通过上一篇字符串处理函数博客中查看strncmp函数的模拟实现过程。


  • 小结

本博客所讲的内存操作函数和字符串相关函数最大的区别就是‘\0’无关,只与内存字节数有关,使用时一定要注意参数的单位是字节。

对于大量的字符处理函数不必强制记忆,只需有一个知晓函数存在的程度即可,使用时可以查阅相关文档直接使用。

猜你喜欢

转载自blog.csdn.net/qq_42351880/article/details/86777516