C语言从入门到放弃——字符串和内存操作函数

news2025/1/12 12:02:28

字符串,是一种由双引号引起的一整串字符,在C语言中,字符串是没有类型的,通常我们将字符串放在字符数组当中,同时,我们对于字符串的操作是很频繁的,因为对于字符串的操作频繁,所以C语言本身提供了一些对字符串进行处理的函数。

目录

字符操作函数

strlen

 strcpy

 strcat

strcmp

 strstr

strtok

strerror

其他字符操作函数

参数符合条件返回真

字符转换

内存操作函数

memcpy

 memmove

 memcmp

总结


字符操作函数

strlen

strlen用于计算我们的字符串长度,对于strlen而言,是不会将结尾处的'\0'计算进去的,所以我们在用strlen的时候注意,参数的结尾一定要有'\0',不然strlen会返回一个随机值,对于我们的strlen来说,返回值一定是一个无符号的数,字符串的长度不可能出现负数,最小也是0,知道这些注意事项之后,我们进行模拟实现,因为我们知道strlen是计算'\0'之前出现的数,那我们就可以定义一个计数器,用循环语句,将'\0'作为我们的判断条件,每次进入的时候都将计数器加一次,同时我们的字符串也像后加加,当遇到'\0'的时候,不在进入循环,返回计数器的值。

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

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

int main()
{
	char ch[] = "asdfghj";
	int len1 = strlen(ch);
	int len2 = my_strlen(ch);
	printf("%d\n", len1);
	printf("%d\n", len2);
	return 0;
}

 strcpy

strcpy,字符串拷贝,是将我们想要拷贝的字符串拷贝到目标空间的一个函数,同样,对于我们想要拷贝的字符串,也是需要结尾处一定要有'\0'的,不然会在字符串后面带上一堆乱码,因为我们的strcpy也是以'\0'为判断条件,还有需要注意的是,我们要让目的地的空间放的下我们要拷贝进去的字符串,不然就会进行越界,目的地也必须是可变的,不能是常量字符串,那知道这些注意事项,我们进行模拟实现。

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

char* my_strcpy(char* ch1, const char* ch2)
{
	char* cp = ch1;
	assert(ch1 && ch2);
	while (*ch1++=*ch2++)
	{
		;
	}
	return cp;
}
int main()
{
	char ch1[25] = { 0 };
	char ch2[] = "asdfghjkl";
	//strcpy(ch1, ch2);
	my_strcpy(ch1, ch2);
	printf("%s", ch1);
	return 0;
}

 strcat

strcat,字符串追加,可以将一个字符串的内容追加到另一个字符串的后面,这里要注意我们的两个字符串结尾处要有'\0',并且我们的目标空间要足够大,能放的下两个字符串才可以,并且我们的目的地要可以被修改,实现的话和我们的srecpy差不多,只需要提前找到我们目的地的'\0',然后进行追加。

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

char* my_strcat(char* ch1, const char* ch2)
{
	char* cp = ch1;
	assert(ch1 && ch2);
	while (*ch1++)
	{
		;
	}
	ch1--;
	while (*ch1++ = *ch2++)
	{
		;
	}
	return cp;
}

int main()
{
	char ch1[20] = "hello ";
	char ch2[] = "world";
	//strcat(ch1, ch2);
	my_strcat(ch1, ch2);
	printf("%s", ch1);
}

 

那如果我们让它把自己的内容追加在自己的后面怎么办呢?那要自己追加自己,我们模拟实现的思路就行不通了,那我们优化一下,一开始记录下我们要追加在后面的字符串的地址,然后追加行不行呢?这样的话,目的地的字符串就变成了一个结尾没有'\0'的字符串,就会一直追加下去,那我们可不可以先记住要追加字符串的起始地址,然后用一个计数器,记住要追加的字符串有多少字符,追加完这些字符后,我们自己补一个'\0',是不是问题就解决了。

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

char* my_strcat(char* ch1, const char* ch2)
{
	char* cp = ch1;
	assert(ch1 && ch2);
	const char* c = ch2;
	int count = 0;
	while (*ch1++)
	{
		;
	}
	ch1--;
	while (*ch2++)
	{
		count++;
	}
	while (count--)
	{
		*ch1++ = *c++;
	}
	*ch1 = '\0';
	return cp;
}

int main()
{
	char ch1[20] = "hello ";
	char ch2[] = "world";
	//strcat(ch1, ch2);
	my_strcat(ch1, ch1);
	printf("%s", ch1);
}

