字符串函数和内存函数

本篇主要介绍字符串的库函数操作内存的库函数的使用和注意事项

文章中的函数名是可以通过点击查看详细介绍的

目录

1.求字符串长度

1.1 strlen

2.长度不受限制的字符串函数

2.1 strcpy 字符串拷贝函数

2.2 strcat 字符串追加函数

2.3 strcmp

3.长度受限制的字符串函数介绍

3.1 strncpy

3.2 strncat

3.3 strncmp

4.字符串查找

4.1 strstr

4.2 strtok

5.错误信息报告

5.1 strerror

6.字符操作

7.内存操作函数

7.1 memcpy

7.2 memmove

7.3 memset

7.4 memcmp


前言

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

1.求字符串长度

1.1 strlen

size_t strlen(const char* str);

size_t 就是 unsigned int 类型。

注意:

  • 字符串已经'\0'作为结束标志,strlen函数返回的是在字符串中'\0'前面出现的字符个数(不包含'\0')。
  • 参数指向的字符串必须要以‘\0'结束。
  • 注意函数的返回值为size_t,是无符号的(易错)

示例:

#include
#include
int main()
{
	int len = strlen("abcedf");
	printf("%d\n", len);//6

	char arr[] = "abc\0def";
	len = strlen(arr);
	printf("%d\n", len);//3

	char arr1[3] = { 'a','b','c' };//没有 '\0'
	len = strlen(arr1);
	printf("%d\n", len);//随机值

	return 0;
}

返回值是 size_t 类型

#include
#include//VS不写头文件,会把返回值当为int类型
int main()
{
	//strlen函数的返回值是size_t - 无符号整形
	if (strlen("abc") - strlen("abcdef") > 0)
	{
		printf(">\n");//输出
	}
	else
	{
		printf("<=\n");
	}
	return 0;
}

自己模拟实现strlen函数,3种方法

1.计数器

2.递归

3.指针-指针

#include
#include

//1.计数器
int my_strlen1(const char* arr)
{
	assert(arr != NULL);
	int count = 0;
	while (*arr)
	{
		count++;
		arr++;
	}
	return count;
}

//2.递归
int my_strlen2(const char* arr)
{
	if (*arr != '\0')
	{
		return my_strlen2(arr + 1) + 1;
	}
	else
	{
		return 0;
	}
}

//3.指针-指针
int my_strlen3(const char* arr)
{
	char* start = arr;
	while (*arr != '\0')
	{
		arr++;
	}
	return arr - start;
}

int main()
{
	char arr[] = "bit";
	int ret = my_strlen3(arr);
	printf("%d\n", ret);
	return 0;
}

2.长度不受限制的字符串函数

2.1 strcpy 字符串拷贝函数

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

把 source 处的字符串拷贝到 destination 处。

注意:

  •  源字符串必须以'\0'结束。
  • 会将源字符串中的'\0'拷贝到目标空间。
  • 目标空间必须足够大,以确保能存放源字符串。
  • 目标空间必须可变。

 示例:

#include
#include

int main()
{
	char arr1[] = "abcdef";
	char arr2[20] = { 0 };
	strcpy(arr2, arr1);//arr2是目的地,拷贝到'\0',并且把'\0'拷贝过去 输出abcdef
	printf("%s\n", arr2);

	char arr3[] = "abc\0def";
	strcpy(arr2, arr3);//拷贝到'\0',并且把'\0'拷贝过去  输出abc
	printf("%s\n", arr2);

	//目标空间必须可变
	//char* p = "abcdefghi";
	//char arr4[20] = "hehe";
	//strcpy(p, arr4);//错误,p指向的是常量字符串,不能被修改

	return 0;
}

自己模拟实现 strcpy 函数

#include
#include

//返回是目标空间的起始地址
char* my_strcpy(char* destination, const char* source)
{
	assert(destination && source);//不能为空
	int start = destination;
	while (*destination++ = *source++);
	return start;
}

int main()
{
	char arr1[] = "abcdef";
	char arr2[20] = "0";
	my_strcpy(arr2,arr1);
	printf("%s\n", arr2);
	printf("%s\n", my_strcpy(arr2, arr1));
	return 0;
}

2.2 strcat 字符串追加函数

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

将 source 处字符串追加到 destination 处

注意:

  • 源字符串必须以‘\0'结束。
  • 目标空间必须有足够的大,能容纳下源字符串的内容。
  • 目标空间必须可修改。
  • 字符串自己不能给自己追加。

示例:

#include
#include
int main()
{
	char arr1[20] = "hello ";
	char arr2[] = "world";
	//追加
	strcat(arr1, arr2);//从字符串的'\0'开始追加
	printf("%s\n", arr1); //输出 hello world
	
	return 0;
}

我们通过自己模拟实现,就可以理解为什么不可以自己给自己追加:

#include
#include

char* my_strcat(char* desination, const char* source)
{
	assert(desination && source);
	char* start = desination;

	while (*desination)//找到目标空间的第一个'\0'
	{
		desination++;
	}

	while (*desination++ = *source++);//拷贝字符串

	return start;
}

