C语言进阶复习(三)--字符串函数&内存函数专题

           此文章重点讲字符串的库函数的使用和注意事项及模拟实现

前言C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。 字符串常量 适用于那些对它不做修改的字符串函数.

字符串函数

strlen

size_t strlen ( const char * str );

strlen:是专门用来求字符串长度的函数,从字符串首地址往后遍历,直到遇见’\0’停止。
1.字符串已经 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中 ‘\0’ 前面出现的字符个数(不包含 ‘\0’ )。
2.参数指向的字符串必须要以 ‘\0’ 结束。

strlen函数的模拟实现,三种方式
//计数器方式
int my_strlen(const char * str)
{
	int count = 0;
	while(*str)
	{
		count++;
		str++;
	}
	return count;
}

//不创建临时变量计数器   递归
int my_strlen(const char * str)
	{
	if(*str == '\0')
		return 0;
	else
		return 1+my_strlen(str+1);
}

//指针-指针的方式 同类型指针加减操作为两者之间的元素个数
int my_strlen(char *s)
{
	char *p = s;
	while(*p != '\0' )
	{
		p++;
	}
	return p-s;
}


strcpy

char* strcpy(char * destination, const char * source );

strcpy:将源指向的C字符串复制到目标指向的字符数组中,包括终止null(’\0’)字符(并在这一点上停止).
1.源字符串必须以 ‘\0’ 结束。
2.会将源字符串中的 ‘\0’ 拷贝到目标空间。
3.目标空间必须足够大,以确保能存放源字符串。
4.目标空间必须可变

char *my_strcpy(char *dest, const char*src)
{
	char *ret = dest;
	assert(dest != NULL);//计算表达式,当结果为FALSE时,打印诊断消息并终止程序
	assert(src != NULL);
	while(*src)
	{
		*dest=*src;
		dest++;
		src++;
	}
	//或者简便写
	//while((*dest++ = *src++))
	//{
	//		;
	//}
	return ret;
}

strcat

char * strcat ( char * destination, const char * source );

将源字符串的副本附加到目标字符串。目标中的终止空字符由源的第一个字符覆盖,并且在结尾包含一个空(’\0’)字符,这两个字符串在目标串destination中连接形成的新字符串.
1.源字符串必须以 ‘\0’ 结束。
2.目标空间必须有足够的大,能容纳下源字符串的内容。
3.目标空间必须可修改。

char *my_strcat(char *dest, const char*src)
{
	char *ret = dest;
	assert(dest != NULL);
	assert(src != NULL);
	while(*dest)
	{
		dest++;  //先走到终止字符'\0';
	}
	while((*dest++ = *src++)) //再追加
	{
		;
	}
	return ret;
}

strcmp

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

这个函数开始比较每个字符串的第一个字符。如果它们相等,继续比较下一个,直到字符不同或终止为空字符为止。
规则:
str1字符串大于str2字符串,则返回大于0的数字
str1字符串等于str2字符串,则返回0
str1字符串小于str2字符串,则返回小于0的数字

//比较两个字符串大小
int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);
	while (*str1 && *str2)
	{
		if (*str1 > *str2)
		{
			return 1;
		}
		else if (*str1 < *str2)
		{
			return -1;
		}
		else   //是相同的了,就往后继续判断
		{
			++str1;
			++str2;
		}
	}
	if (*str1 == '\0' && *str2 == '\0') //两个串前面都相同且长度相等  等于
	{
		return 0;
	}
	else if (*str1 == '\0')  //前面都相等,但是,str1长度较小  小于
	{
		return -1;
	}
	else       //前面都相等,但str2长度较小   大于
	{
		return 1;
	}
}

strstr

char * strstr ( const char *str1, const char *str2 );

返回一个指向str1中第一次出现的str2的指针,如果str2不是str1的一部分,则返回一个空指针.
(可以说是串里简单的模式匹配算法)。

char *my_strstr(const char* str1, const char* str2 )
{
	assert(str1);
	assert(str2);
	char *cp = str1;
	char *substr = str2;
	char *s1 = NULL;
	if(*str2 == '\0')//模式串为空串 直接返回nULL
		return NULL;
	while(*cp)
	{
		s1 = cp;
		substr = str2;  //要是模式串一旦没全部匹配,便回溯到开头,主串往后移一位,继续判断
		while(*s1 && *substr && (*s1 == *substr))
		{
			s1++;
			substr++;
		}
		if(*substr == '\0')//模式串匹配完毕 返回主串匹配的位置的指针
			return cp;
		cp++;//没全部匹配,主串往后移一位  
	}
}

strncpy

char * strncpy ( char * destination, const char * source, size_t num );

将源的第一个num字符复制到目标。如果源C字符串的结尾是在复制num字符之前找到,填充目的空间,直到写入的num字符个数减少为0;如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个

//strncpy 指定几个字符拷贝
char* my_strncpy(char* dst, const char* src,size_t num)
{
	assert(dst);
	assert(src);
	assert(num > 0);
	char* str1 = dst;
	while(*dst++ = *src++)
	{
		num--;
	}
	while (num--)  //不够,后面追加 '\0'
	{
		*dst= '0';
	}
	return str1;
}

