C语言进阶——字符函数和字符串函数(上)

重点:

重点介绍处理字符和字符串的库函数的使用和注意事项

1、求字符串长度

        strlen

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

        strcpy

        strcat

        strcmp


1、函数介绍

1.1  strlen

函数原型:size_t strlen (const char *str);

1、字符串以‘\0’作为结束标志,strlen函数返回的是(从参数给的地址开始)在字符串中‘\0’前面出现的字符个数(不包含‘\0’)

2、参数指向的字符串必须要以‘\0’结束(strlen的正常使用,要有‘\0’,否则返回值为一个随机值)

3、注意函数的返回值为size_t,是无符号的(易错)

4、模拟实现strlen函数

讲解:

(1)strlen函数的返回类型和参数类型为什么这么设计?

        ①返回类型:strlen是求字符串长度的,求出的长度是不可能为负数的,所以返回类型设置为size_t也是合情合理的。(typedef unsigned int size_t)

        ②参数类型:因为指针+1向后跳过一个字符且为了程序的健壮性(指针指向的内容是不变的,但指针变量本身可以修改),所以参数类型设置为const char* str。

(2)模拟实现strlen函数:

分析:

        strlen函数是统计字符串长度的,统计的是字符串‘\0’之前的字符个数;模拟strlen函数,那函数的返回类型,参数类型一致。

实现统计字符串长度的方式有三种:

        ①计算器实现:while循环判断字符是否不为‘\0’,不为‘\0’,计算器count加1,为‘\0’则统计结束。

        ②递归实现:递归的思想就是大事化小——把一个大型问题层层转化为与原问题相似的小问题来求解,递归要有两个必要条件,条件一 ——存在限制条件,递归一次跳过一个字符,条件二 ——递归出口,当字符为‘\0’是,开始回归。

        ③指针 - 指针实现:指针减指针的绝对值,得到两个指针之间的元素个数(注:两个指针要指向同一块空间)。

代码实例:

#include<stdio.h>
#include<assert.h>

//计算器实现
size_t my_strlen1(const char* str)
{
	//断言指针的有效性
	assert(str != NULL);
	size_t count = 0;
	//统计从参数给的地址开始在字符串中'\0'前面出现的字符个数
	while (*str++ != '\0')
	{
		count++;
	}
	return count;
}
//递归实现
size_t my_strlen2(const char* str)
{
	assert(str != NULL);
	if (*str != '\0')
	{
		return 1 + my_strlen2(1 + str);//每一次递归趋于递归出口的语句
	}
	else
	{
		return 0;//递归出口
	}
}
//指针 - 指针实现(指针-指针,得到两个指针之间的元素个数)
size_t my_strlen3(const char* str)
{
	assert(str != NULL);
	//首字符的地址
	const char* start = str;
	//‘\0’字符的地址
	while (*str)
	{
		str++;
	}
	//‘\0’字符的地址 - 首字符的地址,得到字符串的长度
	return str - start;
}

int main()
{
	char arr[] = "abcdef";
	size_t len1 = my_strlen1(arr);
	size_t len2 = my_strlen2(arr);
	size_t len3 = my_strlen3(arr);
	printf("%d %d %d\n", len1, len2, len3);
	return 0;
}

(3)注意函数的返回值为size_t,是无符号的

下面代码的运行结果是什么?

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

int main()
{
	const char* str1 = "abcdef";
	const char* str2 = "bbb";
	if (strlen(str2) - strlen(str1) > 0)
	{
		printf("str2>str1\n");
	}
	else
	{
		printf("srt1>str2\n");
	}
	return 0;
}

运行结果:

为什么呢?str2的长度是3,str1的长度是6, 3 - 6 = -3执行else语句打印str1 > str2,当你这么想就错了,因为 strlen函数的返回类型是size_t,为无符号整形,无符号整形在内存中全为数值位,所以-3是一个很大的整数,所以条件为真打印str2 > str1。

1.2  strcpy(string copy——字符串拷贝)

