C语言——字符串函数与内存函数

news2024/11/17 17:39:54

目录

前言

一. 函数介绍

1.1 strlen

1.2 strcpy

 1.3 strcat

1.4 strcmp

1.5 strncpy

 1.6 strncat

 1.7 strncmp

 1.8 strstr

 1.9 strtok

1.10 strerror

 1.11 字符分类函数

1.12 memcpy

 1.13 memmove

1.14 memcmp

 二. 函数的模拟实现

2.1 模拟实现strlen

2.2 模拟实现strcpy

2.3 模拟实现strcat

2.4 模拟实现strstr

2.5 模拟实现strcmp

2.6 模拟实现memcpy


前言

在之前的函数中我们学习了很多的函数,今天我们就再来讲几个常见并且实用的函数并且模拟实现他们。

一. 函数介绍

1.1 strlen

size_t strlen(const char* str);

首先是咱们的老熟人strlen函数,求字符串长度,字符串是以“\0”作为结束标志,而strlen函数他的返回值也就是在字符串中“\0”之前出现过的字符个数(并不包括“\0”),参数指向的字符必须以“\0”结束,注意strlen函数的返回类型需要是无符号的类型即size_t

那如何使用呢?如下:

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

非常的方便。

1.2 strcpy

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

这个函数我们之前也见过,这是拷贝字符串函数,将destination拷贝上source的内容,需要注意source必须以“\0”结尾,并且拷贝会将“\0”拷入destination中,并且destination的空间需要足够大并且destination的空间内容是允许改变的。

 1.3 strcat

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

这个函数的作用在于在目标代码的地方追加上新的内容,需要的注意的地方是source依然需要为“\0”结尾,destination所指向的空间需要足够大且能够修改。

1.4 strcmp

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

字符串比较函数,用于比较字符串的大小,比较的方式是一位一位比较,若第一位相同则比较第二位,如若一直相同,则直到“\0”为止,标准规定:

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

1.5 strncpy

char * strncpy ( char * destination, const char * source, size_t num );

这个函数比起strcpy仅仅只是多了一个n,参数也就只是多了一个num,那两者之间是不是有什么关联,又为什么会出现这个strncpy,原因是strcpy只是复制字符串,但不限制复制的数量,很容易造成缓冲溢出。strncpy要安全一些。strncpy能够选择一段字符输出,strcpy则不能。也就是说strncpy是可以选择拷贝的长度的相比较而言造成越界的可能性会大大降低。拷贝num个字符从source字符串到目标空间。如果source字符串的长度小于num,则拷贝完source字符串之后,在destination的后边追加0,直到num个。

 1.6 strncat

char * strncat ( char * destination, const char * source, size_t num );

这个函数也是与strcat函数是几乎没有区别的,只是在原来的追加上限制了追加字符的个数,所需要的注意也与strncpy一样。

// strncat example 
#include <stdio.h>
#include <string.h>
int main ()
{
char str1[20];
char str2[20];
strcpy (str1,"To be ");
strcpy (str2,"or not to be");
strncat (str1, str2, 6);
puts (str1);
return 0;
}

 1.7 strncmp

int strncmp ( const char * str1, const char * str2, size_t num );

strncmp函数与上面差别并不大,结束的条件是比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。

// strncmp example 
#include <stdio.h>
#include <string.h>
int main ()
{
 char str[][5] = { "R2D2" , "C3PO" , "R2A6" };
 int n;
 puts ("Looking for R2 astromech droids...");
 for (n=0 ; n<3 ; n++)
 if (strncmp (str[n],"R2xx",2) == 0)
{
  printf ("found %s\n",str[n]);
}
 return 0;
}

 1.8 strstr

char * strstr ( const char *str1, const char * str2);

这个函数是判断你的字符串str2中第一次出现str1的指针,也就是查看str1是否为str2的子字符串。

// strstr example 
#include <stdio.h>
#include <string.h>
int main ()
{
 char str[] ="This is a simple string";
 char * pch;
 pch = strstr (str,"simple");
 strncpy (pch,"sample",6);
 puts (str);
 return 0;
}

 1.9 strtok

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

这个函数相对而言就是比较陌生了

sep参数是个字符串,定义了用作分隔符的字符集合。
第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。

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

 

