一万字带你详解C语言字符函数、字符串函数、内存函数_&是c语言中是什么(1)

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

▶ 目标字符串必须可修改
▶ 不能自己追加自己

💦 strcmp

🔗 函数原型和头:
在这里插入图片描述
🔗 函数的返回值:
在这里插入图片描述

🔗 功能:
在这里插入图片描述


❌ 字符串的比较

#include<stdio.h>
#include<string.h>
int main()
{
	char\* p1 = "def";
	char\* p2 = "abc";
	if(p1 > p2)
		printf(">\n");
	else 
		printf("<\n");
	return 0;
}

📝 分析:
在这里插入图片描述


✔ 字符串的比较

#include<stdio.h>
#include<string.h>
int main()
{
	char\* p1 = "def";
	char\* p2 = "abcdef";
	printf("%d\n", strcmp(p1, p2));
	return 0;
}

📝 分析:
在这里插入图片描述

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

💦 strncpy

🔗 函数原型和头:
在这里插入图片描述

🔗 函数的返回值:
在这里插入图片描述
🔗 功能:
在这里插入图片描述


#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[20] = "abcdef";
	char arr2[] = "qwer";
	strncpy(arr1, arr2, 2);
	printf("%s\n", arr1);
	return 0;
}

⭕ 结果:
在这里插入图片描述


❓ 当指定的个数num大于源字符串时

#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[20] = "abcdef";
	char arr2[] = "qwe";
	strncpy(arr1, arr2, 6);
	printf("%s\n", arr1);
	return 0;
}

⭕ 结果:
在这里插入图片描述


💨 总结:
▶ 拷贝num个字符从源字符串到目标空间
▶ 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后面追加0,直到num个

💦 strncat

🔗 函数原型和头:
在这里插入图片描述

🔗 函数的返回值:
在这里插入图片描述
🔗 功能:
在这里插入图片描述


#include<stdio.h.>
#include<string.h>
int main()
{
	char arr1[20] = "hello ";
	char arr2[] = "world";
	strncat(arr1, arr2, 3);
	printf("%s\n", arr1);
	return 0;
}

⭕ 结果:
在这里插入图片描述


❓ strncat能否自己给自己追加

#include<stdio.h.>
#include<string.h>
int main()
{
	char arr1[20] = "hello";
	strncat(arr1, arr1, 6);
	printf("%s\n", arr1);
	return 0;
}

⭕ 结果和分析:
在这里插入图片描述

💦 strncmp

🔗 函数原型和头:
在这里插入图片描述

🔗 函数的返回值:
在这里插入图片描述
🔗 功能:
在这里插入图片描述


#include<stdio.h>
#include<string.h>
int main()
{
	char\* p1 = "abcdef";
	char\* p2 = "abcpdf";
	int ret = strncmp(p1, p2, 4);
	printf("%d\n", ret);
	return 0;
}

⭕ 结果:
在这里插入图片描述

四、字符串查找

💦 strstr

🔗 函数原型和头:
在这里插入图片描述
🔗 函数的返回值:
在这里插入图片描述
🔗 功能:
在这里插入图片描述


#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[] = "abcdefabcdef";
	char arr2[] = "bcd";
	char\* ret = strstr(arr1, arr2);
	if(ret == NULL)
		printf("没找到\n");
	else
		printf("找到了:%s\n", ret);
	return 0;
}

⭕ 结果:
在这里插入图片描述
📝 分析:
strstr在目标字符串中查找子串,如果找到了就返回目标字符串中首次出现的源字符串的地址,否则返回一个空指针

💦 strtok

🔗 函数原型和头:
在这里插入图片描述

🔗 函数的返回值:
在这里插入图 片描述

🔗 功能:
在这里插入图片描述

🔗 函数详解:
▶ strDelimit的参数是一个字符串,定义了用作分隔符的字符集合
▶ strToken指定一个字符串,它包含了0个或者多个由strDelimit串中一个或者多个分隔符分割的标记
▶ strtok函数找到strToken中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改)
▶ strtok函数的第一个参数不为NULL,函数将找到strToken中的第一个标记,strtok函数将保存它在字符串中的位置
▶ strtok函数的第一个参数为NULL,函数将在同一个字符串中被保存的位置开始,查找下一个标记
▶ 如果字符串中不存在更多的标记,则返回NULL指针
▶ strtok函数被调用一次,只会切割一次,所以想要切割多次,就需要多次调用


