【C语言航路】第十一站:字符串、字符和内存函数(中)

简介: 【C语言航路】第十一站:字符串、字符和内存函数

4.strcmp

(1)strcmp库函数文档

1.这个函数有两个参数都是const char*类型的,因为我们不会进行修改,我们只会进行查看

2.这个函数的作用是比较两个字符串的大小,比较规则是:从第一个字符依次开始比较,一个字符一个字符比较,谁的ASCII码值大,谁就大。如果相等则比较后一个字符。

3.这个函数的返回值是int类型,如果str1大于str2,则返回一个大于0的数,如果相等则返回0,如果小于则返回小于0的一个数

4.在vs环境下:大于返回1,等于返回0,小于返回-1。但是在其他编译器上不一定成立

(2)strcmp的使用

#include<stdio.h>
#include<string.h>
int main()
{
  char arr1[] = "abcdef";
  char arr2[] = "abc";
  int ret = strcmp(arr1, arr2);
  printf("%d\n", ret);
  return 0;
}

(3)strcmp的模拟实现

#include<stdio.h>
#include<assert.h>
int my_strcmp(const char* str1, const char* str2)
{
  assert(str1 && str2);
  while (*str1 == *str2)
  {
    if (*str1 == '\0')
    {
      return 0;
    }
    str1++;
    str2++;
  }
  if (*str1 > *str2)
  {
    return 1;
  }
  else
  {
    return -1;
  }
  //在一些编译器上是这样实现的
  //return *str1 - *str2;
}
int main()
{
  char arr1[] = "abcefg";
  char arr2[] = "abc";
  int ret = my_strcmp(arr1, arr2);
  printf("%d\n", ret);
  return 0;
}

注意事项(函数不安全的原因)

strcpy,strcat,strcmp这些函数都是不安全的函数,因为他们都是长度不受限制的函数,如果目标空间不是很大,则会出现问题。所以我们vs上使用这些函数需要使用开头的那个预处理指令

#define _CRT_SECURE_NO_WARNINGS 1

而为了让这些更安全,我们就有了strncpy,strncat,strncmp这些长度受限制的函数

5.strncpy

(1)strncpy库函数文档

这个函数有三个参数,char* destination,const char* source和size_t num,前两个参数的意思是目标空间的地址和源头的地址,num的意思是要拷贝几个字节

意思是拷贝前num个字节到destination中

返回类型是char*返回destination的地址

(2)strncpy的使用以及注意事项

1.拷贝num个字符从源字符串到目标空间。

2.如下图1所示,如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。

3.如下图2所示,我们在使用的时候要注意,不会将最后的\0给拷贝进来,需要拷贝几个字符就拷贝几个字符.

(3)strncpy的模拟实现

需要注意的事项是,由于拷贝受两个条件控制

如果num为0导致的结束的话,也就是拷贝了一部分,那么我们就不会补充\0

如果是由于源头字符串拷贝完了,但是num还没结束导致的结束,那么我们就要补充\0,要注意我们这个num是少减了一次1的,所以要前置--

第一个循环中,num和赋值的操作是不可以进行交换顺序的,这是因为&&的短路现象

#include<stdio.h>
#include<assert.h>
char* my_strncpy(char* dest, const char* src, size_t num)
{
  assert(dest && src);
  char* ret = dest;
  while (num && (*dest++ = *src++))
  {
    num--;
  }
  if (num)
  {
    while (--num)
    {
      *dest++ = '\0';
    }
  }
  return ret;
}
int main()
{
  char arr[] = "xxxxxxxxxxxxxxxxxxxxx";
  char* p = my_strncpy(arr, "hello", 10);
  printf("%s\n", p);
  return 0;
}

6.strncat

(1)strncat的库函数文档

这个的参数和返回类型与strncpy是一样的

不同的是函数的功能是追加source的前n的字符

这前n个字符追加后是需要补充一个\0的

(2)strncat的使用以及注意事项

