详解C语言string.h中常用的14个库函数(一)

news2024/11/22 20:49:42

我计划讲解C语言string.h这个头文件中,最常用的14个库函数。为了让大家更加深入的理解这些函数,部分函数我会模拟实现。篇幅所限,如果文章太长了,可能会较难坚持读完,所以我会分几篇博客来讲述。本篇博客主要讲解的函数有:strlen, strcpy, strcat, strcmp。这四个函数是最基础,最常见的字符串操作函数,在字符串相关的场景中会频繁使用,希望大家在阅读完本篇博客后,对它们的原理、使用方式了如指掌,运用自如。考虑到有朋友可能不太理解C语言字符串的一些基础概念,本篇博客会先铺垫一些基础知识,已经熟悉C语言字符串的朋友们可以跳过。

字符串必备知识

什么是字符串?就是一串字符。C语言如何表示字符串呢?用双引号括起来一串字符即可。如:"hello world"就是一个字符串。

C语言中的字符串的结束标志是\0这个转义字符。比如"hello world"这个字符串,本质上,在内存中存储的是hello world\0,在所有的字符后面,会隐藏一个\0。注意:\0作为一个整体,是一个字符,它的ASCII码值是0。

字符串一般用字符数组来存储,比如:

char arr[] = "hello world";

就把"hello world"这个字符串存储到了字符数组arr里。此时数组arr的长度是多少呢?要把hello world这11个字符算上,后面还有一个\0,总共12个字符。但是,如果计算的是字符串的长度,是不算最后的\0的,长度就是11。

字符串的长度计算的是\0之前出现了几个字符,但是不包含\0本身!那如何求字符串长度呢?这就要引出今天要讲解的第一个库函数了。

string.h中的库函数

strlen

size_t strlen ( const char * str );

strlen是用来求字符串的长度的。只需要给它传字符串首字符的地址,它就会计算出该字符串的长度。注意:字符串常量,即单引号引起来的字符串,作为一个表达式,其值为首字符的地址,可以作为strlen的参数。除此之外,字符串也可以保存到字符数组中,数组名表示首元素地址,也可以作为参数。

字符串直接作为参数:

int len1 = strlen("abc"); // 3

char* str = "defg";
int len2 = strlen(str); // 4

数组名作为参数:

char arr[] = "abc";
int len = strlen(arr); // 3

strlen的返回值是size_t类型的。size_t是一个无符号整型。所以以下程序会输出什么?

if (strlen("abc") - strlen("abcd") < 0)
	printf("<\n");
else
	printf(">=");

看起来应该是“小于”,但结果是“大于等于”,原因是无符号整型的差还是无符号整型,一定是大于等于0的。

下面我们来模拟实现strlen。实现思路很简单,从首字符开始,向后数,直到遇到\0就停下来。

size_t my_strlen(const char* str)
{
	assert(str);
	
	size_t count = 0;
	while (*str)
	{
		++count;
		++str;
	}
	
	return count;
}

当然,我们也可以一直向后找\0,根据“指针-指针得到的是指针之间的元素个数”的原理,用\0的地址减首字符的地址,也可以得到字符串的长度。

size_t my_strlen(const char* str)
{
	assert(str);
	
	const char* eos = str; // end of str
	while (*eos)
	{
		++eos;
	}
	
	return eos - str;
}

当然,如果不创建临时变量,也可以使用递归实现,这种实现并不推荐,因为递归是有缺陷的,递归深度太深可能导致栈溢出。递归实现思路是:字符串的长度=1+从下一个字符开始数的长度。也就是说,strlen(str) = 1+strlen(str+1)。当然,如果str是空字符串,即*str=='\0',长度就为0。

size_t my_strlen(const char* str)
{
	assert(str);
	
	if (*str)
		return 1 + my_strlen(str + 1);
	else
		return 0;
}

strlen总结:

  1. strlen是用来求字符串长度的,会返回\0之前出现了几个字符。
  2. 参数指向的字符串必须以\0结尾,否则结果是随机值。
  3. 函数的返回值类型是size_t的,size_t是一个无符号整型。

strcpy

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

strcpy是用来完成字符串拷贝的。它有2个参数,分别是目的地和起始位置。比如,把字符串arr1拷贝到arr2里,要这么写:

char arr1[20] = {0};
char arr2[] = "abc";
strcpy(arr1, arr2);

