字符.字符串操作函数和内存操作函数的总结

简介: 字符.字符串操作函数和内存操作函数的总结

前言:

字符,字符串操作函数和内存操作函数一直是我学习过程中思路特别乱,总是记不住的一块知识点,所以在这里进行一个系统的复习,方便进行复习和整理

1.字符篇:

1.字符函数:

1.分类:

字符函数主要包括两个类型的函数:

1.字符分类函数 2.字符转换函数

2.字符分类函数:

字符分类函数是专门用来进行字符分类的函数,这类函数的头文件统一引用#include<ctype.h>

这类函数的参数和返回类型都是相同的,其具体如下:

以islower为例:

int islower(char c)

其参数为相应的字符或者相应的字符对应的ASCII值,其返回值为:如果为假,则返回0,若为真,则返回1.

字符分类函数的具体函数类型如下:

1.islower:用来判断参数是否为小写字母

2.isupper:用来判断是否为大写字母

3.iscntrl:用来判断任何控制字符

4.isspace:用来判断空白字符

5.isdigit:用来判断十进制数字

6.isxdigit:用来十六进制数字

7.isalpha:用来判断字母A-Z或者a-z

8.isalnum:用来判断字母或者数字

9.ispunct:用来判断标点符号

10.isgraph:用来判断任何图形字符

11.isprint:用来判断任何可打印字符,包括图形字符和空白字符

#include<ctype.h>
int main()
{
  int a = islower('C');
  printf("%d", a);
}

结果如下:

C不是小写字母,所以判断返回为空,即0.

3.字符转换函数:

字符转换函数主要是负责将字符转换成其他字符的函数,其头文件也是#include<ctype.h>

这类函数的参数和返回类型都是相同的,具体如下:

int tolower(int c)

其参数为对应的字符的ASCII值,我们直接传入字符即可,返回值为转换完的字符的ASCII值,故我们直接拿一个char类型的变量接收即可。

字符转换函数的具体函数如下:

1.tolower:大写转小写字母的函数

2.toupper:小写转大写字母的函数

注意:如果本身传的就是对应的大写小写字符,则直接返回这个字符且不会做任何处理,例如toupper(A),则返回A,不进行任何操作。

#include<ctype.h>
int main()
{
  char c = 'A';
   c = tolower(c);
  printf("%c", c);
}

结果如下:

将大写字母A转换为a,执行过的结果如图。

2.字符串函数:

字符串函数的操作的主要对象就是字符串,其中包括对字符串字符个数的统计,对字符进行操作等等,下面让我们详解一下,并模拟一些常见字符串函数的模拟实现:

首先注意,字符串函数的头文件包含一律用:#include<string.h>

1.strlen函数:

stren函数是用来计算字符串中字符个数的函数,其具体的函数形式如下:

size_t strlen(const char*str)

strlen函数的特点,自动计数直到遇到’\0’为止,然后返回\0之前的字符串的字符个数。

注意:strlen不会把\0自己计入到字符的个数里,注意别搞错个数。

2.strcpy函数:

strcpy函数是用来拷贝字符串的函数,具体的函数形式如下:

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

此函数的第一个参数是拷贝的目的地,第二个参数是拷贝的源字符串,这个函数最后会返回拷贝完后的字符串的指针。

strcop函数的特点:1.strcpy函数拷贝时,\0也会被拷贝过去,且当把\0拷贝过去后,整个拷贝也结束了

2.目标空间必须要足够大,以确保能存放原字符串,目标空间必须可变,不能是常量字符串

3.倘若源字符串为空,则直接返回第目标字符串的地址

模拟实现如下:

#include<string.h>
char* my_strcpy(char* dest, const char* src)//第一个参数为目标空间,第二个参数为源字符串
{
   char*tmp=dest;
  if (src == NULL)
  {
    return dest;
  }
  while (*dest++ = *src++)//这里很巧妙,不仅仅可以在\0处停止,同时还可以把\0也拷贝到目标字符串中,一举两得的一步
  {
    ;
  }
  return tmp;
}
int main()
{
  char arr1[] = "ABCDEF";
  char arr2[] = "HE";
  printf("%s", my_strcpy(arr1, arr2));
}