1.对于这个函数,我们需要注意的就是追加之后后面会补充一个\0的

2.即便超出了source的范围,也只是补一个\0,不会补充多个\0

#include<stdio.h>
#include<string.h>
int main()
{
  char arr[] = "xxx\0xxxxxxxxxxxxxxx";
  char* p = strncat(arr, "hello", 3);
  return 0;
}

#include<stdio.h>
#include<string.h>
int main()
{
  char arr[] = "xxx\0xxxxxxxxxxxxxxx";
  char* p = strncat(arr, "hello", 10);
  return 0;
}

(3)strncat的模拟实现

#include<stdio.h>
#include<assert.h>
char* my_strncat(char* dest, const char* src, size_t num)
{
  assert(dest && src);
  char* ret = dest;
  while (*dest != '\0')
  {
    dest++;
  }
  while (num--)
  {
    if ((*dest++ = *src++) == '\0')
    {
      return ret;
    }
  }
  *dest = '\0';
  return ret;
}
int main()
{
  char arr[] = "xxx\0xxxxxxxxxxxxxxx";
  char* p = my_strncat(arr, "hello", 3);
  return 0;
}

7.strncmp

(1)strncmp的库函数文档

函数的功能是比较前n个字符的大小,返回一个值。

如果str1大,则返回大于0的数

如果str2大,则返回小于0的数

如果相等,则返回0

(2)strncmp的使用

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

#include<stdio.h>
#include<string.h>
int main()
{
  char* p1 = "abcdef";
  char* p2 = "abcfda";
  int ret = strncmp(p1, p2, 3);
  printf("%d\n", ret);
  return 0;
}

#include<stdio.h>
#include<string.h>
int main()
{
  char* p1 = "abcdef";
  char* p2 = "abcfda";
  int ret = strncmp(p1, p2, 4);
  printf("%d\n", ret);
  return 0;
}

#include<stdio.h>
#include<string.h>
int main()
{
  char* p1 = "abdef";
  char* p2 = "abcfda";
  int ret = strncmp(p1, p2, 4);
  printf("%d\n", ret);
  return 0;
}

8.strstr

(1)strstr库函数文档

这个函数有两个参数,都是const char* 类型的

功能是子str1中查找是否存在str2字符串

返回一个const char* 类型的地址,如果存在,则返回在str1中第一次出现str2的地址,如果不存在,则返回NULL

(2)strstr的使用

#include<stdio.h>
#include<string.h>
int main()
{
  char arr1[] = "abcdef";
  char arr2[] = "bcd";
  const char* ret = strstr(arr1, arr2);
  if (ret != NULL)
  {
    printf("%s\n", ret);
  }
  else
  {
    printf("找不到\n");
  }
  return 0;
}

#include<stdio.h>
#include<string.h>
int main()
{
  char arr1[] = "abcdef";
  char arr2[] = "bcdf";
  const char* ret = strstr(arr1, arr2);
  if (ret != NULL)
  {
    printf("%s\n", ret);
  }
  else
  {
    printf("找不到\n");
  }
  return 0;
}

(3)strstr的模拟实现

我们在这里采用的是暴力循环遍历的方法。当然也可以使用KMP算法,在此不做介绍了

#include<stdio.h>
#include<assert.h>
char* my_strstr(const char* str1, const char* str2)
{
  assert(str1 && str2);
  if (*str2 == '\0')
  {
    return (char*)str1;
  }
  const char* s1 = NULL;
  const char* s2 = NULL;
  const char* cp = str1;
  while (*cp)
  {
    s1 = cp;
    s2 = str2;
    while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
    {
      s1++;
      s2++;
    }
    if (*s2 == '\0')
    {
      return cp;
    }
    cp++;
  }
  return NULL;
}
int main()
{
  char arr1[] = "abcdef";
  char arr2[] = "bcdef";
  char* ret = my_strstr(arr1, arr2);
  if (ret != NULL)
  {
    printf("%s\n", ret);
  }
  else
  {
    printf("找不到\n");
  }
  return 0;
}

