字符函数和字符串函数

目录

求字符串长度

strlen

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

strcpy

strcat

strcmp

长度受限制的字符串函数介绍

strncpy

strncat

strncmp

字符串查找

strstr

strtok

错误信息报告

strerror

内存操作函数

memcpymemmovememsetmemcmp

 前言:C语言中对字符和字符串的处理是频繁的,但是C语言本身是没有字符串类型的,字符串通常放在常量字符串中或字符数组中。字符串常量适用于那些对它不做修改的字符串函数.

求字符串长度

strlen

strlen
size_t strlen(const char*str);
字符串已经'\0'作为结束标志,strlen函数返回的是在字符串中'\0'前面出现的字符个数(不包含'\0').
参数指向的字符串必须要以'\0'结束 否则求出的是随机值
注意函数的返回值为size_t,是无符号的(易错)
学会strlen的模拟实现:

可以给模拟实现取个名字叫my_strlen,接着给一个数组 将数组名传入 传址调用

int main()
{
	char arr[] = "abc";
	//求到\0之前的字符个数 且必须有\0
	//char arr[] = { 'a','b','c' };//这里没有\0     随机值
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

实现my_strlen函数功能:

因为my_strlen不回改变数组内字符的内容,所以用const修饰,保护指针所指向的内容。

这里可以说一下:const char*p表示指针是变量 指向一个常量。常量指针->指向常量的指针

char const*p表示指针是常量,指向一个变量。指针常量->指针类型的常量

记忆方法:从左到右和谁连接谁不变.

同时,因为避免接受空指针,最好添加一个还能输assert 

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

完整代码:

#include<stdio.h>
#include<string.h>
#include<assert.h>
int my_strlen(const char*str)
{
	int count = 0;
	assert(*str != NULL);
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}
int main()
{
	char arr[] = "abc";
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

注意:这里有一个坑!

int main()
{
	if (strlen("abc") - strlen("abcdef") > 0)
	{
		printf(">\n");
	}
	else
	{
		printf("<=");
	}
	return 0;
}//结果是什么? > 因为strlen的返回值无符号数!坑!
用my_strlen可以算出<=

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

strcpy

char*strcpy(char*destination,const char*source)
                       目标空间                        原
Copies the C string pointed by source into the array pointed by destiantion,including the terminating null
character(and stopping at that point)
源字符串必须以‘\0’结束
会将源字符串中的‘\0’拷贝到目标空间
目标空间足够大,以确保能存放源字符串
目标空间必须可变
学会模拟实现


#include<stdio.h>
#include<string.h>
int main()
{
 
	//char arr[20] = { 0 };//arr数组可修改
	//char* str = "**********";//这个str是一个常量字符串 不可修改
	char arr[20] = "###########";
	//char arr[20] = "##";
	//char* p = "hello world";
	//arr="hello"//error arr是个地址 hello要放在空间中
	strcpy(arr,"hello");//string copy
	//char arr2[] = { 'a','b','c' };//没有\0
	//保证目标空间要足够大

	//char*p = "hello"传递过去的只是h的地址
	printf("%s\n", arr);
	//源字符串没有\0 会越界访问
	return 0;
}

 strcpy模拟实现:目的地的字符串是可以被修改的,而源头我们不希望它被修改用const,这样的好处是:如果不小心改了编译器会报错

#include<stdio.h>
#include<assert.h>
char* my_strcpy(char* dest, const char* src)
{
	assert(dest && src);//保证接收的不是空指针
	char* tmp = dest;
	while (*dest++ = *src++)
	{
		;
	}
	return tmp;
}
int main()
{
	char arr1[10] = "abcdef";
	char arr2[] = "123";
	char *p= my_strcpy(arr1, arr2);
	printf("%s", p);


	return 0;
}
#include<stdio.h>
#include<assert.h>
char* my_strcpy(char* dest, const char* src)
{
	assert(dest && src);
	char* tmp = dest;
	while (*src != '\0')
	{
		*dest = *src;
		dest++;
		src++;
	}
	*dest = *src;
}
int main()
{
	char arr1[10] = "abcdef";
	char arr2[] = "123";
	char* p = my_strcpy(arr1, arr2);
	printf("%s", p);
	return 0;
}

 

strcat

strcat字符串追加函数 
char*strcat(char*destination,const char*source);
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 destination of both in destination.
源字符串必须以‘\0’结束
目标空间必须足够的打,能够容得下源字符串的内容
目标空间必须可修改
字符串自己给自己追加,如何?

然而,对于自我追加的情况,即源字符串和目标字符串是同一个字符串时,strcat函数并不能正常工作。这是因为一个字符数组只有一个空间,当需要在一个空间进行自己的字符串追加时,一旦找到'\0'(字符串结束符)后,将下一个字符(例如'a')赋给'\0',那么此字符串就没有终止空字符了,导致程序行为未定义。因此,为了安全,通常不推荐使用strcat函数自我追加。

好的,我来尝试用更通俗的语言解释一下。

想象一下,你有一个空的玻璃杯,你想往里面倒水。当你倒进去一些水后,你会在杯子里看到水位上升。但是,如果你试图把已经在水中的水再倒回杯子里,那么水位就不会再上升了,因为杯子里的水和你要倒的水其实是同一种东西。

同样的道理,strcat函数就像是那个玻璃杯,而要追加的字符串就像是水。当你试图把一个字符串追加到另一个字符串的末尾时,就像把水倒进杯子一样,结果是可以预见的。但是,如果你试图把同一个字符串追加到它自己(也就是同一个"玻璃杯")的末尾,那么结果就变得不可预测了。因为你无法确定这个字符串在哪里结束,所以当程序试图找到一个确定的结束位置时,可能会找不到,从而导致错误。

int main()
{
	char arr[] = "abcd";
	strcat(arr, arr);//不能 \0被改变了 找不到结束的标志\0
	printf("%s\n", arr);
	return 0;
}

 

 

#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[20] = "hello" ;//world
	char arr2[] = "world";
	strcat(arr1,arr2);//字符串追加(连接)
	printf("%s\n", arr1);
	return 0;
}

模拟实现: 

char* my_strcat(char*dest,const char*src)
{
	char* ret = dest;
	//找到目标字符串中的\0
	assert(dest && src);
	while (*dest)
	{
		dest++;
	}
	//源数据追加过去(包含\0)
	while (*dest++ = *src++)
	{
		;
	}
	return ret;//返回的目标空间的起始地址

}
int main()
{
	char arr1[20] = "hello ";//world
	char arr2[] = "world";
	my_strcat(arr1, arr2);//字符串追加(连接)
	printf("%s\n", arr1);
	return 0;
}

注意strcat和strcpy的区别:strcat追加字符串  strcpy拷贝字符串;特别注意strcat从\0位置开始追加,会追加所有内容包含world后隐藏的\0

strcpy只能单纯的拷贝字符串,arr1数组中原有的内容会被覆盖 而且strcpy会从arr2拷贝所有的内容到arr1中包含自身的\0

 

 

 

strcmp

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 untill the charaters differ or until a terminating null-character is reached.
标准规定:
第一个字符串大于第二个字符串则返回大于0的数字
第一个字符串等于第二个字符串,贼返回0
第一个字符串小于第二个字符串,则返回小于0的数字
那么如何判断两个字符串?
一个一个比过去 abcd..排序

#include<string.h>
#include<stdio.h>
int main()
{
	char* p = "abcdef";
	char* q = "abbb";
	int ret=strcmp(p, q);
	if (ret > 0)
	{
		printf("p>q");
	}
	else if (ret < 0)
	{
		printf("p<q");
	}
	else
	{
		printf("q=p");
	}
	return 0;
}

 

#include<Stdio.h>
#include<string.h>
#include<assert.h>
int my_strcmp(const char*s1,const char*s2)
{
	assert(s1 && s2);
	while (*s1 == *s2)
	{
		if (*s1 == '\0'||*s2=='\0')
		{
			return 0;
		}
		s1++;
		s2++;
	}
	if (*s1 > *s2)
	{
		return 1;

	}
	else
	{
		return -1;
	}

	return *s1 - *s2;
		

}
int main()
{
	char* p = "abcdef";
	char* q = "abbb";
	int ret=my_strcmp(p, q);
	if (ret > 0)
	{
		printf("p>q");
	}
	else if (ret < 0)
	{
		printf("p<q");
	}
	else
	{
		printf("q=p");
	}
	return 0;
}

 //strcpy - 复制
//strcat - 追加
//strcmp - 比较
//-------------长度不受限制

长度受限制的字符串函数介绍

strncpy

 

strncat

strncmp

字符串查找

strstr
int main()
{
	char arr1[] = "abcdefabcdef";
	char arr2[] = "bcd";
	//在arr1中查找arr2数组
	//找到返回b地址 否则返回空指针
	char*ret=strstr(arr1,arr2);
	if (ret == NULL)
	{
		printf("没找到");
	}
	else
	{
		printf("找到了:%d", ret);

	}

	return 0;
}

 模拟实现:

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


char* my_strstr( const char* str1,const char* str2)
{
	if (*str2 == '\0')
	{
		return (char*)str1;
	}
	// abbbcdef
	//bbc
	assert(str1 && str2);
	char* s1 = NULL;
	char* s2 = NULL;
	char* cp = str1;
	while (*cp)
	{
		s1 = cp;
		s2 = str2;
		while (*s1&&*s2&&*s1 == *s2)
		{
			s1++;
			s2++;

		}
		if (*s2 == '\0')
		{
			return (char*)cp;
		}
		cp++;
	}
	return NULL;
}
int main()
{
	char arr1[] = "abcdefabcdef";
	char arr2[] = "bcd";
	//在arr1中查找arr2数组
	//找到返回b地址 否则返回空指针
	char* ret = my_strstr(arr1, arr2);
	if (ret == NULL)
	{
		printf("没找到");
	}
	else
	{
		printf("找到了:%d", ret);

	}

	return 0;
}
//    KMP算法 - 字符串中找字符串的算法

 

 为什么要设置三个变量s1 ,s2分别遍历和比对 pc用来记录或者说返回 确保说能够找到

也就是s1和s2不相等的时候s2回去str2的位置,pc向前走然后s1在pc的基础上向后走就算p1==p2了p1和p2依然要向后走直到s2找到\0如果pc走到了\0都找不到那么就是找不到了

#include<stdio.h>
#include<assert.h>
const char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);
	
	const char* cp = str1;
	
	//如果*cp指向了\0就直接结束循环,\0就算ASCII中的0
	
	while (*cp)
	{
		const char* s1 = cp;
		const char* s2 = str2;
		//s1 s2都是不等于\0如果等于就结束循环
		while (*s1 == *s2 && *s1 & *s2)
		{
			//相等之后向后寻找
			s1++;
			s2++;
		}
		
		if (*s2 == '\0')
		{
			//找到了就返回刚刚pf保存的地址
			return cp;
		}
		//这里说明没有相等 继续往后找
		cp++;
	}
	return NULL;
	//*cp指向\0都找不到就直接返回空
	
}
int main()
{
	char arr1[] = "yu jia yang";
	char arr2[] = "yang";

	char* ret = my_strstr(arr1, arr2);
	if (ret == NULL)
	{
		printf("没有找到");
	}
	else
	{
		printf("%s", ret);
	}

	return 0;
}

 

