C语言之字符串+内存函数

目录

一 字符函数 

1.strlen函数(求字符串长度)

1.1 :函数介绍

1.2 strlen的三种模拟实现

1.3易错点

2 strcpy函数(拷贝字符串)

2.1函数介绍

2.2 strcpy模拟实现

2.3 strncpy函数

3 strcat函数(追加字符串)

3.1 函数介绍

3.2strcat模拟实现

         3.3 strncat函数(可指定追加的字符个数)

4 strcmp函数(比较字符串)

4.1函数介绍

4.2strcmp模拟实现

4.3 strncmp函数

5 strstr函数(查找连续的子字符串)

5.1函数介绍

 5.2strstr模拟实现

6 strtok函数

6.1函数介绍

7 strerror函数

7.1函数介绍

 7.1.1 errno

7.2 字符分类函数 

7.3 转换函数 (针对字母大小写)

二 内存函数

8 memcpy函数

8.1 函数介绍

8.2 memcpy的模拟实现

9 memmove函数

9.1函数介绍

 9.2 memmove模拟实现

9.3 memmove的简要分析

10.memcmp函数的使用


一 字符函数 

1.strlen函数(求字符串长度)

size_t strlen( const char *string )

1.1 :函数介绍

1) strlen 函数返回的是在字符串中 '\0' 前面出现的字符个数(不包含 '\0' )
2)参数指向的字符串必须要以 '\0' 结束。
3)注意函数的返回值为 size_t(相当于unsigned int) ,是无符号数( 易错

1.2 strlen的三种模拟实现

1)计数器

#include<stdio.h>
#include<assert.h>
int my_strlen(const char* s)
{
	assert(s);
	int count = 0;
	while (*s!='\0')
	{
		count++;
		s++;
	}
	return count;
}

2)递归

#include<stdio.h>
#include<assert.h>
int my_strlen(const char* str)
{
	assert(str);
	if (*str == '\0')
	{
		return 0;
	}
	else
	{
		return 1 + my_strlen(str+1);
	}
}

3)指针相减

#include<stdio.h>
#include<assert.h>
int my_strlen(const char* str)
{
	assert(str);
	const char*  ret = str;
	while (*ret != '\0')
	{
		ret++;
	}
	return ret - str;
}

1.3易错点

int main()
{
	if (strlen("abc") - strlen("abcdef") > 0)//无符号数相减的结果也是无符号数,这里的-3会转换成无符号数--一个非常大的正数
		printf(">");//这里打印的结果为>
	else
		printf("<=");
	return 0;
}

strlen函数的返回值为无符号数,结果为负数会转换为一个特别大的正数

2 strcpy函数(拷贝字符串)

char *strcpy( char *strDestination, const char *strSource )

2.1函数介绍

1)源字符串必须以'\0'结束,返回值为目标指针
2)会将源字符串中的'\0'也拷贝到目标空间中
3)目标空间必须足够大可改变,以确保能存放源字符串

2.2 strcpy模拟实现

char*strcpy(char* dest, const char* src)
{
	assert(dest && src);
	char* ret = dest;
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}

2.3 strncpy函数


char*strncpy(char*strDest,const char *strSource,size_t count)

比起strcpy,strncpy可以指定比较的字符个数,其余和strcpy用法一样

易错点

int main()
{
	char arr1[] = "xxxxxxxxxxxx";
	char arr2[] = "he";
	strncpy(arr1, arr2, 5);//原本arr2只有两个字符,剩下的三个默认传'\0'
	printf("%s\n", arr1);

}

3 strcat函数(追加字符串)

char *strcat( char *strDestination, const char *strSource )

3.1 函数介绍

1)原字符串必须以'\0'结束,返回值为目标指针
2)目标空间必须足够大,以确保能存放源字符串
3)目标空间必须可改变

3.2strcat模拟实现

char *my_strcat(char* dest, char* src)
{
	char* ret = dest;
	assert(dest && src);
	while (*dest != '\0')
	{
		dest++;
	}
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}

3.3 strncat函数(可指定追加的字符个数)

char *strncat( char *strDest, const char *strSource, size_t count )

#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[20] = "hello\0xxxxxxxxx";
	char arr2[] = "world";
	strncat(arr1, arr2, 7);
	printf("%s\n", arr1);//arr1变为helloworld\0xxxx
}

4 strcmp函数(比较字符串)

int strcmp( const char *string1, const char *string2 )

4.1函数介绍

注意:比较对应字符的ASCII码值的大小,不是比较字符串长度

返回值