// strtok example
#include <stdio.h>
#include <string.h>
int main ()
{
 char str[] ="- This, a sample string.";
 char * pch;
 printf ("Splitting string \"%s\" into tokens:\n",str);
 pch = strtok (str," ,.-");
 while (pch != NULL)
{
  printf ("%s\n",pch);
  pch = strtok (NULL, " ,.-");
}
 return 0;
}

又或:

#include <stdio.h>
int main()
{
 char *p = "123456789@hello.world";
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.10 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;
}
Edit & Run

 1.11 字符分类函数

函数如果他的参数符合下列条件就返回真
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任何可打印字符,包括图形字符和空白字符

字符转换

int tolower ( int c );
int toupper ( int c );

// isupper example 
#include <stdio.h>
#include <ctype.h>
int main ()
{
 int i=0;
 char str[]="Test String.\n";
 char c;
 while (str[i])
{
  c=str[i];
  if (isupper(c))
    c=tolower(c);
  putchar (c);
  i++;
}
 return 0;
}

1.12 memcpy

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

函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。这个函数在遇到 '\0' 的时候并不会停下来。如果source和destination有任何的重叠,复制的结果都是未定义的。

// memcpy example 
#include <stdio.h>
#include <string.h>
struct {
 char name[40];
 int age;
} person, person_copy;
int main ()
{
 char myname[] = "Pierre de Fermat";
 // using memcpy to copy string:
 memcpy ( person.name, myname, strlen(myname)+1 );
 person.age = 46;
 // using memcpy to copy structure:
 memcpy ( &person_copy, &person, sizeof(person) );
 printf ("person_copy: %s, %d \n", person_copy.name, person_copy.age );
 return 0;
}

 1.13 memmove

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

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

 

// memmove example 
#include <stdio.h>
#include <string.h>
int main ()
{
 char str[] = "memmove can be very useful......";
 memmove (str+20,str+15,11);
 puts (str);
 return 0;
}

1.14 memcmp

int memcmp ( const void * ptr1,const void * ptr2,size_t num );

比较从ptr1和ptr2指针开始的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;
}

 二. 函数的模拟实现

2.1 模拟实现strlen

对于strlen的模拟实现我们是有三种方法的:

//计数器方式
int my_strlen(const char * str)
{
int count = 0;
while(*str)
{
count++;
str++;
}
return count;
}
//递归
int my_strlen(const char * str)
{
if(*str == '\0')
return 0;
else
return 1+my_strlen(str+1);
}
//指针-指针的方式
int my_strlen(char *s)
{
   char *p = s;
   while(*p != ‘\0’ )
       p++;
   return p-s;
}

2.2 模拟实现strcpy

//1.参数顺序
//2.函数的功能,停止条件
//3.assert 断言指针是否有效
//4.const修饰指针,指针不可改变
//5.函数返回值
char *my_strcpy(char *dest, const char*src)
{
char *ret = dest;
assert(dest != NULL);
assert(src != NULL);
while((*dest++ = *src++))
{
    ;
}
return ret;
}

2.3 模拟实现strcat

char *my_strcat(char *dest, const char*src)
{
char *ret = dest;
assert(dest != NULL);
assert(src != NULL);
while(*dest)
{
dest++;
}
while((*dest++ = *src++))
{
;
}
return ret;
}

2.4 模拟实现strstr

对于strstr函数的实现是有一些复杂的,首先将str2的指针需要不断移动,str1找到了之后也需要移动,如果错误,则需要返回str1的初始地址,所以我们需要创建三个指针变量,代码如下:

char *  strstr (const char * str1, const char * str2)
{
    char *cp = (char *) str1;
    char *s1, *s2;
    if ( !*str2 )
      return((char *)str1);
    while (*cp)
   {
        s1 = cp;
        s2 = (char *) str2;
        while ( *s1 && *s2 && !(*s1-*s2) )
            s1++, s2++;
        if (!*s2)
            return(cp);
        cp++;
   }
    return(NULL);

2.5 模拟实现strcmp

实现strcmp则需要将dest与src的指针不断往后移动直到比较出大小为止,并且返回值相等时返回0,大于时返回1,小于时返回-1,代码如下:

int my_strcmp (const char * src, const char * dst)
{
    int ret = 0 ;
assert(src != NULL);
 assert(dest != NULL);
    while( ! (ret = *(unsigned char *)src - *(unsigned char *)dst) && *dst)
        ++src, ++dst;
    if ( ret < 0 )
        ret = -1 ;
    else if ( ret > 0 )
        ret = 1 ;
    return( ret );
}

2.6 模拟实现memcpy

实质上与strcpy差异并不大,需要注意我们并不知道传入的类型,需要采用void*指针,在后续步骤中要记得将类型转换为char*类型。

void * memcpy ( void * dst, const void * src, size_t count)
{
    void * ret = dst;
assert(dst);
 assert(src);
    /*
    * copy from lower addresses to higher addresses
    */
    while (count--) {
        *(char *)dst = *(char *)src;
        dst = (char *)dst + 1;
        src = (char *)src + 1;
   }
    return(ret);
}

这就是本章的内容,咱们下期再见!

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

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

相关文章

阿里测试员晒薪资条,看完真的扎心了...

前几天&#xff0c;有位老粉私信我&#xff0c;说看到某95后学弟晒出阿里的工资单&#xff0c;他是真酸了…想狠补下技术&#xff0c;努力冲一把大厂。 为了帮到他&#xff0c;也为了大家能在最短的时间内做面试复习&#xff0c;我把软件测试面试系列都汇总在这一篇文章了。 …

Windows10 把两张图片合并成一张图片

Windows10把两张图片合并成一张图片 文章目录Windows10把两张图片合并成一张图片1. 背景2. "画图"实现多图拼接1. 背景 相比截图功能&#xff0c;在 Google 的 Chrome 浏览器上&#xff0c;整页截屏功能仍需要安装额外的插件才能完成&#xff0c;这一点 微软的 bing…

【K3s】第18篇 详解 Kubernetes 调度器工作原理

目录 1、概述 2、kube-scheduler 3、kube-scheduler 调度流程 4、支持两种方式配置调度器的过滤和打分行为 4.1 调度策略 4.2 调度策略 5、总结 1、概述 在 Kubernetes 中&#xff0c;调度 是指将Pod放置到合适的节点上&#xff0c;以便对应节点上kubelet能够运行这些 P…

ChatGPT来临,架构师何去何从?

你好&#xff0c;我是李运华。 最近科技圈大火的事件就是ChatGPT的全球火热流行。这款由OpenAI公司在2022年11月底推出的聊天机器人&#xff0c;创造了史上消费应用最快达成1亿用户的历史&#xff0c;在推出仅仅两个月后&#xff0c;月活跃用户估计已达1亿&#xff0c;成为历史…

dbever连接kerberos认证的hive

文章目录一、本地安装kerberos客户端二、本地kerberos客户端登录三、dbever连接hive一、本地安装kerberos客户端 下载地址&#xff1a;https://web.mit.edu/kerberos/dist/index.html 安装&#xff1a;下一步或者自定义安装即可 安装后会自动生成配置文件&#xff1a;C:\Pro…

强化学习RL 01: Reinforcement Learning 基础

目录 RL理解要点 1. RL数学基础 1.1 Random Variable 随机变量 1.2 概率密度函数 Probability Density Function(PDF) 1.3 期望 Expectation 1.4 随机抽样 Random Sampling 2. RL术语 Terminologies 2.1 agent、state 和 action 2.2 策略 policy π 2.3 奖励 reward …

利用GPT-3 Fine-tunes训练专属语言模型

利用GPT-3 Fine-tunes训练专属语言模型 文章目录什么是模型微调&#xff08;fine-tuning&#xff09;&#xff1f;为什么需要模型微调&#xff1f;微调 vs 重新训练微调 vs 提示设计训练专属模型数据准备清洗数据构建模型微调模型评估模型部署模型总结什么是模型微调&#xff0…

cesium: 绘制线段(008)

第008个 点击查看专栏目录 本示例的目的是介绍如何在vue+cesium中绘制线段,左键点击开始绘制,右键点击取消绘制 直接复制下面的 vue+cesium源代码,操作2分钟即可运行实现效果. 文章目录 示例效果配置方式示例源代码(共139行)相关API参考:专栏目标示例效果 配置方式 1)…

Win11的两个实用技巧系列清理磁盘碎片、设置系统还原点的方法

Win11如何清理磁盘碎片?Win11清理磁盘碎片的方法磁盘碎片过多&#xff0c;会影响电脑的运行速度&#xff0c;所以需要定期清理&#xff0c;这篇文章将以Win11为例&#xff0c;给大家分享的整理磁盘碎片方法相信很多用户都会发现&#xff0c;随着电脑使用时间的增加&#xff0c…