strtok

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

#include<stdio.h>
#include<String.h>


int main()
{
	char arr[] = "nihao@world.hello";
	char* p = "@.";
	char tmp[20] = { 0 };
	strcpy(tmp, arr);
	char* ret = NULL;
	for (ret = strtok(tmp, p); ret != NULL; ret = strtok(NULL,p))
	{
		printf(" % s\n", ret);
	}
	return 0;
}

 

错误信息报告

strerror

strerror
char *strerror(int errnum);
返回错误码,所对应的错误信息
include<errno.h>必须包含的头文件

使用库函数的时候
调用库函数失败的时,都会设置错误码
 

// 全局的错误码
int errno;
#include<errno.h>
int main()
{
	/*printf("%s\n", strerror(0));
	printf("%s\n", strerror(1));
	printf("%s\n", strerror(2));
	printf("%s\n", strerror(3));
	printf("%s\n", strerror(4));
	printf("%s\n", strerror(5));*/

	FILE* pf = fopen("test.txt", "r");
	if (pf = NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	//...
	fclose();


	return 0;
}

//相关函数 perror - 更好!
// 
//打印错误信息

//strerror 将错误码转化为错误信息 但是打不打印完全靠自己

//perror - 

//字符函数

有兴趣自行了解啦!

还有很多的库函数呢

#include<stdio.h>
#include<ctype.h>
int main()
{
	char ch = '#';
	char ch = 'A';
    //判断大写字母
	//if (ch >= 'A' && ch <= 'Z')
		
		
	// {
	// }
	// 这也是一种判断大小写的方法
	//isdigit 如果是数字字符返回非0的值,如果不是数字字符,返回0
	//int ret = isdigit(ch);
	//int ret = islower(ch);//判断是不是小写字母
	printf("%d\n", ret);//
	return 0;
}
//字符转换
int main()
{
	char arr[20] = { 0 };
	scanf("%s", arr);
	int i = 0;
	while (arr[i] != '\0')
	{
		if (isupper(arr[i]))//如果里面有大写字母
		{
			arr[i]=tolower(arr[i]);//转化为小写
			
		}
		printf("%c ", arr[i]);
		i++;
	}
	return 0;
}
//int tolower(int c)
//int toupper(int c)

//1.字符分类函数
//2.字符转换函数

 

内存操作函数

memcpy

内存操作函数
memcpy - 应该拷贝不重叠的内存
memmove函数可以处理内存重叠的情况

//内存函数

int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[20] = { 0 };
	//strcpy() 是拷贝字符串的 遇到\0才停止
	// 01 00 00 00 02 00 00 00...
	//char*strcpy(char*dest,char*src)
	return 0;
}

 注意:数据长度

