C进阶_字符串库函数

news2024/12/26 10:53:09

目录

求字符串长度

strlen

常规实现

递归实现

指针-指针实现

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

strcpy

模拟实现strcpy

strcat

模拟实现strcat

strcmp

模拟实现strcmp 

长度受限制的字符串函数

strncpy

strncat

strncmp


求字符串长度

strlen

size_t strlen ( const char * str );

字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包
含 '\0' )。

参数指向的字符串必须要以 '\0' 结束。

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

学会strlen函数的模拟实现:

常规实现

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

递归实现

int my_strlen(const char* str)
{
	assert(str != NULL);
	if (*str != '\0')
		return 1 + my_strlen(str + 1);
	else
		return 0;
}

指针-指针实现

int my_strlen(const char* str)
{
	const char* start = str;
	assert(str != NULL);
	while (*str)
	{
		str++;
	}
	return str - start;
}

在strlen的文档中,返回类型是size_t

size_t strlen( const char *string );

为什么是size_t?这是因为strlen是求字符长度的,求出的长度是不可能为负数的,而size_t其实就是unsigned int即无符号整型。

但是注意size_t有它的坏处,见下:

int main()
{
	if (strlen("abc") - strlen("abcdef") > 0)
		printf(">");
	else
		printf("<");
	return 0;
}

执行这段代码,结果竟然是>。这是因为3-6虽然是-3,但由于是无符号整型,它就被当成正数了。

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

strcpy

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

Copies the C string pointed by source into the array pointed by destination, including the
terminating null character (and stopping at that point).

源字符串必须以\0结束。

会将源字符串中的\0拷贝到目标空间。

目标空间必须足够大,以确保能存放源字符串。

目标空间必须可变。

为什么源字符串必须以\0结束?如果不以\0结束,编译器就会在内存中持续往后读取字符,直到遇到\0,比如:

int main()
{
	char arr1[100] = {0};
	char arr2[] = { 'h','e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd' };
	strcpy(arr1, arr2);
	printf("%s", arr1);
	return 0;
}

并没有成功返回0。

那什么是会将源字符串中的\0拷贝到目标空间?

int main()
{
	char arr1[100] = {0};
	char arr2[] = { 'h','e', 'l', 'l', '\0', ' ', 'w', 'o', 'r', 'l', 'd' };
	char arr3[] = "hello\0world";
	strcpy(arr1, arr2);
	printf("%s\n", arr1);
	strcpy(arr1, arr3);
	printf("%s\n", arr1);
	return 0;
}

目标空间不够大会怎样?

int main()
{
	char arr1[5] = { 0 };
	char arr2[] = "hello world";
	strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

 

虽然成功打印了,但会报错。

什么是目标空间必须可变?

int main()
{
	char* p = "abcdefghijklm";
	char arr[] = "hello world";
	strcpy(p, arr);
	printf("%s\n", p);
	return 0;
}

可以看到程序崩溃了。这是因为abcdefghijklm是常量字符串,而不是变量。所以要用数组来存放字符串。

模拟实现strcpy

char* my_strcpy(char* dest,const char* src)
{
	char* ret = dest;
	assert(dest&&src);
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}
int main()
{
	char arr1[30] = "";
	char arr2[] = "hello world";
	my_strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

strcat

请看strcat的文档

Append a string.

char *strcat( char *strDestination, const char *strSource );

Return Value

Each of these functions returns the destination string (strDestination). No return value is reserved to indicate an error.

Parameters

strDestination

Null-terminated destination string

strSource

Null-terminated source string

可以看到函数原型和strcpy的原型几乎一样。

但作用不同,strcat的作用是Append a string即在目标字符串后追加源字符串。

目标空间要有\0,源字符串必须以\0结束。

为什么?假设我要把world追加到hello后边……

编译器先去目标空间中找到\0,从源字符串中,从w开始\0覆盖掉,然后依次把字符传到目标空间,直到遇到\0结束。

不论是目标字符串还是源字符串,都是遇到\0结束。

目标空间必须有足够的大,能容纳下源字符串的内容。

目标空间必须可修改。道理同strcpy。

先来模拟实现下strcat。

模拟实现strcat

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

	//1. 找目标空间的\0
	while (*dest)
	{
		dest++;
	}
	//2. 追加
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}

字符串自己给自己追加,如何?可能会有问题。

当字符串给自己追加时……比如要将"abcdef"追加给"abcdef",dest会在while循环中找到\0。

之后\0被a覆盖。接着在a后边追加bcdef直到*dest被赋值成\0停止循环。

可是在这里\0已经被覆盖掉了,怎么遇到\0?这就是问题所在

 

strcmp

在MSDN中查阅strcmp的文档:

Remarks

The strcmp function compares string1 and string2 lexicographically and returns a value indicating their relationship.

Return Value

The return value for each of these functions indicates the lexicographic relation of string1 to string2.

ValueRelationship of string1 to string2
< 0string1 less than string2
0string1 identical to string2
> 0string1 greater than string2

可以发现strcmp是按字典序对两个字符串比较大小,当string1小于string2时返回值小于0,当string1等于string2时返回值等于0,当string1大于string2时返回值大于0。

在Visual Studio环境下,是小于的情况返回-1,是等于的情况返回0,是大于的情况返回1。但这并不意味着在所有IDE或编译器下都返回-1、0、1。

现在对abc和abcdef进行比较:

#include <stdio.h>
#include <string.h>
int main()
{
	char arr1[] = "abq";
	char arr2[] = "abcdef";
	int ret = strcmp(arr1,arr2);
	printf("%d", ret);
	return 0;
}

在ASCII码中,q的值大于c,所以返回值为1:

模拟实现strcmp 

#include <assert.h>
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;
}