一文了解 requestAnimationFrame

requestAnimationFrame 的基本使用 requestAnimationFrame 是什么 window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画&#xff0c;并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数&#xff0c;该回调函数会在浏…

想要实现无人机控制算法二次开发,一定不要错过这个matlab工具包

什么是Matlab工具包 Matlab工具包提供了无人机位置环控制算法、Simulink示例程序&#xff0c;通过Matlab ROS Toolbox建立Matlab&#xff08;Simulink&#xff09;与ROS之间的通信链路&#xff0c;Prometheus项目中将提供转为Matlab设计的控制接口&#xff0c;同时将继承Prome…

Scala-特质

特质 特质的混入用法 特质的叠加 特质和抽象类的区别 特质自身类型 特质 Scala 语言中&#xff0c;采用特质 trait&#xff08;特征&#xff09;来代替接口的概念&#xff0c;也就是说&#xff0c;多个类具有相同 的特质&#xff08;特征&#xff09;时&#xff0c;就可以…

ELK + Kafka 测试

配置file beat输出到 Kafkalogstash服务器从kafka获取数据并输出到es集群在es集群上查看索引kibana界面添加索引查看数据1.配置file beat输出到 Kafka 1.1 Filebeat机器配置数据采集和输出目标 做好域名解析 # vim /usr/local/filebeat/filebeat.yml # 修改输出目标为kafka…

CRM客户管理系统哪个好用?盘点前十名!

CRM客户管理系统排行&#xff1f;盘点前十名&#xff01; CRM客户管理系统是一种集成多种功能的软件系统&#xff0c;可以帮助企业跟进和管理客户关系、提高销售业绩、优化营销策略等。对于企业来说&#xff0c;选择一款适合自己的CRM系统非常重要&#xff0c;因为它能够直接影…

使用kotlin编写html dsl框架

前排提醒&#xff0c;这个框架就是我写着玩的&#xff0c;如果您已经会使用vue或其他前端框架&#xff0c;这篇文章可能对您没有什么意义。即使您不会如上提到的框架&#xff0c;也不要对该框架报有过高的期待&#xff0c;该框架更多的是&#xff0c;我自己的自娱自乐。 这里还…

新闻格式的演变及其对我们消费新闻方式的影响

这些年来&#xff0c;我们消费新闻的方式发生了翻天覆地的变化&#xff0c;从印刷报纸和广播新闻时代到 24 小时新闻频道和数字新闻平台时代。随着新闻媒体的发展&#xff0c;新闻呈现的格式也发生了变化。今天&#xff0c;新闻格式在新闻如何被受众传播和理解方面起着至关重要…

工程机械焊接件焊接结构件三维扫描检测外观质量控制-CASAIM三维扫描检测仪

焊接已发展为制造业中的一种重要的加工方法&#xff0c;广泛应用于航空、航天、冶金、石油、汽车制造以及国防等领域。工程机械焊接件品种繁多、几何形状复杂&#xff0c;焊接件质量的好坏将直接影响到产品的使用寿命长短。对焊缝表面尺寸测量及评定表面焊缝缺陷时&#xff0c;…

不到3天给公司省下近10万,低代码是真给老板省钱

早上领导开组会提了个似曾相识的需求&#xff0c;客户想要一个点餐系统。需求是用户登录后可以直接享有会员权益&#xff0c;还要提供外卖管理系统、配送系统、数据中台、BI数据分析、系统托管等O2O解决方案。 这是餐饮业典型的业务场景&#xff0c;如果缺乏必要的数字化工具支…

GitLab分支管理规范

GitLab 分支管理规范 本规范用于描述日常研发流程中关于 GitLab 上代码分支使用的规则, 大家共同严格遵守规范, 避免出现分支管理混乱现象, 保证日常的发版上线工作顺利进行。 Workspace: 工作区, 平时我们写代码的地方Index: 暂存区, 写完代码后让它变成的待提交的状态Repos…

《系统架构设计》-05-架构模型(Architecture Model)

文章目录1. 概述1.1 模型1.2 软件设计领域的架构模型2. 领域模型&#xff08;Domain Model&#xff09;2.1 概念2.2 示例3. 设计模型&#xff08;Design Model&#xff09;3.1 概念3.2 创建过程4. 代码模型4.1 概念4.2 创建过程5. 三种模型的关系1. 概述 1.1 模型 诠释&#…