如果复制的数据类型是char 那么数据长度就等于元素个数。注意(结构体等)

#include<stdio.h>
#include<string.h>
 int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[20] = { 0 };
	//这个函数的参数类型是void*

	memcpy(arr2, arr1, 20);//5个元素每个元素四个字节 也就是20个字节

	return 0;
}
char a[10]="yujiayang";
void*p=memcpy(a+3,a,2);

这里的意思是是讲从a开始的两个字节的数据也就是yu 复制到a+3开始的内存中也就是i那个位置

运行后变成yujyuyang (自己思考一下其他类型)

//memcpy的模拟实现
#include<assert.h>
void* my_memcpy(void* dest, const void* src,size_t num)
{
	void* ret = dest;
	assert(dest && src);
	while (num--)
	{
		*(char*)dest = *(char*)src;//为什么类型转化 因为void*是任意类型
		dest=(char*)dest+1;
		src = (char*)src + 1;//++优先级高
		//单目操作符从右向左


	}
	return ret;//返回目标空间
	
}
int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[20] = { 0 };
	//这个函数的参数类型是void*

	int ret =my_memcpy(arr2, arr1, 20);//5个元素每个元素四个字节 也就是20个字节
    //int ret = my_memcpy(arr1+2,arr1,20);
//            1 2 1 2 3 4 5 8 9 10 //有重叠的部分会出问题
//            1 2 1 2 1 2
	return 0;
}