9.strtok

(1)strtok的库函数文档

这个函数有两个参数,str是目标字符串。它是需要被修改的

delimiters是一个字符串,定义了用作分隔符的集合

函数的功能是,在str字符串中找到,delimiter这个集合中的任意一个字符,然后将最先出现的这个字符修改为\0,并且返回分割好的这个字符串的地址,并且内部有一个静态变量记录之前切割的位置

这个str可以是一个null,如果传的是空指针的话,那么就在后面的字符串中找到集合中的某个元素,并且修改为\0,然后返回这个字符串。

(2)strtok的使用以及注意事项

第二个参数sep是个字符串,定义了用作分隔符的字符集合

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

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

strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。

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

如果字符串中不存在更多的标记,则返回 NULL 指针。

#include<stdio.h>
#include<string.h>
int main()
{
  char arr[] = "192.168.120.86#56789";
  char sep[] = ".#";
  char buf[30] = { 0 };
  strcpy(buf, arr);
  char* ret = NULL;
  for (ret = strtok(buf, sep); ret != NULL; ret = strtok(NULL, sep))
  {
    printf("%s\n",ret);
  }
  return 0;
}

10.strerror

(1)strerror的库函数文档

这个函数它会接受一个整型的数字,这个数字是一个错误码,然后返回一个字符串的地址,我们可以打印出这个字符串的地址,显示我们的错误信息

C语言在运行时,如果发生错误,就会将错误码存放到errno

这个变量中,而这个函数可以将错误码翻译成字符串

相关文章
|
8月前
|
安全 C语言 C++
比较C++的内存分配与管理方式new/delete与C语言中的malloc/realloc/calloc/free。
在实用性方面,C++的内存管理方式提供了面向对象的特性,它是处理构造和析构、需要类型安全和异常处理的首选方案。而C语言的内存管理函数适用于简单的内存分配,例如分配原始内存块或复杂性较低的数据结构,没有构造和析构的要求。当从C迁移到C++,或在C++中使用C代码时,了解两种内存管理方式的差异非常重要。
279 26
|
8月前
|
安全 C语言
C语言中的字符、字符串及内存操作函数详细讲解
通过这些函数的正确使用,可以有效管理字符串和内存操作,它们是C语言编程中不可或缺的工具。
395 15
|
存储 编译器 程序员
【C语言】内存布局大揭秘 ! -《堆、栈和你从未听说过的内存角落》
在C语言中,内存布局是程序运行时非常重要的概念。内存布局直接影响程序的性能、稳定性和安全性。理解C程序的内存布局,有助于编写更高效和可靠的代码。本文将详细介绍C程序的内存布局,包括代码段、数据段、堆、栈等部分,并提供相关的示例和应用。
667 5
【C语言】内存布局大揭秘 ! -《堆、栈和你从未听说过的内存角落》
|
存储 缓存 算法
【C语言】内存管理函数详细讲解
在C语言编程中,内存管理是至关重要的。动态内存分配函数允许程序在运行时请求和释放内存,这对于处理不确定大小的数据结构至关重要。以下是C语言内存管理函数的详细讲解,包括每个函数的功能、标准格式、示例代码、代码解释及其输出。
527 6
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
558 1
|
程序员 C语言 C++
【C语言基础】:动态内存管理(含经典笔试题分析)-2
【C语言基础】:动态内存管理(含经典笔试题分析)
|
程序员 编译器 C语言
【C语言基础】:动态内存管理(含经典笔试题分析)-1
【C语言基础】:动态内存管理(含经典笔试题分析)
|
Java 数据库连接 C语言
C语言进阶教程(内存分配常见问题分析)
C语言进阶教程(内存分配常见问题分析)
176 0
|
存储 缓存 算法
C语言内存问题详细分析之完善
C语言内存问题详细分析之完善
255 0
C语言内存问题详细分析之完善