C语言字符、字符串函数和内存操作函数详解

一、字符函数

1. 字符分类函数

用于判断一个字符是否属于某种字符类型
这些函数的使用都需要包含头文件 ctype.h

函数其参数符合下列条件就返回非零整数,否则返回0
iscntrl任何控制字符如:整数输出控制字符%d、%-5d;字符串输出控制字符%s等
isspace空格字符:空格“ ”,换页‘\f’,换行‘\n’,回车’\r,水平制表符’\t’,垂直制表符‘\v’
isdigit十进制数字0~9
isxdigit十六进制数字,包含所有十进制数字,小写字母a~f ,大写字母A~F
islower小写字母
isupper大写字母
isalnum字母或数字
ispunct标点符号,任何不属于数字或字母的图形字符(可打印)
isgraph任何图形字符
isprint任何可打印字符,包括图形字符和空白字符

举例:使用isspace函数判断‘ ’和‘\0’是否为空白字符

int isspace ( int ch );
#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <ctype.h>

int main()
{
	int ret1 = isspace(' ');
	printf("ret1=%d\n", ret1);
	int ret2 = isspace('\0');
	printf("ret2=%d\n", ret2);
	return 0;
}

运行结果为
在这里插入图片描述

说明“ ”是空白字符,‘\0’不是空白字符

2. 字符转换函数

这些函数的使用都需要包含头文件 ctype.h

int tolower ( int ch );//将参数穿进去的大写字母转小写
int toupper ( int ch );//将参数穿进去的小写字母转大写

举例:将一个字符串中的小写字母转成大写

#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <ctype.h>

int main()
{
	char arr[] = "LY_likun";
	int i = 0;
	char c = 0;
	while (arr[i])//arr[i]为'\0'时,'\0'的ASCII码值为0,退出循环
	{
		c = arr[i];
		if (islower(c))
		{
			c= toupper(c);
		}
		putchar(c);
		i++;
	}
	return 0;
}

二、字符串函数

这些函数的使用都需要包含头文件 string.h

1. strlen的使用和模拟实现

size_t strlen ( const char* str );

①strlen函数返回的是在字符串中 ‘\0’ 前面出现的字符个数(不包含 ‘\0’ )。
②参数指向的字符串必须要以 ‘\0’ 结束。
③注意函数的返回值是size_t,是无符号整形(易错)
下面是strlen的一种模拟实现方法

#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

//不创建临时变量模拟实现strlen
size_t my_strlen(const char* arr)
{
	if (*arr == '\0')
		return 0;
	else
		return 1 + my_strlen(arr + 1);
}