#include<stdio.h>
#include<string.h>
int main()
{
	char arr[] = "wanghong@deboke.csdn";
	char\* p = "@.";
	char temp[30] = { 0 };//拷贝一份arr于temp,让temp去切割
	strcpy(temp, arr);
	char\* ret = NULL;

	ret = strtok(temp, p);
	printf("%s\n", ret);
	
	ret = strtok(NULL, p);
	printf("%s\n", ret);
	
	ret = strtok(NULL, p);
	printf("%s\n", ret);
	
	return 0;
}

⭕ 结果与分析:
在这里插入图片描述


✔ 但是我们通常会将多次分割的这个操作使用循环来实现,非常的妙啊 !!!

#include<stdio.h>
#include<string.h>
int main()
{
	char arr[] = "wanghong@deboke.csdn";
	char\* p = "@.";
	char temp[30] = { 0 };
	strcpy(temp, arr);
	char\* ret = NULL;
	
	for(ret = strtok(temp, p); ret != NULL; ret = strtok(NULL, p))
	{
		printf("%s\n", ret);
	}
	return 0;
}

五、错误信息报告

💦 strerror

🔗 函数原型和头:
在这里插入图片描述

🔗 函数的返回值:在这里插入图片描述

🔗 功能:
在这里插入图片描述

🔗 函数详解:
在调用库函数失败时,都会设置错误码
C语言中有一个全局的错误码 -> int errno ,只要调用库函数发生了错误,就会把错误码放到errno里去
这里strerror就会把错误码翻译成对应的错误信息,然后再把错误信息以字符串首地址返回回来
通常strerror都会和errno一起使用
使用errno需要头文件errno.h


#include<stdio.h>
#include<string.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));
	return 0;
}

⭕ 结果与分析:

在这里插入图片描述


❓ 具体是怎么用的
注:以下所使用的对文件操作的一些函数会在后面进行了解

#include<stdio.h>
#include<string.h>
#include<errno.h>
int main()
{
	//以读的形式打开test.txt文件,这个文件如果不存在就会打开失败,然后返回一个空指针
	FILE\* pf = fopen("test.txt", "r");
	//失败就输出失败的原因,也就题错误信息
	if(pf == NULL)
	{
		printf("%s\n", strerror(errno));								
		return 1;
	}
	//...
	fclose(pf);//关闭文件
	pf == NULL;
	
	return 0;
}

⭕ 结果:
在这里插入图片描述


💦 perror

🔗 函数原型和头:
在这里插入图片描述

🔗 函数的返回值:在这里插入图片描述

🔗 功能:
在这里插入图片描述


#include<stdio.h>
#include<stdio.h>
int main()
{
	FILE\* pf = fopen("test.txt", "r");
	if(pf == NULL)
	{
		perror("fopen");		
		return 1;
	}
	//...
	fclose(pf);
	pf == NULL;
	
	return 0;
}

⭕ 结果与分析:
在这里插入图片描述

六、字符操作函数

1、字符分类函数

在这里插入图片描述
🧿 注:简单介绍几个,其余的函数可以照猫画虎


💦 isdigit

🔗 函数原型和头:
在这里插入图片描述
🔗 函数的返回值:
在这里插入图片描述
🔗 功能:
在这里插入图片描述

#include<stdio.h>
#include<ctype.h>
int main()
{
	char ch = '@';
	//如果是数字字符返回非0的值,否则返回0
	int ret = isdigit(ch);
	printf("%d\n", ret);
	return 0;
}

💦 islower

🔗 函数原型和头:
在这里插入图片描述

🔗 函数的返回值:
在这里插入图片描述

🔗 功能:
在这里插入图片描述

#include<stdio.h>
#include<ctype.h>
int main()
{
	char ch = 'A';
	//如果是小写字母返回非0的值,否则返回0
	int ret = islower(ch);
	printf("%d\n", ret);
	return 0;
}

