C语言字符串函数和内存函数

一、求字符串长度

在使用字符串函数时都需要引头文件 <string.h>

1.strlen

strlen是打印字符串中含有字符的个数。
使用示例:

int main()
{
	char str[] = "abc";
	int ret = strlen(str);
	//strlen是计算'\0'之前字符的个数,不包括'\0'。
	printf("%d", ret);//结果为3
	return 0;
}

strlen的模拟实现:

//计数器方式
int my_strlen(const char* str)
{
	int count = 0;
	while (*str++)
	{
		count++;
	}
	return count;
}

//不创建临时变量计数器的方法
int my_strlen(char* str)
{
	if (*str == '\0')
	{
		return 0;
	}
	else
	{
		return 1 + my_strlen(str + 1);
	}
}

//指针-指针的方式
int my_strlen(char* s)
{
  char* p = s;
	while (*p != '\0')
		p++;
	return p - s;
}

int main()
{
	char str[] = "abcdefgh";
	printf("%d\n", my_strlen(str));
	return 0;
}

注意:strlen返回的是无符号数。

二、长度不受限制的字符串函数

1.strcpy

strcpy是将一个字符串中的字符拷贝到另一个空间。
1.源字符串必须以’\0’结束。
2.会将源字符串中的’\0’拷贝到目标空间。
3.目标空间必须足够大,以确保能存放源字符串。
4.目标空间必须可变。
使用示例:

int main()
{
	char str1[] = "XXXXXXXXXX";
	char str2[] = "Juice";
	printf(strcpy(str1, str2));//strcpy会返回目标空间的首字符地址,打印结果为Juice。
	return 0;
}

模拟实现:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <assert.h>
char* my_strcpy(char* dest, const char* sorc)
{
	char* ret = dest;
	assert(dest && sorc);
	while (*dest++ = *sorc++)
	{
		;
	}
	return ret;
}

int main()
{
	char str1[] = "abcdefg";
	char str2[] = "efg";
	printf("%s\n", my_strcpy(str1, str2));//结果为efg。
	return 0;

}

2.strcat

strcat函数是将源字符串追加到目标字符串的后面。
1.目标字符串必须包含’\0’,因为是从目标字符串的’\0’开始追加。
2.源字符串必须以’\0’结束。
3.会将源字符串中的’\0’拷贝到目标空间。
4.目标空间必须足够大,以确保能存放源字符串。
5.目标空间必须可变。

int main()
{
	char str1[50] = "Hello!";
	char str2[] = "World";
	printf("%s", strcat(str1, str2));//str1是目标字符串,str2是源字符串
	                                 //将str2追加到str1的后面,打印结果为 Hello!World
}

strcat的模拟实现:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <assert.h>
char* my_strcat(char* str1, const char* str2)
{
	assert(str1 && str2);
	char* ret = str1;
	while (*str1)
	{
		str1++;
	}
	while (*str2)
	{
		*str1++ = *str2++;
	}
	*str1 = *str2;
	return ret;
}

int main()
{
	char str1[50] = "hello ";
	char str2[] = "bit!";
	printf("%s\n", my_strcat(str1, str2));//打印结果为 hello bit!
	return 0;
}

3.strcmp

strcmp比较的不是字符串的长度,而是按照ASCII码比较两个字符串中对应位置上的字符的大小,如果相同,就比较下一对,直到不同或者都遇到’\0’。

规定:1.第一个字符串大于第二个字符串,则返回大于0的数字。
2.第一个字符串小于第二个字符串,则返回小于0的数字。
3.第一个字符串等于第二个字符串,则返回0。
使用示例:

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abq";
	int ret = strcmp(arr1, arr2);//c的ASCII码值小于q的ASCII码值,所以strcmp返回一个小于0的数。
	printf("%d", ret);//打印结果为-1
}

strcmp的模拟实现:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

int my_strcmp(const char* str1, const char* str2)
{
	while (*str1++ && *str2++)
	{
		if (*str1 > *str2)
		{
			return 1;
		}
		else if (*str1 < *str2)
		{
			return -1;
		}
		else
		{
			return 0;
		}
	}
}

void print_strcmp(int(*cmp)(const char* str1, const char* str2),const char*str1,const char*str2)
{
	if (cmp(str1, str2) > 0)
	{
		printf("str1大于str2");
	}
	else if (cmp(str1, str2) < 0)
	{
		printf("str1小于str2");
	}
	else
	{
		printf("str1等于str2");
	}
}

int main()
{
	char str1[] = "abcd";
	char str2[] = "accd";
	char str3[] = "ab";
	print_strcmp(my_strcmp,str1,str2);//结果为str1小于str2
	return 0;
}

三、长度受限制的字符串函数

1.strncpy

strncpy的使用方法与strcpy一样,只不过可以规定要拷贝字符串的个数。
1.拷贝n个字符从源字符串到目标空间。
2.如果源字符串的长度小于n,则拷贝完字符串之后,在目标的后面追加0,直到n个。

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



