字符串函数、字符处理函数和内存操作函数

目录

字符串函数

strlen

strcpy

strcat

strcmp

strncpy

strncat

strncmp

strstr

strtok

strerror

字符处理函数

字符分类函数

字符转换函数

内存操作函数

memcpy

memmove

memcmp

memset


字符串函数

strlen

size_t strlen ( const char* str )

函数作用

计算字符串的长度,但是不包括'\0'

注意

1.字符串中必须要有'\0'以'\0'作为结束标志

2.返回的是字符串中'\0'前面出现的字符的个数

3.返回值是无符号数类型  size_t == unsigned int

int main()
{
	int len1 = strlen("abcdef");
	printf("%d\n", len1);
	char arr[] = { 'a','b','c' };//没有终止符是不可以的,得到的是随机值,会一直往后找,知道找到\0为止
	int len2 = strlen(arr);
	printf("%d\n", len2);
	return 0;
}

模拟实现 

int my_strlen(const char* str)
{
	assert(str != NULL);
	int count = 0;
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}

strcpy

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

函数作用

将src的字符串内容拷贝到dest中

注意

1.源字符串必须以'\0'结束

2.会将源字符串中的'\0'拷贝到目标空间,目标空间必须足够大,以确保能够存放源字符串

3.目标空间必须可变

模拟实现

char* my_strcpy(char* dest, const char* src)
{
	assert(dest != NULL);
	assert(src != NULL);
	char* ret = dest;
	while(*dest++ = *src++)
	{
		;
	}
	return ret;
}

strcat

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

函数作用

在dest后面追加src中的字符串

注意

1.注意目标空间不要不写大小,必须保证目标空间足够大,可修改,不然会越界访问

2.从目标字符串中的第一个'\0'开始进行追加,源字符串必须以'\0'结束

模拟实现

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

strcmp

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

函数作用

比较两个字符串是否相等,相等则返回0,否则返回其他

注意

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

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

strncpy

char* strncpy(char* destination, const char* source, size_t count)

函数作用

将src中规定字节数的内容拷贝到dest中

注意

如果src的长度小于count,不够的地方会补0

char* my_strncpy(char* dest, const char* src, size_t count)
{
	assert(dest && src);
	char* ret = dest;
	while (count && (*dest++ = *src++))
	{
		count--;
	}
	if (count)
	{
		while (--count)
		{
			*dest++ = '\0';
		}
	}
	return ret;
 }

strncat

char* strncat(char* destination, const char* source, size_t count)

函数作用

在dest后面追加src中指定字节数的字符串内容

注意

1.追加完成之后会主动在追加内容后面放(覆盖)一个'\0'进去

2.如果追加个数大于src,那么不会追加多出来的部分

模拟实现

char* my_strncat(char* dest, const char* src, size_t count)
{
	assert(dest && src);
	char* ret = dest;
	while (*dest++)
	{
		;
	}
	dest--;
	while (count--)
	{
		if (!(*dest++ = *src++))
		{
			return ret;
		}
	}
	*dest = '\0';
	return ret;
}

strncmp

char* strncmp(const char* str1, const char* str2, size_t count)

函数作用

比较两个字符串中指定字节数的字符是否相等

模拟实现

int my_strncmp(const char* str1, const char* str2, size_t count)
{
	assert(str1 && str2 && count);
	while ((*str1 == *str2) && count)
	{
		if (*str1 == '\0')
		{
			return 0;
		}
		str1++;
		str2++;
		count--;
	}
	return *str1 - *str2;
}

strstr

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

函数作用

查询str2是否是str1的子串

注意

1.寻找字符串中是否有子串,若存在多个,则只找第一个
2.存在则从被查找对象的对应子串首地址返回,找不到则返回空指针

3.切记abbc中找bc这种案例!!!所以是要多次查找的,在这里就不要轻易动str1和str2了

char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);
	char* s1 = (char*)str1;
	char* s2 = (char*)str2;
	char* start = (char*)str1;//防止出现上述情况,所以多次遍历
	if (*str2 == '\0')
	{
		return (char*)str1;//str2为空字符串就返回str1
	}
	while (*start)
	{
		s1 = start;
		s2 = (char*)str2;
		while (*s1 && *s2 && *s1 == *s2)
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			return start;
		}
		start++;//更新遍历的初始位置和查找位置
	}
	return NULL;
}

strtok

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

函数作用

将字符串按照指定的分隔符取出

注意

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