函数原型:char* strcpy(char* destination, const char* source);

1、把source指向的C字符串拷贝到destination指向的数组中,包括结束的‘\0’字符(并在该点停止);

2、源字符串必须以‘\0’结束(因为strcpy拷贝到‘\0’字符,才会停止);

3、目标空间必须足够大,以确保能存放源字符串(避免溢出。程序崩溃);

4、目标空间必须可变(例:字符数组的空间可变,常量字符串的空间不可变);

5、学会模拟实现。

6、source和destination所指向的内存区域不可以重叠。

讲解:

(1)strcpy函数的返回类型和参数类型为什么这么设置呢?

        ①返回类型:返回目标空间的起始地址——char*(函数有返回值,可以链式访问)

        ②参数类型:要将源字符串拷贝到目标空间中,所以要两个字符指针变量分别接收源字符串和目标空间的起始地址;为了程序的健壮性源字符串只是被拷贝,源字符串内容不变,所以使用const修饰(const如果放在*的左边,const修饰的是指针指向的内容;const如果放在*的右边,const修饰的是指针变量本身)。

(2)strcpy拷贝到‘\0’字符,才会停止。

代码实例:

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

int main()
{
	char arr1[] = "##################";
	char arr2[] = "abc\0def";
	//将arr2拷贝到arr1中
	strcpy(arr1, arr2);
	//打印拷贝后的arr1
	printf(arr1);
	return 0;
}

F10开始调试,观察拷贝后目标空间内存的变化:

 (3)模拟实现strcpy

分析:

        strcpy是拷贝字符串的,把源指向的C字符串拷贝到目标空间指向的数组中,包括结束的‘\0’字;模拟strcpy函数,那函数的返回类型,参数类型一致。

函数体的实现:

        ①定义一个char* ret变量存储目标空间的起始地址

        ②将src指向的字符串拷贝到dest指向的数组中,while循环一次拷贝src一个字符到dest中,直到拷贝‘\0’结束。

        ③返回目标空间起始地址

#include<stdio.h>
#include<assert.h>

char* my_strcpy(char* dest, const char* src)
{
	//断言指针的有效性
	assert(dest && src);
	//存放目标空间的起始地址
	char* ret = dest;
	//将src所指向内容拷贝到dest所指向数组(拷贝到'\0'才停止)
	//*dest++ = *src++
	//等价于
	//*dest = *src
	//dest++;src++
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}

int main()
{
	//将arr2拷贝到arr1
	char arr1[] = "####################";
	char arr2[] = "abc";
	//链式访问:注有返回值才可以
	printf("%s\n", my_strcpy(arr1, arr2));
	return 0;
}

1.3  strcat

函数原型:char* strcat(char* destination, const char* source );

1、将源字符串的副本(包括‘\0’)追加到目标空间,目标空间中的‘\0’字符被源字符串的第一个字符覆盖。

2、目标空间字符串必须有‘\0’(因为strcat追加,从目标字符串的‘\0’开始)

3、源字符串必须以‘\0’结束(因为strcat追加,直到追加到源字符串的‘\0’才停止,包含‘\0’)

4、目标空间必须足够大,能容纳下源字符串的内容(避免溢出,程序崩溃)

5、目标空间必须可修改

6、字符串自己给自己追加,如何?(不可以,因为目标中的‘\0’字符被源的第一个字符覆盖,源字符串就没有了‘\0’,一直追加,直至空间溢出,程序崩溃)

7、source和destination所指向的内存区域不可以重叠。

讲解:

(1)函数的返回类型与参数为什么这样设计?

        ①返回类型:返回目标空间的起始地址——char*(函数有返回值,可以链式访问)

        ②参数:要给目标空间追加字符串,就需要两个字符指针变量分别接收目标空间的起始地址和追加字符串的起始地址;为了程序的健壮性,使用const修饰源指向的内容。

(2)模拟实现strcat:

分析:

        strcat是追加字符串的,把源指向的C字符串的副本(包括\0)追加到目标空间中,从目标空间的\0开始追加;模拟strcat函数,那函数的返回类型和参数类型一致。

函数体的实现:

        ①定义一个char* ret变量存储目标空间的起始地址

        ②找到目标空间的\0

        ③将源字符串追加到目标空间中,直到追加到源字符串的\0才停止(包括\0)。

        ④返回目标空间的起始地址

//模拟实现strcat
#include<stdio.h>
#include<assert.h>

char* my_strcat(char* dest, const char* src)
{
	//断言指针的有效性
	assert(dest && src);
	//存放目标空间的起始地址
	char* ret = dest;
	//找到目标空间的\0
	while (*dest)
	{
		dest++;
	}
	//追加到源字符串的\0
	while (*dest++ = *src++)
	{
		;
	}
	//返回目标空间的起始地址
	return ret;
}

int main()
{
	char arr[20] = "hello ";
	//在arr后追加word
	my_strcat(arr, "word");
	//打印追加后的arr
	printf("%s\n", arr);
	return 0;
}

1.4  strcmp

函数原型:int strcmp(const char* str1,const char* str2);

1、比较字符串的大小,比较的是两个字符串对应位置字符的ASCII码值(自左向右),如果相等,则继续比较,直到出现不同的字符或遇到‘\0’才停止。

2、标准规定:

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

        ②第一个字符串等于第二个字符串,则返回0

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

讲解:

(1)返回类型和参数为什么这么设计?

        ①返回类型:返回有正有负的整数——int(VS环境下:大于返回1,等于返回0,小于返回-1)

        ②参数:比较两个字符串的大小,就需要知道两个字符串的起始地址;为了程序的健壮性,都使用const修饰指针指向的内容。

(2)模拟实现strcmp

分析:

        strcmp是比较字符串大小的,比较的是对应位置字符的ASCII值,如果相等,则继续比较,直到字符不同或遇到\0字符。(等于返回0,大于返回1,小于返回-1)

函数体实现:

        ①相等:while循环比较对应位置的字符是否相等,相等注意特殊情况——先判断是否遇到\0字符,遇到直接返回0,否则两个字符串的地址都向后跳过一个字符,继续比较;不相等则退出循环

        ②不相等:如果*str1 > *str2,则返回1,否则返回-1

#include<stdio.h>
#include<assert.h>

int my_strcmp(const char* str1, const char* str2)
{
	//断言指针的有效性
	assert(str1 && str2);
	//比较对应字符的ASCII值:
	//1、相等:
	while (*str1 == *str2)
	{
		//注意判断是否遇到终止\0字符——是则直接返回0,否则都向后跳一个字符
		if (*str1 == '\0')//循环条件时相等,所以如果一个为0,两个也为0
		{
			return 0;
		}
		else
		{
			str1++;
			str2++;
		}
	}
	//2、不相等:大于返回1,小于返回-1
	if (*str1 > *str2)
	{
		return 1;
	}
	else
	{
		return -1;
	}
}

int main()
{
	char arr1[] = "abq";
	char arr2[] = "abc";
	//arr1与arr2比较
	int ret = my_strcmp(arr1, arr2);
	printf("%d\n", ret);
	return 0;
}

总结:

strcpy:拷贝字符串函数

strcat:追加字符串函数

strcmp:比较字符串函数

这些函数都是长度不受限制的字符串函数,在使用时都要遇到\0才停止,所以函数不安全。

C语言中对齐进行了优化:长度受限制的字符串函数:strncpy,strncat,strncmp——他们与原先的函数相比,都加一个参数num,能控制拷贝、追加、比较的字符个数。

今天就到这结束,后续更新strncpy,strncat,strncmp,strstr,strtok,strerror等库函数。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何把ipa文件(iOS安装包)安装到iPhone手机上? 附方法汇总