2、字符转换函数

函数功能
tolower大写转小写
toupper小写转大写

💦 tolower

🔗 函数原型和头:
在这里插入图片描述

🔗 函数的返回值:
在这里插入图片描述

🔗 功能:
在这里插入图片描述

#include<stdio.h>
#include<ctype.h>
int main()
{
	char arr[20] = { 0 };
	scanf("%s", arr);
	int i = 0;
	while(arr[i] != '\0')
	{
		printf("%c ", tolower(arr[i]));
		i++;
	}
	return 0;
}

⭕ 结果:
在这里插入图片描述

七、内存操作函数

💦 memcpy

🔗 函数原型和头:
在这里插入图片描述

🔗 函数的返回值:
在这里插入图片描述

🔗 功能:
在这里插入图片描述


#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 };
	memcpy(arr2, arr1, 20);//注意第3个参数的单位是字节
	return 0;
}

⭕ 结果:
在这里插入图片描述

❓ 我们注意到memcpy的前2个参数是void*类型的,那是不是说它可以拷贝不同的数据

#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[] = "abcdef";
	char arr2[20] = { 0 };
	memcpy(arr2, arr1, 6);
	printf("%s\n", arr2);
	return 0;
}

⭕ 结果:
在这里插入图片描述


❓ 对于strcpy函数在拷贝字符串时,如果遇到’\0’它会停止拷贝,那么思考memcpy在拷贝字符串时遇到’\0’是否也会停止

#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[] = "abc\0def";
	char arr2[20] = { 0 };
	memcpy(arr2, arr1, 6);
	printf("%s\n", arr2);
	return 0;
}

⭕ 结果:
在这里插入图片描述


这里有一个场景 ❓
在这里插入图片描述

#include<stdio.h>
#include<string.h>
int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	memcpy(arr1 + 2, arr1, 20);
	return 0;
}

⭕ 结果与分析:
在这里插入图片描述

💦 memmove

🔗 函数原型和头:
在这里插入图片描述

🔗 函数的返回值:
在这里插入图片描述
🔗 功能:
在这里插入图片描述


#include<stdio.h>
#include<string.h>
int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	memmove(arr1 + 2, arr1, 20);
	return 0;
}

⭕ 结果与分析:
在这里插入图片描述

💦 memset

🔗 函数原型和头:
在这里插入图片描述

🔗 函数的返回值:
在这里插入图片描述

🔗 功能:
在这里插入图片描述


#include<stdio.h>
#include<string.h>
int main()
{
	int arr[10] = { 0 };
	memset(arr, 1, 20);
	return 0;
}

⭕ 结果与分析:
在这里插入图片描述


❓ 使用memset设置一个10个元素的数组的元素为1

#include<stdio.h>
#include<string.h>
int main()
{
	int arr[10] = { 0 };
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		memset(&arr[i], 1, 1);
	}
	return 0;
}

⭕ 结果:
在这里插入图片描述

💦 memcmp

🔗 函数原型和头:
在这里插入图片描述

🔗 函数的返回值:
在这里插入图片描述

🔗 功能:
在这里插入图片描述


#include<stdio.h>
#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);//注意第3个参数的单位是字节
	printf("%d\n", ret);
	return 0;
}

八、函数的模拟实现

💦 strlen