第一个字符串大于第二个字符串,则返回大于 0 的数字
第一个字符串等于第二个字符串,则返回 0
第一个字符串小于第二个字符串,则返回小于 0 的数字

4.2strcmp模拟实现

#include<stdio.h>
#include<assert.h>
int my_strcmp(const char* str1, const char* str2)
{
	assert(*str1 && *str2);
	while (*str1 == *str2)
	{
		if (*str1 =='\0')//两字符串完全相等时,str1指向'\0'时,str2也会指向'\0'
		{
			return 0;
		}
		str1++;
		str2++;

	}
	return *str1 - *str2;
	/*if (*str1 > *str2)
	{
		return 1;
	}
	else
		return -1;*/
}

4.3 strncmp函数

int strncmp( const char *string1, const char *string2, size_t count )

使用:

#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abcdeg";
	printf("%d\n", strncmp(arr1, arr2, 5));//链式访问(函数中调用函数)
	return 0;
}

比较到出现对应字符不相等或者一个字符串结束或者count个字符全部比较完

返回值和strcmp相同 

5 strstr函数(查找连续的子字符串)

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

5.1函数介绍

str1为被查找的字符串,str2为要查找的字符串

返回值:

1)如果找到了,返回后面字符串在前面字符串中首次出现的地址

2)如果找不到,返回空指针

使用:

#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[] = "abcdefabcdef";
	char arr2[] = "bcd";
	char* ret = strstr(arr1, arr2);
	if (NULL == ret)
		printf("没找到\n");
	else
		printf("%s\n", ret);//结果:bcdefabcdef
	return 0;
}
//char arr[]="";//空字符串
//char *p="";//空字符串;
//char*p=NULL//空指针

 5.2strstr模拟实现

#include<stdio.h>
#include<string.h>
char *my_strstr(const char *str1,const char *str2)
{
	assert(*str1 && *str2);
	char* s1 = str1;
	char* s2 = str2;
	char* tmp = s1;
	if (!*str2)
	{
		return ((char*)str1);
	}//当str2字符串为""时--》\0
	while (*tmp)
	{
		s1 = tmp;
		s2 = str2;
		while (*s1 && *s2 && * s1 == *s2)
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			return (char*)tmp;
		}
		tmp++;
	}
	return NULL;
}
int main()
{
	char arr1[] = "abbbcdef";
	char arr2[] = "bbc";
	char* ret = my_strstr(arr1, arr2);
	if (NULL == ret)
	{
		printf("没找到\n");
	}
	else
		printf("%s\n", ret);
	return 0;
}

6 strtok函数

char* strtok(char* str, const char* sep)

6.1函数介绍

1)  sep 参数是个字符串,定义了用作分隔符的字符集合
2)  第一个参数指定一个字符串,它包含了 0 个或者多个由 sep 字符串中一个或者多个分隔符分割的标记。

3)  strtok 函数找到 str 中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。         (注: strtok函数会改变被操作的字符串,所以在使用 strtok 函数切分的字符串一般都是临时拷贝      的内容并且可修改)
4)  strtok 函数的第一个参数不为 NULL ,函数将找到 str 中第一个标记, strtok 函数将保存它在字符串中的位置。
5)  strtok 函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标
记。
6)  如果字符串中不存在更多的标记,则返回 NULL 指针。

使用:

#include<stdio.h>
#include<string.h>
int main()
{
	const char* p = "@.";
	char arr[] = "[email protected]";
	char buf[50] = { 0 };
	strcpy(buf, arr);
	char* str = NULL;
	for (str = strtok(buf, p); str != NULL; str = strtok(NULL, p))
	{
		printf("%s\n", str);
	}
	return 0;
}

运行截图:

 

7 strerror函数

 char *strerror( int errnum )

7.1函数介绍

用于传入错误码,打印错误信息

c语言中规定了一些错误码,而strerror函数可以帮我们把错误码翻译成错误信息

  • 0    No  error
  • 1    Operation  not  permitted
  • 2    No  such  file  or  directory
  • 3    No  such  process
  • .............

 7.1.1 errno

errno是c语言提供的一个全局变量,可以直接使用,头文件为errno.h

当库函数使用的时候,发生错误会把errno这个全局的错误变量设置为本次执行库函数产生的错误码(即产生的错误码会存放在errno这个变量里)

使用:

#include<stdio.h>
#include<string.h>
#include<error.h>
int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (NULL == pf)
	{
		//出错误的原因是什么
		printf("%s\n", strerror(errno));
		return 0;
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

7.2 字符分类函数 

头文件:<ctype.h> 

函数           如果他的参数符合下列条件就返回真
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       任何可打印字符,包括图形字符和空白字符

7.3 转换函数 (针对字母大小写)

int tolower(int c)

int toupper(int c)

#include<stdio.h>
#include<string.h>
int main()
{
	char ch = 0;
	ch = getchar();
	if (islower(ch))
	{
		ch = toupper(ch);//输入为小写-->大写
	}
	else
	{
		ch = tolower(ch);//输入为大写--》小写
	}
	printf("%c\n", ch);
	return 0;
}

二 内存函数

8 memcpy函数

void *memcpy( void *dest, const void *src, size_t count )

8.1 函数介绍

1)  函数 memcpy src 的位置开始向后复制count 个字节的数据到 dest 的内存位置。
2)  这个函数在遇到 '\0' 的时候并不会停下来。
3)  如果 src dest 有任何的重叠,复制的结果都是未定义的。
4) 返回值为dest
使用:
#include<stdio.h>
#include<string.h>
int main()
{
    int arr3[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr4[5] = { 0 };
	memcpy(arr4, arr3, 20);//20还可以写成5*sizeof(arr3[0])
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%d\n", arr4[i]);//1 2 3 4 5
	}
	return 0;
 }

8.2 memcpy的模拟实现

#include<stdio.h>
#include<assert.h>
void* my_memcpy(void* dest, const void* src,size_t num)
{
	void* ret = dest;
	assert(dest && src);
	while (num--)
	{
		*(char*)dest = *(char*)src;
		//*(char*)dest++ = *(char*)src++;//这种写法编译器不支持

		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return ret;
}
int main()
{
	int arr3[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr4[5] = { 0 };
	my_memcpy(arr4, arr3 + 5, 5 * sizeof(arr3[0]));//拷贝内存时不可重叠(vs支持重叠的,其他编译器不一定)
	memcpy(arr4, arr3, 20);//20还可以写成5*sizeof(arr3[0])
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%d\n", arr4[i]);//1 2 3 4 5
	}
	return 0;
}

即如果源内存和目标内存出现重叠,memcpy就无法处理,这时我们就需要memmove函数进行处理。

9 memmove函数

void *memmove( void *dest, const void *src, size_t count )

9.1函数介绍

这里的count单位为字节

可以理解为是memcpy的升级版,处理的源内存和目标内存是可以重叠的

使用: 

#include<stdio.h>
#include<string.h>
int main()
{
	int arr3[] = { 1,2,3,4,5,6,7,8,9,10 };
	memmove(arr3 + 2, arr3, 20);
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%d\n", arr3[i]);//1 2 1 2 3
	}
}

 9.2 memmove模拟实现

#include<stdio.h>
#include<string.h>
void* my_memmove(void* dest, const void* src, size_t num)//num单位为字节
 //src赋给dest
{
	void* ret = dest;
	assert(dest && src);
	//前——>后
	if(src > dest)
	{
		while (num--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	else
	{
		while (num--)
		{
			*((char*)dest + num) = *((char*)src + num);
		}
	}
	return ret;

}
int main()
{
	int arr3[] = { 1,2,3,4,5,6,7,8,9,10 };
	memmove(arr3 + 2, arr3, 20);
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%d\n", arr3[i]);//1 2 1 2 3
	}
}

src——赋给——dest 

9.3 memmove的简要分析

dest<src时 src只能从前往后依次赋值给dest

如果从后往前的话(当src指向原本是4的位置时,此时已经被赋值成6,这样打印结果就会出现问题)

 src<dest<src+num时src<dest且两者有重叠部分

同理:src此时只能从后往前依次赋值给dest

 

dest>src时 这种情况那种顺序都行,但为了方便,和上面那种情况都归为dest>src的情况(从后往前赋值)

 

综上:memcpy-->拷贝不重叠的内存

          memmove-->处理(重叠,不重叠都行)的内存拷贝

注意:VS2019里的memcpy比较完善,也可以处理重叠内存拷贝

10.memcmp函数的使用

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

用法:与strcmp相比,除了比较字符,还可以比较整型

           比较从ptr1和ptr2指针开始的num个字节

返回值

 使用:

#include<stdio.h>
#include<string.h>
int main()
{
	int arr1[] = { 1,2,7,4,5 };
	int arr2[] = { 1,2,3,4,5 };//这里比较的字节顺序是(VS:小端字节序方式)07 00 00 00
	int ret = memcmp(arr1, arr2, 9);//7>3
    printf("%d\n", ret);//打印结果为大于0的数
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_63451038/article/details/123699654