int main()
{
	char arr1[20] = "hello ";
	char arr2[] = "world"; 
	my_strcat(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

自己给自己追加,desination 和 source 相同,会修改自己最后的'\0',使得拷贝遍历的时候找不到自己的'\0'。

2.3 strcmp

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

注意:

C语言标准规定

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

如何比较两个字符串:

比较的是相同位置上字符的ASCII码值,相等比较下一位,如果比到'\0'都相等,返回0。否则第一个ASCII码值大则返回大于0的数字,则小返回小于0的数字。

示例:

#include

int main()
{
	if ("abcdef" == "cdefg");//可以比较,比较的是两个常量字符串的首字符地址
	
	//比较两个字符串的内容的时候,应该使用strcmp

	char arr1[] = "abcdef";
	char arr2[] = "cdefg";
	
	int ret = strcmp(arr1, arr2);//比较的是对应位置上字符的ASCII码值,
	//相等比较下一位,如果比到'\0'都相等,返回0
	//arr1的ASCII码值大,返回大于0的数字,
	//arr1的ASCII码值小,返回小于0的数字

	printf("%d\n", ret);

	return 0;
}

自己模拟实现:

#include
#include
int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);
	while (*str1 == *str2)
	{
		if (*str1 == '\0')
		{
			return 0;
		}
		str1++;
		str2++;
	}
	//return *str1 - *str2;//没有说必须返回-1,或1
	if (*str1 > *str2)
	{
		return 1;
	}
	else
		return -1;
}

int main()
{
	char arr1[] = "qwer";
	char arr2[] = "asdf";

	int ret = my_strcmp(arr1, arr2);
	printf("%d\n", ret);

	return 0;
}

3.长度受限制的字符串函数介绍

3.1 strncpy

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

拷贝num个字符从源字符串到目标空间。

如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。

示例:

#include
#include
int main()
{
	char arr1[] = "abcdef";
	char arr2[5] = "xxxx";
	//数字大于arr1长度是会补\0的
	strncpy(arr2, arr1, 3);//abcx
	printf("%s\n", arr2);
	return 0;
}

3.2 strncat

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

将 source 处的前num的字符追加到destination字符串后面'\0'处。

#include
#include
int main()
{
	char arr1[20] = "hello ";
	char arr2[] = "world";
	//数字大于arr2长度 不会再补多余的'\0'
	strncat(arr1, arr2, 3);//追加完后面会补'\0'
	printf("%s\n", arr1);//输出hello wor
	return 0;
}

strncmp可以自己给自己追加。num写成自身长度。

3.3 strncmp

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

比较到出现一个字符不一样或者一个字符串结束或者num个字符全部比较完

返回值: 

字符串函数和内存函数_第1张图片

 示例:

#include
#include

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abc";
	//输入要比较几个字符
	int ret = strncmp(arr1, arr2, 3);
	printf("%d\n", ret);
	return 0;
}

4.字符串查找

4.1 strstr

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

在str1中找str2首次出现的地址 ,没有找到返回空指针

示例: 

#include
#include
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "bcd";
	char* p = strstr(arr1, arr2);
	if (p == NULL)
	{
		printf("没有找到\n");
	}
	else
	{
		printf("%s\n", p);//bcdef
	}

	return 0;
}

自己模拟实现

#include

char* my_strstr(const char* str1, const char* str2)
{
	while (*str1!='\0')
	{
		const char* p1 = str1;
		const char* p2 = str2;
		while (*p1 == *p2)
		{
			if (*(p2+1) == '\0')
			{
				return (char*)str1;
			}
			p1++;
			p2++;
		}
		str1++;
	}
	return NULL;
}

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "bcd";
	char* p = my_strstr(arr1, arr2);
	if (p == NULL)
	{
		printf("没有找到\n");
	}
	else
	{
		printf("%s\n", p);//cdef
	}
	return 0;
}

4.2 strtok

char* strtok(char* str,const char* delimiters);

对有分割符的字符串操作,将它们分隔开来。 

用法: 

  • delimiters参数是个字符串,定义了用作分隔符的字符集合
  • 第一个参数指定一个字符串,它包含了0个或者多个由delimiters字符串中一个或者多个分隔符分割的标记。
  • strtok函数找到str中的下一个标记,并将其用'\0'结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
  • strtok函数的第一个参数不为NULL,函数将找到str中第一个标记,把它修改为'\0',strtok函数将保存它在字符串中的位置。strtok函数的第一个参数为NULL,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
  • 如果字符串中不存在更多的标记,则返回NULL指针。

示例:

