c语言基础知识——字符串和内存函数(下)

目录

前言

 一、上文补充

strerror

 注意要点

字符分类函数

字符转换函数 

二、内存操作函数

memcpy

 注意要点

模拟实现

 特殊情况

memmove

 注意要点

模拟实现

memcmp

注意要点 

memset 

注意要点 


前言

前面,我们较为详细地讲述了,对字符进行操作的字符串函数。相信大家对字符串的拷贝、追加和比较函数以及字符串中的查找函数有了一定的认识。那么,问题来了,如果一个数组的元素,不是字符,而是其他各种类型,那该怎样进行拷贝和比较等操作呢?接下来我们就来探索,c语言中的内存操作函数。复习字符串函数请跳伞

c语言基础知识——字符串和内存函数(上)

 一、上文补充

其实上一篇博客所写的字符串函数中,除了对字符串进行操作的函数外,还有报告错误信息、对字符进行操作的函数。

strerror

char * strerror ( int errnum );

 注意要点

  • 这个函数是错误信息报告函数,参数是一个整形,0、1、2、3每个数字对应一个错误信息。
  • 该函数返回一个指针,指向对应的错误信息字符串。

例如:

#include
#include
int main()
{
	printf("错误信息为:%s\n", strerror(0));
	printf("错误信息为:%s\n", strerror(1));
	printf("错误信息为:%s\n", strerror(2));
	printf("错误信息为:%s\n", strerror(3));
	printf("错误信息为:%s\n", strerror(4));
	return 0;
}

打印的结果如下:

c语言基础知识——字符串和内存函数(下)_第1张图片

 实际使用的时候肯定不会这么用滴,那啥时候能用上呢?该怎么用呢?我们来看看下面这段代码:

等一下,看之前,先做一些铺垫吧!因为这段代码涉及到文件操作部分。我就简略说一下吧。

首先我们定义一个“文件指针”指向一个名为“test.txt”的文件,接着用fopen函数打开。

结果本地没有这个文件,打不开,我们就要打印错误信息。那错误信息从哪里来呢?

原来,c语言的库函数在调用失败的时候,会将一个错误码(数字)存放在一个叫errno的变量中。

联想到strerror的参数是一个整形,……这下大家应该猜到该怎么使用了吧?只需把errno当作参数传给strerror函数,就可以得到想要的错误信息啦!

#include 
#include 
#include //必须包含的头文件
int main()
{
	FILE* pFile;
	pFile = fopen("test.txt", "r");//fopen调用失败返回NULL
	if (pFile == NULL)
	printf("错误信息为: %s\n", strerror(errno));//errno:是最新出现的错误码,
												//再出现其他类型错误就被改成其他值了
	return 0;
}

打印结果如下:

 是不是很简单呐?

字符分类函数

函数 参数满足以下条件就返回真
iscntrl
任何控制字符
isspace
空白字符:空格 ‘ ’ ,换页 ‘\f’ ,换行 '\n' ,回车 ‘\r’ ,制表符 '\t' 或者垂直制表符 '\v'
isdigit
十进制数字 0~9
isxdigit
十六进制数字,包括所有十进制数字,小写字母 a~f ,大写字母 A~F
islower
小写字母 a~z
isupper
大写字母 A~Z
isalpha
字母 a~z A~Z
isalnum
字母或者数字, a~z,A~Z,0~9
ispunct
标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph
任何图形字符
isprint
任何可打印字符,包括图形字符和空白字

字符转换函数 

int tolower ( int c ); //将大写变为小写返回
int toupper ( int c );//将小写变成大写返回

这些函数基本上就是判断一些字符是否是想要的字符,如果是,返回真,否则返回假。比较简单。也不需要记,用到的时候查找一下即可。

二、内存操作函数

memcpy

void * memcpy ( void * destination, const void * source, size_t num );

 注意要点

  • 函数memcpysource的位置开始向后复制num个字节的数据到destination的内存位置。
  • 这个函数在遇到 '\0' 的时候并不会停下来。
  • 如果sourcedestination有任何的重叠,复制的结果都是未定义的。

前两个很容易理解,因为传入参数类型未知,只能事先将需要拷贝的字节数传参进去,从而进行拷贝操作,而且只会按照字节数拷贝,不会管是否越界。

第三个要点是什么意思呢?我们先来模拟实现一下这个函数,自然就明白了。

模拟实现

