【C语言】字符函数和字符串函数

函数介绍

strlen

在这里插入图片描述

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

代码一:

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
 
int main()
{
	char arr[] = "abc\0def";
	int len = strlen(arr);
	printf("%d", len);
	return 0;
}

在这里插入图片描述

代码二:

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
 
int main()
{
	char arr[3] = { 'a','b','c' };
	int len = strlen(arr);
	printf("%d\n", len);
	return 0;
}

在这里插入图片描述

代码三:

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
 
int main()
{
	if (strlen("abc") - strlen("abcdef") > 0) // 无符号数减去无符号数得到的是无符号数
	{
		printf(">\n");
	}
	else
	{
		printf("<=\n");
	}
	return 0;
}
 

这里如果按数学中的计算来看,3-6原本为-3,小于0输出<=,但是大家一定要注意,strlen函数的返回值类型是size_t(无符号整型),同时无符号数减去无符号数得到的是无符号数,因此判断结果为>0;

在这里插入图片描述
在这里插入图片描述

strcpy

在这里插入图片描述

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

strcpy的基本使用:

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
 
int main()
{
	char arr1[] = "abcdef";
	char arr2[20] = { 0 };
	strcpy(arr2, arr1);//将arr1的数据拷贝到arr2中
	printf("%s\n", arr2);
	return 0;
}

在这里插入图片描述

代码一:

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
 
int main()
{
	char arr1[] = { 'a','b','c' };
	char arr2[20] = "xxxxxxxx";
	strcpy(arr2, arr1);
	printf("%s\n", arr2);
	return 0;
}
//此程序会崩溃

代码二:

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
 
int main()
{
	char arr1[] = "abc\0def";
	char arr2[20] = "xxxxxxxx";
	strcpy(arr2, arr1);
	printf("%s\n", arr2);
	return 0;
}

在这里插入图片描述
代码三:

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
 
int main()
{
	char arr1[] = "abcdef";
	char arr2[3] = { 0 };
	strcpy(arr2, arr1);
	printf("%s\n", arr2);
	return 0;
}
//程序会报错

【C语言】字符函数和字符串函数_第1张图片

代码四:

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
 
int main()
{
	char* p = "abcdef"; //指针所指向的字符串为常量字符串,其内容不能被修改。
	char arr2[20] = "JX_BC";
	strcpy(p, arr2);
	printf("%s\n", arr2);
	return 0;
}
//该程序会崩溃

strcat

在这里插入图片描述

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

strcat的基本使用:

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

在这里插入图片描述

strcat函数的原理:从目的地字符串的 ‘\0’ 的位置开始追加,把 ‘\0’ 覆盖掉一直追加,直到将源头的字符串内容全部拷贝到目的地,包括 ’\0‘ 。

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
 