注意:拷贝时,会把arr2中的"abc"拷贝到arr1中,包括结尾的\0

strcpy会返回目标空间的起始地址,方便函数的链式访问,比如:

printf("%s\n", strcpy(arr1, arr2));

以上代码,在把arr2中的字符串拷贝到arr1中后,顺便把arr1打印出来,看看有没有拷贝成功。

下面讲讲模拟实现。其实重点是拷贝的过程,也就是*dst++ = *src++,把src指向的字符拷贝到dst指向的空间中,并且2个指针向后走,直到src遇到\0,此时结束循环,返回起始地址。注意dst在拷贝的过程中一直在向后走,所以需要在最开始先保存下来,方便最后返回目标空间的地址。

char* my_strcpy(char* dst, const char* src)
{
	assert(dst && src);

	char* ret = dst;
	while (*dst++ = *src++)
	{
		;
	}
	return ret;
}

strcpy总结:

  1. strcpy是用来拷贝字符串的。
  2. 源字符串必须以\0结束,否则会一直拷贝字符,直到遇到内存中的\0
  3. 拷贝时,会把源字符串结尾的\0也拷贝到目标空间中。
  4. 目标空间必须足够大,能够容纳拷贝的源字符串,否则会导致内存的越界访问。
  5. 目标空间必须可变(可修改)。

strcat

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

strcat是完成字符串追加的,它可以在目标字符串后面追加源字符串。可以理解为,先从目标空间中找到\0,即目标字符串的结尾,然后从\0所在的位置开始,向后追加源字符串。比如:在abc后面追加def后就得到了abcdef,追加的时候会把源字符串的\0也追加过去。函数会返回目标字符串的起始地址。

其实可以简单理解为:先找到目标字符串结尾的\0,然后从把源字符串以strcpy的方式拷贝到目标字符串后面,大家看到模拟实现后就明白了。

char* my_strcat(char* dst, const char* src)
{
	assert(dst && src);
	char* ret = dst;

	while (*dst)
	{
		++dst;
	}

	while (*dst++ = *src++)
	{
		;
	}
	return ret;
}

其实就是在strcpy的模拟实现的基础上,加上了下面的代码,即找dst中的\0

while (*dst)
{
	++dst;
}

根据以上的实现,能不能自己给自己追加呢?比如:

char arr[10] = "abc";
strcat(arr, arr);

我们想再arr后面追加arr,预期结果是,“abc"后面追加"abc"得到"abcabc”,但是根据以上模拟实现的代码,在追加的同时,会把src中的\0给覆盖掉,所以源字符串中就没有\0了,在拷贝的时候,本来是遇到\0就停止了,但是一直找不到\0,就导致无限循环。

那如何实现自己给自己追加呢?这就要用到strncpy函数了,这个函数我会在下一篇博客中介绍。

strcat总结:

  1. strcat是用来追加字符串的,会把源字符串追加到目标字符串后面。
  2. 源字符串和目标字符串都必须以\0结束。
  3. 目标空间必须足够大,以容纳追加的字符串。
  4. 目标空间必须可变(可修改)。
  5. 不能自己给自己追加。

strcmp

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

strcmp是用来比较2个字符串的。比较方式是:从第一个字符开始,一个一个往后比,直到遇到第一对不同的字符或者都遇到\0。如果遇到第一对不同的字符,则比较其ASCII码值,哪个大,对应的字符串就更大;如果都遇到\0,则2个字符串相等。函数会根据不同的大小关系返回不同的值,当str1>str2时,返回一个正整数;如果str1<str2,返回一个负整数;如果str1==str2,返回0。

根据以上的描述,可以模拟实现,如下:

int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);

	while (*str1 == *str2)
	{
		if (*str1 == '\0')
			return 0;

		++str1;
		++str2;
	}

	return *str1 - *str2;
}

strcmp总结:

  1. strcmp是用来比较2个字符串的大小的。
  2. 假设从左到右2个参数分别是str1和str2。如果str1>str2,返回正整数;如果str1<str2,返回负整数;如果str1==str2,返回0。
  3. 比较时,是根据从2个字符串的第一个字符开始比较,向后比,直到遇到不同的字符或者都遇到\0。如果遇到不同的字符,则ASCII码值大的字符对应的字符串更大;如果都遇到\0,则2个字符串相等。
  4. 2个字符串结尾都应该有\0,没有的话,会一直向后比较字符,如果都相等,会一直比下去。