int main()
{
	char* arr = "LY_likun";
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

自己尝试尝试其他的模拟实现方式吧!

2. strcpy的使用和模拟实现

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

①Copies the C string pointed by source into the array pointed by destination, including the terminating null character (and stopping at that point).
②源字符串必须以 ‘\0’ 结束,会将源字符串中的 ‘\0’ 拷贝到目标空间。
③目标空间必须足够大,必须可修改

strcpy的使用:

int main()
{
	char des[256] = {0};
	char* src = "LY_likun";
	strcpy(des, src);
	printf("%s\n", des);//
	return 0;
}

结果打印出LY_likun

模拟实现:

char* my_strcpy(char* des, const char* src)
{
	assert(des);
	assert(src);
	char* ret = des;//用ret保存des起始位置的地址
	while (*des++=*src++)
	{
		;
	}
	return ret;
}
 
int main()
{
	char arr1[256] = "*****************";
	char arr2[] = "LY_likun" ;
	char* ret=my_strcpy(arr1, arr2);
	printf("%s\n", ret);
	printf("%s\n", my_strcpy(arr1, arr2));
	printf("%s\n", arr1);
	return 0;
}

结果打印出
LY_likun
LY_likun
LY_likun

3.strcat的使用和模拟实现

char* strcat ( char* dest,const char* src );

①Appends a copy of the source string to the destination string. The terminating null character in destination is overwritten by the first character of source, and a null-character is included at the end of the new string formed by the concatenation of both in destination.
②目标字符串都必须有’\0’,否则不知道从哪里开始拼接。
③源字符串必须以’\0’结束,
④目标空间必须足够大,必须可修改
⑤字符串不能自己拼接自己

strcat的使用:

int main()
{
	char dest[256] = "LY ";
	char src[] = "likun";
	strcat(dest, src);
	printf("%s\n", dest);
	return 0;
}

打印出LY likun

strcat的模拟实现:

char* my_strcat(char* dest, const char* src)
{
	assert(dest&& src);
	char* ret = dest;
	while (*dest)
	{
		dest++;
	}
	//错误写法:while (*dest++); *dest是'\0'时,循环终止,后置++让dest+1,
	//那么下一步“拼接”将在'\0'之后进行,LY \0likun\0,最终打印出来的是LY 。
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}

int main()
{
	char arr1[256] = "LY ";
    char arr2[] = "likun";
	my_strcat(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

结果打印出LY likun

为什么字符串不能自己拼接自己呢?
我们假设一个空间足够大的字符数组arr,其中存放着"abcd"字符串,并strcat(arr,arr)
结果如图
在这里插入图片描述

4.strcmp的使用和模拟实现

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

①This function starts comparing the first character of each string. If they are equal to each other, it continues with the following pairs until the characters differ or until a terminating null-character is reached.
②strcmp本质上在比较两个字符串对应位置上ASCII码值的大小。若str1>str2,返回大于0的数字;str1=str2,返回0;str1<str2,返回小于0的数字。

strcat的使用:

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abcdef";
	char arr3[] = "abbbbbbb";
	char arr4[] = "abz";
	printf("%d\n", strcmp(arr1, arr2));
	printf("%d\n", strcmp(arr1, arr3));
	printf("%d\n", strcmp(arr1, arr4));
	return 0;
}

打印结果为
0
1
-1

strcat的模拟实现:

int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);
	while (*str1++ == *str2++ && *str1 != '\0')//当*str1与*str2不等时退出循环,或两者相等等于\0时退出循环
	{
		;
	}
	return *str1 - *str2;
}

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abcdzz";
	int ret = my_strcmp(arr1, arr2);
	printf("ret=%d\n", ret);
	return 0;
}

打印结果为ret=-20

5.strncpy、strncat、strncmp的使用

char* strncpy ( char* dest,const char* src,size_t num );

①Copies the first num characters of source to destination. If the end of the source C string (which is signaled by a null-character) is found before num characters have been copied, destination is padded with zeros until a total of num characters have been written to it.
②拷贝num个字符从源字符串到目标空间。
③如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。

strncpy的使用:

int main()
{
	char dest[256] = "xxxxxxxxxxxxxxx";
	char src[] = "LY_likun";
	strncpy(dest, src, 12);
	return 0;
}

源字符串长度小于12,复制完再追加三个0('\0'),直到复制12个字符
追加了三个0(‘\0’)

char* strncat ( char* dest,const char* src,size_t num );

①Appends the first num characters of source to destination, plus a terminating null-character.
If the length of the C string in source is less than num, only the content up to the terminating null-character is copied.
②如果源字符串的长度小于num,只拼接到源字符串的’\0’

strcat的使用:

int main()
{
	char dest[256] = "xxxxx\0xxxxxxxxxx";
	char src[] = "LY_likun";
	strncat(dest, src, 12);
	return 0;
}

从目标字符串'\0'处开始拼接,源字符串长度小于num,只拼接到源字符串的'\0'
只拼接到源字符串的’\0’

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

①Compares up to num characters of the C string str1 to those of the C string str2.
This function starts comparing the first character of each string. If they are equal to each other, it continues with the following pairs until the characters differ, until a terminating null-character is reached, or until num characters match in both strings, whichever happens first.
②最多比较str1和str2的前num个字符,全部相等返回0、大于则返回一个大于零的整数,小于则返回一个小于零的整数
③如果两字符串一模一样但长度小于num,比较到两者’\0’位置为止。
strncmp的使用:

int main()
{
	char* str1 = "abcddd";
	char* str2 = "abceee";
	int ret = strncmp(str1, str2, 3);
	printf("%d\n", ret);
	return 0;
}

打印结果为0