结果如下:

即将HE拷贝到目标字符串中,且拷贝\0,所以我打印第一个字符串的时候遇到\0就自动停止了

3.strcat函数:

strcat是用来追加在字符串后面追加字符串的函数,具体的函数形式如下:

charstract(chardestination,char*source)

此函数的参数和strcpy基本类似,也是传入一个目的字符串和一个源字符串,返回的类型也是追加后的目的字符串的首元素的地址。

strcat的函数特点:

1.strcat会从目的地字符串的\0开始,向后继续将想要追加的字符串追加上来,且会覆盖这个\0,字符串的追加也会在遇到\0后停止。

2.目标空间必须要足够大,方便源字符串追加,否则会越界访问。

3.目标空间必须是可修改的。

模拟实现的过程如下:

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;
}

与strcpy函数模拟实现不同的是,它首先需要找到目的字符串的\0,然后从这个\0开始覆盖,直到遇到源字符串的\0为止。

注意!!!!!!:strcat函数是不能追加自己本身的,因为由于覆盖,追加自己的\0被覆盖了,导致循环不能停止,所以strcat不能追加自己,但是strncat限制长度的就可以追加。

4.strcmp函数:

strcmp适用于进行字符串的比较,比较的基准是字符串对应的ASCII值得先后,其基本的函数具体形式如下:

int strcmp(const charsrc,const chardest)

同前面的strcpy和strstr一样,strcmp传参也是传入两个字符串的地址,然后依次比较这两个字符串相应位置的字符的大小,一旦比较出来直接返回结果,不进行后续的比较了,倘若一直没结果就直到得到dest的\0为止。

返回值:1.当字符串1的字符小于字符串2的字符时,返回值<0

2. 当字符串1的字符等于字符串2的字符时,返回值=0

3.当字符串1的字符大于字符串2的字符时,返回值>0

模拟实现的过程如下:

int my_strcmp(const char* str1, const char* str2)
{
  assert(str1);
  assert(str2);
  while (*str1 == *str2)//这里的逻辑很妙,循环用两个相等,这样就可以处理相等但长度不相等的情况
  {
    if (*str1 == '\0')
    {
      return 0;
    }
    str1++;
    str2++;//注意,这里str1和str2是同时变化的,所以我判断一个为\0的时候,由于相等,则另一个也为\0了,故相等。
  }
  return *str1 - *str2;
}

注意思考这段的代码的逻辑,假如两者一直相等直到\0时,则str1到\0的时候,另一个也到\0了,此时两者是相等的,但假若一个到\0一个,没到,则就会按不相等跳出循环,然后直接比较两个字符串ASCII值,这便是strcmp的比较逻辑。

5.strncpy,strncat,strncmp函数:

这三个函数可以理解为限制长度的strcpy,strcat,strcmp函数,用法相同只是限制长度,在这里就不过多赘述了。

特别注意的是:此时的strnact是可以自己追加自己的,但要控制好长度,让其正好在\0之前一位停止。

6.strstr函数:

strstr函数是用来寻找目的字符串内部是否有子字符串的,其函数的具体格式如下:

charstrstr(const charstr1,const char*str2)

第一个参数为目的字符串,第二个参数为源字符串,这里的目的就是判断目的字符串内部是否有源字符串。

倘若寻找到,则返回目的字符串中的源字符串的第一个元素的地址,反之,如果找不到,就返回NULL。

strstr函数的一些细节:若strstr中的目的字符串str2中只有\0,则返回str1即目的字符串的地址。

模拟实现的代码如下:

char* my_strstr(const char*str1,const char*str2)
{
      if(str2== NULL)
      {
         return str1;
      }
      char*s1=NULL;
      char*s2=NULL;
      char*cp=str1;
      while(*cp)
      {
           s1=cp;//倘如循环结束,cp++,然后重置s1的位置重新进行按次序比较的过程,这样就能让s1实时记录cp之后的位置
           s2=str2;
           while(*s1==*s2)
           {
               s1++;
               s2++;
               if(*s2=='\0')
               {
                 return cp;
               }
           }
          cp++;
      }
      return NULL;
}