总结

  1. strlen是用来求字符串长度的,统计的是\0之前出现的字符个数。
  2. strcpy是用来拷贝字符串的。有2个参数,目标空间在前面,源字符串在后面;strcat是用来追加字符串的,也是2个参数,目标字符串在前面,源字符串在后面,比strcpy多了1个步骤,即先在目标空间中找到\0,再拷贝。
  3. strcmp是用来比较字符串的。前面字符串和后面字符串比较,如果前面大会返回正整数,前面小会返回负整数,前后相等返回0。

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

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

相关文章

FPGA时序约束(三)时序约束基本路径的深入分析

系列文章目录 FPGA时序约束&#xff08;一&#xff09;基本概念入门及简单语法 FPGA时序约束&#xff08;二&#xff09;利用Quartus18对Altera进行时序约束 文章目录 系列文章目录前言基本时序路径时钟偏差寄存器到寄存器&#xff08;reg2reg&#xff09;建立时间余量保持时…

PHP实现以数组var_dump,array_combine等函数的方法功能举例

目录 前言 一、什么是数组 二、把两个数组合并成一个数组 1.1运行流程&#xff08;思想&#xff09; 1.2代码段 1.3运行截图 三、自动创建数组的一个案例 1.1运行流程&#xff08;思想&#xff09; 1.2代码段 1.3运行截图 前言 1.若有选择&#xff0c;可实现在目录里…

小朋友排队

[蓝桥杯 2014 省 B] 小朋友排队 题目描述 n n n 个小朋友站成一排。现在要把他们按身高从低到高的顺序排列&#xff0c;但是每次只能交换位置相邻的两个小朋友。 每个小朋友都有一个不高兴的程度。开始的时候&#xff0c;所有小朋友的不高兴程度都是 0 0 0。 如果某个小朋友…

Python 学习曲线 从 Python 新手到 Pro

Python 学习曲线 从 Python新手到 Pro 使用代码片段介绍&#xff1a; Python 是世界上最通用和使用最广泛的编程语言之一&#xff0c;以其简单性、可读性和多功能性而闻名。 在本文中&#xff0c;我们将探讨一系列示例场景&#xff0c;其中代码由具有三个不同专业知识水平的程序…

文件系统和动静态库

目录 再识文件属性 查看文件属性的原理 初识inode 了解磁盘 什么是磁盘 磁盘的结构 磁盘的存储结构 CHS寻址 磁盘的逻辑结构 使用LBA地址的意义 理解文件系统 页框和页帧 分治思想管理 Linux ext2文件系统 软硬链接 软链接 硬链接 文件的三个时间 动静态库 …

java运行python脚本,待完善版

参考资料: windows下调用CMD运行方式 兼容linux/windows,同步异步方式 指定特殊运行环境的运行(如:anaconda运行环境) 整合以上三种方式终极版源码 相关内容: 调用python脚本传参说明 如果不传参数,python脚本可以随意写,比如:

【课程介绍篇】C/C++后台开发岗位技能知识树

1 C/C后台开发岗位技能知识树 2 Linux C/C后台架构开发 成长体系课程 3 C技术历史更新 https://www.0voice.com/uiwebsite/html/courses/

《UVM实战》学习笔记——第七章 UVM中的寄存器模型2——期望值/镜像值、自动/显示预测、操作方式

文章目录 前言一、寄存器模型对DUT的模拟1.1 期望值和镜像值1.2 常见操作对期望值和镜像值的影响 二、prediction分类2.1 自动预测2.2 显式预测 三、访问寄存器方式四、mem和reg的联系和差别五、内建built_in sequence5.1 寄存器模型内建序列5.2 存储器模型内建序列5.3 禁止域名…

安装APP时弹窗未知风险未知开发者,还能下载吗?

随着移动互联网的不断普及&#xff0c;人们的日常生活已与手机密不可分。根据相关研究&#xff0c;在使用手机时&#xff0c;人们90%以上的时间都花在某些应用程序上&#xff0c;巨大的需求使得各种各样的APP被开发出来。然而人们在使用APP时必须更加注意其是否来源可信企业&am…

