字符串函数与内存函数(一)

简介: 字符和字符串的库函数的使用和注意事项

字符串函数与内存函数总汇(讲解的)


  1. 求字符串长度:strlen
  2. 长度不受限制的字符串函数:strcpy strcat strcmp
  3. 长度受限制的字符串函数:strncpy strncat strncmp
  4. 字符串查找:strstr strtok
  5. 错误信息报告:strerror
  6. 内存操作函数:memcpy memmove memset memcmp


写在前面的话:


C语言本身是没有字符串类型的


字符串通常放在常量字符串(不做修改)中或者字符数组(可以修改)中来处理


字符串函数介绍


strlen(字符串长度计算函数)

定义:


size_t strlen ( const char * str );


注意:

  • 字符串以 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包 含 '\0' )
  • 参数为字符指针,且是指向的字符串必须要以 '\0' 结束的指针(否则会出错)
  • size_t 的定义为 typedef unsigned int size_t;即 strlen 函数返回类型为无符号整型


易错题:


#include <stdio.h>
#include <string.h>
int main()
{
  const char* str1 = "abcdef";
  const char* str2 = "abc";
  if (strlen(str2) - strlen(str1) > 0)
  {
    printf("呵呵\n");
  }
  else
  {
    printf("哈哈\n");
  }
  return 0;
}
//输出结果:呵呵
//解释:对于strlen函数返回的是无符号整形,无符号数的加减还是无符号数(永远大于0)


  • 函数演示:


#include<stdio.h>
#include<string.h>
int main()
{
  char ch[] = "abcdef";
  printf("%d\n", strlen(ch));
  return 0;
}
//输出结果:6


  • 模拟实现:


#include<stdio.h>
#include<assert.h>
size_t my_strlen(const char* str)
//对于传入的指针我们并希望对它指向的对象的内容进行修改,我们可以用const进行修饰
{
  assert(str);//传入指针不能为NULL,记得包含头文件<assert.h>
  size_t len = 0;
  while (*str++)//*的优先级高于后置++,如果为'\0'则不进入循环('\0'的ASCII码值为0)
  {//进入后指针str++
    len++;
  }
  return len;
}


strcpy/strncpy(字符串拷贝函数)

  • 定义:


char *strcpy( char *strDestination//目标字符串//, 
const char *strSource//来源字符串);
char *strncpy( char *strDest, const char *strSource, size_t count//拷取长度(字节为单位));


区别:

strcpy:一直拷贝到'\0'(包括'\0')(并不安全)


strncpy:参数count用来控制拷贝字符串的长度(如果超过了源字符串的长度则会在拷完后再补0)(相对安全)


注意:

  1. 源字符串必须以 '\0' 结束
  2. 目标空间必须足够大(以确保能存放源字符串),且必须可修改


函数演示:


#include <stdio.h>
#include <string.h>
int main()
{
  char str1[] = "Hello world!";
  char str2[20] = { 0 };
  char str3[20] = { 0 };
  strcpy(str2, str1);
  strncpy(str3, str1, 2);
  printf("%s\n", str2);
  printf("%s\n", str3);
  return 0;
}
//输出结果:
//Hello world!
//He


  • 模拟实现:


char* my_strcpy(char* des, const char* src)
{
  assert(des&&);//指针不为NULL
  char* ret = des;//记录字符串首元素地址
  while (*des++ = *src++)//先*指针进行赋值,赋值后对des指向的内容进行判断,再进行指针++
  {
    ;
  }
  return ret;//返回字符串首元素地址
}
#include<assert.h>
char* my_strncpy(char* des, const char* src, int n)
{
  assert(des && src);
  char* ret = des, *cp = (char*)src;
  int count = 0;
  while (*cp++)//计算源字符串长度
  {
    count++;
  }
  for (int i = 0; i < n; i++)
  {
    if (i < count)
    {
      *des++ = *src++;
    }
    else//拷贝个数大于长度补0
    {
      *des++ = '0';
    }
  }*des = '\0';//末尾加上结束符
  return ret;
}


strcat/strncat(字符串拼接函数)

  • 定义:


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


区别:

strcat:将源字符串拼接到目标字符串后面(从目标字符串的'\0'位置开始拼接,一直拼接到源字符串'\0'处,包括'\0')


strncat:参数count用来控制拼接字符个数(大于源字符串个数也只拼接到源字符串的'\0')


注意:

  1. 源字符串必须以 '\0' 结束
  2. 目标空间必须足够大(以确保能存放拼接的字符) ,且必须可变


函数演示:


#include <stdio.h>
#include <string.h>
int main()
{
  char str1[100] = "hello ";
  char str2[100] = "hello ";
  char str3[] = "world!";
  strcat(str1, str3);
  strncat(str2, str3, 4);
  printf("%s\n", str1);
  printf("%s\n", str2);
  return 0;
}
//输出结果:
//hello world!
//hello worl


  • 模拟实现:


char* my_strcat(char* des, const char* str)
{
  assert(des && str);
  char* ret = des;
  //找到des最后一个字符
  while (*des)
  {
    des++;
  }
  //将str中字符串拷贝至des末尾
  while (*des++ = *str++)
  {
    ;
  }
  return 0;
}
char* my_strncat(char* des, const char* str, size_t n)
{
  assert(des && str);
  char* ret = des;
  while (*des)
  {
    des++;
  }
  for (size_t i = 0; i < n && *str; i++)
  {
    *des++ = *str++;
  }
  *des = '\0';//末尾追加结束符
  return ret;
}


strcmp/strncmp(字符串比较函数)

  • 定义:


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


区别:

strcmp:比较每个字符对应的ASCII码值大小


strncmp:参数count控制了比较字符数量(比较字符个数不超过两字符串个数(含'\0')较少者)


标准规定:

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


  1. 第一个字符串等于第二个字符串,则返回0


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


注意:

比较到出现另个字符不一样或者一个字符串结束或者count个字符全部比较完


函数演示:


#include <stdio.h>
#include <string.h>
int main()
{
  char str1[] = "abcd";
  char str2[] = "abcd";
  char str3[] = "accd";
  printf("%d\t", strcmp(str1, str2));
  printf("%d\t", strcmp(str1, str3));
  printf("%d\t", strncmp(str1, str2,2));
  printf("%d\t", strncmp(str1, str3,2));
  return 0;
}
//输出结果:0    -1    0    -1


  • 模拟实现:


int my_strcmp(const char* str1, const char* str2)
{
  assert(str1 && str2);
//字符相等就比较下一个,中间如果存在两字符不相等则结束遍历
  while (*str1 && *str2 && *str1 == *str2)
  {
    str1++;
    str2++;
  }
//结果返回两者ASCII码差值
  return (*str1 - *str2);
}
int my_strncmp(const char* str1, const char* str2, size_t n)
{
  assert(*str1 && *str2);
  for (size_t i = 0; i < n - 1  && *str1 && *str2; i++)
  {
//不同时直接结束,再返回此刻两者的差值
    if (*str1 != *str2)
      break;
    str1++;
    str2++;
  }
//或结束遍历后(n-1次)直接比较最后一位字符
  return (*str1 - *str2);
}


strstr(字符串查找函数)

  • 定义:


char *strstr( const char *string, const char *strCharSet );


注:如果字符串strCharSet在string出现,则返回string中第一次出现该字符串的首地址,否则返回NULL


  • 模拟实现:
  1. 首先在str1中找到与str2首字符相同的字符,后对str2后面的字符进行逐个比较
  2. 如果在后续逐个比较过程中出现了不同的字符,这时候就需要str1返回到之前刚开始对字符比较的地方的后一个位置再进行比较,且str2需要返回到首字符
  3. 如果在后续逐个比较过程中,str2指向的字符为\0这就代表在str1中找到了str2这个字符串,则返回str2首字符对应于str1所在的地址
  4. 还有则是遍历后str1指向的字符为\0(在没满足str2指向的字符为\0时),这就表示在str1中找不到str2这个字符串,则返回NULL


char* my_strstr(const char* str1, const char* str2)
{
  assert(str1 && str2);
  if (*str2 == '\0')
    return (char*)str1;//如果str2指向的对象如:char ch[]="";(即内容为'\0')直接返回str1
  const char* s1;
  const char* s2;
  const char* p = str1;
//str1指向的对象内容不为'\0'则进入循环
  while (*p)
  {
//进入循环调整s2,s1位置
    s1 = p;
    s2 = str2;
//相同则进行遍历
    while (*s1 && *s2 && *s1 == *s2)
    {
      s1++;
      s2++;
    }
//s2遍历到结束符即找到了,返回此次开始的地址
    if (*s2 == '\0')
    {
      return (char*)p;
    }
//(上面条件不满足)s1到了'\0'则说明开始位置p都到了结束符,那么已经找不到了
    if (*s1 == '\0')
    {
      return NULL;
    }
//此次查找没有找到,则使开始位置p指向下一个位置
    p++;
  }
  return NULL;
}


strtok(字符串切分函数)

  • 定义:


char *strtok( char *str//被切分对象, 
const char *sep//分隔符号集合);


注意:

  1. 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记
  2. strtok函数找到str中的首个分隔符,并将其用'\0'替代,且返回分隔符前一个字符串首元素地址
  3. strtok函数的第一个参数不为 NULL ,函数将找到strToken中首个分隔符,strtok函数会记忆该分隔符后一个字符的位置
  4. strtok函数的第一个参数为NULL,函数将在同一个字符串中被记忆的位置开始,查找下一个分隔符
  5. 如果字符串中不存在更多的标记,则返回 NULL 指针


函数演示:


#include <stdio.h>
#include <string.h>
int main()
{
  char str[] = "- This, a sample string.";
  char* pch;
  pch = strtok(str, " ,.-");
  while (pch != NULL)
  {
    printf("%s\t", pch);
    pch = strtok(NULL, " ,.-");
  }
  return 0;
}
//输出结果:This    a    sample    string
#include <stdio.h>
#include <string.h>
int main()
{
  char* p = "abcde@163.com";
  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\t", str);
  }
}
//输出结果:abcde    163    com


  • 模拟实现:


char* strtok (char* str, const char* delim)
{
  // 生成替换字符表
  char table[256] = {0};
    while (*delim != '\0')
    {
        table[*delim] = 1;
        delim++;
    }
  // 使用 static 类型指针保存上一次函数调用时的字符串地址
  static char* pstr = NULL;
  if (str != NULL)
  {
    pstr = str;
  }
  // 保证 pstr 指向以非替换字符为首的子字符串
  while (*pstr != '\0' && table[*pstr] == 1)
  {
    pstr++;
  } 
  // ret 保存返回子字符串的首地址
  char* rst = (*pstr != '\0') ? pstr : NULL;
  while (*pstr != '\0')
  {
        if (table[*pstr] == 1)
        {
            // 切割得到子字符串,且 pstr 最后指向子字符串的下一字符
            *pstr++ = '\0';
            break;
        }
        else
        {
            pstr++;
        }
  }
  return rst;
}


strerror(返回错误原因的描述字符串函数)

  • 定义:


char *strerror( int errnum );


  • 区别:

strerror:从内部数组中搜索错误号 errnum,并返回一个指向错误消息字符串的指针

perror:打印+strerror(直接打印出来)


  • 函数演示:


#include <stdio.h> 
#include <string.h> 
int main() 
{ 
    FILE *stream; 
    if ((stream=fopen("crt_fopen.c", "r")) == NULL) //以读取的方式打开文件
    { 
        perror("perror says open failed"); 
        printf("strerror says open failed: %s\n", strerror(errno)); 
    }
    else 
    { 
        printf("open succeeded on input file\n"); 
        fclose(stream); 
    } 
    return 0; 
}


注:通过fopen()函数打开指定的文件,如果打开该文件失败,则fopen()函数的返回值是NULL,此时可以通过perror()函数或者strerror()函数显示错误信息

相关文章
|
1月前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
34 3
|
1月前
|
程序员 C++ 容器
在 C++中,realloc 函数返回 NULL 时,需要手动释放原来的内存吗?
在 C++ 中,当 realloc 函数返回 NULL 时,表示内存重新分配失败,但原内存块仍然有效,因此需要手动释放原来的内存,以避免内存泄漏。
|
1月前
|
存储 程序员 编译器
C语言——动态内存管理与内存操作函数
C语言——动态内存管理与内存操作函数
|
1月前
|
编译器 C语言 C++
详解C/C++动态内存函数(malloc、free、calloc、realloc)
详解C/C++动态内存函数(malloc、free、calloc、realloc)
176 1
|
1月前
|
程序员 C语言
C语言内存函数精讲
C语言内存函数精讲
|
27天前
|
存储 C语言
【c语言】字符串函数和内存函数
本文介绍了C语言中常用的字符串函数和内存函数,包括`strlen`、`strcpy`、`strcat`、`strcmp`、`strstr`、`strncpy`、`strncat`、`strncmp`、`strtok`、`memcpy`、`memmove`和`memset`等函数的使用方法及模拟实现。文章详细讲解了每个函数的功能、参数、返回值,并提供了具体的代码示例,帮助读者更好地理解和掌握这些函数的应用。
23 0
|
1月前
|
C语言 C++
c语言回顾-内存操作函数
c语言回顾-内存操作函数
40 0
|
1月前
|
存储 C语言 C++
来不及哀悼了,接下来上场的是C语言内存函数memcpy,memmove,memset,memcmp
本文详细介绍了C语言中的四个内存操作函数:memcpy用于无重叠复制,memmove处理重叠内存,memset用于填充特定值,memcmp用于内存区域比较。通过实例展示了它们的用法和注意事项。
66 0
|
1月前
一刻也没有为它哀悼~接下来登场的是动态内存分配的malloc与realloc以及free函数
一刻也没有为它哀悼~接下来登场的是动态内存分配的malloc与realloc以及free函数
70 0
|
3月前
|
存储 编译器 C语言
【C语言篇】数据在内存中的存储(超详细)
浮点数就采⽤下⾯的规则表⽰,即指数E的真实值加上127(或1023),再将有效数字M去掉整数部分的1。
379 0