字符函数和字符串函数

1.strlen函数

  • size_t strlen ( const char * str );

函数功能:

  • 获取字符串的长度,即返回一个字符串到中到\0'字符之前的的字符个数
//int main()
//{
//	char* arr = "abcdef";
//	size_t len = strlen(arr);
//	printf("%d\n", len);//6
//
//	return 0;
//}

int main()
{
	char* arr = "abc\0def";
	size_t len = strlen(arr);
	printf("%d\n", len);//3

	return 0;
}

注意:strlen的返回值是size_t类型,是无符号的

int main()
{
	char str1[] = "abcdef";
	char str2[] = "bbb";
	if (strlen(str2) - strlen(str1) > 0)
	{
		printf("str2>str1\n");
	}
	else
	{
		printf("srt1>str2\n");
	}
	return 0;
}

代码解读:

  • 因为strlen的返回值是size_t类型的,两个size_t类型的值相减,结果仍然是size_t类型
    3-6结果是-3,因为是size_t类型,会被编译器当成一个很大的正数,因此打印str2>str1

1.1strlen的模拟实现

方法1:计数器

size_t my_strlen(const char* str)
{
	assert(str);

	char* p = str;
	int count = 0;
	while (*p != '\0')
	{
		count++;
		p++;
	}

	return count;
}

方法2:递归

size_t my_strlen(const char* str)
{
	assert(str);

	char* p = str;
	if (*p == '\0')
		return 0;

	return 1 + my_strlen(p + 1);
}

方法三:指针-指针

size_t my_strlen(const char* str)
{
	assert(str);

	char* p = str;
	while (*p != '\0')
	{
		p++;
	}

	return (p - str);
}

2.strcpy函数

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

函数功能:

  • 将source处的内容拷贝到destination指向的空间,包括'\0'

注意事项:

  • source中的内容要有'\0'
  • 会将'\0'一起拷贝
  • destination指向的空间要足够大

2.1strcpy的模拟实现

char* my_strcpy(char* destination, const char* source)
{
	assert(destination && source);

	char* ret = destination;
	while (*destination++ = *source++)
	{
		;
	}

	return ret;
}

3.strcat函数

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

函数功能:

  • 在destination末尾追加字符串source

注意事项:

  • 先找到destination字符串中的'\0',在'\0'处开始拷贝
  • 会将source字符串中的'\0'一起拷贝
  • destination字符串必须要有'\0';source字符串也必须要有'\0'
  • destination的空间必须足够大

3.1strcat的模拟实现

char* my_strcat(char* destination, const char* source)
{
	assert(destination && source);
	
	char* ret = destination;
	//找到'\0'
	while (*destination++)
	{
		;
	}

	//拷贝
	while (*destination++ = *source++)
	{
		;
	}

	return ret;
}

问:能不能自己给自己追加?

答:我们自己写的模拟实现代码是不能追加给自己追加的,程序会陷入死循环;库函数中的strcat更加完善,能够完成自己追加自己,但还是不建议这样做


4.strcmp函数

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

函数功能:

  • 比较两个字符串

注意事项:

  • str1>str2,返回正数
  • str1==str2,返回0
  • str1<str2,返回负数
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abcdeg";
	int ret = strcmp(arr1, arr2);
	if (ret > 0)
		printf("str1 > str2\n");
	else if (ret == 0)
		printf("str1 == str2\n");
	else
		printf("str1 < str2\n");

	return 0;
}

//输出:str1 < str2

4.1strcmp的模拟实现

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;
}

上面的strcpy,strcat都是长度不受限制的字符串函数,与它们相对应的是长度受限制的字符串函数


5.strncpy函数

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

函数功能:

  • 相较于strcpy,strncpy函数多了一个num参数,该参数表示要拷贝的字节个数
    也就是我们可以指定拷贝的字符个数

注意事项:

  •  destination指向的空间必须足够大
  • 如果source中的字符串个数小于num,则拷贝完source的字符串后自动补'\0'
  • 如果source中的字符串个数大于num,则仅拷贝num个字符,不会加上'\0'
int main()
{
	char arr1[20] = "xxxxxxxxx";
	char arr2[] = "abcde";
	strncpy(arr1, arr2, 3);
	printf("%s\n", arr1);

	return 0;
}

//输出:abcxxxxxx

5.1strncpy的模拟实现

char* my_strncpy(char* destination, const char* source, int num)
{
	assert(destination && source);

	char* ret = destination;

	while (num--)
	{
		if (*source == '\0')
			*destination++ = '\0';
		else
			*destination++ = *source++;
	}

	return ret;
}

6.strncat函数

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

函数功能:

  • 从source中追加num个字符到destination中

注意事项:

  • destination的空间必须足够大
  • 如果source字符串个数小于num,则相当于追加source字符串
  • 如果source字符串个数大于num,则追加完num个字符后,自动补上'\0'

