C语言学习——字符函数和字符串函数

news2024/11/29 20:59:07

🌇个人主页:平凡的小苏

📚学习格言:别人可以拷贝我的模式,但不能拷贝我不断往前的激情

🛸C语言专栏:https://blog.csdn.net/vhhhbb/category_12174730.html

小苏希望大家能从这篇文章中收获到许多,如果大家觉得这篇文章对你有帮助,请给小苏点赞+收藏+评论

 

 

目录

0.前言 

1. 函数介绍

1.1 strlen

 1.2 strcpy

1.3 strcmp

 1.4 strcat

1.5 strstr

1.6 strtok

 1.7 strerror

1.8 memcpy

1.12 memmove

 1.13 memcmp


 

0.前言 

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

1. 函数介绍

1.1 strlen

size_t strlen ( const char * str );

解释:size_t是返回类型,const char*str是参数类型

  • 字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包
'\0' )
  • 参数指向的字符串必须要以 '\0' 结束。
  • 注意函数的返回值为size_t,是无符号的( 易错
  • 学会strlen函数的模拟实现

strlen的模拟实现:代码如下

#include<stdio.h>
//模拟实现strlen
//第一种方法
#include<assert.h>
int my_strlen(const char* str)
{
	int count = 0;
	assert(*str);
	while (*str != '\0')
	{
		str++;
		count++;
	}
	return count;
}
int main()
{
	char ch[] = "hello bit";
	int len = my_strlen(ch);
	printf("%d\n", len);
	return 0;
}

#include<stdio.h>
//模拟实现strlen
//第二种方法
#include<assert.h>
int my_strlen(const char* str)
{
	if (*str != '\0')
	{
		return 1 + my_strlen(str+1);
	}
	else
	{
		return 0;
	}
}
int main()
{
	char ch[] = "hello bit";
	int len = my_strlen(ch);
	printf("%d\n", len);
	return 0;
}

#include<stdio.h>
//模拟实现strlen
//第三种方法
#include<assert.h>
int my_strlen(const char* str)
{
	assert(*str);
	const char* start = str;
	while (*str != '\0')
	{
		str++;
	}
	return str - start;
}
int main()
{
	char ch[] = "hello bit";
	int len = my_strlen(ch);
	printf("%d\n", len);
	return 0;
}

注:这里给出了三种strlen的实现方法,第一种是利用循环的方法实现,第二种是利用递归的方法,第三种是利用了指针相减可得元素个数的方法

 1.2 strcpy

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

解释:char*是strcpy的返回类型,strcpy需要传两个char*类型的参数

注意事项: 

  • 源字符串必须以 '\0' 结束。
  • 会将源字符串中的 '\0' 拷贝到目标空间。
  • 目标空间必须足够大,以确保能存放源字符串。
  • 目标空间必须可变。
  • 学会模拟实现。

 strcpy的模拟实现:代码如下

#include<stdio.h>
#include<assert.h>
//模拟实现strcpy
char* my_strcpy(char* str1, const char* str2)
{
	char* start = str1;
	assert(str1 && str2);
	while (*str1++=*str2++)
	{
		;
	}
	return start;
}
int main()
{
	char ch1[20] = "0";
	char ch2[] = "hello bit";
	char *ret = my_strcpy(ch1, ch2);
	printf("%s\n", ch1);
	return 0;
}

1.3 strcmp

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

解释:strcmp的返回类型为int,并且需要传两个char*类型的参数。

标准规定:

  • 当str1大于str2时则返回大于0的数
  • 当str1等于str2时则返回等于0的数
  • 当str1小于str2时则返回小于0的数

strcmp的模拟实现:代码如下

#include<stdio.h>
#include<assert.h>
//模拟实现strcmp
int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);
	while (*str1 == *str2)
	{
		if (*str1 == '\0')
		{
			return 0;
		}
		str1++;
		str2++;
	}
	if (*str1 > *str2)
	{
		return  1;
	}
	else
		return -1;
}
int main()
{
	char ch1[] = "abcq";
	char ch2[] = "abcq";
	int num = my_strcmp(ch1, ch2);
	if (num > 0)
	{
		printf(">");
	}
	else if(num<0)
	{
		printf("<");
	}
	else
	{
		printf("=");
	}
	return 0;
}

 1.4 strcat

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

解释:strcat的返回类型为char*,需要传两个为char*类型的参数来接收