int main()
{
	char arr[] = "abc@def.com";
	char buf[30] = "";
	char* p = "@.";
	strcpy(buf, arr);
	char* ret = strtok(arr, p);
	printf("%s\n", ret);
	ret = strtok(NULL, p);//对相同串多次切割时第一个参数传入NULL
	printf("%s\n", ret);
	ret = strtok(NULL, p);
	printf("%s\n", ret);
	return 0;
}
运行结果:
abc
def
com
int main()
{
	char arr[] = "abc@def.com";
	char buf[30] = "";
	char* p = "@.";
	strcpy(buf, arr);
	char* ret = NULL;
	for (ret = strtok(arr, p); ret != NULL;ret=strtok(NULL,p))
	{
		printf("%s\n", ret);
	}
	return 0;
}
运行结果:
abc
def
com

strerror

char* strerror(int errnum)

函数作用

解析字符串错误代码的含义

注意

1.需要包含<string.h>,<errno.h>
2.返回错误码所对应的错误信息

错误码    错误信息
0 - No error - 没有错误
1 - Operation not permitted - 操作不允许
2 - No such file or directory - 没有该文件
 ……
 errno 是一个全局的错误码的变量,当C语言的库函数在执行过程中发生了错误,就会把对应的错误码赋值到errno中

int main()
{
	char* str = strerror(errno);
	printf("%s\n", str);
	return 0;
}
运行结果:
No error

字符处理函数

字符分类函数

<ctype.h>

iscntrl任何控制字符
isspace空白字符:空格' ',换页'\f',换行'\n',回车'\f',制表符'\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 main()
{
	char ch1 = 'a';
	char ch2 = '@';
	int ret1 = islower(ch1);
	int ret2 = islower(ch2);
	printf("%d\n", ret1);
	printf("%d\n", ret2);
	return 0;
}
运行结果:
2
0

字符转换函数

int tolower(int c)//转小写字母

int toupper(int c)//转大写字母

int main()
{
	char ch = tolower('A');
	putchar(ch);
	return 0;
}
运行结果:
a

内存操作函数

操作对象可以是整型数组,结构数组等

memcpy

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

函数作用

拷贝指定字节数的内容

注意

num为拷贝的字节数!!!

模拟实现

void* my_memcpy(void* dest, const void* src, size_t num)
{
	assert(dest && src);
	void* ret = dest;
	while (num--)
	{
		*(char*)dest = *(char*)src;
		++(char*)dest;//++和*同级,结合方向都为从右往左,所以要先强转再++
		++(char*)src;
	}
	return ret;
}

 希望在同一个数组中实现覆盖的功能,如12345678910将1234拷贝给3456,编变成12123478910

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memcpy(arr + 2, arr, 20);
	return 0;
}

这样是错误的,因为源数组被改变了,正着拷贝会覆盖源数据,不如倒着拷贝

但是上面倒着拷贝的思维也不是完全正确的,因为上面是前面覆盖后面,但是如果是后面覆盖前面,倒着拷贝的方法就又不可以了,所以,重叠拷贝问题由memmove处理,其实memmove不重叠问题也可以实现

实际上,C语言库函数中memcpy对于重叠问题也是可以实现的

memmove

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

模拟实现

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

上面的第18行中,详解:小端,假如01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00,想要把123拷贝到234,就要从3的最后一个字节开始拷贝,也就是04前面的00,这个时候如果我们直接+12个字节,那么会指到04的位置,所以需要-1,指到03的最后一个字节再进行拷贝

弄明白上面的内容之后,可以再对其进行优化

void* my_memmove(void* dest, const void* src, size_t num)
{
	assert(dest && src);
	void* ret = dest;
	if (dest < src)
	{
		//从前向后拷贝
		while (num--)
		{
			*(char*)dest = *(char*)src;
			++(char*)dest;
			++(char*)src;
		}
	}
	else
	{
		//从后向前拷贝
		//优化:
		while (num--)//经过这一步,就已经实现了-1操作
		{
			*((char*)dest + num) = *((char*)src + num);
		}
	}
	return ret;
}

判断方式2:

if (dest<src || dest>(char*)src + count)
{
	//从先往后
}
else
{
	//从后往前
}

memcmp

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

函数作用

比较指定字节数的元素是否相等

int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 1,2,5,4,3 };
	int ret = memcmp(arr1, arr2, 8);
	printf("%d\n", ret);
	return 0;
}
运行结果:
0

memset

void* memset(void* destination, int c, size_t num)

函数作用

将数组中的元素按照字节数设置

注意

内存设置函数,num同样是字节数,不是元素数,不要用错!!!

int main()
{
	int arr[10] = { 0 };
	memset(arr, 1, 4);
	return 0;
}

上面代码执行完函数之后,为什么数组里面的数字不是1000000000?原因是改变的是字节数,不是元素数,即改变为01010101 00000000 ……

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值