6.1strncat的模拟实现

char* my_strncat(char* destination, const char* source, int num)
{
	assert(destination && source);

	char* ret = destination;

	//找'\0'
	while (*destination != '\0')
	{
		destination++;
	}

	while (num--)
	{
		if (num < 0 || *source == '\0')
			break;

		*destination++ = *source++;
	}

	*destination = '\0';

	return ret;
}

7.strncmp函数

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

函数功能:

  • 比较str1和str2中前num个字符构成的字符串的大小
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abcdef";
	int ret = strncmp(arr1, arr2, 3);
	if (ret > 0)
		printf("str1>str2\n");
	else if (ret == 0)
		printf("str1==str2\n");
	else
		printf("str1<str2\n");

	return 0;
}
//输出:str1 == str2

8.strstr函数

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

函数功能:

  • 在字符串str1中查找字符串str2
  • 如果找到了,返回str2第一个字符在str1中的地址;如果找不到,返回NULL
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "cde";
	char* ret = strstr(arr1, arr2);
	if (ret)
		printf("%s\n", ret);
	else
		printf("找不到\n");

	return 0;
}
//输出:cdef

8.1strstr的模拟实现

char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);

	if (*str2 == '\0')
		return str1;

	int i = 0;//str1的下标
	int j = 0;//str2的下标

	while (str1[i])
	{
		while (str1 && str2 && str1[i] == str2[j])
		{
			i++;
			j++;
		}

		if (str2[j] == '\0')
			return str1 + i - j;

		i = i - j + 1;
		j = 0;
	}

	return NULL;
}

9.strtok

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

函数功能:

  • 根据delimiters中的字符分割str字符串中的内容
int main()
{
	char arr1[] = "baiyahua@qq.com";
	char arr2[] = "@.";
	char* ret = strtok(arr1, arr2);
	printf("%s\n", ret);

	return 0;
}
//输出:baiyahua

注意事项:

  • delimiters中是自己定义的分隔符的集合
  • str中包含delimiters中的分隔符
  • 如果strtok的第一个参数不为空,会将str中的第一个分隔符替换成'\0',并记录该位置,返回起始位置的地址
  • 如果strtok的第一个参数为空,从上次记录的位置开始寻找分隔符
  • 如果没有找到分隔符,返回NULL

由于strtok会改变字符串的内容,所以我们一般先拷贝一份原来的字符串,对拷贝的字符串进行操作

int main()
{
	char arr1[] = "baiyahua@qq.com";
	char* p = "@.";
	char copyArr1[50] = { 0 };
	strcpy(copyArr1, arr1);

	char* s = strtok(copyArr1, p);
	while (s != NULL)
	{
		printf("%s\n", s);
		s = strtok(NULL, p);
	}

	return 0;
}
//输出:
//baiyahua
//qq
//com

10.strerror

  • char * strerror ( int errnum );

函数功能:

  • 翻译错误码对应的错误信息,并返回错误信息的起始地址

在C语言中,使用库函数发生错误时,会将错误码存放在一个叫errno的全局变量中;strerror的作用就是返回errnum中的值所对应的错误信息,然后返回错误信息的起始地址

int main()
{
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		printf("打开文件失败,原因是:%s\n", strerror(errno));
		return 0;
	}
	else
	{
		printf("打开文件成功\n");
	}

	fclose(pf);
	pf = NULL;

	return 0;
}
//输出:打开文件失败,原因是: No such file or directory

10.1perror

  • void perror ( const char * str );

函数功能:

  • 直接将错误码翻译成错误信息,并打印出来

perror相当于strerror+printf;在使用库函数出错时,它首先会打印str字符串,再将错误码对应的错误信息打印

int main()
{
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("打开文件失败,原因是");
		return 0;
	}
	else
	{
		printf("打开文件成功\n");
	}

	fclose(pf);
	pf = NULL;

	return 0;
}
//输出:打开文件失败,原因是: No such file or directory

strerror和perror

  • 如果只想要错误码对应的错误信息,用strerror
  • 如果还想打印出错误信息,用perror更方便

11.字符函数

11.1字符分类函数

常见的字符分类函数:

函数 符合下面的条件返回真
iscntrl 任何控制字符
isspace 空白字符,空格' ',换页'\f',换行'\n',回车'\r',制表符'\t',垂直制表符'\v'
isdigit 数字0~9
isxdigit 十六进制数字,0~9,a~f或A~F
islower 小写字母,a~z
isupper 大写字母,A~Z
isalpha 字母,a~z,A~Z
isalnum 字母或数字,0~9,a~z,A~Z
ispunct 标点符号,任何不属于数字或字母的可打印字符
isgraph 任何图形字符
isprint 任何可打印字符

我们就拿其中一个函数举例,其他函数同理

isdigit函数

  • int isdigit ( int c );