注意事项: 

  • 源字符串必须以 '\0' 结束。
  • 目标空间必须有足够的大,能容纳下源字符串的内容。
  • 目标空间必须可修改。
  • 字符串自己给自己追加,如何?(自己给自己追加会覆盖自己的斜杠0)

模拟实现strcat:代码如下

#include<stdio.h>
#include<assert.h>
//模拟实现strcat
char* my_strcat(char* dest, const char* sour)
{
	char* start = dest;
	assert(dest && sour);
	while (*dest != '\0')//先找到dest的斜杠0位置再进行追加
	{
		dest++;
	}
	while (*dest++ = *sour++)
	{
		;
	}
	return start;
}
int main()
{
	char arr1[20] = "hello";
	char arr2[] = " bit";
	char *ret = my_strcat(arr1, arr2);
	printf("%s\n", ret);
	return 0;
}

1.5 strstr

char * strstr ( const char * str1 , const char * str2 );
解释:strstr是从str1找有没有str2的字符串,如果找到就返回str1的匹配到的字符串的起始地址。strstr的返回类型是char*,需要传两个为char*类型的参数

 模拟实现strstr:代码如下

#include<stdio.h>
#include<assert.h>
//模拟实现strstr
char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);
	if (str2 == '\0')//如果str2是斜杠零,则就不用匹配了直接返回str1的起始地址
	{
		return (char*)str1;
	}
	const char* cp = str1;
	const char* s1 = NULL;
	const char* s2 = NULL;
	while (*cp)
	{
		s1 = cp;
		s2 = str2;
		while (*s2!='\0' && *s1 != '\0' && *s1 == *s2)//这里只要是s1或者s2等于斜杠0都跳出循环
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')//如果s2等于斜杠0,就说明匹配成功了
		{
			return (char *)cp;
		}
		cp++;
	}
	return NULL;
}
int main()
{
    char ch1[] = "abbbcd";
    char ch2[] = "";
	char* ret = my_strstr(ch1, ch2);
	printf("%s\n", ret);
	return 0;
}

1.6 strtok

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

解释:

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

strtok的使用例子:

#include <stdio.h>
int main()
{
   char *p = "zhangpengwei@bitedu.tech";
 const char* sep = ".@";
 char arr[30];
 char *str = NULL;
 strcpy(arr, p);//将数据拷贝一份,处理arr数组的内容
 for(str=strtok(arr, sep); str != NULL; str=strtok(NULL, sep))
 {
 printf("%s\n", str);
 }
}

 1.7 strerror

char * strerror ( int errnum );

返回错误码,所对应的错误信息。

/* strerror example : error list */

#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));
    //errno: Last error number
  return 0;
}

 字符分类函数:

函数如果它的参数符合下列条件就返回真
iscntrl任何控制字符
isspace空白字符:空格'',换页'\f',换行'\n',回车'\r',制表符'\t'
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 tolower(int c);大写字母转小写字母,返回字母的ascii码值

int toupper(int c);小写字母转大写字母,返回字母的ascii码值

1.8 memcpy

void * memcpy ( void * destination , const void * source , size_t num );
解释:
  • 函数memcpysource的位置开始向后复制num个字节的数据到destination的内存位置。
  • 这个函数在遇到 '\0' 的时候并不会停下来。
  • 如果sourcedestination有任何的重叠,复制的结果都是未定义的。

memcpy的模拟实现

 

#include<stdio.h>
#include<assert.h>
//模拟实现内存拷贝memcpy
//void * memcpy ( void * destination, const void * source, size_t num );
void* my_memcpy(void* dest, const void* src, size_t num)
{
	void* ret = dest;
	assert(dest && src);
	while (num--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return ret;
}
int main()
{
	int arr1[10] = { 0 };
	int arr2[] = { 1,2,3,4,5,6,7,8,9,10 };
	int *ret = (int *)my_memcpy(arr1, arr2, 5 * sizeof(int));
	for (int i = 0; i < 5; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

1.12 memmove

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

解释:

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

memmove的模拟实现:代码如下

#include<stdio.h>
#include<assert.h>
//void* memmove(void* destination, const void* source, size_t num);
//模拟实现memmove
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--)
		{

			*((char*)dest + num) = *((char*)src + num);
		}
	}
	return ret;
}

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

 1.13 memcmp