有一种情况是str1和str1大小一样时返回0,所以在进入while循环后就要判断*str1是不是等于\0,如果是就说明两个字符串大小相等,就返回0。

当遇到不同的字符时,while循环会结束,如果*str1大于*str2就返回1,否则,也就是*str小于*str2时返回-1。

刚刚提到:

不意味着在所有IDE或编译器下都返回-1、0、1。

那就来模拟下不是返回-1、0、1的情况:

#include <assert.h>
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;
}

注意看最后,return *str1-str2;就是直接返回两个字母的ASCII值的差。

虽然C语言有它的标准,但标准并没有具体规定大于和小于的情况返回多少,只规定返回的值是大于0还是小于0。

像strcpy、strcat、strcmp这些函数都是长度不受限制的字符串函数。不受限制也就意味着相对来说不安全,比如刚才演示的strcat,让字符串自己给自己追加,如果没有遇到\0就会陷入死循环。如果限制了长度们就不会陷入死循环。

长度受限制的字符串函数

为了尽可能地提高安全性,后来C语言又引入了一些新的函数,这些函数是长度受限制的字符串函数。这里仅作简单介绍,会用即可~

strncpy

它的函数原型是:

char *strncpy( char *strDest, const char *strSource, size_t count );

作用是:

拷贝num个字符从源字符串到目标空间。

如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。

调试以下代码:

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

 对arr1进行监视,会发现strncpy不会在拷贝后给字符串加上\0。

当拷贝10个字符时:

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

这印证了刚才提到的

如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。

strncat

它的函数原型是:

char *strncat( char *strDest, const char *strSource, size_t count );

查阅strncat的文档可得知

Appends the first num characters of source to destination, plus a terminating null-character.
If the length of the C string in source is less than num, only the content up to the terminating
null-character is copied.

可知在追加后会加上\0。此外如果源字符串的长度小于第三个参数的值,那么只会对源字符的\0之前的字符进行拷贝。

下面调试以下代码: 

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

可以发现abcdef的长度明显大于4,那么只会拷贝前四个字符。

再来调试以下代码:

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

可以看到,是从目标字符串的\0开始追加字符,追加四个字符,到最后添加\0。 

在追加后如果打印此字符串,最后的xx就不会被打印了。

strncmp

它的函数原型是:

int strncmp( const char *string1, const char *string2, size_t count );

与strcmp一样,都是按字典序对两个字符串比较大小,当string1小于string2时返回值小于0,当string1等于string2时返回值等于0,当string1大于string2时返回值大于0。

调试以下代码:

#include <string.h>
#include <stdio.h>
int main()
{
	char* p1 = "abcdef";
	char* p2 = "abcqwer";
	int ret = strncmp(p1, p2, 3);
	printf("%s", ret);
	return 0;
}