#include
void* my_memcpy(void* dest, const void* str, size_t num)
{
	assert(dest && str);
	void* tmp = dest;
	while (num--)
	{
		*((char*)dest + num) = *((char*)str + num);
	}
	return tmp;
}

思路是这样的:

  • 首先判断传过来的参数是否为空指针,如果是,就报错。
  • 接着,用临时指针变量tmp把目的地址存起来,因为到最后要返回这个地址。
  • 然后,进入循环,每次循环都拷贝一个字节的内容,拷贝完使num自减,下次循环就可以拷贝前一个字节的内容。
  • 最后,返回目的指针。 

 特殊情况

那么如果两个指针指向的字符串有重叠,会发生什么呢? 

假如有一个数组,其元素为 1,2,3,45,6,7,8,9,0。我们想要把3,4,5,6拷贝放到1,2,3,4位置,理想结果是3,4,5,6,5,6,7,8,9,0。那我们用代码实现一下:

#include
int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,0 };
	memcpy(arr1, arr1+2, 16);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
}

如果使用标准c语言的编译器,运行结果是 5 6 5 6 5 6 7 8 9 0,这是为什么呢?

原来,当我们将6拷贝放到4的位置时,4这个位置就会被覆盖为6,原来的4就丢失了,后面拷贝4,3时,遇到的元素就是6,5,所以不能正确拷贝。(注意,vs编译器对该函数进行了改良,结果与理想结果一样。)

memmove

void * memmove ( void * destination , const void * source , size_t num );

 注意要点

  • memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
  • 如果源空间和目标空间出现重叠,就得使用memmove函数处理。

 也就是说,memmove函数与memcpy函数一样,唯一区别就是memmove可以实现重叠目标空间的拷贝!!!

那要怎么实现呢?还记得刚刚那个案例吗?一个数组,其元素为 1,2,3,45,6,7,8,9,0。我们想要把3,4,5,6拷贝放到1,2,3,4位置,理想结果是3,4,5,6,5,6,7,8,9,0。出错的原因在于,当6拷贝到4这个位置时,4会被覆盖。(当5拷贝到3这个位置时,3会被覆盖。)那我们先拷贝3和4不就得了吗?反正前面的1和2覆盖掉没啥影响。嗷,原来只需把从后往前拷贝编程从前往后拷贝就完了。先别急,再想想,真的是这样吗?

如果要把1,2,3,4拷贝放到3,4,5,6位置呢?是不是又需要从后向前拷贝了?

嗷~到这里,大家应该明白了,如果目的指针指向的位置在源指针前面,就要从前往后拷贝。先拷贝3,再拷贝4,这样才不会使3,4被5,6提前覆盖。

c语言基础知识——字符串和内存函数(下)_第2张图片  如果目的指针指向的位置在源指针后面,就要从后往前拷贝。先拷贝4,再拷贝3,这样才不会使4,3被2,1提前覆盖。

 c语言基础知识——字符串和内存函数(下)_第3张图片

 明白了这个原理,我们就可以实操来实现一下这个函数啦:

模拟实现

#include
void* my_memmove(void* dest,const void* str,int num)
{
	void* tmp = dest;
	if (dest > str)
	{
		while (num--)
		{
			((char*)dest)[num] = ((char*)str)[num];
		}	
	}
	else
	{
		while (num--)
		{
			*((char*)dest)-- = *((char*)dest)--;
		}
	}
	return tmp;
}

memcmp

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

注意要点 

  • 比较从ptr1ptr2指针开始的num个字节
  • 返回值如下:

c语言基础知识——字符串和内存函数(下)_第4张图片

 和strcmp类似,只不过添加了要比较的字节数。

模拟实现比较简单,大家自己动手试试吧!

memset 

void * memset (void * ptr,int value,size_t num ) ;

注意要点 

  •  将 ptr 指向的内存块的第一个字节数设置为指定值。
  • num是要修改的字节数。
  • 每个字节的内存都会被修改为指定值。

一般用于初始化,直接将指针所指向的内存全部初始化成0。但是注意第三点,如果num是指针后内存的大小,value是1,那么后面这些内存中每个字节的内存的内容都会被改为1,而不是将每个元素改为1,所以不能这样初始化。 又因为这个特性,大小端不同的环境有时也会带来不同的结果。

 好啦,字符串和内存函数到这里就介绍完了。同志们有没有什么问题呢?欢迎在评论区留言。

你可能感兴趣的:(c语言基础知识,c语言)