int main()
{
	char str1[50] = "XXXXXX";
	char str2[] = "Juice";
	//strncpy的第三个参数是要操作的字符串个数
	printf("%s", strncpy(str1, str2, 5));//打印结果为 JuiceX。
	printf("%s", strncpy(str1, str2, 6));//打印结果为 Juice,因为把'\0'也拷贝进去了。
}

strncpy的模拟实现:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <assert.h>

char* my_strncpy(char* dest, const char* src, int num)
{
	char* ret = dest;
	while (num-- && *src)
	{
		*dest++ = *src++;
	}
	if (num != -1)
	{
		*dest = 0;
	}
	return ret;
}

int main()
{
	char str1[50] = "hello";
	char str2[] = "bit";
	my_strncpy(str1, str2, 2);//打印结果为billo
	printf("%s", str1);
	return 0;
}

2.strncat

strncat的使用方法和strcat一样,只不过可以规定追加的字符个数。
1.strncat在将源字符串追加到目标空间时,如果追加的字符不包括’\0’,那么除了将所追加的n个字符追加到目标字符串后面,还会自动再追加一个’\0’在后面。
2.如果n大于源字符串的字符个数,那么也只会将源字符串中的字符追加到目标字符串后面。
使用示例:

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

int main()
{
	char arr1[20] = "abcdef\0XXXXXXXX";
	char arr2[] = "qwe";
	strncat(arr1, arr2, 3);//将arr2中的三个字符追加到arr1中字符'\0'。
	printf("%s", arr1);//打印结果为abcdefqwe。
	return 0;
}

strncat的模拟实现:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

char* my_strncat(char* dest, const char* src, int num)
{
	char* tmp = dest + strlen(dest);
	while (num-- && *src)
	{
		*tmp++ = *src++;
	}
	*tmp = 0;
	return dest;
}

int main()
{
	char str1[20] = "Hello!";
	char str2[] = "bit";
	printf("%s\n", my_strncat(str1, str2, 3));//打印结果为Hello!bit
	return 0;
}

3.strncmp

strncmp的用法与strcmp一样,只不过可以规定要比较字符串的前n个。

1.比较到出现一个不一样的字符,或者一个字符串结束,或者n个字符全部比较完。

使用示例:

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

int main()
{
	char str1[] = "abcdef";
	char str2[] = "abcdtyuiop";
	int ret = strncmp(str1, str2, 4);//比较前四个字符。
	printf("%d", ret);//打印结果为0
	return 0;
}

strncmp的模拟实现

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <assert.h>

int my_strncmp(char* str1, const char* str2, int n)
{
	assert(str1 && str2);
	while (n--)
	{
		if (*str1 > *str2)
		{
			return 1;
		}
		else if (*str1 < *str2)
		{
			return -1;
		}
		else
		{
			str1++;
			str2++;
		}
		if (*str1 == '\0' || *str2 == '\0')
		{
			return 0;
		}
	}
	return 0;
}

int main()
{
	char str1[] = "abcdef";
	char str2[] = "abcdtyuiop";
	int ret = my_strncmp(str1, str2, 4);//比较前四个字符。
	printf("%d", ret);//打印结果为0
	return 0;
}

四、字符串查找

1.strstr

在一个字符串中找另一个字符串,看一个字符串是不是另一个字符串的子串。

strstr(str1,str2)//str2字符串是str1的子串,就返回str2在str1中第一次出现的首字符的地址。
//str2不是str1的子串,就返回 NULL(空指针)。

使用示例:

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


int main()
{
	char arr1[] = "abcdJuicefghuJuice";
	char arr2[] = "Juice";
	printf("%s",strstr(arr1, arr2));//打印的结果是JuicefghuJuice
	
	return 0;
}

strstr的模拟实现:

#define  _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <assert.h>

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

int main()
{
	char str1[] = "hello bit! hello World";
	char str2[] = "bit";
	char str3[] = "Hi";
	char str4[] = "helLo";
	char str5[] = "hello";
	if (my_strstr(str1, str2) == NULL)
	{
		printf("找不到子串");
	}
	else
	{
		printf("%s\n", my_strstr(str1, str2));//打印结果为bit! hello World
	}
	return 0;
}

2.strtok

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

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

使用示例:

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