strcmp

strcmp,字符串比较,顾名思义,用来比较两个字符串的大小的,在C语言中,我们不能直接用大于小于号来确定字符串谁大谁小,但是本质上,字符串比较的是下标一样的字符的ASCII码值,如果大于则返回一个大于0的数,如果小于返回一个小于0的数,如果等于返回0,那知道这些我们就可以对其进行模拟实现。

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

int my_strcmp(const char* ch1, const char* ch2)
{
	assert(ch1 && ch2);
	while (*ch1++ == *ch2++)
	{
		if (*ch2 == '\0')
		{
			return 0;
		}
	}
	ch1--;
	ch2--;
	return *ch1 - *ch2;
}
int main()
{
	char ch1[] = "asdfghjkq";
	char ch2[] = "asdfghjkl";
	int len1 = strcmp(ch1, ch2);
	int len2 = my_strcmp(ch1, ch2);
	if (len1 > 0 && len2 > 0)
	{
		printf("ch1>ch2");
	}
	else if (len1 == 0 && len2 == 0)
	{
		printf("ch1==ch2");
	}
	else
	{
		printf("ch1<ch2");
	}
	return 0;
}

 strstr

strstr,字符串查找,可以在一个字符串里面找另一个字符串,在实现strstr的时候,我们要注意,查找的时候可能会出现"dddf"里面找"ddf",当是这样的时候,我们找到"dd",如果让本身的地址进行加加,就会使让我们对比下一个字符串的时候对比的是'd'和'f',这样就算我们这个字符串包括另一个字符串也会返回空指针。

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

char* my_strstr(const char* ch1, const char* ch2)
{
	assert(ch1 && ch2);
	const char* cp1 = ch1;
	const char* cp2 = ch2;
	const char* p = ch1;
	while (*p)
	{
		cp2 = ch2;
		cp1 = p;
		while (*cp1!='\0'&&*cp2!='\0'&& * cp1 == *cp2)
		{
			cp1++;
			cp2++;
		}
		if (*ch2 == '\0')
		{
			return (char*)p;
		}
		p++;
	}
	return NULL;
}
int main()
{
	char ch1[] = "asdfffghjsd";
	char ch2[] = "ffh";
	char* cp1 = strstr(ch1, ch2);
	char* cp2 = my_strstr(ch1, ch2);
	if (cp1 == NULL && cp2 == NULL)
	{
		printf("ch2不是ch1的字串");
	}
	else
	{
		printf("ch2是ch1的字串");
	}
	return 0;
}

strtok

strtok,是一个特殊的函数,它可以将字符串分割,按照我们给出的分隔符分隔,我们进行第一次传参的时候,把字符串传过去,然后开始找分隔符,找到分隔符用'\0'代替,当要进行第二次分割的的时候,传参传空指针即可,当传参传空指针的时候,我们的strtok函数会找到上一次标记的地址,然后向后进行切割,如果在我们的字符串中,没有我们的分隔符,就会返回一个空指针。

strerror

strerror是一个用来报告错误的函数,它可以将错误码转换成错误信息,其中我们的错误码会保存在一个叫做errno的函数中,它需要引头文件<errno.h>。

其他字符操作函数

除去我们上面的这些字符操作函数,还有很多,只是用处不是很大,下面对其做一些简单的介绍:

参数符合条件返回真

iscntrl
任何控制字符
isspace
空白字符:空格 ‘ ’ ,换页 ‘\f’ ,换行 '\n' ,回车 ‘\r’ ,制表符 '\t' 或者垂直制表符 '\v'
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
任何可打印字符,包括图形字符和空白字符

字符转换

tolower转换成小写字母
toupper转换成大写字母

除了这些字符操作函数之外,还有一些,是在上面有些函数的基础上进行改造的,有strncpy、strncat、strncmp,这些函数都比原函数多了一个需要传的参数,这个参数限制我们要将多少个字符放到目的地或者进行比较,实现也和上面差不多,就不过多介绍了。除了我们这些能操作字符串的函数之外,还有一种函数,它比我们这种字符串的操作函数能应用的范围更加广泛一些,用于操作我们的内存。

内存操作函数

memcpy

memcpy,内存拷贝函数,它和strcpy的用处一样,传参是三个,第三个参数用于确定,我们要拷贝多少个字节到目的地,实现也与strlen大同小异。

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