这是只比较前三个字符,输出结果为:

如果比较前四个字符,调试下边的代码:

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

输出结果为:

这是因为在ASCII中,q的值大于d,所以返回-1。

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

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

相关文章

前端工具(运用造型)

CSS预处理器的使用方法 1、什么是css预处理器 CSS预处理器是一种专门的编程语言&#xff0c;用来为CSS增加一些编程特性&#xff08;CSS本身不是编程语言&#xff09;不需要考虑浏览器兼容问题&#xff0c;因为CSS预处理器最终编译和输出的仍是标准的CSS样式。可以在CSS预处理…

磨金石教育摄影技能干货分享|简述特效在影视制作中的四大作用

近三年因为疫情的原因&#xff0c;极少去影院去看电影。 想起来上次看电影还是去年八月份&#xff0c;当时上映的是科幻大作《沙丘》。看科幻电影&#xff0c;最大的期待就是导演编剧们对外星球与外太空场景的塑造。那些逼真的场景与炫酷的战舰航天器&#xff0c;满足了我对未知…

设计模式_结构型模式 -《适配器模式》

设计模式_结构型模式 -《适配器模式》 笔记整理自 黑马程序员Java设计模式详解&#xff0c; 23种Java设计模式&#xff08;图解框架源码分析实战&#xff09; 概述 如果去欧洲国家去旅游的话&#xff0c;他们的插座如下图最左边&#xff0c;是欧洲标准。而我们使用的插头如下图…

Kindle 可旋转桌面时钟

前言 自己的 Kindle 吃灰很久了&#xff0c;想做个时钟用&#xff0c;但是网上可选的时钟网站比较少&#xff0c;这些时钟网站里面&#xff0c;要么太简单 界面也比较丑陋&#xff0c;要么内容太多 有些本末倒置了&#xff0c;要么网址特别长 输入网址的时候太麻烦。 干脆自己…

【ROS】—— 机器人导航(仿真)—导航原理(十七)

文章目录前言1. 导航模块简介1.1 全局地图1.2 自身定位1.3 路径规划1.4 运动控制1.5 环境感知2. 导航之坐标系前言 &#x1f4e2;本系列将依托赵虚左老师的ROS课程&#xff0c;写下自己的一些心得与笔记。 &#x1f4e2;课程链接:https://www.bilibili.com/video/BV1Ci4y1L7ZZ …

Min_25筛详解

概述 Min_25是日本一个ACM选手的ID&#xff0c;这个筛法是他发明的&#xff0c;所以称之为Min_25筛。它能在亚线性复杂度求出一类积性函数的 fff 的前缀和&#xff0c;前提 是这个积性函数在质数和质数的幂位置的函数值比较好求。借助埃拉托色尼筛的思想 将原问题转化成与质因…

Allegro如何导出和导入设计规则操作指导

Allegro如何导出和导入设计规则操作指导 当需要借用另外一款PCB的设计规则时候,Allegro支持把PCB设计规则导入到另外一块PCB中,如下图 具体操作如下 打开规则管理器打开后如下图

2023.1.15 学习周报

文章目录摘要文献阅读1.题目2.摘要3.介绍4.本文贡献5.PROPOSED METHOD5.1 Problem Formulation5.2 Personalized Time Intervals5.3 Embedding Layer5.4 Time Interval-Aware Self-Attention5.4.1 Time Interval-Aware Self-attention Layer5.4.2 Causality5.4.3 Point-Wise Fe…

QT可直接安装的离线版最后版本5.14.2

以前用c#来做组态&#xff0c;自定义控件开发起来也还过得去&#xff0c;但QT的控件和graphics view貌似更有优势&#xff0c;个人观点吧&#xff01;工控领域的组态用上QT还是不错的选择。 从2000前开始使用qt&#xff0c;算起来也有20多年了。个人感觉用起来最顺手的应该时Q…

【PHP】一文详解如何连接Mysql数据库(附源码)

&#x1f482;作者简介&#xff1a; THUNDER王&#xff0c;一名热爱财税和SAP ABAP编程以及热爱分享的博主。目前于江西师范大学会计学专业大二本科在读&#xff0c;同时任汉硕云&#xff08;广东&#xff09;科技有限公司ABAP开发顾问。在学习工作中&#xff0c;我通常使用偏后…