函数功能:

  • 判断c是否是一个数字字符,如果是数字返回非0;如果不是,返回0
int main()
{
	int ch = '1';
	if (isdigit(ch))
		printf("是数字字符\n");
	else
		printf("不是数字字符\n");

	return 0;
}
//输出:是数字字符

11.2字符转换函数

  • int toupper ( int c );
  • int tolower ( int c );

函数功能:

  • toupper:如果c是小写字母,则返回c的大写字母;否则就直接返回c
  • tolower:如果c是大写字母,则返回c的小写字母;否则就直接返回c
int main()
{
	char arr[] = "Test String.\n";
	char* p = arr;
	while (*p)
	{
		if (isupper(*p))
			*p = tolower(*p);
		p++;
	}
	printf("%s\n", arr);

	return 0;
}
//输出:test string.

12.内存函数

12.1memcpy函数

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

函数功能:

  • 从source指向的内存拷贝num个字节到destination中,返回destination首元素的地址

前面的strcpy是只能对字符串进行拷贝的函数,而memcpy能对任意类型的数据进行拷贝

int main()
{
	int arr1[20] = { 0 };
	int arr2[] = { 1,2,3,4,5,6,7 };
	memcpy(arr1, arr2, 20);
	for (int i = 0; i < 5; i++)
	{
		printf("%d ", arr1[i]);
	}
	printf("\n");

	return 0;
}
//输出1 2 3 4 5

12.1.1memcpy的模拟实现

void* my_memcpy(void* destination, const void* source, size_t num)
{
	assert(destination && source);

	void* ret = destination;
	while (num--)
	{
		*(char*)destination = *(char*)source;
		destination = (char*)destination + 1;
		source = (char*)source + 1;
	}

	return destination;
}

如果我们想要对重叠的内存进行拷贝,my_memcpy是完成不了的;库函数中的memcpy虽然可以进行重叠内存的拷贝,但我们规定好:

  • 两段不重叠的内存拷贝用memcpy
  • 两段有重叠的内存拷贝用memmove

12.2memmove函数

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

函数功能:

  • 从source指向的内存拷贝num个字节到destination中,返回destination首元素的地址

同样是拷贝内存,用法也与memcpy一样,只不过对于重叠内存的拷贝我们一般选择memmove

12.2.1memmove的模拟实现

void* my_memmove(void* destination, const void* source, size_t num)
{
	assert(destination && source);

	void* ret = destination;

	if (source < destination)
	{
		//从后往前拷贝
		while (num--)
		{
			*((char*)destination + num) = *((char*)source + num);
		}
	}
	else
	{
		//从前往后拷贝
		while (num--)
		{
			*(char*)destination = *(char*)source;
			destination = (char*)destination + 1;
			source = (char*)source + 1;
		}
	}

	return ret;
}


12.3memset函数

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

函数功能:

  • 设置ptr指向内存的内容改为value;以字节为单位,一共该num个字节
int main()
{
	char arr[] = "Welcome to my world.\n";
	memset(arr + 14, 'x', 3);
	printf("%s", arr);

	return 0;
}
//输出:Welcome to my xxxld.

注意事项:

  • 该函数是以字节为单位进行修改的,每次只会修改一个字节

12.4memcmp函数

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

函数功能:

  • 以字节为单位比较两块内存
  • ptr1>ptr2,返回正数;
  • ptr1==ptr2,返回0
  • ptr1<ptr2,返回负数
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8 };
	int arr2[] = { 1,2,3,4,0x11111105 };
	int ret = memcmp(arr1, arr2, 17);
	printf("%d\n", ret);

	return 0;
}
//输出0,前16字节都是相等
//arr1和arr2的第十七字节数据都是05,所以相等

字符函数和字符串函数的内容就到这!

相关推荐

  1. 【C语言】字符函数字符串函数

    2023-12-25 18:38:01       17 阅读

最近更新

  1. TCP协议是安全的吗?

    2023-12-25 18:38:01       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2023-12-25 18:38:01       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2023-12-25 18:38:01       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2023-12-25 18:38:01       18 阅读

热门阅读

  1. c++ map unordered_map 区别

    2023-12-25 18:38:01       34 阅读
  2. CentOS+web

    2023-12-25 18:38:01       40 阅读
  3. MongoDB创建和查询视图(二)

    2023-12-25 18:38:01       36 阅读
  4. linux 内核时间计量方法

    2023-12-25 18:38:01       26 阅读
  5. matlab一本通 学习笔记三

    2023-12-25 18:38:01       35 阅读
  6. redis和数据库的同步问题

    2023-12-25 18:38:01       36 阅读
  7. Spring Cloud Alibaba 之 Sentinel

    2023-12-25 18:38:01       41 阅读
  8. QML中加载数据时卡顿怎么处理

    2023-12-25 18:38:01       39 阅读