void* my_memcpy(void* dest, const void* src, size_t byte)
{
	void* p = dest;
	assert(dest && src);
	while (byte--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return p;
}
int main()
{
	int arr1[20] = { 0 };
	int arr2[] = { 1,2,3,4,5,6,7,8,9,10 };
	int i = 0;
	//memcpy(arr1, arr2, 20);
	my_memcpy(arr1, arr2, 20);
	for (; i < 5; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

 memmove

memmove对比上面的memcpy功能基本一致,但是memmove可以进行目的地和要拷贝的内容重叠在一起的时候,拷贝出来没有问题。

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

void* my_memmove(void* dest, const void* src, size_t byte)
{
	void* p = dest;
	assert(dest && src);
	while (byte--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return p;
}

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

 memcmp

memcmp,和我们的字符串比较函数一样,都是比较大小用的,它比较的值有一个范围,和strncmp是一样的,实现也差不多,不进行过多讲解。

总结

对于这些我们来说,熟练使用这些函数,在某种情况下可以更快的去实现我们的想法,所以要多运用这些函数。

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

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

相关文章

为什么需要预选器?

无论是采用模拟IF处理的传统频谱仪&#xff0c;还是采用数字IF处理的现代频谱仪&#xff0c;都是扫频式架构&#xff0c;通过第一级本振(LO)的调谐实现射频的扫频测试。熟悉频谱仪架构的朋友都了解&#xff0c;在第一级混频器之前都会存在一个预选器&#xff0c;如图1所示&…

点成分享|器官芯片——小白鼠的拯救者?

在新药研发的漫长过程中&#xff0c;实验动物模型是药物从临床前试验阶段进入到临床试验阶段的金标准。实验动物模型有助于人们了解疾病的起源、病理生理特征、疾病机制、识别药物靶标、评估新药物的疗效和人体毒性以及进行药代动力学评价等。常用的实验动物模型包括小鼠、大鼠…

黑马学ElasticSearch(七)

目录&#xff1a; &#xff08;1&#xff09;RestClient查询文档-快速入门 &#xff08;2&#xff09;RestClient查询文档-match、term、range、bool查询 &#xff08;3&#xff09;RestClient查询文档-排序和分页 &#xff08;4&#xff09;RestClient查询文档-高亮显示 &…

git版本回退(git reset、git revert、git stash)

文章目录回退的两种情况1.已 commit&#xff0c;未push到远程仓库。git reset --soft &#xff08;撤销commit&#xff09;git commit --amend&#xff08;修改commit 提交的内容&#xff09;git reset --mixed&#xff08;撤销 commit 和 add 两个动作&#xff09;2.已 commit…

联合证券|左手消费,右手TMT!超270只股票新年获“买入”“推荐”

2023年开年A股商场交投继续火热&#xff0c;出资组织在活跃呼吁出资者布局的一起&#xff0c;自己又更加看好哪些标的和赛道&#xff1f; Wind数据显现&#xff0c;2023年头&#xff0c;券商关于大消费、TMT等方向装备价值更为喜爱&#xff0c;到1月10日&#xff0c;给予“买入…

LOAM、LEGO-LOAM与LIO-SAM的知识总结

文章目录LOAM、LEGO-LOAM与LIO-SAM的知识总结1.概要2.传感器信息读取3.数据的预处理4.激光雷达里程计4.1特征点提取4.2特征点关联匹配4.2.1 标签匹配4.2.2 两步LM优化4.2.3 LIO-SAM优化4.2.3.1 IMU预积分4.2.3.2 关键帧的引入4.2.3.3 因子图4.2.3.4 GPS因子4.2.3.5 回环因子5. …

代码随想录算法训练营第十五天字符串 java : 层序遍历 226.翻转二叉树 101. 对称二叉树

文章目录前言Leetcode 102 二叉树的层序遍历题目讲解Leetcode 226.翻转二叉树题目讲解Leetcode 101. 对称二叉树题目讲解递归法总结前言 递归三定律 确定参数和返回值确认终止条件确认单层递归的逻辑 Leetcode 102 二叉树的层序遍历 题目讲解 /*** Definition for a binar…

大批量数据需要导出导入时,使用mysql 快速导出和导入 csv

使用MYSQL命令行模式 导出into outfile 导入load data导出 into outfile&#xff1a;mysql> select * from cdkeyduihuan into outfile d:/cdk.csv FIELDS TERMINATED BY ,;Query OK, 1049990 rows affected (1.16 sec)d:/cdk.csv 导出数据保持的文件目录。FIELDS TERMINATE…

windows ngnix 配置https

因为客户需求&#xff0c;需要把原来的http换成https&#xff0c;还不能影像原来http的访问。 看了许多网上的资料&#xff0c;经过实践。我总结下相关步骤及怎么配置的。 第一步&#xff0c;把http换成https这里需要ssl证书 ssl证书生成&#xff0c;我接触的有2种免费方式。…

Kettle的安装以及简单使用

Kettle是一款开源免费的ETL工具&#xff0c;ETL全称 Extract - Transform - Load 意味着数据抽取&#xff0c;转换&#xff0c;装载的过程。 ETL是将业务系统的数据经过抽取、清洗转换之后加载到数据仓库的过程&#xff0c;目的是将企业中的分散、零乱、标准不统一的数据整合到…

二叉树19:最大二叉树

主要是我自己刷题的一些记录过程。如果有错可以指出哦&#xff0c;大家一起进步。 转载代码随想录 原文链接&#xff1a; 代码随想录 leetcode链接&#xff1a;654. 最大二叉树 题目&#xff1a; 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地…

【ElementUI】一.axios拦截器;二.ElementUI

目录 一.axios拦截器 1.axios模块的作用&#xff1a;是对基于http请求的封装。在浏览器端对异步请求对象XMLHttpRequest进行封装 2.拦截器&#xff1a; &#xff08;1&#xff09;请求拦截器&#xff1a;对客户端发起的请求进行统一的前期处理&#xff08;token、时间戳、co…

分支与循环语句 - 练习题

目录 一、分支语句 1. 判断一个数是否为奇数 2. 输出1-100之间的奇数 法1&#xff1a;遍历1-100所有的数字&#xff0c;判断是否为奇数&#xff0c;再输出 法2&#xff1a;奇数从1开始&#xff0c;等差为2&#xff0c;所以循环2 二、循环语句 1. 计算 n的阶乘。 2. 计算…

ArcGIS基础实验操作100例--实验84查找面到直线的最近点位置

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 高级编辑篇--实验84 查找面到直线的最近点位置 目录 一、实验背景 二、实验数据 三、实验步骤 &#…

Acwing---1015.摘花生

摘花生1.题目2.基本思想3.代码实现1.题目 Hello Kitty想摘点花生送给她喜欢的米老鼠。 她来到一片有网格状道路的矩形花生地(如下图)&#xff0c;从西北角进去&#xff0c;东南角出来。 地里每个道路的交叉点上都有种着一株花生苗&#xff0c;上面有若干颗花生&#xff0c;经…

Hudi集成Spark(二)Spark SQL方式

文章目录环境准备创建表插入数据查询数据更新数据删除数据覆盖数据修改表结构&#xff08;Alter Table&#xff09;修改分区存储过程&#xff08;Procedures&#xff09;Catalog&#xff1a;可以和Spark或者Flink中做一个共享&#xff0c;共享之后&#xff0c;计算引擎才可以去…

跨域与Nginx总结

跨源资源共享&#xff08;CORS&#xff09;是一种机制&#xff0c;他使浏览器可以访问由其他域提供的Web资源。通常&#xff0c;浏览器会使用同源策略来限制从脚本中发出的HTTP请求。例如&#xff0c;如果一个网站的资源位于https://haha.com&#xff0c;那么它就不能发出对htt…

使用ResNet50实现CIFAR10数据集的训练

如果对你有用的话&#xff0c;希望能够点赞支持一下&#xff0c;这样我就能有更多的动力更新更多的学习笔记了。&#x1f604;&#x1f604; 使用ResNet进行CIFAR-10数据集进行测试&#xff0c;这里使用的是将CIFAR-10数据集的分辨率扩大到32X32&#xff0c;因为算力相关的…

C语言常用内存函数的深度解析

文章目录前言memcpymemcpy函数的使用memcpy函数的自我实现memmovememmove函数的使用memmove函数的自我实现memcmpmemcmp函数的使用memcmp函数的自我实现memsetmemset函数的使用memset函数的自我实现写在最后前言 内存函数的使用广泛度大于常用字符串函数的使用广泛度&#xff0…

教程- VTK.js的基本介绍

VTK.js的核心是标准可视化工具包(VTK)库的JavaScript移植&#xff0c;这是一个c库&#xff0c;旨在促进数据可视化&#xff0c;在此基础上构建了科学可视化应用程序Paraview。VTK.js没有使用OpenGL&#xff0c;而是利用WebGL&#xff0c;主要关注几何和体渲染。因此&#xff0c…