#include
#include
int main()
{
	char arr[] = "[email protected]";
	char buf[30] = { 0 };
	strcpy(buf, arr);//后面操作buf,不会修改原数据
	const char* p = "@.";//const 更严谨,分割符的字符合集

	char* str = strtok(buf, p);//传入buf不为空,返回最开始的地址'x',把第一个分割符'@'改为'\0',并保存第一个分割符'@'的位置
	printf("%s\n", str);

	str = strtok(NULL, p);//传入空指针,返回刚才保存的位置下一个字符位置'c',从刚刚保存的位置开始寻找下一个分割符'.',改为'\0',保存这个位置。
	printf("%s\n", str);
	 
	str = strtok(NULL, p);//传入空指针,返回刚才保存的位置下一个字符位置's',从刚刚保存的位置开始寻找下一个分割符'\0',保存这个位置
	printf("%s\n", str);
 
	str = strtok(NULL, p);//返回空指针

	return 0;
}

字符串函数和内存函数_第2张图片

可以发现这样有很多重复代码,我们可以利用如果字符串中不存在更多的标记,则返回NULL指针,这条性质,循环打印

#include
#include
int main()
{
	char arr[] = "[email protected]";
	char buf[30] = { 0 };
	strcpy(buf, arr);//后面操作buf,不会修改原数据
	const char* p = "@.";//const 更严谨

	char* str = NULL;
	for (str = strtok(buf, p); str != NULL; str = strtok(NULL, p))//最后会strtok返回NULL
	{
		printf("%s\n", str);
	}
	
	return 0;
}

 可以发现strtok函数有记忆功能,可以记住上次调用保存的位置,那么可以说明使用了静态变量

5.错误信息报告

5.1 strerror

char* strerror(int errnum);

返回错误码errnum所对应的错误信息。

每个错误码都有对应的错误信息。

示例:

字符串函数和内存函数_第3张图片

C语言的库函数在调用失败的时候,会将一个错误码存放在一个叫:errno的变量中
当我们想知道调用库函数的时候发生了什么错误信息
就可以将:errno中的错误码翻译成错误信息,使用时要包含头文件
没有错误是0

在文件操作时的使用情况。 

 perror 函数相当于printf 函数和 strerror 函数一起使用的效果。

6.字符操作

以下函数都要包含头文件  

字符分类函数:

函数 如果它的参数符合下列条件就返回真(非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);//转换为大写

7.内存操作函数

字符串相关函数都是对字符串进行操作的,而下面函数都是对内存直接操作的函数,也需要包含头文件

7.1 memcpy

 内存拷贝函数

void* memcpy(void* destination,const void* source,size_t num);
  • 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
  • 这个函数在遇到'\0'的时候并不会停下来。
  • 如果source和destination有任何的重叠,复制的结果都是C语言标准未定义的。有重叠的应该使用memmove函数

示例:

#include
#include
#include
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[8] = { 0 };
	//把arr1中的前五个数据拷贝到arr2中  是整形不能用strcpy
	memcpy(arr2,arr1,20);//从arr1中拷贝20个字节放到arr2中

	for (int i = 0; i < 8; i++)
	{
		printf("%d ", arr2[i]);
	}
    return 0;
}

自己模拟实现memmove函数:

void* my_memcpy(void* destination, const void* source, size_t num)
{
	void* ret = destination;
	assert(destination && source);
	while (num--)
	{
		*(char*)destination = *(char*)source;
		destination = (char*)destination + 1;
		source = (char*)source + 1;
	}
	return ret;
}

 

7.2 memmove

void* memmove(void* destination,const void* source,size_t num);
  • 和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
  • 如果源空间和目标空间出现重叠,就得使用memmove函数处理。

 当我们拷贝有重叠部分的内容时,比如:

int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
memcpy(arr1 + 2, arr1, 20);

 可能会出现这种情况,没有达到想要的效果字符串函数和内存函数_第4张图片

 所以要使用memcpy函数

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

 自己模拟实现memmove函数

void* my_memmove(void* dest, const void* src, size_t num)
{
	void* ret = dest;
	assert(dest && src);
	if (dest > src)//从后往前拷贝
	{
		while (num--)
		{
			*((char*)dest + num) = *((char*)src + num);
		}
	}
	else//从前往后拷贝
	{
		while (num--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	return ret;
}

注意:

在内存重叠的时候,使用memcpy可能会出现意想不到的结果建议在内存重叠的情况,使用memmove函数

C语言:memcpy拷贝不重叠的内存,重叠的就交给memmove

memmove 包含 memcpy 的功能

vs上可以使用memcpy操作重叠内容,但不能保证其他编译器可以

 

7.3 memset

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

将ptr指向的空间的前num个字节设置为value,以字节为单位设置内存。

示例:

字符串函数和内存函数_第5张图片

 

7.4 memset

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

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

返回值如下 :与 strcmp 和 strncmp 都类似字符串函数和内存函数_第6张图片

 示例:

#include
#include

int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 1,2,3,4,6 };
	int ret = memcmp(arr1, arr2, 16);
	printf("%d\n", ret);//0 相等

	ret = memcmp(arr1, arr2, 17);
	printf("%d\n", ret);//小于0

	return 0;
}

本篇结束 

字符串函数和内存函数_第7张图片

你可能感兴趣的:(C语言,库函数,字符串,内存,指针)