strtok

  1. 函数原型与功能概述
  • 在 C 语言中,strtok函数的原型为char *strtok(char *str, const char *delim)
  • 它的主要功能是将字符串str按照delim中指定的分隔符进行分割,返回被分割出来的子字符串。
  1. 工作原理
  • 首次调用
  • 当第一次调用strtok时,需要将待分割的字符串str作为第一个参数传入。strtok函数会在str中查找第一个不是分隔符(由delim指定)的字符,然后从这个字符开始,找到下一个分隔符(或者字符串结束符\0),将分隔符替换为\0,并返回这个子字符串的指针。
  • 后续调用
  • 对于后续的调用,第一个参数需要传入NULLstrtok函数会从上一次分割的位置之后继续查找下一个子字符串,同样将找到的分隔符替换为\0,并返回新的子字符串指针。
  • 分割过程结束
  • 当没有更多的子字符串可分割时,strtok返回NULL
  1. 示例代码
  • 例如,将一个逗号分隔的字符串进行分割:
#include <stdio.h>
   #include <string.h>

   int main() {
       char str[] = "apple,banana,cherry";
       char *token;

       token = strtok(str, ",");
       while (token!= NULL) {
           printf("%s\n", token);
           token = strtok(NULL, ",");
       }

       return 0;
   }


  • 在这个示例中,首先将str作为第一个参数调用strtok得到第一个子字符串("apple"),然后在循环中不断传入NULL作为第一个参数调用strtok,依次得到 "banana" 和 "cherry",直到strtok返回NULL,循环结束。


  1. 注意事项
  • strtok函数会修改原始字符串,将分隔符替换为\0
  • 它不是线程安全的,如果在多线程环境下需要对字符串进行分割,应该考虑使用可重入的替代函数(如strtok_r,它是strtok的可重入版本,在一些多任务操作系统中使用)。

字符函数和字符串函数和内存函数_入门

strerror

  1. 函数原型与功能概述
  • 在 C 语言中,strerror函数的原型为char *strerror(int errnum)
  • 其功能是根据给定的错误码errnum,返回一个描述该错误的字符串。这个函数主要用于将系统调用或库函数返回的错误码转换为人类可读的错误信息字符串。
  1. 错误码的来源
  • 在 Unix 和类 Unix 系统(如 Linux)中,系统调用和很多库函数在执行失败时会返回一个特定的错误码(通常是一个小的整数)。例如,open函数在无法打开一个文件时可能返回 - 1,并设置全局变量errno为一个对应的错误码(如ENOENT表示文件不存在,EACCES表示权限不足等)。
  1. 示例代码
  • 以下是一个简单的示例,展示如何使用strerror函数:
#include <stdio.h>
   #include <string.h>
   #include <errno.h>

   int main() {
       FILE *fp = fopen("nonexistent_file.txt", "r");
       if (fp == NULL) {
           int err = errno;
           printf("Error opening file: %s\n", strerror(err));
       }
       return 0;
   }


  • 在这个示例中,尝试打开一个不存在的文件。fopen函数返回NULL表示失败,然后通过errno获取错误码,并使用strerror函数将错误码转换为相应的错误描述字符串并打印出来。


  1. 可移植性注意事项
  • 虽然strerror函数在大多数 Unix 和类 Unix 系统以及一些其他操作系统上都存在,但不同操作系统对于相同错误码的错误描述可能会有细微差异。
  • 在一些多线程环境下,对errno变量(用于存储错误码)的访问可能需要特殊处理,因为errno可能是一个全局变量,多个线程同时访问可能会导致问题。不过,现代操作系统通常提供了线程安全的机制来处理errno,如使用pthread库的线程环境下,每个线程有自己独立的errno副本(在某些实现中)。

字符分类函数

  1. isalnum函数
  • 函数原型int isalnum(int c);
  • 功能:判断字符c是否为字母或数字。如果c是一个字母(a - zA - Z)或者数字(0 - 9),则函数返回非零值(真),否则返回 0(假)。
  • 示例