苹果APP安装包ipa如何安装在手机上&#xff1f;很多人不知道怎么把ipa文件安装到手机上&#xff0c;这里就整理了苹果APP安装到iOS设备上的方式&#xff0c;仅供参考 苹果APP安装包ipa如何安装在手机上&#xff1f;使用过苹果手机的人应该深有感触&#xff0c;那就是苹果APP安…

登录appuploader

登录appuploader 常规使用登录方法 双击appuploader.exe 启动appuploader 点击底部的未登录&#xff0c;弹出登录框 在登录框内输入apple开发者账号 如果没有apple开发者账号&#xff0c;只是普通的apple账号&#xff0c;请勾选上未支付688 然后软件会提示输入验证码&#…

【Spring/MySQL数据库系列】数据库事务的特点与隔离级别

⭐️前面的话⭐️ 本文已经收录到《Spring框架全家桶系列》专栏&#xff0c;本文将介绍有关数据库事务的特点以及隔离级别。 &#x1f4d2;博客主页&#xff1a;未见花闻的博客主页 &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f4…

【C++ STL】 趣学stackqueuepriority_queue【对话情景版】

文章目录 &#x1f4cd;前言C STL 之 stack&queue基础知识及其模拟实现&#x1f4cd;容器适配器&#x1f388;什么是适配器&#xff1f;&#x1f388;STL标准库中stack和queue的底层结构&#x1f388;deque的简单介绍(了解)&#x1f4cc;deque的原理介绍&#x1f4cc;deque…

Python学习27:存款买房(A)

描述‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‭‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‪‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‪‬ 你刚刚大学毕业&#xff0c;…

基于Hebb学习的深度学习方法总结

基于Hebb学习的深度学习方法总结 0 引言1 前置知识1.1 Hebb学习规则1.2 Delta学习规则 2 SoftHebb学习算法2.1 WTA(Winner Take All)2.2 SoftHebb2.3 多层Hebb网络2.4 Hebb学习的性能测评 3 参考文献 0 引言 总所周知&#xff0c;反向传播算法&#xff08;back-propagating, B…

图神经网络:(大型图的有关处理)在Pumbed数据集上动手实现图神经网络

文章说明&#xff1a; 1)参考资料&#xff1a;PYG官方文档。超链。 2)博主水平不高&#xff0c;如有错误还望批评指正。 3)我在百度网盘上传了这篇文章的jupyter notebook和有关文献。超链。提取码8848。 文章目录 Pumed数据集文献阅读继续实验 Pumed数据集 导库 from torch_…

不会Elasticsearch标准查询语句,如何分析数仓数据?

1 Elasticsearch的查询语句 ES中提供了一种强大的检索数据方式,这种检索方式称之为Query DSL,Query DSL是利用Rest API传递JSON格式的请求体(Request Body)数据与ES进行交互&#xff0c;这种方式的丰富查询语法让ES检索变得更强大&#xff0c;更简洁。 1.1 查询预发 # GET /…

案例分享|地弹现象导致DCDC电源芯片工作不正常

很多读者都应该听过地弹&#xff0c;但是实际遇到的地弹的问题应该很少。本案例就是一个DCDC电源芯片的案例。 1. 问题描述 如下图1 &#xff0c;产品其中一个供电是12V转3.3V的电路&#xff0c;产品发货50K左右以后&#xff0c;大约有1%的产品无法启动&#xff0c;经过解耦定…

【C++】深入剖析C++11新特性

目录 一、C11简介 二、统一的列表初始化 1.&#xff5b;&#xff5d;初始化 2.std::initializer_list 三、声明 1.auto 2.decltype 3.nullptr 四、范围for 五、final和oberride 六、STL中一些变化 1.array 2.forward_list 3.unordered_map和unordered_set 七、右…

Python入门(十一)while循环(一)

while循环&#xff08;一&#xff09; 1.简介2.使用while循环3.让用户选择何时退出4.使用标志5.使用break退出循环6.在循环中使用continue7.避免无限循环 作者&#xff1a;xiou 1.简介 for循环用于针对集合中的每个元素都执行一个代码块&#xff0c;而while循环则不断运行&am…

