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

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

前言:

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

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算法的内容,希望经过这篇文章,我们对于内存和字符串,字符的操作能力能不断增强!!!!!

目录
相关文章
|
2月前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
36 3
|
1天前
|
存储 缓存 算法
【C语言】内存管理函数详细讲解
在C语言编程中,内存管理是至关重要的。动态内存分配函数允许程序在运行时请求和释放内存,这对于处理不确定大小的数据结构至关重要。以下是C语言内存管理函数的详细讲解,包括每个函数的功能、标准格式、示例代码、代码解释及其输出。
22 6
|
21天前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
40 6
|
2月前
|
程序员 C++ 容器
在 C++中,realloc 函数返回 NULL 时,需要手动释放原来的内存吗?
在 C++ 中,当 realloc 函数返回 NULL 时,表示内存重新分配失败,但原内存块仍然有效,因此需要手动释放原来的内存,以避免内存泄漏。
|
2月前
|
存储 程序员 编译器
C语言——动态内存管理与内存操作函数
C语言——动态内存管理与内存操作函数
|
2月前
|
编译器 C语言 C++
详解C/C++动态内存函数(malloc、free、calloc、realloc)
详解C/C++动态内存函数(malloc、free、calloc、realloc)
211 1
|
2月前
|
存储 C语言
【c语言】字符串函数和内存函数
本文介绍了C语言中常用的字符串函数和内存函数,包括`strlen`、`strcpy`、`strcat`、`strcmp`、`strstr`、`strncpy`、`strncat`、`strncmp`、`strtok`、`memcpy`、`memmove`和`memset`等函数的使用方法及模拟实现。文章详细讲解了每个函数的功能、参数、返回值,并提供了具体的代码示例,帮助读者更好地理解和掌握这些函数的应用。
27 0
|
2月前
|
C语言 C++
c语言回顾-内存操作函数
c语言回顾-内存操作函数
41 0
|
2月前
|
存储 C语言 C++
来不及哀悼了,接下来上场的是C语言内存函数memcpy,memmove,memset,memcmp
本文详细介绍了C语言中的四个内存操作函数:memcpy用于无重叠复制,memmove处理重叠内存,memset用于填充特定值,memcmp用于内存区域比较。通过实例展示了它们的用法和注意事项。
71 0
|
2月前
一刻也没有为它哀悼~接下来登场的是动态内存分配的malloc与realloc以及free函数
一刻也没有为它哀悼~接下来登场的是动态内存分配的malloc与realloc以及free函数
77 0