strncat

char * strncat ( char * destination, const char * source, size_t num );

将源的第一个num字符附加到目标,加上一个终止的空字符;
如果源中的C字符串的长度小于num,那么只复制到终止null字符之前的内容.

//strcat 指定拼接几个字符
char* my_strncat(char* dst, const char* src, size_t num)
{
	assert(dst);
	assert(src);
	assert(num > 0);
	char* str1 = dst;
	while (*dst)
	{
		dst++;
	}
	while (num--)
	{
		*dst++ = *src++;
	}
	return str1;
}

strncmp

int strncmp ( const char * str1, const char * str2, size_t num );

与strcmp类似,但是条件多了比较个数的限制。
比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完.

//指定几个字符比较大小
char* my_strncmp(const char* dst, const char* src, size_t num)
{
	assert(dst);
	assert(src);
	assert(num > 0);
	int ret = 0; 
	while (num--)
	{
	   ret = *dst - *src;
		if (ret > 0)
		{
			return 1;
		}
		else if(ret < 0)
		{
			return  -1;
		}
		else
		{
			src++;
			dst++;
		}
	}
	if (ret==0)
		{
			return 0;
		}
}	

内存函数(操作的单位是字节)

memcpy

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

内存拷贝,按字节,可以拷贝任意类型
1.函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
2.这个函数在遇到 ‘\0’ 的时候并不会停下来。
3.如果source和destination有任何的重叠,复制的结果都是未定义的

//memcpy函数:以字节为单位对字符串的拷贝
void* my_memcpy(void* dst, const void* src, size_t num)
{
	assert(dst&&src);
	char* str1 = (char*)dst;
	const char* str2 = (char*)src;
	while (num--)
	{
		*str1++ = *str2++;
	}
	return str1;
}


memmove

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

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

//menmove函数:解决memcpy函数不能解决的内存重叠问题
void* my_memmove(void* dst, const void* src, size_t num)
{
	assert(dst&&src);
	char* str2 = (char*)dst;
	const char* str1= (char*)src;
	
	if (str2 >str1 && str2 < str1+ num)//前面的往后面拷贝
	{
		// 从后往前拷贝
		for (int i = num - 1; i >= 0; --i)
		{
			str2[i] = str1[i];
		}
	}
	else
	{
		// 从前往后拷贝
		for (int i = 0; i < num; ++i)
		{
			str2[i] = str1[i];
		}
	}
	return dst;
}
int main()
{
	int a1[9] = { 1, 2, 3, 4, 5 };
	my_memcpy(a1 + 4, a1, sizeof(int)* 5);//前面的往后面拷贝,可能把内存重叠部分先拷贝成新的值 再最后又拷贝给目的地址就是这个新的值(不同的值)了,
	//本来要的结果是 1,2,3,4,1,2,3,4,5     此结果确是1,2,3,4,1,2,3,4,1       其中内存重叠的部分的5先被拷贝成了1 ,最后拷贝过去的也就是1而不是5    解决方法就是从后往前开始拷贝,就不会在自身拷贝走之前被提前拷贝成别的值了
	//所以就要用memmove函数来解决memcpy函数不能解决的内存重叠问题
	printf("%d\n", my_memcpy(a1 + 4, a1, sizeof(int)* 5));
	

	my_memmove(a1 + 4, a1, sizeof(int)* 5);
	printf("%d\n", my_memmove(a1 + 4, a1, sizeof(int)* 5));


	int a2[9] = {-1,-1,-1, -1,1, 2, 3, 4, 5 };
	my_memcpy(a2, a2 + 4, sizeof(int)* 5);
	printf("%d\n", my_memcpy(a2, a2 + 4, sizeof(int)* 5));

	my_memmove(a2, a2 + 4, sizeof(int)* 5);//后面的往前面拷贝 不存在上述情况,直接从前往后拷贝即可
	printf("%d\n", my_memmove(a2, a2 + 4, sizeof(int)* 5));
	return 0;
}

memcmp

int memcmp( const void *buf1, const void *buf2, size_t count );

功能:比较内存区域buf1和buf2的前count个字节。该函数是按字节比较的
返回值:
当buf1 < buf2时,返回值<0
当buf1 = buf2时,返回值=0
当buf1 > buf2时,返回值>0

memset

void* memset(void* str,int value,size_t num)

功能:以str的起始位置开始的num个字节的内存区域用整数value进行填充
返回值:目标str内存起始地址
memset用来对一段内存空间全部设置为某个字符,一般用在对定义的字符串进行初始化为‘‘/0’。如果用malloc分配的内存,一般只能使用memset来初始化(直接操作内存里的字节)。
memset可以方便的清空一个结构类型的变量或数组,它可以一字节一字节地把整个数组设置为一个指定的值。

字符分类函数

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

发布了40 篇原创文章 · 获赞 65 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_44785014/article/details/104566875
今日推荐