1、计数器的版本(需要临时变量)
#include<stdio.h>
int my\_strlen(char\* str)
{
	int count = 0;
	while(\*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}
int main()
{
	char arr[] = "abc";
	int len = my\_strlen(arr);
	printf("%d\n", len);
	return 0;
}

❗ 缺陷和不足:
❌ 在my_strlen函数里对指针直接进行解引用这是不安全的,因为指针一定得是有效的
❌ my_strlen函数的功能是求字符串的长度,而这个my_strlen函数有权限去改变字符串里的内容,因此这是不安全的
❌ my_strlen这个函数的返回值是int,如果返回的是一个负数,那可能就当场懵逼
💯优化
✔ 使用断言assert,需要引头文件<assert.h>
✔ 使用const对str进行限制
✔ 将函数的返回值int更改为size_t
✔ 对代码整体进行更简洁的优化

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

2、递归版本(不使用临时变量)
#include<stdio.h>
#include<assert.h>
int my\_strlen(const char\* str)
{
	assert(str);
	if(\*str)
		return 1 + my\_strlen(str+1);
	else 
		return 0;
}
int main()
{
	char arr[] = "abc";
	printf("%d\n", my\_strlen(arr));
	return 0;
}

3、指针版本(指针-指针)

字符串长度 = '\0’的地址 - 首元素的地址

#include<stdio.h>
#include<assert.h>
int my\_strlen(char\* first, char\* end)
{
	assert(first);
	assert(end);
	return end - first;
}
int main()
{
	char arr[] = "abc";
	int left = 0;
	int right = sizeof(arr)/sizeof(arr[0]) - 1;
	printf("%d\n", my\_strlen(&arr[left], &arr[right]));
	return 0;
}

💦 strcpy

#include<stdio.h>
#include<assert.h>
void my\_strcpy(char\* dest, char\* src)
{
	while(\*src != 0)
	{
		\*dest = \*src;
		dest++;
		src++;
	}
	src = 0;//将str1的最后一个元素拷贝为0
}
int main()
{
	char arr1[20] = { 0 };
	char arr2[] = "hello bit";
	my\_strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

❗ 缺陷和不足:
❌ 在my_strcpy函数里对指针直接进行解引用这是不安全的,因为指针一定得是有效的
❌对于my_strcpy这个函数的2个参数,其一是目标字符串,其二是源字符串,目标字符串必须保证可被修改,而源字符串不能被修改
❌ 这里是先把除 ‘\0’ 其它字符先拷贝,再拷贝 ‘\0’
❌ 这个函数的返回值是void,而在库里是char*,返回的是目标字符串的首地址 :
在这里插入图片描述

💯优化
✔ 使用断言assert,需要引头文件<assert.h>
✔ 使用const对str2进行限制
✔ 将 ‘\0’ 和其它字符一起进行拷贝
✔ 将函数的返回值设置为char*

#include<stdio.h>
#include<assert.h>
char\* my\_strcpy(char\* dest, const char\* src)
{
	assert(dest);
	assert(src);
	char\* temp = dest;//备份一份首地址
	while (\*dest++ = \*src++)
	{
		;
	}
	return temp;//返回备份的首地址 
}
int main()
{
	char arr1[20] = { 0 };
	char arr2[] = "hello bit";
	printf("%s\n", my\_strcpy(arr1, arr2));
	return 0;


![img](https://img-blog.csdnimg.cn/img_convert/5da0d1236091cf3869565f76443e5ee7.png)
![img](https://img-blog.csdnimg.cn/img_convert/ac9c9b4cdf8a77fb6dda01ec22352595.png)

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**


}

❗ 缺陷和不足:
❌ 在my_strcpy函数里对指针直接进行解引用这是不安全的,因为指针一定得是有效的
❌对于my_strcpy这个函数的2个参数,其一是目标字符串,其二是源字符串,目标字符串必须保证可被修改,而源字符串不能被修改
❌ 这里是先把除 ‘\0’ 其它字符先拷贝,再拷贝 ‘\0’
❌ 这个函数的返回值是void,而在库里是char*,返回的是目标字符串的首地址 :
在这里插入图片描述

💯优化
✔ 使用断言assert,需要引头文件<assert.h>
✔ 使用const对str2进行限制
✔ 将 ‘\0’ 和其它字符一起进行拷贝
✔ 将函数的返回值设置为char*

#include<stdio.h>
#include<assert.h>
char\* my\_strcpy(char\* dest, const char\* src)
{
	assert(dest);
	assert(src);
	char\* temp = dest;//备份一份首地址
	while (\*dest++ = \*src++)
	{
		;
	}
	return temp;//返回备份的首地址 
}
int main()
{
	char arr1[20] = { 0 };
	char arr2[] = "hello bit";
	printf("%s\n", my\_strcpy(arr1, arr2));
	return 0;


[外链图片转存中...(img-JqFcCkoT-1715794310234)]
[外链图片转存中...(img-FSCs7VF2-1715794310234)]

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值