虚幻引擎4利用粒子系统实现物体轨迹描绘

虚幻引擎4利用粒子系统实现物体轨迹描绘 目录 虚幻引擎4利用粒子系统实现物体轨迹描绘前言粒子系统利用粒子系统实现物体轨迹描绘创建粒子系统将粒子系统的产生位置绑定到运动物体上 小结 前言 由于在物体运动时&#xff0c;想要观察其总的运动轨迹&#xff0c;以便对其控制做…

CANoe-如何在Trace窗口显示SYN和FIN报文、同一条以太网报文在Trace窗口中的多条显示

1、如何在Trace窗口显示SYN和FIN报文 当我们使用CANoe实现TCP通信时,希望在Trace窗口直观显示报文的类型:SYN、ACK、FIN。显然Trace窗口也是支持这样的功能的。但很多时候由于一些人为的不正确的设置和配置,造成无法显示。 如果想解析出SYN报文,首先在Trace窗口选择正确的…

java注解

Target({ElementType.METHOD, ElementType.TYPE}) 注解的适用范围&#xff0c;可以用在什么地方&#xff0c;超过这个作用范围,编译的时候就会报错 值说明ElementType.METHOD用于描述方法ElementType.TYPE用于描述类、接口(包括注解类型) 或enum声明ElementType.LOCAL_VARIAB…

编程语言中,循环变量通常都用 i?你知道为什么吗?

01 前天&#xff0c;我在朋友圈发了一个问题&#xff1a; 为什么编程中&#xff0c;循环变量通常都是用 i ? 没想到&#xff0c;回复的人这么多&#xff01;要连翻好几页。 这个问题&#xff0c;有 2/3 的人回答正确&#xff0c;有少部分人知道&#xff0c;但是不太确定。 习惯…

camunda如何发布和调用rest服务接口

一、camunda如何发布rest服务接口 Camunda BPM 平台本身提供了 REST API 接口&#xff0c;可以用于管理和操作 Camunda 平台中的各种资源和数据&#xff0c;如流程定义、流程实例、任务等。因此&#xff0c;我们可以通过编写 Camunda 应用程序的方式&#xff0c;将 Camunda RE…

最流行的开源 LLM (大语言模型)整理

本文对国内外公司、科研机构等组织开源的 LLM 进行了全面的整理。 Large Language Model (LLM) 即大规模语言模型&#xff0c;是一种基于深度学习的自然语言处理模型&#xff0c;它能够学习到自然语言的语法和语义&#xff0c;从而可以生成人类可读的文本。 所谓"语言模…

基于DBSCAN密度聚类的风电-负荷场景削减方法

​目录 ​ 1 主要内容 基于密度聚类的数据预处理&#xff1a; 场景提取&#xff1a; 算法流程&#xff1a; 2 部分程序 3 程序结果 4 下载链接 1 主要内容 该程序复现文章《氢能支撑的风-燃气耦合低碳微网容量优化配置研究》第三章内容&#xff0c;实现的是基于DBSCAN…

八股文大全

八股文大全 1. 基础篇1.1 网络基础1.1.1 TCP 三次握手1.1.2 TCP四次挥手![在这里插入图片描述](https://img-blog.csdnimg.cn/90a6997e8d414c84b499167c99da0397.png)1.1.3 TCP常见面试题 1. 基础篇 1.1 网络基础 1.1.1 TCP 三次握手 三次握手过程&#xff1a; 客户端——发…

开心档之MySQL 数据类型

目录 MySQL 数据类型 数值类型 日期和时间类型 字符串类型 MySQL 中定义数据字段的类型对你数据库的优化是非常重要的。 MySQL 支持多种类型&#xff0c;大致可以分为三类&#xff1a;数值、日期/时间和字符串(字符)类型。 数值类型 MySQL 支持所有标准 SQL 数值数据类型…