#include <stdio.h>
   #include <ctype.h>

   int main() {
       char c1 = 'a';
       char c2 = '9';
       char c3 = '!';
       printf("%d\n", isalnum(c1));// 输出1(非零,表示真)
       printf("%d\n", isalnum(c2));// 输出1
       printf("%d\n", isalnum(c3));// 输出0
       return 0;
   }


  1. isalpha函数
  • 函数原型int isalpha(int c);
  • 功能:判断字符c是否为字母。如果c是一个字母(a - zA - Z),则函数返回非零值(真),否则返回 0(假)。
  • 示例
#include <stdio.h>
   #include <ctype.h>

   int main() {
       char c1 = 'b';
       char c2 = '8';
       char c3 = '?';
       printf("%d\n", isalpha(c1));// 输出1
       printf("%d\n", isalpha(c2));// 输出0
       printf("%d\n", isalpha(c3));// 输出0
       return 0;
   }


  1. isdigit函数
  • 函数原型int isdigit(int c);
  • 功能:判断字符c是否为数字。如果c是一个数字(0 - 9),则函数返回非零值(真),否则返回 0(假)。
  • 示例
#include <stdio.h>
   #include <ctype.h>

   int main() {
       char c1 = '5';
       char c2 = 'a';
       char c3 = ' ';
       printf("%d\n", isdigit(c1));// 输出1
       printf("%d\n", isdigit(c2));// 输出0
       printf("%d\n", isdigit(c3));// 输出0
       return 0;
   }


  1. islower函数
  • 函数原型int islower(int c);
  • 功能:判断字符c是否为小写字母。如果c是一个小写字母(a - z),则函数返回非零值(真),否则返回 0(假)。
  • 示例
#include <stdio.h>
   #include <ctype.h>

   int main() {
       char c1 = 'z';
       char c2 = 'A';
       char c3 = '9';
       printf("%d\n", islower(c1));// 输出1
       printf("%d\n", islower(c2));// 输出0
       printf("%d\n", islower(c3));// 输出0
       return 0;
   }


  1. isupper函数
  • 函数原型int isupper(int c);
  • 功能:判断字符c是否为大写字母。如果c是一个大写字母(A - Z),则函数返回非零值(真),否则返回 0(假)。
  • 示例
#include <stdio.h>
   #include <ctype.h>

   int main() {
       char c1 = 'B';
       char c2 = 'b';
       char c3 = '3';
       printf("%d\n", isupper(c1));// 输出1
       printf("%d\n", isupper(c2));// 输出0
       printf("%d\n", isupper(c3));// 输出0
       return 0;
   }


  1. isspace函数
  • 函数原型int isspace(int c);
  • 功能:判断字符c是否为空白字符。空白字符包括空格' '、制表符'\t'、换行符'\n'、垂直制表符'\v'、换页符'\f'和回车符'\r'。如果c是其中一种空白字符,则函数返回非零值(真),否则返回 0(假)。
  • 示例
#include <stdio.h>
   #include <ctype.h>

   int main() {
       char c1 ='';
       char c2 = 'a';
       char c3 = '\n';
       printf("%d\n", isspace(c1));// 输出1
       printf("%d\n", isspace(c2));// 输出0
       printf("%d\n", isspace(c3));// 输出1
       return 0;
   }


  1. ispunct函数
  • 函数原型int ispunct(int c);
  • 功能:判断字符c是否为标点符号。标点符号是除字母、数字和空白字符之外的可打印字符。如果c是标点符号,则函数返回非零值(真),否则返回 0(假)。
  • 示例
#include <stdio.h>
   #include <ctype.h>

   int main() {
       char c1 = '!';
       char c2 = 'a';
       char c3 = '9';
       printf("%d\n", ispunct(c1));// 输出1
       printf("%d\n", ispunct(c2));// 输出0
       printf("%d\n", ispunct(c3));// 输出0
       return 0;
   }

字符转换函数

  1. tolower函数
  • 函数原型int tolower(int c);
  • 功能:将大写字母转换为小写字母。如果输入的字符c是大写字母(A - Z),则将其转换为对应的小写字母(a - z),并返回转换后的字符;如果输入的字符不是大写字母,则直接返回该字符本身。
  • 示例