6.strstr的使用和模拟实现

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

①Returns a pointer to the first occurrence of str2 in str1, or a null pointer if str2 is not part of str1.
The matching process does not include the terminating null-characters, but it stops there.
②如果字符串str2是“\0”,则返回str1
strstr的使用:

int main()
{
	char* str1 = "abcddefhdeg";
	char* str2 = "de";
	char* ret=strstr(str1, str2);
	printf("%s\n", ret);
	return 0;
}

打印结果为defhdeg

strstr的模拟实现:

#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <assert.h>
char* my_strstr(const char* str1, const char* str2)
{
	const char* s1 = NULL;
	const char* s2 = NULL;
	assert(str1 && str2);
	const char* wait = str1;

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

	while (*wait)
	{
		s1 = wait;
		s2 = str2;
		while (*s1 == *s2 && *s1 !='\0' && *s2 != '\0')//防止越界访问
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			return wait;
		}
		wait++;
	}
		return NULL;
}

7.(扩展)strtok、strerror、perror的使用

strtok

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

①sep参数指向⼀个字符串,定义了用作分隔符的字符集合。例sep字符串"@。"分隔符有 @ 。两个
②strtok函数找到str中的下⼀个分隔符,替换为\0,返回⼀个指向这个分隔符的指针,并保存它在字符串中的位置
③strtok函数的第⼀个参数不为 NULL ,函数将找到str中第⼀个分隔符;
strtok函数的第⼀个参数为 NULL ,函数将在同⼀个字符串中被保存的位置之后,查找下⼀个分隔符
④如果查找一个分隔符时遇到\0,返回指向\0的指针,下一次返回NULL。

strtok的使用:

int main()
{
	char str[] = "LY.likun@csdn.com";
	char sep[] = "@.";
	for (char* ret = strtok(str, sep); ret != NULL; ret = strtok(NULL, sep))
	{
		printf("%s", ret);
	}
	return 0;
}

打印结果为:LYlikuncsdncom

strerror

char* strerror( int errnum )

①strerror函数可以把参数部分错误码对应的错误信息的字符串地址返回来。
②在不同的系统和C语言标准库的实现中都规定了⼀些错误码,⼀般是放在errno.h 这个头文件中说明的,(使用需要包含头文件 errno.h)C语言程序启动的时候就会使用⼀个全面的变量errno来记录程序的当前错误码,只不过程序启动的时候errno是0,表示没有错误,当我们在使用标准库中的函数的时候发生了某种错误,就会将对应的错误码,存放在errno中,而⼀个错误码的数字是整数很难理解是什么意思,所以每⼀个错误码都是有对应的错误信息的。strerror函数就可以将错误对应的错误信息字符串的地址返回。

strerror的使用:(理解即可)

#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
	for (int i = 0; i <= 10; i++)
	{
		printf("%d:%s\n", i, strerror(i));
	}
	return 0;
}

打印结果为:
0:No error
1:Operation not permitted
2:No such file or directory
3:No such process
4:Interrupted function call
5:Input/output error
6:No such device or address
7:Arg list too long
8:Exec format error
9:Bad file descriptor
10:No child processes

#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
	FILE* pFile;
	pFile = fopen("unexist.ent", "r");
	if (pFile == NULL)
		printf("Error opening file unexist.ent: %s\n", strerror(errno));
	return 0;
}

打印结果为:
Error opening file unexist.ent: No such file or directory

perror

void perror( const char* str );

①perror函数能直接将错误信息打印出来
②perror函数打印完参数部分的字符串后,再打印⼀个冒号和⼀个空格,再打印错误信息。

#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
	FILE* pFile;
	pFile = fopen("unexist.ent", "r");
	if (pFile == NULL)
		perror("Error opening file unexist.ent");
	return 0;
}

打印结果为:
Error opening file unexist.ent: No such file or directory

三、内存操作函数

内存操作函数可以对任意类型的数据进行操作
这些函数的使用都需要包含头文件 string.h

memcpy

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

①函数memcpy从src的位置开始向后复制num个字节的数据到destination指向的内存位置。
②这个函数在遇到 ‘\0’ 的时候并不会停下来。
③src和dest不能有任何的重叠(不能包含相同地址)
例:

int src[] = { 1,2,3,4,5,6,7,8,9 };
//想让src[]={1,2,1,2,3,4,5,8,9};
memcpy(src+2, src, 5 * sizeof(src[0]));
//错误写法,前两个参数中指针会包含相同地址(3,4,5重叠)
//src会变成src[]={1,2,1,2,1,2,1,8,9};

memcpy的使用:

int main()
{
	//把src里面的元素全部复制到dest里面
	int dest[100] = { 0 };
	int src[] = { 1,2,3,4,5,6,7,8,9 };
	int sz = sizeof(src) / sizeof(src[0]);
	memcpy(dest, src, sz * sizeof(src[0]));
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", dest[i]);
	}
	return 0;
}

打印结果为:1 2 3 4 5 6 7 8 9

memcpy的模拟实现:

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

memmove

void* memmove ( void* dest,const coid* src,size_t num )

①功能与memcpy相似
②和memcpy的差别是memmove函数处理的源内存块和目标内存块是可以重叠的。
③如果源空间和目标空间出现重叠,就得使用memmove函数处理。

memmove的使用:

int main()
{
	int src[] = { 1,2,3,4,5,6,7,8,9 };
	//要src[] = { 1,2,1,2,3,4,5,8,9 };
	int sz = sizeof(src) / sizeof(src[0]);
	memmove(src+2, src, 5* sizeof(src[0]));
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", src[i]);
	}
	return 0;
}

打印结果为:1 2 1 2 3 4 5 8 9

memmove的模拟实现:
在这里插入图片描述
那么令:
src<dest时,src从尾向到首移动
否则,src从首到尾移动

void* my_memmove(void* dest, const void* src, size_t num)
{
	void* ret = dest;
	assert(dest && src);
	if (src < dest)
	{
		src = (char*)src + num - 1;//使src指向最后一个字节
		dest = (char*)dest + num - 1;//使dest指向最后一个字节
		while (num--)
		{
			//实现从尾到首移动
			*(char*)dest = *(char*)src;
			src = (char*)src - 1;
			dest = (char*)dest - 1;
		}
	}
	else
	{
		while (num--)
		{
			//实现从首到尾移动
			*(char*)dest = *(char*)src;
			src = (char*)src + 1;
			dest = (char*)dest + 1;
		}
	}
	return ret;
}

memset

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

①memset是⽤来设置内存的,将内存中的值以字节为单位设置成想要的内容。
②注意是以字节为单位设置

memset的使用:

int main()
{
	char arr[] = "LYlikunnnnnn";
	memset(arr, 'n', 6 * sizeof(arr[0]));
	printf("%s\n", arr);
	return 0;
}

打印结果为:nnnnnnnnnnnn

int main()
{
	int arr[] = {0,1,2,3,4,5,6,7,8,9};
	int sz = sizeof(arr) / sizeof(arr[0]);
	memset(arr, 0, sz*sizeof(arr[0]));
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

打印结果为:0 0 0 0 0 0 0 0 0 0

错误操作:

int main()
{
	//想把arr中每个元素都设置为1
	int arr[] = {0,1,2,3,4,5,6,7,8,9};
	int sz = sizeof(arr) / sizeof(arr[0]);
	memset(arr, 1, sz*sizeof(arr[0]));
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

打印结果如下图
在这里插入图片描述
因为memset以字节为单位进行操作,所以对一个整数进行置1操作时将其设置为0x 01 01 01 01,转换为十进制数是16843009
通常使用memset对整数数组进行置零操作

memcmp

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

①比较从ptr1和ptr2指针指向的位置开始,向后的num个字节(最多比较num个字节)
②ptr1>ptr2则返回1,ptr1=ptr2则返回0,ptr1<ptr2则返回-1

memcmp的使用:

int main()
{
	int arr1[] = { 1,2,3,4,5,6 };
	int arr2[] = { 1,2,3,8,8,8 };
	int ret=memcmp(arr1, arr2, 5 * sizeof(arr1));
	printf("%d\n", ret);
	return 0;
}

打印结果为:-1

完~。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值