注意,这个函数模拟实现的逻辑,首先定义s1和s2,s2作为源字符串的起始地址,s1作为目的字符串的起始地址,控制cp的进度向后移动,倘如cp到了\0证明没有子字符串,直接返回NULL,每次对s1都开始刷新为cp,s2刷新为str2的值,一旦进入相等的情况,s1和s2就向后移动去寻找,倘若s2到达\0证明有子字符串,反之就直接跳出循环,进行下一次循环即可。

KMP算法后续我会学完之后解析一下!!!!!!

7.strtok函数:

strtok是一个非常怪异的函数,其目的主要是用来切割字符串,它的具体函数参数如下:

charstrtok(charstr,const char*sep)

函数解析:

1.第一个参数指定一个字符串,它包含0个或者多个由sep字符串中的一个或多个分隔符分割的标记。

2.第二个参数指定另一个字符串,它包含了用作分隔符的各种符号的字符串集合

3.strtok函数找到str中的下一个标记,并将其用\0结尾,返回一个指向这个标记的指针(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝内容并且可以修改)

3.strtok函数的第一个参数不能为NULL,函数将找到str中的第一个标记,并且会保存它的位置(存\0的地址)

4.strtok函数的第一个参数为NULL,函数将在同一个字符串中被保存的位置开始,查找下一个标记

5.如果不存在更多的标记,则返回NULL指针。

下面是一个strtok函数的应用实例:

char* r = NULL;
for (r = strtok(buf, p); r != NULL; r = strtok(NULL, p))
{
  printf("%s\n", r);
}

由于其strtok会自动保存上一次分隔符的地址,所以我们除了第一次传字符串的地址外,后续传只要传NULL即可,然后让strtok从上一次的位置开始找下一个分隔符。

8.sterror和perror函数:

这两个函数都是可以用来判断错误的函数,sterror函数引用头文件为:#include<stdio.h>,当我们在调用某些库函数时一旦失败就会设置错误码,这些错误码errno是全局的,引用头文件#include<errno.h>,这些错误码通过sterror函数就可以由数字变成相应的字符串。

sterror的函数具体格式:

char* sterror(int errno)

当我们使用perror则比sterror更加智能,它可以直接输出相应的错误,只需要传入相应的文件名即可。

perror的函数格式:

void perror("文件名“)

sterror和perror的关系就是:
perror==printf(”文件名:%s\n",sterror(errno));

在我们动态开辟和操作文件的时候,经常要使用perror来进行测试和对报错进行判断。

2.内存篇:

内存操作之所以和字符串操作放在一起,主要是因为两者有很多共通之处,所以接下来让我们再看看内存操作函数:

!!!!!必须要强调,在内存操作函数里,size_t类型规定的都是字节数,即内存操作的基本单位而不是元素个数,这个别搞错,千万别搞错!!!!

1.memcpy函数:

如同strcpy函数一样,不过不同的是,这一次操作的是内存而非字符串。且memmcpy是不能用拷贝重叠的内存的,那个需要用memmove函数

基本格式:

void* memcpy(voiddestination,const void source,int num)

参数的分析如下:memcpy是可以拷贝任何类型的数据的,所以我们用void*,void来规定其类型,memcpy拷贝了num个字节的数据就会停止,即将source的数据拷贝到destination中,对于void*,void类型的数据,我们完全可以将其强制转换转为我们想要的数据类型。拷贝数据我们虽然不知道数据类型是什么,但只要利用char类型数据一个一个拷贝即可,正好对应一个字节。

函数实现如下:

void * memcpy ( void * dst, const void * src, size_t count)
{
  void * ret = dst;
   assert(dst);
  assert(src);
  while (count) 
  {
    *(char *)dst = *(char *)src;
    dst = (char *)dst + 1;//注意,一定要先将其转化为char*类型后,在对其相加减
    src = (char *)src + 1;
    count--;
  }
  return ret;
}

2.memcmp函数:

memcmp是用来进行内存比较的,整体的思路与strcmp大体相同,但不同的地方在于,它是一个一个字节比较的而非一个字符一个字符去比较的,

格式如下:

int memcmp(const voidptr1,const voidptr2,size_t num)

模拟实现的函数如下:

int memcmp(const void*ptr1,const void*ptr2,size_t num)
{
  assert(ptr1);
  assert(ptr2);
   while(*((char*)ptr1)==*((char*)ptr2),num)
   {      
           (char*)ptr1)++;
           (char*)ptr2)++;
           num--;
   }
    return *((char*)ptr1)-*((char*)ptr2);
}