int memcmp ( const void * ptr1 , const void * ptr2 , size_t num );
解释:
  • 比较从ptr1ptr2指针开始的num个字节
  • 返回值如下:

 

使用例子:

/* memcmp example */
#include <stdio.h>
#include <string.h>

int main ()
{
  char buffer1[] = "DWgaOtP12df0";
  char buffer2[] = "DWGAOTP12DF0";

  int n;

  n=memcmp ( buffer1, buffer2, sizeof(buffer1) );

  if (n>0) printf ("'%s' is greater than '%s'.\n",buffer1,buffer2);
  else if (n<0) printf ("'%s' is less than '%s'.\n",buffer1,buffer2);
  else printf ("'%s' is the same as '%s'.\n",buffer1,buffer2);

  return 0;
}

 好了!小苏的字符串库函数的分享到这里就结束了!有什么不足的地方请大佬们多多指教!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/166050.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

实战Kaggle比赛:预测房价

实战Kaggle比赛&#xff1a;预测房价 目录 下载和缓存数据集访问和读取数据集数据预处理训练K折交叉验证模型选择提交Kaggle预测 本节我们将通过Kaggle比赛&#xff0c;将所学知识付诸实践。 Kaggle的房价预测比赛是一个很好的起点。 此数据集由Bart de Cock于2011年收集 (D…

Linux--线程互斥与同步--0112 13

线程互斥 1.背景概念 临界资源&#xff1a;多线程执行流共享的资源就叫做临界资源。 临界区&#xff1a;每个线程内部&#xff0c;访问临界资源的代码就叫做临界区。 互斥&#xff1a;任何时刻&#xff0c;互斥保证有且只有一个执行流进入临界区 &#xff0c;对临界资源起保…

36岁北邮硕士,四段大厂经历,当初为了涨薪频繁跳槽,被裁后投遍所有公司,基本都不回复!...

今天给大家分享一位36岁北邮硕士的职场经历&#xff1a;2013年北邮硕士毕业后&#xff0c;分别在乐视、字节、阿里、小米待过&#xff0c;2022年被小米裁员&#xff0c;几乎投遍了boss上所有公司&#xff0c;基本都是已读不回。只有一个小公司的hr看了简历后回了一句“加油”。…

绕过某博客查看文章验证码,关注公众号得验证码

之前也写过一篇&#xff0c;当时使用Burpsuite抓包&#xff0c;改包&#xff0c;有点杀鸡用牛刀了。 虽然我挺支持为知识那啥的&#xff0c;但是吧要我去关注公众号太麻烦了 绕过查看文章需要验证码 其实就是改一个返回的字段&#xff0c;既然后端也是改&#xff0c;那我前端…

Google Earth Engine基础使用方法(一)

Google Earth Engine 1、注册账号1.1、设置谷歌账号辅助邮箱1.2、进入Google Earth Engine(如果第一次注册失败怎么办)1.3、进入Google Earth Engine Editor2、Editor主界面2.1、上传自己的矢量数据2.2、分享代码给别人2.3、保存代码2.4、几个有效快捷键2.5、搜索框有什么用3、…

SAP S/4 FAGLGVTR错误解决

本次年结支持过程中&#xff0c;一个客户的年结操作出现问题&#xff0c;问题的解决还颇费周折&#xff0c;稍稍记录一下。客户的SAP 版本是 S/4, 通过 FAA_CMP 事务码切换固定资产年度的时候&#xff0c;提示上一已关闭的会计年度与当前会计年度相同。 这个消息的意思是FI 的会…

【自学Python】Python字符串出现次数

Python字符串出现次数 Python字符串出现次数教程 在开发过程中&#xff0c;很多时候我们有统计单个字符或者 字符串 在另一个字符串中出现次数的需求&#xff0c;在 Python 中&#xff0c;统计字符串出现次数我们使用 count() 函数。 Python count()函数详解 语法 S.count…

Python实战项目1——自动获取小说工具

&#x1f935;‍♂️ 个人主页老虎也淘气 个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f44d;&#x1f3fb; 收藏…

【C++】二叉树进阶OJ题

​&#x1f320; 作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《吃透西嘎嘎》 &#x1f387; 座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录&#x1f449;根据二叉…

前端开发:Webpack的使用总结