int main()
{
	char str[] = "Juice@WRLD*999!999";
	char buf[30] = { 0 };
	strcpy(buf, str);
	const char* sep = "@*!";
	//strtok(buf, sep);只找第一个标记。
	//strtok(NULL, sep);从保存好的位置开始继续往后找,一次找一个sep中的字符。
	char* ptr = NULL;
	//注意:
	//char* ptr = strtok(buf, sep);  ×
	//错误的初始化方法,当在循环外初始化直接将strtok(buf,sep)赋值给ptr,会导致buf字符串中的'@'被替换为'\0'。
	//当for循环中再次调用strtok(buf,sep)函数时,实际上是对字符串"Juice"使用了strtok函数,而不是"Juice@WRLD*999!999"。
	//导致最后循环输出只能输出Juice
	for (ptr = strtok(buf, sep); ptr!= NULL; ptr = strtok(NULL, sep))
	{
		printf("%s\n", ptr);//打印结果为:
		                    //Juice
		                    //WRLD
		                    //999
		                    //999
	}
	//另外一种方法
    /*char* ptr = strtok(buf, sep);
    while (ptr != NULL)
    {
    	 printf("%s\n", ptr);
    	 ptr = strtok(NULL, sep);
    }*/

	return 0;
}

五、错误信息报告

1.strerror

返回错误码所对应的错误信息。
必须包含头文件<errno.h>与<string.h>。

使用示例:

#define  _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <limits.h>

int main()
{
	int* p = (int*)malloc(INT_MAX);
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));//当malloc开辟空间失败时,会将错误码放到errno里面。
	}   //用strerror可以将错误码翻译。    //打印结果为Not enough space
	return 0;
}

六、字符操作

在这里插入图片描述字符转换

int tolower (int c);//将大写字母转换为小写字母
int toupper (int c);//将小写字母转换为大写字母

七、内存操作函数

1.memcpy

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

函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置
1.这个函数在遇到’\0’的时候并不会停下来。
2.如果source和destination有任何的重叠,复制的结果都是未定义的。

使用示例:

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


int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[5] = { 0 };
	memcpy(arr2, arr1, 20);//int类型占4个字节,拷贝20个字节,把1 2 3 4 5拷贝到arr2.
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%d ", arr2[i]);//打印结果为1 2 3 4 5 
	}
	return 0;
}

memcpy的模拟实现:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <assert.h>
void* my_memcpy(void* dest, const void* src, int count)
{
	assert(dest && src);
	char* dest1 = dest;
	char* src1 = src;
	while (count--)
	{
		*dest1++ = *src1++;
	}
	return dest;
}

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[] = { 99,88,77,66,55 };
	my_memcpy(arr, arr2, 20);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);//打印结果为99 88 77 66 55 6 7 8 9 10
	}
	return 0;
}

2.memmove

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

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

在这里插入图片描述

如图所示,当出现(1)对应的情况时,就需要先从src的头部开始复制;也就是memmove源码中的if分支,这部分源码和memcpy的实现是一致的;当出现(2)对应的情况时,就需要先从src的尾部开始复制,防止出现了覆盖现象。这就是memmove比memcpy多的一个考虑点,所以说,在实际使用时,使用memmove是比memcpy更安全的。

memmove的模拟实现:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

void* my_memmove(void* dest, const void* src, int count)
{
	char* buf1 = dest;
	char* buf2 = src;
	if (dest <= src|| (char*)dest >= ((char*)src + count))
	{
		while (count--)
		{
			*buf1++ = *buf2++;
		}
	}
	else
	{
		while (count--)
		{
			*(buf1 + count) = *(buf2 + count);
		}
	}
	return dest;
}

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[] = { 99,88,77 };
	my_memmove(arr1+2, arr1, 24);
	int sz = sizeof(arr1) / sizeof(arr1[0]);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr1[i]);//打印结果为1 2 1 2 3 4 5 6 9 10
	}
}

3.memcmp

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

比较从ptr1和ptr2两个指针开始的num个字节
1.ptr1指向的内容大于ptr2时,函数返回大于0的数。
2.ptr1指向的内容等于ptr2时,函数返回0。
3.ptr1指向的内容小于ptr2时,函数返回小于0的数。

使用示例:

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

int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 1,2,3,4,0x11223305 };
	printf("16字节比较结果:%d\n",memcmp(arr1, arr2, 16));
	//打印结果为0,因为前16个字节为前4个整形,相等返回0。
	
	printf("17字节比较结果:%d\n", memcmp(arr1, arr2, 17));
	//打印结果为0,因为前17个字节为前四个整形加上1个字节,两个数组第17个字节存的都是 05 所以相等返回0。
	
	printf("18字节比较结果:%d\n", memcmp(arr1, arr2, 18));
	//打印结果为-1,因为arr1的第18个字节存的是00,而arr2的第18个字节存的是33,所以比较结果为arr1<arr2 返回-1。
	return 0;
}

arr1的内存:
在这里插入图片描述
arr2的内存:
在这里插入图片描述

4.memset

void * memset(void * destination, int c, size_t count);

内存设置函数,将目标指针destination后面的count个字节设置为c。

使用示例:

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

int main()
{
	int dest[] = { 99,88,77,66,55 };
	memset(dest, 0x99, 20);//将20个字节全部设置为16进制的99
}

memset以前的dest数组内存:
在这里插入图片描述
memset以后的dest数组内存:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值