24考研——专业院校选报指南(3步决定专业选择、11大类本科对应考研专业简析、6步决定目标院校)

文章目录一、专业选择指导1.1 考研整体形势1.2 考研专业选报1.2.1 专业设置1.2.2 专硕专业设置1.2.3 专业代码含义1.2.4 区分“学硕和专硕”1.2.5 专业选择步骤&#xff08;跨专业考研难度&#xff09;1.2.6 跨专业考研简析&#xff08;法硕/教育/会计、审计、图书情报/思想政治…

汇编【王爽】实验3、4

实验3 编程、编译、链接、跟踪 assignment 1 编译链接生成可执行文件 assignment 2 debug将程序载入内存&#xff0c;设置CS:IP&#xff1a;程序所在内存段的段地址为DS075CDS075CDS075C&#xff0c;则PSP的地址为075C:0075C:0075C:0&#xff0c;程序的地址为076C:0(075C1…

C++ 初了解模板

一. 泛型编程 我们若是想实现一个需要对各类数据通用的功能&#xff0c;在C语言中是不太现实的&#xff0c;而在C中&#xff0c;我们可以运用函数重载&#xff0c;但我们依然需要写出多个内容极其类似的函数&#xff0c;例如想要实现交换 void Swap(int& a, int& b…

2022这特殊的一年,再见!

望着窗外的夕阳以及还未完全融化的积雪&#xff0c;我想是时候给这特殊的一年写篇总结了。于是我翻看了2021年的&#xff0c;发现文末所定的2022年目标。终于明白为什么老人不玩手机可以坐一下午了&#xff0c;因为往事回想起来就和电视连续剧一样。年初参加了开运跑&#xff0…

【UE】pak的mount(带源码解析)

本文使用的引擎版本为UE4.27 为了方便理解&#xff0c;文中选取的代码均为部分截取&#xff0c;只截取与小节相关的部分 文章目录概述几个涉及到的结构Mount时机pak读取优先级目录优先级根据文件名定优先级综上所述概述 正常的散文件加载是使用FFileHelper::LoadFileToArray等…

【阶段四】Python深度学习06篇:深度学习项目实战:卷积神经网络进行狗狗图像分类项目

本篇的思维导图: 项目背景 应用Keras框架构建卷积神经网络进行狗狗图像分类的预测,以及模型的优化。主要用来熟悉Keras卷积层、池化层网络的使用以及模型的优化方法。 数据获取 本次建模数据来源于网络,数据项统计如下: 数据集为狗狗数据集,来自全国各地的狗狗图…

算法进阶指南:第一章练习题

1.The Pilots Brothers refrigerator 牛客竞赛-The Pilots Brothers refrigerator 116. 飞行员兄弟 - AcWing题库 开关问题的特点是每个开关只会作用某个特定范围&#xff0c;所以每个开关最多操作一次&#xff0c;且操作先后次序对最后结果无影响。用16位二进制存储状态&am…

Unity 过场工具(Cutscene)设计(一)

Unity 过场工具(Cutscene)设计&#xff08;一&#xff09; 游戏中通常会涉及到过场内容的制作&#xff0c;从而来进行一些强表现&#xff0c;从而来进行剧情相关的串联&#xff0c;使游戏表现类容更丰富。比较典型的游戏 像原神&#xff0c;天刀等等游戏。 过场工具制作选择 过…

Java程序设计实验2 | Java语言基础(1)

*本文是博主对Java各种实验的再整理与详解&#xff0c;除了代码部分和解析部分&#xff0c;一些题目还增加了拓展部分&#xff08;⭐&#xff09;。拓展部分不是实验报告中原有的内容&#xff0c;而是博主本人自己的补充&#xff0c;以方便大家额外学习、参考。 目录 一、实验…

写在壬寅年末,2023年春节

先回顾过去几年写过的年末总结写在戊戌年末&#xff0c;2019年春节写在己亥年末&#xff0c;2020年春节写在庚子年末&#xff0c;2021年春节写在辛丑年末&#xff0c;2022年春节又一个农历年即将过去&#xff0c;写下这样的年末总结&#xff0c;已经是第5年&#xff0c;于是便有…