#include <stdio.h>
   #include <ctype.h>

   int main() {
       char c1 = 'A';
       char c2 = 'a';
       char c3 = '9';
       printf("%c\n", tolower(c1));// 输出 'a'
       printf("%c\n", tolower(c2));// 输出 'a'
       printf("%c\n", tolower(c3));// 输出 '9'
       return 0;
   }


  1. toupper函数
  • 函数原型int toupper(int c);
  • 功能:将小写字母转换为大写字母。如果输入的字符c是小写字母(a - z),则将其转换为对应的大写字母(A - Z),并返回转换后的字符;如果输入的字符不是小写字母,则直接返回该字符本身。
  • 示例
#include <stdio.h>
   #include <ctype.h>

   int main() {
       char c1 = 'a';
       char c2 = 'A';
       char c3 = '9';
       printf("%c\n", toupper(c1));// 输出 'A'
       printf("%c\n", toupper(c2));// 输出 'A'
       printf("%c\n", toupper(c3));// 输出 '9'
       return 0;
   }

memcpy

  1. 函数原型与功能概述
  • 在 C 和 C++ 中,memcpy函数的原型为void *memcpy(void *dest, const void *src, size_t n);
  • 它的主要功能是将一段内存(由src指向)中的数据复制到另一段内存(由dest指向)中,复制的字节数由n指定。
  1. 参数含义
  • dest:目标内存地址,是复制数据的接收端。这个指针指向的内存区域必须有足够的空间来容纳要复制的数据,否则可能会导致缓冲区溢出等错误。
  • src:源内存地址,是要被复制数据的起始位置。
  • n:要复制的字节数。
  1. 返回值
  • memcpy函数返回一个指向目标内存区域(dest)的指针。这个返回值在某些情况下可以方便地进行链式操作或者在函数调用后对目标区域进行进一步操作。
  1. 使用示例(C 语言)
  • 例如,将一个数组的部分数据复制到另一个数组中:
#include <stdio.h>
   #include <string.h>

   int main() {
       int arr1[5] = {1, 2, 3, 4, 5};
       int arr2[5];
       memcpy(arr2, arr1, sizeof(int) * 3);
       for (int i = 0; i < 5; i++) {
           if (i < 3) {
               printf("%d ", arr2[i]);
           } else {
               printf("0 ");
           }
       }
       return 0;
   }


  • 在这个示例中,memcpyarr1数组的前 3 个元素(共sizeof(int) * 3字节)复制到arr2数组中。然后遍历arr2数组并输出结果,可以看到前 3 个元素被正确复制,后 2 个元素未被初始化(这里简单输出 0 表示)。
  • 再例如,复制结构体数据:
#include <stdio.h>
   #include <string.h>

   struct Point {
       int x;
       int y;
   };

   int main() {
       struct Point p1 = {1, 2};
       struct Point p2;
       memcpy(&p2, &p1, sizeof(struct Point));
       printf("p2.x = %d, p2.y = %d\n", p2.x, p2.y);
       return 0;
   }


  • 这里通过memcpyp1结构体的内容复制到p2结构体中,然后输出p2的成员变量,可以看到数据被正确复制。


  1. 注意事项
  • 如果源和目标内存区域有重叠,memcpy的行为是未定义的。在这种情况下,可以使用memmove函数,它可以正确处理重叠的内存区域。
  • 当复制非基本数据类型(如结构体中包含指针等情况)时,需要确保在目标区域中相关的指针等资源能够被正确处理,仅仅复制字节可能会导致后续使用时出现问题(例如浅复制可能需要进一步处理以实现深复制的效果)。

字符函数和字符串函数和内存函数_内存区域_02

字符函数和字符串函数和内存函数_内存区域_03