int main()
{
	char arr1[20] = "hello \0xxxxxxxx";
	char arr2[] = "world";
	strcat(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

在这里插入图片描述

strcmp

下面演示一种错误的字符串比较方式:

if ("abcdef" == "bcdefg") //这里比较的是两个字符串首字符的地址,而不是字符串的内容

这种比法语法上是没有任何问题的,但是这个代码比的不是两个字符串的内容。原因是这两个字符串在作为表达式的时候,它们两的值是第一个字符的地址,因此在用==号比的时候比的其实是这两个字符串的首个字符的地址是否相等,并没有比较两个字符串的内容。

在这里插入图片描述

  • 第一个字符串大于第二个字符串,则返回大于0的数字。
  • 第一个字符串等于第二个字符串,则返回0。
  • 第一个字符串小于第二个字符串,则返回小于0的数字。
  • 温馨提示:VS系统下默认三个返回值分别为:-1,0,1。

strncpy

大家可能发现了 strcpy,strcat,strcmp 这三个函数在使用时对源字符串没有长度限制,几乎是将源字符串的内容全部进行操作。在VS编译器中的这些函数显得不安全了,因此VS会提醒你在其后加上 _s ,或者在首行加上 #define _CRT_SECURE_NO_WARNINGS。

由于这些原因,C语言又引入了 strncpy,strncat,strncmp 等长度受限制的一组相对来说比较安全的函数。

在这里插入图片描述

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

strncpy的基本使用:

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
 
int main()
{
	char arr1[] = "abcdef";
	char arr2[5] = { 0 };
	strncpy(arr2, arr1, 3);
	printf("%s\n", arr2);
	return 0;
}

在这里插入图片描述

strncat

在这里插入图片描述

  • strncat 函数再追加完后自动会在其后补上一个 \0。
  • 如果输入的追加长度大于源字符串中的字符个数,那么在追加完源字符串(包括 \0 )后不会再凑剩下的字符了。
  • strncat 可以自己给自己追加。
#define _CRT_SECURE_NO_WARNINGS
#include 
#include
 
int main()
{
	char arr1[20] = "hello \0xxxxx";
	char arr2[] = "abcdef";
	strncat(arr1, arr2, 3);
	printf("%s\n", arr1);
	return 0;
}

在这里插入图片描述

strncmp

在这里插入图片描述

  • 比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。
#define _CRT_SECURE_NO_WARNINGS
#include 
#include
 
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abc";
	int ret = strncmp(arr1, arr2, 3);
	printf("%d\n", ret);
	return 0;
}

在这里插入图片描述

当然在VS中 strcpy_s,strcat_s,strcmp_s 也可以用来作为长度受限的函数。

strstr

在这里插入图片描述

strstr的基本使用:

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
 
int main()
{
	char arr1[] = "abcdebcdef";
	char arr2[] = "bcd";
	char* p = strstr(arr1, arr2);
	if (p == NULL)
	{
		printf("找不到");
	}
	else
	{
		printf("%s\n", p);
	}
	return 0;
}

若目标字符串中出现多次源字符串的内容,返回第一次源字符串出现的位置。

在这里插入图片描述

strchr

和 strstr 函数比较类似的函数,用于找出一个字符在字符串中第一次出现的位置。

strchr 基本用法:

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
 
int main()
{
	char arr1[] = "abcdebcdef";
	char str = 'e';
	char* p = strchr(arr1, str);
	if (p == NULL)
	{
		printf("找不到");
	}
	else
	{
		printf("%s\n", p);
	}
	return 0;
}

在这里插入图片描述

strrchr

strrchr 函数用于找出一个字符在字符串中最后一次出现的位置。

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
 
int main()
{
	char arr1[] = "abcdebcdef";
	char str = 'e';
	char* p = strrchr(arr1, str);
	if (p == NULL)
	{
		printf("找不到");
	}
	else
	{
		printf("%s\n", p);
	}
	return 0;
}

在这里插入图片描述

strtok

在这里插入图片描述

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

strtok函数的基本用法:

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
 
int main()
{
	char arr[] = "[email protected]";
	const char* p = "@.";
	char buf[30] = { 0 };
	char* str = NULL;
	strcpy(buf, arr);//将数据拷贝一份,处理arr数组的内容
	for (str = strtok(buf, p); str != NULL; str = strtok(NULL, p))
	{
		printf("%s\n", str);
	}
	return 0;
}

在这里插入图片描述

strerror

在这里插入图片描述

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

strerror函数的基本使用:

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
 
int main()
{
	char* p = strerror(0);
	printf("%s\n", p);
 
	char* p1 = strerror(1);
	printf("%s\n", p1);
 
	char* p2 = strerror(2);
	printf("%s\n", p2);
	return 0;
}

在这里插入图片描述
通过以上的例子,我们可以将 strerror 函数看作将错误码(0,1,2)翻译为错误信息。

上例的代码只是为了方便演示功能才举出的例子,事实上C语言的库函数在调用失败的时候,会将一个错误码存放在一个叫 errno 的变量中,当我们想知道在调用库函数是发生了什么错误信息,就可以将 errno 中的错误码翻译成错误信息。

演示打开读取关闭文件的过程,代码示例如下:

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
#include
 
int main()
{
	//打开文件
	//打开文件的时候,若打开的方式为“r”
	// 若文件存在则打开,若文件不存在则打开失败
	//若打开文件失败,会返回NULL
	FILE* pf = fopen("test.txt", "r"); //意思是 以读的形式打开文件test.txt
	if (pf == NULL)
	{
		printf("打开文件失败,原因是:%s\n", strerror(errno));
		return 1;
	}
	//读写文件
	//……
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

由于我的电脑中不存在这个文件,因此运行结果如下:

在这里插入图片描述

perror

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
#include
 
int main()
{
	FILE* pf = fopen("test.txt", "r"); 
	if (pf == NULL)
	{
		perror("打开文件失败");
		return 1;
	}
	fclose(pf);
	pf = NULL;
	return 0;
}

在这里插入图片描述

memcpy

在这里插入图片描述

  • 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
  • 这个函数在遇到 ‘\0’ 的时候并不会停下来。
  • 如果source和destination有任何的重叠,复制的结果都是未定义的。
  • num的单位是字节。
  • void*:通用类型的指针,可以接受任意类型的地址 。但是这类指针不能直接进行解引用和±运算。由于 memcpy 函数的设计者不知道未来程序员使用memcpy拷贝什么类型的数据,因此使用void* 来设计这个函数。

memcpy函数的基本使用:

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
 
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[8] = { 0 };
	memcpy(arr2, arr1, 20);//将arr1中前5个数据拷贝到arr2中 
	return 0;
}

【C语言】字符函数和字符串函数_第2张图片

不光是 int 类型的数据可以,float 类型的数据同样也行。

memmove

在这里插入图片描述

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

memmove函数的基本使用:

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

在这里插入图片描述

memcmp

在这里插入图片描述

  • 比较从ptr1和ptr2指针开始的num个字节。
  • 返回值如下:
    【C语言】字符函数和字符串函数_第3张图片

memcmp函数的基本使用:

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
 
int main()
{
	int arr1[] = { 1,2,3,4,6 };
	int arr2[] = { 1,2,3,4,7 };
	printf("%d\n", memcmp(arr1, arr2, 16));
	printf("%d\n", memcmp(arr1, arr2, 17));
	return 0;
}

在这里插入图片描述

memset

【C语言】字符函数和字符串函数_第4张图片

  • memset是以字节为单位来进行设置,使用时要注意。

memset函数的基本使用:

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
 
int main()
{
	char arr[] = "hello world";
	memset(arr, 'x', 6);
	printf("%s\n", arr);
	return 0;
}

在这里插入图片描述

库函数的模拟实现

模拟实现strlen

方法一:计数器

int my_strlen(const char* str) //const 放在*的左边保护的是指针所指向的内容
{
	int count = 0;
	while (*str)
	{
		count++;
		str++;
	}
	return count;
}
 
int main()
{
	char arr[] = "hello world";
	int len = my_strlen(arr);
	printf("%d", len);
	return 0;
}

方法二:递归(不创建临时变量,求字符串长度)

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
 
int my_strlen(const char* str) //const 放在*的左边保护的是指针所指向的内容
{
	if (*str != '\0')
		return 1 + my_strlen(str+1);
	else
		return 0;
}
 
int main()
{
	char arr[] = "hello world";
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

方法三:指针-指针

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
 
int my_strlen(const char* str) //const 放在*的左边保护的是指针所指向的内容
{
	char* start = str;
	while (*str != '\0')
	{
		str++;
	}
	return str - start;
}
 
int main()
{
	int len = my_strlen("abcdef");
	printf("%d\n", len);	
	return 0;
}

模拟实现strcpy

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
 
//返回目标空间的起始位置
char* my_strcpy(char* dest, const char* src)//源头的数据不能发生变化,因此加上const进行保护
{
	char* start = dest;
	assert(dest && src);//断言保证两个指针有效
	while (*dest++ = *src++)
	{
		;
	}
	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;
}

在这里插入图片描述

模拟实现strcat

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
#include
 
char* my_strcat(char* dest, const char* src)
{
	assert(dest && src);
	char* start = dest;
	while (*dest != '\0') //找出\0
	{
		dest++;
	}
	while (*dest++ = *src++) //拷贝字符串
	{
		;
	}
	return start;
}
 
int main()
{
	char arr1[20] = "hello \0xxxxxxxx";
	char arr2[] = "world";
	my_strcat(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

在这里插入图片描述

模拟实现strstr

strstr 函数的模拟实现也是数据结构中串的匹配实现的暴力算法:BF算法。

为了使匹配字符串的过程中,如果发生匹配失败,能够让str1指向目标字符串中字符的下一个字符,str2能够重新回到要找的字符串开始位置,我们需要使用双指针,一个用来记住当前位置,一个来方便匹配过程中指针的移动。

【C语言】字符函数和字符串函数_第5张图片

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
 
char* my_strstr(const char* str1, const char* str2)
{
	char* s1 = NULL;
	const char* s2 = NULL;
	const char* cp = str1;
	while (*cp)
	{
		s1 = cp;
		s2 = str2;
		while (*s1 && *s2 && *s1 == *s2)
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			return cp;
		}
		cp++;
	}
	return NULL;
}
 
int main()
{
	char arr1[] = "abbbcdef";
	char arr2[] = "bbc";
	char* p = my_strstr(arr1, arr2);
	if (p == NULL)
	{
		printf("找不到\n");
	}
	else
	{
		printf("%s\n", p);
	}
	return 0;
}

在这里插入图片描述

模拟实现strcmp

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
#include
 
int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);
	while (*str1 == *str2)
	{
		if (*str1 == '\0')
			return 0;
		str1++; 
		str2++;
	}
	//if (*str1 > *str2)
		//return 1;
	//else
		//return -1;
	return *str1 - *str2;
}
 
int main()
{
	char arr1[] = "abzqw";
	char arr2[] = "abq";
	printf("%d\n", my_strcmp(arr1, arr2));
	return 0;
}

在这里插入图片描述

模拟实现memcpy

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
#include
 
void* my_memcpy(void* dest, const void* src, size_t num)
{
	void* start = dest;
	assert(dest && src);
	while (num--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return start;
}
 
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[8] = { 0 };
	my_memcpy(arr2, arr1, 20);
	return 0;
}

【C语言】字符函数和字符串函数_第6张图片

模拟实现memmove

在我们模拟之前我们不妨想想这个问题,在重叠部分进行拷贝时,应该考虑dest指向的字符串与src指向的字符串的位置关系来确定如何使得拷贝src字符在未被拷贝之前不被修改,因此我们需要进行讨论:

【C语言】字符函数和字符串函数_第7张图片

经过分析,我们可以得出情况一将采用(后->前),情况二和三采用(前->后)来进行拷贝是合理的,因此代码示例如下:

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
#include
 
void my_memmove(void* dest, const void* src, size_t num)
{
	char* start = dest;
	assert(dest && src);
	if (dest < src)
	{
		while (num--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	else
	{
		while (num--)
		{
			*((char*)dest + num) = *((char*)src + num);
		}
	}
	return start;
}
 
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr1+2, arr1, 20);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

在这里插入图片描述

在这里需要注意的是:在C语言中,memcpy 用来拷贝不重叠的部分,重叠的部分交给 memove来做,如果 memove可以完成100%,那么memcpy可以完成60%。但在VS中比较特殊的是,memcpy可以进行重叠部分的拷贝,memove和memcpy都可以完成100%。

字符分类函数

【C语言】字符函数和字符串函数_第8张图片

以上函数在使用时应引用头文件

举几个函数使用的例子:

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
 
int main()
{
	printf("%d\n", isdigit('x'));
	printf("%d\n", isdigit('6'));
	printf("%d\n", isspace('x'));
	printf("%d\n", isspace(' '));
	printf("%d\n", islower('x'));
	printf("%d\n", islower('X'));
	return 0;
}

【C语言】字符函数和字符串函数_第9张图片

字符转换函数

【C语言】字符函数和字符串函数_第10张图片

  • 经过实验貌似这两个函数只能对单个字符进行大小写转换。

函数使用示例如下:

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
 
int main()
{
	printf("%c\n", tolower('X'));
	printf("%c\n", toupper('x'));
	return 0;
}

在这里插入图片描述

写出一个代码将一句话转换为全小写输出:

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
 
int main()
{
	char arr[20] = { 0 };
	gets(arr); //接收字符串
	int i = 0;
	while (arr[i])
	{
		if (isupper(arr[i]))
		{
			arr[i] = tolower(arr[i]);
		}
		printf("%c", arr[i]);
		i++;
	}
	return 0;
}

在这里插入图片描述
【C语言】字符函数和字符串函数_第11张图片

你可能感兴趣的:(C语言,c语言,开发语言)