memmove
//操作符贪心法
//c语语言生动解剖

int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	//               1 2 1 2 3 4 5 8 9 10
	memmove(arr1 + 2, arr1, 20);//可以处理内存不重叠的情况
	return 0;
}

 //整数 小端存储
//01 00 00 00 | 02 00 00 00| 03 00 00 00 |04 00 00 00| 05 00 00 00|06 00 00 00|07 00 00 00|...0a 00 00 00
//一个一个字节的拷贝过去
//memcpy-只要实现了不重叠拷贝就可以了
//而VS的实现既可以拷贝不重叠也可以拷贝重叠内存

//模拟实现
#include<assert.h>
void* my_memmove(void* dest, const void* src,size_t num)
{
	void* ret = dest;
	assert(dest && src);
	//可以把源数据从前向后拷贝 - 也可以从后向前拷贝
	if (dest < src)
	{
		//从前->后
		while (num--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	else
	{
		//后->前
		while (num--)//20 19
		{
			*((char*)dest + num) = *((char*)src + num);

		}
	}
}
int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	//               1 2 1 2 3 4 5 8 9 10
	my_memmove(arr1 + 2, arr1, 20);
	return 0;
}//如果要打印直接遍历数组打印即可


memset
//memcmp - 内存比较
#include<string.h>
int main()
{
	float arr1[] = { 1.0,2.0,3.0,4.0 };
	float arr2[] = { 1.0,3.0 };
	int ret = memcmp(arr1, arr2, 8);
	//memcpy - strcmp 相等返回0
	printf("%d\n", ret);
	return 0;
}

memcmp
//memset - 内存设置

int main()
{
	int arr[10] = { 0 };
	memset(arr,1,20);//以字节为单位设置内存的
	//01 00 00 00
	//01 01 01 01
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值