memmove

  1. 函数原型与功能概述
  • 在 C 和 C++ 中,memmove函数的原型为void *memmove(void *dest, const void *src, size_t n);
  • 其功能是将src所指向的内存区域中的n个字节的数据移动到dest所指向的内存区域。与memcpy类似,但memmove能够正确处理源内存区域和目标内存区域重叠的情况。
  1. 参数含义
  • dest:目标内存地址,是数据要移动到的位置。
  • src:源内存地址,是要被移动数据的起始位置。
  • n:要移动的字节数。
  1. 返回值
  • memmove函数返回一个指向目标内存区域(dest)的指针。
  1. 处理重叠内存区域的原理
  • 当源内存区域和目标内存区域重叠时:
  • 如果srcdest之前,memmove会从源内存区域的起始位置开始复制数据到目标内存区域,这种情况下,数据的复制顺序是正常的顺序,不会影响到尚未复制的数据。
  • 如果srcdest之后,memmove会从源内存区域的末尾开始复制数据到目标内存区域的末尾,然后逐步向前复制,这样可以避免在复制过程中覆盖还未被复制的数据。
  1. 使用示例(C 语言)
  • 以下是一个处理重叠内存区域的示例:
#include <stdio.h>
   #include <string.h>

   int main() {
       char arr[] = "abcdef";
       // 目标区域在源区域之后,有重叠
       memmove(arr + 1, arr, 3);
       printf("%s\n", arr);
       return 0;
   }


  • 在这个示例中,memmovearr数组中从起始位置开始的 3 个字节(即 "abc")移动到从arr + 1开始的位置。由于memmove能够正确处理重叠情况,最终arr数组的内容变为 "aabcde"。
  • 再看一个源区域在目标区域之后的示例:
#include <stdio.h>
   #include <string.h>

   int main() {
       char arr[] = "abcdef";
       // 源区域在目标区域之后,有重叠
       memmove(arr, arr + 1, 3);
       printf("%s\n", arr);
       return 0;
   }


  • 这里memmovearr数组中从arr + 1开始的 3 个字节(即 "bcd")移动到arr的起始位置。由于memmove正确处理了重叠,最终arr数组的内容变为 "bcddef"。

字符函数和字符串函数和内存函数_入门_04

memcmp

字符函数和字符串函数和内存函数_内存区域_05

memset

  1. 函数原型
  • 在 C 和 C++ 中,memset函数的原型为void *memset(void *s, int c, size_t n);
  1. 功能
  • 该函数用于将指定内存块中的前n个字节设置为特定的值c
  • 这里的s是指向要设置内存块的起始地址的指针,可以是数组、结构体等各种数据类型的指针。
  • 虽然c的类型为int,但在实际操作中,函数会将c转换为unsigned char类型,然后用这个unsigned char类型的值来填充内存块。
  1. 示例(C 语言)
  • 清零数组
  • 例如,将一个整数数组清零:
#include <stdio.h>
     #include <string.h>

     int main() {
         int arr[5];
         memset(arr, 0, sizeof(arr));
         for (int i = 0; i < 5; i++) {
             printf("%d ", arr[i]);
         }
         return 0;
     }


  • 在这个例子中,memsetarr数组中的所有字节都设置为 0。因为arr是一个包含 5 个int类型元素的数组,在大多数系统中int占 4 个字节,所以sizeof(arr)20字节,memset会将这 20 个字节都设置为 0,从而达到将数组元素清零的效果。
  • 填充字符数组
  • 再如,填充一个字符数组:
#include <stdio.h>
     #include <string.h>

     int main() {
         char str[10];
         memset(str, 'A', 5);
         str[5] = '\0';
         printf("%s\n", str);
         return 0;
     }


  • 这里将str字符数组的前 5 个字节设置为字符'A',然后手动添加字符串结束符'\0',以便能够正确地将其作为字符串进行输出,最终会输出"AAAAA"


  1. 注意事项
  • 当使用memset初始化非字符类型数组(如int数组)时,要确保填充的值在目标类型的合法取值范围内。例如,如果用memsetint数组填充为 1(对于大多数系统中int占 4 字节,实际上是将每个字节设置为 1),可能会得到意外的结果,因为这不是通常意义上对int类型的赋值操作。
  • 在 C++ 中,虽然memset仍然可以使用,但对于类对象的初始化,更推荐使用构造函数等面向对象的初始化方法。