数据结构与算法基础-学习-21-查找之平衡二叉树(AVL树)

目录 一、个人理解 二、最小失衡子树 三、平衡调整的四种类型 1、LL型 2、RR型 3、LR型 4、RL型 四、如何平衡调整 1、LL型调整 2、LR型调整 五、宏定义 六、结构体类型定义 1、AVL树结点类型 2、AVL树类型 3、AVL树结点搜索路径类型 七、函数定义 1、初始化AV…

基于CMS项目的JDBC的实战

基于CMS项目的JDBC的实战 使用的Javase技术&#xff0c;进行控制台输出的客户管理系统&#xff08;CMS&#xff09;&#xff0c;主要功能包含登录&#xff0c;注册、客户信息的展示&#xff0c;客户信息的更新&#xff0c;客户信息添加删除客户、退出系统。 设计创建数据库 …

PEX高效批量网络装机

目录 一、部署PXE远程安装服务 1&#xff09;PXE概述 若要搭建PEX网络体系&#xff0c;必须满足以下几个前提条件 2&#xff09;搭建PXE远程安装服务器 ①安装并启用 TFTP 服务 ②安装并启用 DHCP 服务 ​编辑 ③准备 Linux 内核、初始化镜像文件 ④准备 PXE 引导程序 …

CUDA下载与对应版本查询

文章目录 1 算力&#xff0c;CUDA Driver Version&#xff0c;CUDA Runtime Version2 显卡型号3 实操4 镜像 1 算力&#xff0c;CUDA Driver Version&#xff0c;CUDA Runtime Version 比如说我们进入pytorch官网中&#xff0c;点击下载&#xff0c;如何找到适合自己的CUDA版本…

SCAU 统计学 实验5

8.14 总体平均值&#xff08;μ&#xff09;&#xff1a;7.0 cm 总体方差&#xff08;σ&#xff09;&#xff1a;0.03 cm 样本平均值&#xff08;x̄&#xff09;&#xff1a;6.97 cm 样本方差&#xff08;s&#xff09;&#xff1a;0.0375 cm 样本大小&#xff08;n&#xff…

复旦MOSS大模型开源了!Github和Hugging Face同时上线

来源&#xff1a;量子位 复旦大模型MOSS&#xff0c;正式开源了&#xff01; 作为国内首个开放测试的类ChatGPT产品&#xff0c;MOSS开源地址一放出&#xff0c;又冲上知乎热搜&#xff1a; 从官网介绍来看&#xff0c;MOSS是一个拥有160亿参数的开源对话语言模型。 它由约7…

EventLog Analyzer:高效保护网络安全的强大工具

网络安全是当今数字化世界中最为重要的话题之一。随着越来越多的组织、企业和个人将其业务转移到互联网上&#xff0c;网络安全问题变得越来越严峻。针对这个问题&#xff0c;EventLog Analyzer提供了一个有效的解决方案&#xff0c;让网络管理员可以更好地监控和保护其网络环境…

【虚拟仿真】Unity3D中实现UI的单击、双击、按压、拖动的不同状态判断

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 之前写了一篇在Unity中鼠标的单击、双击、拖动的文章&#xff…

Improving Language Understanding by Generative Pre-Training 论文阅读

论文题目&#xff1a;通过生成式预训练提高语言理解能力 GPT的全称&#xff1a;Generative Pre-trained Transformer。 Generative是指GPT可以利用先前的输入文本来生成新的文本。GPT的生成过程是基于统计的&#xff0c;它可以预测输入序列的下一个单词或字符&#xff0c;从而生…

春招,进阿里了....

个人背景是东北某 985 科班本硕&#xff0c;做的 测试开发&#xff0c;有两个自己写的小项目。下面是一些印象比较深刻的面试题 阿里一面 什么是软件测试&#xff1f; 软件测试过程中会面向哪些群体&#xff1f; 开发一个软件都要经过哪些阶段&#xff1f; 什么是黑盒测试&…

一块钱看Android Debug: avc denied 已存在的目录不能访问

某三方应用&#xff0c;使用了USB摄像头&#xff0c;启动应用后功能不能使用&#xff0c;看log有如下错误&#xff0c; denied后面{}里的是要执行的动作,比如append,open,execmod,link等等 scontext指的是域,对应的是te文件 上面报错这条对应te文件是untrusted_app.te, scontex…