3.memmove函数:

memmove函数是用来对内存数据自身进行覆盖的函数,其格式如下:

void* memmove(voidddestination,const voidsource,size_t num)

!!!!拷贝的思路就是!!!!:
if(dest<src):
数据从前向后拷贝
else
数据从后向前拷贝

具体实现的代码如下:

void* my_memmove(void* dest, void* src, int num)
{
  void* ret = dest;
  assert(src);
  assert(dest);
  if (dest < src)//当dest<src时,从前向后操作
  {
    while(num--)
    {
      *(char*)dest = *(char*)src;
      dest = (char*)dest + 1;//注意,先转化其类型再进行整数加减操作
      src = (char*)src + 1;
    }
  }
  else//反之,当dest>=src时,从后向前操作
  {
    while(num--)
    {
      *((char*)dest + num) = *((char*)src + num);//从后向前操作,利用num调整即可
    }
  }
  return ret;
}

4.memset函数:

memset函数是用来将空间指定范围的位置改成相应的符号的函数,且这里指定的字节数而不是元素的个数

函数基本格式:

void* memset(void*ptr,int value,size_t num)

格式解析:注意,这里的value绝对不仅仅是数字,它也可以是相应的字符,只是这里直接转化为ASCII值形式展现出来的参数,实际上我们传字符也可以,传数字也可以,然后就会把ptr内部的对应num个字节数的内容改成我们相应的value。

3.总结:

内存操作和字符串操作函数的模拟实现对于代码能力有很强的提升,同时也希望通过这几个函数能够进一步提高我们学习函数时的思路,即看参数,看返回值,看实现功能三个方面,后续我也会更新KMP算法的内容,希望经过这篇文章,我们对于内存和字符串,字符的操作能力能不断增强!!!!!

目录
相关文章
|
1月前
|
程序员 C语言
C语言库函数 — 内存函数(含模拟实现内存函数)
C语言库函数 — 内存函数(含模拟实现内存函数)
33 0
|
19天前
|
C语言
C语言:内存函数(memcpy memmove memset memcmp使用)
C语言:内存函数(memcpy memmove memset memcmp使用)
|
22天前
|
编译器 C语言
字符串与内存函数
字符串与内存函数
26 0
|
5天前
|
存储 编译器 C语言
C语言:字符函数 & 字符串函数 & 内存函数
C语言:字符函数 & 字符串函数 & 内存函数
12 2
|
8天前
|
编译器
练习使用动态内存相关的4个函数:malloc、calloc、realloc、free
在了解使用动态内存相关的四个函数之前,我们先了解一下,为什么要有动态内存分配?
15 0
|
8天前
|
存储 编译器 C++
【C++】内存管理和模板基础(new、delete、类及函数模板)
【C++】内存管理和模板基础(new、delete、类及函数模板)
22 1
|
8天前
|
编译器 C语言 C++
详解内存操作函数
详解内存操作函数
|
1月前
|
编译器 C++
C++ 解引用与函数基础:内存地址、调用方法及声明
C++ 中的解引用允许通过指针访问变量值。使用 `*` 运算符可解引用指针并修改原始变量。注意确保指针有效且不为空,以防止程序崩溃。函数是封装代码的单元,用于执行特定任务。理解函数的声明、定义、参数和返回值是关键。函数重载允许同一名称但不同参数列表的函数存在。关注公众号 `Let us Coding` 获取更多内容。
138 1
|
1月前
|
Java 程序员 编译器
C语言中灵活多变的动态内存,malloc函数 && free函数&& calloc函数 && realloc函数
C语言中灵活多变的动态内存,malloc函数 && free函数&& calloc函数 && realloc函数
|
18天前
|
Linux
Linux rsyslog占用内存CPU过高解决办法
该文档描述了`rsyslog`占用内存过高的问题及其解决方案。
41 4