前言 在前端开发过程中&#xff0c;尤其是现在前端框架的频繁使用的当下&#xff0c;作为前端开发者想必对于Webpack并不陌生&#xff0c;尤其是在使用Vue框架做前端开发的时候&#xff0c;打包时候必用Webpack。还有就是在前端求职面试的时候&#xff0c;Webpack相关的知识点…

mysql新建分区设置阈值(less than)引发的问题

mysql新建分表后&#xff0c;入库之前分表区间的数据&#xff0c;但是再分表中查询不到对应数据。 文章目录问题背景问题解析新建分区sql查看分区查询数据查询数据所在分区修改方案总结LESS THAN相关sql查询分区删除分区先建分区问题背景 初始化表的时候&#xff0c;先建的日期…

(二)Jenkins全局工具配置

目录 1、插件管理 2、Gitee安装 2.1、插件安装 2.2、查看Gitee状态 2.3、配置Gitee 2.4、获取私人令牌 2.5、测试连接 3、全局配置jdk、ant、maven 3.1、jdk配置 3.2、ant配置 3.3、maven配置 4、插件镜像下载地址配置 (一)Jenkins部署、基础配置介绍在windows下安…

事务(transaction)

事务&#xff08;重点 五颗星 ***** 必须理解 必须掌握&#xff09; 1、什么是事务&#xff1a; 一个事务其实就是一个完整的业务逻辑。 假设转账&#xff0c;从A账户向B账户转账10000.将A账户的钱减去10000&#xff08;update语句&#xff09;&#xff0c;将B账 户的钱增加100…

【手写 Vue2.x 源码】第二十六篇 - 数组依赖收集的实现

一&#xff0c;前言 上篇&#xff0c;主要介绍了数组依赖收集的原理 本篇&#xff0c;数组依赖收集的实现 二&#xff0c;对象依赖收集的总结 {}.dep > watcher 目前&#xff0c;“对象本身”和“对象中的每一个属性”都拥有一个 dep 属性&#xff0c;用于做依赖收集 此…

Leetcode:669. 修剪二叉搜索树(C++)

目录 问题描述&#xff1a; 实现代码与解析&#xff1a; 递归&#xff1a; 原理思路&#xff1a; 后序递归&#xff1a; 原理思路&#xff1a; 迭代&#xff1a; 原理思路&#xff1a; 问题描述&#xff1a; 给你二叉搜索树的根节点 root &#xff0c;同时给定最小边界…

#9文献学习--基于元强化学习的边缘计算快速自适应任务卸载

文献&#xff1a;Fast Adaptive Task Offloading in Edge Computing based on Meta Reinforcement Learning 基于深度强化学习DRL的方法&#xff0c;样本效率很低&#xff0c;需要充分的再培训来学习新环境的更新策略&#xff0c;对新环境的适应性很弱。 基于元强化学习的任务…

【数据结构与算法】第十八篇:递归,尾递归,尾调用

知识概览一、递归的引入&#xff08;递归现象&#xff09;二、递归的调用过程与实例分析三、递归的基本思想小tip:链表递归的具体实例四、递归的一般使用条件五、实例分析&#xff1a;斐波那契数列1.原理剖析2.fib优化1 – 记忆化3.fib优化24.fib优化3六、实例分析&#xff1a;…

mac下ssh连接docker使用centos

配置ssh连接docker本机信息 Apple M2/ macOS Ventura 13.1完整实现如下&#xff1a;使用docker下载centos镜像docker pull centos:centos7 # centos7 指定安装版本查看本地镜像# 使用以下命令查看是否已安装了centos7➜ ~ docker images REPOSITORY TAG IMAGE ID …

c++通讯录管理系统

结构体1&#xff0c;知识点&#xff08;结构体&#xff09;&#xff0c;存放人员详情&#xff0c;名字&#xff0c;性别&#xff0c;年龄等 struct person { string m_name; int m_sex; int m_age; string m_phone; string m_addr; };结构体2&#xff0c;知识点 &#xff08;结…

狗厂的N+1+2毕业,我觉得还是挺良心的

最近又跟朋友打听到了新鲜事&#xff0c;年底的新鲜事&#xff0c;什么209万&#xff0c;就是听个乐子&#xff0c;离我太远&#xff0c;什么HR和技术人员产生矛盾&#xff0c;一巴掌眼镜都打飞了&#xff0c;好乱套&#xff0c;今天我跟朋友打听了一些不太乱套的 一、鹅肠 1.…