当前位置: 首页 > news >正文

C语言基础之【指针】(下)

C语言基础之【指针】(下)

  • 指针和字符串
    • 字符指针
    • 字符指针做函数参数
    • const修饰的指针变量
    • 指针数组做为main函数的形参
    • 项目开发常用字符串应用模型
      • while和do-while模型
      • 两头堵模型
      • 字符串反转模型
    • 字符串处理函数
      • strchr()
      • strrchr()
      • strstr()
      • strtok()
      • strcpy()
      • strncpy()
      • strcat()
      • strncat()
      • strcmp()
      • strncmp()
      • sprintf()
      • sscanf()
      • atoi()

往期《C语言基础系列》回顾:
链接:
C语言基础之【C语言概述】
C语言基础之【数据类型】(上)
C语言基础之【数据类型】(下)
C语言基础之【运算符与表达式】
C语言基础之【程序流程结构】
C语言基础之【数组和字符串】(上)
C语言基础之【数组和字符串】(下)
C语言基础之【函数】
C语言基础之【指针】(上)
C语言基础之【指针】(中)


指针和字符串

字符指针

在C语言中,字符串是以字符数组的形式存储的,而字符指针(char *)可以用于操作字符串。


字符指针定义的语法char *指针变量名;

字符指针初始化:

char *str1 = "Hello";  // 指向字符串字面量char str2[] = "World"; // 字符数组
char *str3 = str2;     // 指向字符数组

字符指针的使用:

  1. 访问字符串

    char *str = "Hello";//通过字符指针可以访问字符串中的字符。
    printf("%c\n", *str);       // 输出:H
    printf("%c\n", *(str + 1)); // 输出:e
    
  2. 遍历字符串

    char* str = "Hello";//通过字符指针可以遍历字符串中的所有字符。
    for (int i = 0; str[i] != '\0'; i++)
    {printf("%c", str[i]); // 输出:Hello
    }
    
  3. 修改字符串

    char str[] = "Hello";char *p = str;
    p[0] = 'h'; // 修改字符串内容printf("%s\n", str); // 输出:hello
    
    • 如果字符指针指向的是字符数组,可以通过指针修改字符串的内容

    • 如果字符指针指向的是字符串字面量,则不能修改字符串的内容(行为未定义)

代码示例:字符数组和字符指针的区别

#include <stdio.h>
#include <stdlib.h>int main(void)
{char str1[] = "hello";   // {'h', 'e', 'l', 'l', 'o', '\0'}char m[] = "hello";char* str2 = "hello";    // "hello" 是一个字符串常量, 不能修改。char* n = "hello";//char* str3 = {'h','e','l','l','o','\0'}  error:初始化语法错误//str1[0] = 'R';//str2[0] = 'R';  error printf("str1 = %p\n", str1);printf("m = %p\n", m);printf("str2 = %p\n", str2);printf("n = %p\n", n);system("pause");return EXIT_SUCCESS;
}

输出

str1 = 000000B9A54FF534
m = 000000B9A54FF554
str2 = 00007FF788E7ACF0
n = 00007FF788E7ACF0

分析

1.字符数组 str1m

char str1[] = "hello";   
char m[] = "hello";
  • 存储方式

    • str1m 是字符数组,存储在栈上。
    • 每个数组都有独立的内存空间
    • 数组的内容是 "hello",包括结尾的 \0 字符。
  • 可修改性

    • 数组的内容可以修改。
      str1[0] = 'R';  // 合法,str1 变为 "Rello" 因为str1是字符数组,内容可以修改。
      
  • 地址

    • str1m 的地址不同,因为它们是独立的数组。

2.字符指针 str2n

char* str2 = "hello";  
char* n = "hello";
  • 存储方式

    • str2n 是指向字符串常量的指针,字符串常量 "hello" 存储在只读内存区域(通常是 .rodata 段)
    • 编译器可能会将相同的字符串常量优化为同一内存地址
  • 可修改性

    • 字符串常量的内容不能修改。
      str2[0] = 'R';  // 非法,会导致未定义行为(通常是程序崩溃)
      //因为str2指向的是字符串常量,内容不能修改。
      
  • 地址

    • str2n 的地址可能相同,因为它们指向相同的字符串常量。

// char* str3 = {'h','e','l','l','o','\0'}  error:初始化语法错误

注意

  • 在 C 语言中,对于指针类型的变量,不能直接用花括号 {} 包含的字符列表来初始化。
  • 花括号这种初始化方式通常用于数组的初始化,而不是指针。

字符指针与字符数组的区别:

特性字符指针字符数组
定义char *strchar str[]
内存分配指向字符串字面量(只读)或 动态分配的内存静态分配内存,大小固定
可修改性如果指向字符数组,可以修改
如果指向字符串字面量,不能修改
可以修改
大小sizeof(str) 返回指针的大小(通常为4或8字节)sizeof(str) 返回整个数组的大小
灵活性可以指向不同的字符串不能指向其他字符串

字符指针的常见应用:

示例代码:字符指针的使用

#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main() 
{// 示例1:字符指针指向字符串字面量char* str1 = "Hello";printf("str1: %s\n", str1); // 输出:Hello// 示例2:字符指针指向字符数组char str2[] = "Hello";char* str3 = str2;str3[0] = 'h'; // 修改字符串内容printf("str2: %s\n", str2); // 输出:hello// 示例3:动态分配字符串char* str4 = (char*)malloc(20 * sizeof(char));strcpy(str4, "Hello, World!");printf("str4: %s\n", str4); // 输出:Hello, World!free(str4); // 释放内存// 示例4:字符串数组char* strArr[] = { "Apple", "Banana", "Cherry" };for (int i = 0; i < 3; i++) {printf("strArr[%d]: %s\n", i, strArr[i]); // 输出每个字符串}return 0;
}

输出:

str1: Hello
str2: hello
str4: Hello, World!
strArr[0]: Apple
strArr[1]: Banana
strArr[2]: Cherry

字符指针做函数参数

在C语言中,字符指针(char *)作为函数参数时,通常用于传递字符串或字符数组的地址。


字符指针作为函数参数的注意事项:

1.字符串的长度

  • 字符指针作为函数参数时,字符串的长度信息会丢失

    • 因此通常需要额外传递字符串的长度。
    • 或者依赖 \0 来判断字符串的结束。
    #include <stdio.h>//需要额外传递字符串的长度
    void printString1(char* str, int n)
    {for (int i = 0; i < n; i++){printf("%c", str[i]); // 输出字符串的前n个字符}printf("\n");
    }//依赖 \0 来判断字符串的结束
    void printString2(char* str)
    {for (int i = 0; str[i] != '\0'; i++){printf("%c", str[i]); // 输出字符串}printf("\n");
    }int main()
    {char str[] = "Hello, World!";int n = sizeof(str) / sizeof(str[0]);printString1(str, n); // 输出:Hello, World!printString2(str);    // 输出:Hello, World!return 0;
    }

2.字符串的可修改性

  • 如果字符指针指向的是字符串字面量

    • 如: char *str = "Hello";,则不能通过指针修改字符串的内容(行为未定义)
  • 如果字符指针指向的是字符数组

    • 如: char str[] = "Hello";,则可以通过指针修改字符串的内容
    #include <stdio.h>void modifyString(char* str)
    {str[0] = 'h';
    }int main()
    {char* str1 = "Hello";modifyString(str1); // 未定义行为char str2[] = "Hello";modifyString(str2); // 修改字符串内容printf("%s\n", str2); // 输出:helloreturn 0;
    }
    

示例代码:字符指针作为函数参数的使用

#include <stdio.h>
#include <stdlib.h>
#include <string.h>// 打印字符串
void printString(char *str) 
{printf("%s\n", str);
}// 将字符串转换为大写
void toUpperCase(char *str) 
{for (int i = 0; str[i] != '\0'; i++) {if (str[i] >= 'a' && str[i] <= 'z') {str[i] = str[i] - 32;}}
}// 动态分配字符串
char *createString(const char *src) 
{char *str = (char *)malloc((strlen(src) + 1) * sizeof(char));strcpy(str, src);return str;
}// 打印字符串数组
void printStrings(char *strArr[], int n) 
{for (int i = 0; i < n; i++) {printf("%s\n", strArr[i]);}
}int main() 
{// 示例1:打印字符串char str1[] = "Hello, World!";printString(str1);// 示例2:修改字符串toUpperCase(str1);printString(str1);// 示例3:动态分配字符串char *str2 = createString("动态分配字符串");printString(str2);free(str2);// 示例4:字符串数组char *strArr[] = {"Apple", "Banana", "Cherry"};int n = sizeof(strArr) / sizeof(strArr[0]);printStrings(strArr, n);return 0;
}

输出:

Hello, World!
HELLO, WORLD!
Dynamic String
Apple
Banana
Cherry

注意

字符串数组 做函数参数时,我们通常在函数定义中,封装2个参数。

  • 一个表示字符串数组首地址,一个表示数组元素个数

const修饰的指针变量

const 修饰指针的常见用法:

  1. 保护函数参数
  • 使用 const 修饰指针参数,防止函数内部修改数据。

    void printArray(const int *arr, int n) 
    {for (int i = 0; i < n; i++) {printf("%d ", arr[i]);}printf("\n");
    }
    
  1. 定义常量字符串
  • 使用 const 修饰指针,定义常量字符串。

    const char *str = "Hello, World!";
    // str[0] = 'h'; // 错误:不能修改常量字符串
    

指针数组做为main函数的形参

在C语言中,main 函数可以接受两个参数:argcargv

理解 main 函数的参数及其用法对于编写支持命令行输入的程序非常重要。


main 函数原型的语法

int main(int argc, char *argv[]);
  • argc(参数计数):是一个整形,用于表示命令行参数的数量(包括程序名本身)

    • argc 的最小值为 1,因为 argv[0] 总是程序名。
  • argv(参数向量):是一个指针数组,用于存储命令行参数的字符串


指针数组argv的每个元素是一个 char * 类型的指针,指向一个命令行参数字符串。

  • argv[0] :是程序名,argv[1]argv[argc-1] 是实际的命令行参数。
  • argv[argc]: 是一个空指针(NULL),表示参数列表的结束。

main 函数参数的使用:

1.打印所有命令行参数

  • 通过遍历 argv 数组,可以打印所有命令行参数。

    #include <stdio.h>int main(int argc, char *argv[]) 
    {printf("程序名: %s\n", argv[0]); // 打印程序名for (int i = 1; i < argc; i++) {printf("参数 %d: %s\n", i, argv[i]); // 打印每个参数}return 0;
    }
    

2.处理命令行参数

  • 通过 argcargv 可以处理命令行参数。

    • 例如:解析选项或传递配置信息。
    #include <stdio.h>int main(int argc, char *argv[]) 
    {if (argc < 2) {printf("用法: %s <参数>\n", argv[0]); // 提示用法return 1;}printf("第一个参数: %s\n", argv[1]); // 打印第一个参数return 0;
    }
    

示例代码:运行带参数的 main 函数

#include <stdio.h>int main(int argc, char *argv[]) 
{printf("程序名: %s\n", argv[0]); // 打印程序名printf("参数总数: %d\n", argc - 1); // 打印参数总数// 打印所有命令行参数for (int i = 1; i < argc; i++) {printf("参数 %d: %s\n", i, argv[i]);}return 0;
}
  1. 借助VS编辑工具运行
    • 编写好含有带参数的 main 函数C程序 —>右击该程序所在的项目名 —> 属性—>调试 —> 命令参数 —> 添加命令参数 —> 确定

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  1. 借助记事本、gcc编译工具运行

在这里插入图片描述

在这里插入图片描述
切记粘贴完代码一定要记得按Ctrl + S进行保存
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

项目开发常用字符串应用模型

while和do-while模型

在处理字符串时,经常需要统计某个特定子字符串在主字符串中出现的次数。

  • 给定一个主字符串 p,其内容为 "11abcd111122abcd333abcd3322abcd3333322qqq"
  • 要求编写一个 C 语言程序,统计子字符串 "abcd" 在主字符串中出现的次数
//while模型
#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main(void)
{const char* p = "11abcd111122abcd333abcd3322abcd3333322qqq";int n = 0;int len = strlen("abcd");while (p != NULL){n++;p = p + len;p = strstr(p, "abcd");}printf("n = %d\n", n);return 0;
}

输出:

n = 4

//do-while模型
#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main()
{const char* p = "11abcd111122abcd333abcd3322abcd3333322qqq";int n = 0;int len = strlen("abcd");do{p = strstr(p, "abcd");if (p != NULL){n++; //累计个数p = p + len; //重新设置查找的起点}else //如果没有匹配的字符串,跳出循环{break;}} while (*p != 0); //如果没有到结尾printf("n = %d\n", n);return 0;
}

输出:

n = 4

两头堵模型

在字符串处理过程中,常常需要统计去除首尾空格后字符串中非空字符的数量。

  • 编写一个 C 语言程序,实现一个名为 fun 的函数,该函数的功能是统计一个字符串去除首尾空格后非空字符的数量。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>int fun(char *p, int *n)
{// 检查指针是否为空if (p == NULL || n == NULL){return -1;}int begin = 0;int end = strlen(p) - 1;// 从左边开始,跳过空格while (p[begin] == ' ' && p[begin] != '\0'){begin++;}// 从右边开始,跳过空格while (p[end] == ' ' && end >= begin){end--;}// 如果字符串全是空格,返回错误if (end < begin){return -2;}// 计算非空字符的个数*n = end - begin + 1;return 0;
}int main()
{char *p = "      abcddsgadsgefg      ";int ret = 0;int n = 0;// 调用函数处理字符串ret = fun(p, &n);if (ret != 0){printf("函数执行失败,错误码:%d\n", ret);return ret;}// 输出结果printf("非空字符串元素个数:%d\n", n);return 0;
}

输出:

非空字符串元素个数:14

编写一个C语言程序,判断一个字符串是否是回文字符串。

#include <stdio.h>
#include <stdlib.h>// 判断回文字符串  abcdpba
int str_abcbb(char* str)
{char* start = str;                   // 记录首元素地址char* end = str + strlen(str) - 1;   // 记录最后一个元素地址。while (start < end)    {if (*start != *end) {return 0; // 0 表示 非回文}start++;end--;}return 1;      // 1 表示 回文
}int main(void)
{char s2[] = "abccba";int ret = str_abcbb(s2);if (ret == 0){printf("不是回文\n");}else if (ret == 1){printf("是回文\n");}system("pause");return EXIT_SUCCESS;
}

输出
是回文

字符串反转模型

在字符串处理的诸多场景中,字符串反转是一个常见的需求。

  • 编写一个 C 语言程序,实现字符串反转的功能。
  • 要求创建一个名为 inverse 的函数,该函数接收一个字符串作为参数,并将该字符串的字符顺序进行反转。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>int inverse(char* str)
{// 检查指针是否为空if (str == NULL){return -1;}int begin = 0;int end = strlen(str) - 1;char tmp;// 交换字符,直到begin和end相遇while (begin < end){// 交换元素tmp = str[begin];str[begin] = str[end];str[end] = tmp;begin++;  // 往右移动位置end--;    // 往左移动位置}return 0;
}int main()
{//char *str = "abcdef";// 使用字符数组,而不是字符指针,因为字符指针指向的是常量区,内容不允许修改char str[] = "abcdef";// 调用反转函数int ret = inverse(str);if (ret != 0){printf("函数执行失败,错误码:%d\n", ret);return ret;}// 输出结果printf("str = %s\n", str);return 0;
}

输出:

str = fedcba

字符串处理函数

strchr()

函数的介绍:

strchr() :用于在一个字符串中查找指定字符首次出现的位置。


strchr() 函数会从字符串 str 的开头开始,逐个字符进行比较,查找字符 c 首次出现的位置。

  • 一旦找到该字符,就返回指向该字符的指针

  • 如果遍历完整个字符串都没有找到该字符,则返回 NULL

函数的原型:

char *strchr(const char *str, int c);
  • str:是指向要进行查找的字符串的指针。
  • c:要查找的字符。
    • 虽然参数类型为 int,但实际上是一个字符的 ASCII 值
    • 字符 c 会被转换为 char 类型

函数的返回值:

  • 如果在字符串 str 中找到字符 c:则返回指向该字符首次出现位置的指针
  • 如果在字符串 str 中未找到字符 c:则返回 NULL

函数的使用:

#include <stdio.h>
#include <string.h>int main()
{char str[] = "Hello, World!";char target = 'o';char* result = strchr(str, target);if (result != NULL){printf("字符 '%c' 首次出现在位置: %ld\n", target, result - str);printf("从该位置开始的子字符串: %s\n", result);}else{printf("未找到字符 '%c'\n", target);}return 0;
}

输出

字符 ‘o’ 首次出现在位置: 4
从该位置开始的子字符串: o, World!


函数的注意事项:

  1. 字符查找的范围:strchr 函数会查找字符首次出现的位置,并且会查找包括字符串结束符 '\0' 之前的所有字符。

    • 例如:如果要查找的字符是 '\0',函数会返回指向字符串结束符的指针。
    #include <stdio.h>
    #include <string.h>int main() 
    {char str[] = "Hello";char *result = strchr(str, '\0');if (result != NULL) {printf("找到字符串结束符,位置: %ld\n", result - str);}return 0;
    }
    

    输出:

    找到字符串结束符,位置: 5

strrchr()

函数的介绍:

strrchr():用于查找字符串中某个字符最后一次出现的位置。(即:用于在字符串中从后往前查找指定字符的第一次出现位置)

函数的原型:

char *strrchr(const char *str, int c);
  • str:是指向要被搜索的字符串的指针。
  • c:是要查找的字符。
    • 虽然参数类型为 int,但实际上是一个字符的 ASCII 值
    • 在函数内部会将字符 c 会被转换为 char 类型

函数的返回值:

  • 如果在字符串 str 中找到了字符 c 的最后一次出现位置,则返回一个指向该位置的指针
  • 如果在字符串 str 中未找到字符 c,则返回 NULL

函数的使用:

#include <stdio.h>
#include <string.h>int main() 
{const char *str = "Hello, World!";char target = 'o';char *result = strrchr(str, target);//error:const char *类型的值不能用于初始化 char *类型的实体if (result != NULL) {printf("字符 '%c' 最后一次出现的位置是: %ld\n", target, result - str);printf("从该位置开始的子字符串是: %s\n", result);} else {printf("未找到字符 '%c'\n", target);}return 0;
}

分析错误原因

  • strrchr 的参数str :是 const char* 类型,表示它指向的字符串是常量不能修改。
  • strrchr 的返回值:是 char* 类型,表示它返回的指针可以用于修改字符串。

注意将 char* 赋值给 const char* 是允许的,但反过来不行,因为这会破坏 const 的语义


问题解决方法

  1. str 的类型改为 char*:(如果 str 指向的字符串不需要保护,即:可以修改)

    char str[] = "Hello, World!";
    
    • 这样,str 就是一个可修改的字符数组,strrchr 的返回值可以直接赋值给 result
  2. 使用类型转换:(如果你需要 strconst char* 类型)

    const char* str = "Hello, World!";
    const char* result = strrchr(str, target);
    
    • 这样,result 也是 const char* 类型,与 str 的类型一致。

修改后的代码

方法 1:将 str 改为 char*

#include <stdio.h>
#include <string.h>int main()
{char str[] = "Hello, World!";  // 改为 char[]char target = 'o';char* result = strrchr(str, target);if (result != NULL){printf("字符 '%c' 最后一次出现的位置是: %ld\n", target, result - str);printf("从该位置开始的子字符串是: %s\n", result);}else{printf("未找到字符 '%c'\n", target);}return 0;
}

方法 2:使用类型转换

#include <stdio.h>
#include <string.h>int main()
{const char* str = "Hello, World!";  // 保持 const char*char target = 'o';const char* result = strrchr(str, target);  // 将返回值转换为 const char*if (result != NULL){printf("字符 '%c' 最后一次出现的位置是: %ld\n", target, result - str);printf("从该位置开始的子字符串是: %s\n", result);}else{printf("未找到字符 '%c'\n", target);}return 0;
}

strchr()strrchr() 的区别:

函数搜索方向返回的字符位置
strchr从字符串开头向后搜索字符第一次出现的位置
strrchr从字符串末尾向前搜索字符最后一次出现的位置

strstr()

函数的介绍:

strstr():用于在一个字符串中查找另一个子字符串首次出现的位置。


strstr() 函数会从主字符串 haystack 的起始位置开始,逐个字符地进行比对,尝试找到与子字符串 needle 完全匹配的部分。

  • 一旦找到匹配的子字符串,就返回指向该匹配部分起始位置的指针

  • 若遍历完整个 haystack 字符串都没有找到匹配的 needle 子字符串,就返回 NULL

函数的原型:

char *strstr(const char *haystack, const char *needle);
  • haystack:是指向要进行搜索的主字符串的指针。
    • 这个字符串相当于 “干草堆”,是搜索操作的范围。
  • needle:是指向要查找的子字符串的指针。
    • 这个子字符串就是我们要在 “干草堆” 里找的 “针”。

函数的返回值:

  • 若在 haystack 字符串中找到了 needle 子字符串:函数返回指向该子字符串首次出现位置的指针。
  • 若在 haystack 字符串中未找到 needle 子字符串:函数返回 NULL

函数的使用:

#include <stdio.h>
#include <string.h>int main()
{char haystack[] = "Hello, World! This is a test.";char needle[] = "World";char* result = strstr(haystack, needle);if (result != NULL){printf("子字符串 '%s' 首次出现在位置: %ld\n", needle, result - haystack);printf("从该位置开始的子字符串: %s\n", result);}else{printf("未找到子字符串 '%s'\n", needle);}return 0;
}

输出:

子字符串 ‘World’ 首次出现在位置: 7
从该位置开始的子字符串: World! This is a test.


函数的注意事项:

  1. 空字符串的处理:如果 needle 是一个空字符串(即 ""),strstr() 函数会直接返回 haystack 本身。

    • 因为:空字符串可以被认为在任何字符串的开头都存在
    #include <stdio.h>
    #include <string.h>int main()
    {char haystack[] = "Hello";char needle[] = "";char* result = strstr(haystack, needle);if (result != NULL){printf("找到空字符串,返回主字符串: %s\n", result);}return 0;
    }
    

    输出:

    找到空字符串,返回主字符串: Hello

strchr()strstr() 的区别:

特性strchr()strstr()
搜索对象单个字符子字符串
返回值指向字符的指针指向子字符串的指针
区分大小写
适用场景查找单个字符查找子字符串

strtok()

函数的介绍:

strtok():用于将一个字符串按照指定的分隔符分割成多个子字符串。


strtok() 函数会在字符串 str 中查找由分隔符 delim 分隔的子字符串。

它会将分隔符替换为字符串结束符 '\0',并返回指向分割出的子字符串的指针。

函数会记录当前分割的位置,以便后续调用时继续从该位置进行分割。

  1. 第一次调用

    • 传入待分割的字符串 str
    • strtok() 会找到第一个标记,并在标记末尾添加 \0,返回指向该标记的指针。
    • 内部会保存剩余的字符串,供后续调用使用。
  2. 后续调用

    • 传入 NULL 作为 str 参数。
    • strtok() 会从上一次保存的位置继续查找下一个标记。
  3. 结束条件

    • 当字符串被完全分割后,返回 NULL

函数的原型:

char *strtok(char *str, const char *delim);
  • str:是指向要进行分割的字符串的指针。
    • 在第一次调用 strtok() 时,需要传入待分割的字符串
    • 后续调用时,应传入 NULL,以继续从上次的位置分割,因为函数会记录上一次分割的位置
  • delim:是指向包含分隔符字符的字符串的指针。
    • 分隔符可以是一个或多个字符,函数会根据这些字符来分割字符串。

函数的返回值:

  • 若找到下一个子字符串:返回指向该子字符串的指针
  • 若没有更多的子字符串可分割:返回 NULL

函数的使用:

1.处理连续分隔符

#include <stdio.h>
#include <string.h>int main() 
{char str[] = "Hello,World,How,Are,You";const char delim[] = ",";// 第一次调用 strtok(),传入待分割的字符串char* token = strtok(str, delim);while (token != NULL) {printf("分割出的子字符串: %s\n", token);// 后续调用,传入 NULLtoken = strtok(NULL, delim);}return 0;
}

输出:

分割出的子字符串: Hello
分割出的子字符串: World
分割出的子字符串: How
分割出的子字符串: Are
分割出的子字符串: You

2.处理多分隔符

#include <stdio.h>
#include <string.h>int main()
{char str[] = "Hello;World|This-is/C";const char delim[] = ";|-/";char* token = strtok(str, delim);while (token != NULL){printf("分割出的子字符串: %s\n", token);token = strtok(NULL, delim);}return 0;
}

输出:

分割出的子字符串: Hello
分割出的子字符串: World
分割出的子字符串: This
分割出的子字符串: is
分割出的子字符串: C

函数的注意事项:

  1. 原字符串会被修改strtok() 函数会将分隔符替换为字符串结束符 '\0',因此原字符串会被修改。

    • 如果需要保留原字符串,建议先复制一份再进行分割操作。
    #include <stdio.h>
    #include <string.h>int main() 
    {char original[] = "Apple,Banana,Cherry";char copy[50];strcpy(copy, original);const char delim[] = ",";char *token = strtok(copy, delim);while (token != NULL) {printf("分割出的子字符串: %s\n", token);token = strtok(NULL, delim);}printf("原字符串: %s\n", original);return 0;
    }
    

    输出:

    分割出的子字符串: Apple
    分割出的子字符串: Banana
    分割出的子字符串: Cherry
    原字符串: Apple,Banana,Cherry

  2. 传入的参数必须可读可写:strtok函数拆分字符串是直接在原串上进行的操作,所以要求传入的参数必须,可读可写。

    • 例如:char *str = "www. baidu.com" 不行!!!
  3. 分隔符的处理:连续的分隔符会被视为一个分隔符。

    • 例如:字符串 "Hello,,World" 中连续的两个逗号会被当作一个分隔符处理,只替换第一个为\0,分割结果为 "Hello""World"
  4. 空字符串的处理:如果字符串以分隔符开头,第一个返回的子字符串将是空字符串。

    • 例如:字符串 ",Hello,World" 分割时,第一个返回的子字符串为空

strcpy()

函数的介绍:

strcpy():用于将一个字符串复制到另一个字符串中。

  • 将源字符串(包括终止符 \0)复制到目标缓冲区。
  • 也就是说,它会逐字符地将 src 中的内容复制到 dest 中,直到遇到 src 的结束符 '\0',并将这个结束符也复制到 dest 中。

函数的原型:

char *strcpy(char *dest, const char *src);
  • dest:目标字符串的起始地址,即要将源字符串复制到的内存位置。
    • 必须有足够空间容纳源字符串(包括 \0
  • src:源字符串的起始地址,即要复制的字符串。
    • 必须以 \0 结尾

函数的返回值:

strcpy():返回目标字符串dest的起始地址,即复制后的字符串的地址。

函数的使用:

#include <stdio.h>
#include <string.h>int main()
{char src[] = "Hello, World!";char dest[20]; // 确保足够大char* p = strcpy(dest, src);printf("p: %s\n", p); // 输出:Hello, World!printf("dest: %s\n", dest); // 输出:Hello, World!return 0;
}

函数的注意事项:

  1. 目标字符串的空间足够:目标字符串 dest 必须有足够的空间来容纳源字符串及其终止符 '\0'

    • 由于strcpy() 函数本身不会检查目标字符串的空间是否足够,也不会返回错误信息。
    • 所以如果目标字符串的空间不足,会导致缓冲区溢出,这是一个严重的安全隐患,可能会导致程序崩溃或产生不可预期的行为。
    #include <stdio.h>
    #include <string.h>int main() 
    {char source[] = "Hello";char destination[5];  // 目标字符串空间不足char* p = strcpy(destination, source); // 溢出!destination 容量不足(需要6字节,包含\0)printf("%s\n", p); return 0;
    }
    
  2. 避免自我复制:不要将源字符串和目标字符串设置为同一个字符串,否则可能会导致不可预期的结果。

    char str[] = "Example";
    strcpy(str, str); // 不建议这样做
    

strncpy()

函数的介绍:

strncpy():用于将一个字符串的部分或全部内容复制到另一个字符串中。

  • 它能更好地控制复制的字符数量,一定程度上避免了缓冲区溢出的问题。

strncpy() 函数会尝试将源字符串 src 中的最多 n 个字符复制到目标字符串 dest 中,具体复制规则如下:

  • src 的长度小于 n
    • strncpy() 会将 src 中的所有字符(包括字符串结束符 '\0')复制到 dest 中,然后用 '\0' 填充 dest 中剩余的空间,直到复制满 n 个字符
  • src 的长度大于等于 n
    • strncpy() 只会复制 src 的前 n 个字符到 dest 中,并且不会自动在 dest 的末尾添加字符串结束符 '\0'

函数的原型:

char *strncpy(char *dest, const char *src, size_t n);
  • dest:目标字符串的起始地址,即要将源字符串复制到的内存位置。
  • src:源字符串的起始地址,即要复制的字符串。
  • n:目标字符串的最大长度,即最多复制的字符数。

函数的返回值:

strncpy():返回目标字符串dest的起始地址,即复制后的字符串的地址。

函数的使用:

#include <stdio.h>
#include <string.h>int main() 
{char dest[20];char src[] = "hello world";char* p = strncpy(dest, src, 5);printf("%s\n", p);dest[5] = '\0';printf("%s\n", p);return 0;
}

输出:

hello烫烫烫烫烫烫烫烫烫?N┖濑?
hello

代码示例:更好的使用strncpy()函数的方法

#include <stdio.h>
#include <string.h>int main()
{char src[] = "Hello, World!";char dest[10];char* p = strncpy(dest, src, sizeof(dest) - 1); // 留1字节给\0dest[sizeof(dest) - 1] = '\0';        // 手动添加终止符printf("p: %s\n", p);    // 输出:Hello, Woprintf("dest: %s\n", dest); // 输出:Hello, Woreturn 0;
}

函数的注意事项:

  1. 手动添加字符串结束符:当 src 的长度大于等于 n 时,strncpy() 不会在 dest 的末尾添加 '\0'

    • 因此,在使用 strncpy() 后,需要根据实际情况手动添加字符串结束符,否则在将 dest 作为字符串处理时可能会出现问题
    char dest[5];
    strncpy(dest, "Hello", 5); // 复制5个字符,但未添加\0
    printf("%s\n", dest);      // 可能输出乱码(dest未以\0结尾)
    

代码示例 :更好的理解 strcpy()strncpy() 函数的区别

#include <stdio.h>
#include <string.h>int main()
{char src[] = "hello world";char dest[10] = { 0 };char* p = strncpy(dest, src, 10); // 字符串src 拷贝给destfor (size_t i = 0; i < 10; i++){printf("%c", p[i]);}printf("\n");printf("p= %s\n", p);printf("dest = %s\n", dest);system("pause");return 0;
}

输出

hello worl
p= hello worl烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫橔/戈
dest = hello worl烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫橔/

strcpy()strncpy() 的对比:

特性strcpy()strncpy()
复制长度复制整个字符串(包括 \0最多复制 n 个字符
安全性不安全(不检查缓冲区大小)相对安全(限制最大长度)
自动添加 \0仅在 src 长度 < n 时添加
性能高(直接复制)低(可能需要填充 \0
适用场景已知 src 长度且目标缓冲区足够大时需要控制复制长度,避免溢出

总结:

  • strcpy() 容易导致缓冲区溢出,因此在处理不可信输入时应避免使用。
  • strncpy() 更安全,但需要手动处理字符串的终止符 \0,否则可能导致未定义行为。

strcat()

函数的介绍:

strcat() :用于将一个字符串追加到另一个字符串的末尾。


  • 它会先找到目标字符串 dest 的结束符 '\0',然后从这个位置开始将源字符串 src 的内容复制过去,包括源字符串的结束符 '\0'
  • 最终覆盖掉目标字符串原来结束符的位置,并在追加完成后在新字符串的末尾添加结束符 '\0'

函数的原型:

char *strcat(char *dest, const char *src);
  • dest:目标字符串的起始地址,即要将源字符串追加到的字符串。
    • 它需要有足够的空间来容纳源字符串追加后的内容。
    • 必须是一个以 \0 结尾的有效 C 字符串。
  • src:源字符串的起始地址,即要追加的字符串。

函数的返回值:

strcat():返回目标字符串dest的起始地址,即连接后的字符串的地址。

函数的使用:

#include <stdio.h>
#include <string.h>int main() 
{char dest[20] = "Hello, ";char src[] = "World!";// 使用 strcat() 函数将 src 字符串追加到 dest 字符串末尾strcat(dest, src);printf("追加后的字符串: %s\n", dest);return 0;
}

函数的注意事项:

  1. 目标字符串的空间足够:目标字符串 dest 必须有足够的空间来容纳源字符串追加后的内容,包括源字符串和结束符 '\0'

    • 如果目标字符串的空间不足,会导致缓冲区溢出,这是一个严重的安全隐患,可能会使程序崩溃或产生不可预期的行为。
    #include <stdio.h>
    #include <string.h>int main() 
    {char dest[10] = "Hello";char src[] = " World!";// 目标字符串空间不足strcat(dest, src); printf("%s\n", dest);return 0;
    }
    
  2. 目标字符串必须以 '\0'结尾

    • strcat() 函数通过查找目标字符串的结束符 '\0' 来确定追加的起始位置。

    • 如果目标字符串不以 '\0' 结尾,strcat() 函数会继续在内存中查找,直到找到 '\0' 为止,这可能会导致不可预期的结果。

  3. 自我追加问题:不能将目标字符串和源字符串设置为同一个字符串。

    • 因为这样会导致不可预测的结果。
    char str[20] = "Example";
    strcat(str, str); // 不建议
    

strncat()

函数的介绍:

strncat():用于将源字符串中的最多n个字符追加到目标字符串的末尾。


  • 函数首先找到目标字符串 dest 的结束符 '\0'

  • 从该位置开始,将源字符串 src 中的字符逐个复制到目标字符串 dest 中,最多复制 n 个字符。

    • 如果 src 的长度小于 n,则会将整个源字符串(包括结束符 '\0')追加到目标字符串 dest

    • 如果 src 的长度大于或等于 n,则只会追加n 个字符,并在目标字符串 dest 的末尾添加一个字符串结束符 '\0'

函数的原型:

char *strncat(char *dest, const char *src, size_t n);
  • dest:目标字符串的起始地址,即要将源字符串追加到的字符串。
    • 它需要有足够的空间来容纳追加的字符和最终的字符串结束符 '\0'
    • 必须是一个以 \0 结尾的有效 C 字符串
  • src:源字符串的起始地址,即要追加的字符串。
  • n:最多从源字符串中追加到目标字符串的字符数量。

函数的返回值:

strcat():返回目标字符串dest的起始地址,即连接后的字符串的地址。

函数的使用:

#include <stdio.h>
#include <string.h>int main() 
{char dest[20] = "Hello, ";char src[] = "World!";// 使用 strncat() 函数将 src 字符串的前 3 个字符追加到 dest 字符串末尾strncat(dest, src, 3);printf("追加后的字符串: %s\n", dest);  //输出:Hello, Worreturn 0;
}

函数的注意事项:

  1. 目标字符串的空间:目标字符串 dest 必须有足够的空间来容纳追加的字符和最终的字符串结束符 '\0'

    • 如果空间不足,会导致缓冲区溢出,从而引发程序崩溃或产生不可预期的结果。
    #include <stdio.h>
    #include <string.h>int main() 
    {char dest[10] = "Hello";char src[] = " World!";// 目标字符串空间不足strncat(dest, src, 5); //dest初始存储"Hello"后剩余空间不足以容纳追加的5个字符及结束符printf("%s\n", dest);return 0;
    }
    
  2. 目标字符串需以'\0'结尾

    • strncat() 函数通过查找目标字符串的结束符 '\0' 来确定追加的起始位置。
    • 如果目标字符串不以 '\0' 结尾,函数会继续在内存中查找,直到找到 '\0' 为止,这可能会导致不可预期的结果。

strcat()strncat() 的区别:

特性strcat()strncat()
追加长度追加整个 src 字符串(包括 \0最多追加 n 个字符
自动添加 \0
缓冲区溢出风险高(如果 dest 不够大)较低(可以控制追加的长度)
适用场景已知 dest 足够大需要控制追加长度,避免溢出

strcmp()

函数的介绍:

strcmp():用于完全比较两个字符串。

  • strcmp() 函数会逐字符比较两个字符串,比较是基于字符的 ASCII 值进行的
  • 比较结束的条件为直到遇到不同的字符或者到达字符串的结束符 '\0'

  • 从两个字符串的第一个字符开始,依次比较对应位置的字符。
  • 如果对应位置的字符相同,则继续比较下一个字符,直到遇到不同的字符或者到达字符串末尾
    • 如果在比较过程中遇到不同的字符,函数会根据这两个不同字符的 ASCII 值大小关系返回相应结果。
    • 如果到达字符串末尾都没有遇到不同的字符,说明两个字符串相等,函数返回 0

函数的原型:

int strcmp(const char *str1, const char *str2);
  • str1:指向第一个要比较的字符串的指针。
  • str2:指向第二个要比较的字符串的指针。

函数的返回值:

  • 如果 s1 和 s2 相等,返回 0
  • 如果 s1 按字典序小于 s2,返回一个负整数
  • 如果 s1 按字典序大于 s2,返回一个正整数

函数的使用:

#include <stdio.h>
#include <string.h>int main() 
{char str1[] = "apple";char str2[] = "banana";int result = strcmp(str1, str2);if (result < 0) {printf("%s按字典序小于%s\n", str1, str2);}else if (result > 0) {printf("%s按字典序大于%s\n", str1, str2);}else {printf("%s等于%s\n", str1, str2);}return 0;
}

输出:

apple按字典序小于banana

strncmp()

函数的介绍:

strncmp():用于比较两个字符串 str1str2 的前 n 个字符。


strncmp() 函数会它会逐字符比较这两个字符串,基于字符的 ASCII 值进行判断,直到出现以下情况之一:

  1. 比较完前 n 个字符
  2. 遇到其中一个字符串的结束符 '\0'

函数的原型:

int strncmp(const char *str1, const char *str2, size_t n);
  • s1:指向第一个要比较的字符串的指针。
  • s2:指向第二个要比较的字符串的指针。
  • n:表示最多比较的字符数量。

函数的返回值:

  • 如果 s1 和 s2 的前 n 个字符相等,返回 0
  • 如果 s1 的前 n 个字符按字典序小于 s2 的前 n 个字符,返回一个负整数
  • 如果 s1 的前 n 个字符按字典序大于 s2 的前 n 个字符,返回一个正整数

函数的使用:

#include <stdio.h>
#include <string.h>int main() 
{char str1[] = "Hello, World!";char str2[] = "Hello, C!";char str3[] = "Goodbye";int result1 = strncmp(str1, str2, 5);  // 比较前 5 个字符int result2 = strncmp(str1, str3, 5);  // 比较前 5 个字符printf("strncmp(str1, str2, 5): %d\n", result1);  // 输出:0printf("strncmp(str1, str3, 5): %d\n", result2);  // 输出:正整数(具体值取决于实现)return 0;
}

strcmp()strncmp() 的区别:

特性strcmp()strncmp()
比较长度比较整个字符串(直到 \0只比较前 n 个字符
比较范围无限制n 限制
适用场景需要完全匹配时使用需要部分匹配或限制比较长度时使用

sprintf()

函数的介绍:

sprintf():用于将格式化后的内容存储到一个字符数组中。

  • sprintf 函数的定义位于 <stdio.h> 头文件中。

  • sprintf 函数的用法与 printf 函数类似,但操作的对象是字符串而不是标准输出。

函数的原型:

int sprintf(char *str, const char *format, ...);
  • str:是指向一个字符数组的指针,用于存储格式化后的字符串。
    • 该字符数组必须有足够的空间来容纳格式化后的内容以及字符串结束符 '\0'
  • format:是格式化字符串,它指定了后续参数应该如何被格式化。
    • 格式化字符串中可以包含普通字符格式说明符
      • 格式说明符以 % 开头,用于指定不同类型数据的输出格式。
      • 如: %d 用于整数,%f 用于浮点数,%s 用于字符串等。
  • ...:是可变参数列表,根据 format 字符串中的格式说明符,提供相应数量和类型的参数。

函数的返回值:

成功时:返回值是一个整数,表示成功写入目标字符串的字符数(不包括末尾的 \0

失败时:返回一个负数

函数的使用:

#include <stdio.h>int main() 
{char buffer[50];int num = 123;float f = 3.14;char str[] = "Hello";// 使用 sprintf() 函数将不同类型的数据格式化到 buffer 数组中int result = sprintf(buffer, "整数: %d, 浮点数: %.2f, 字符串: %s", num, f, str);printf("格式化后的字符串: %s\n", buffer);printf("存储的字符数量: %d\n", result);return 0;
}

输出:

格式化后的字符串: 整数: 123, 浮点数: 3.14, 字符串: Hello
存储的字符数量: 38

sscanf()

函数的介绍:

sscanf() :用于从一个字符串中按照指定的格式读取数据,并将读取的数据存储到相应的变量中。

  • sscanf 函数的定义位于 <stdio.h> 头文件中。
  • sscanf 函数的用法与 scanf 函数类似,但输入数据来自字符串而不是标准输入。

sscanf() 函数会根据 format 字符串的规则,从 str 字符串中提取数据,并将这些数据存储到可变参数列表指定的变量中。

它会按照格式说明符依次读取字符串中的数据,直到遇到不匹配的字符或者字符串结束。

函数的原型:

int sscanf(const char *str, const char *format, ...);
  • str:是指向要进行解析的字符串的指针,该字符串包含了需要提取的数据。
  • format:是格式化字符串,它规定了如何从 str 中读取数据。
  • ...:是可变参数列表,一系列用于存储读取数据的变量的地址,这些变量的类型要与 format 中的格式说明符相匹配。

函数的返回值:

成功时:返回成功匹配并赋值的输入项的数量

失败时:返回 EOF

函数的使用:

#include <stdio.h>int main()
{char input[] = "123 3.14";int num;float f;// 使用 sscanf() 从字符串中读取整数和浮点数int result = sscanf(input, "%d %f", &num, &f);if (result == 2){printf("成功读取整数: %d, 浮点数: %.2f\n", num, f);}else {printf("读取数据失败\n");}return 0;
}

输出:

成功读取整数: 123, 浮点数: 3.14


函数的注意事项:

  1. 变量地址传递:在传递用于存储读取数据的变量时,必须传递它们的地址(使用 & 运算符)否则函数无法将数据正确存储到变量中。

    • 例如:对于整数变量 int num,应该传递 &num
  2. 缓冲区溢出风险:当使用 %s 格式说明符读取字符串时,如果没有限制读取的字符数量,可能会导致缓冲区溢出。

    • 可以使用 %nsn 为正整数)的形式来限制读取的最大字符数
      • 例如: %19s 表示最多读取 19 个字符,会预留一个位置给字符串结束符 '\0'
    #include <stdio.h>int main() 
    {char input[] = "This is a very long string";char str[10];// 限制读取的字符数,避免缓冲区溢出sscanf(input, "%9s", str); //最多读取9个字符,留一个位置给字符串结尾的空字符 \0printf("读取的字符串: %s\n", str);return 0;
    }
    

    输出:

    读取的字符串: This

    分析

    1. 为什么输出是 "This"
      • sscanf 使用 %9s 格式说明符,表示最多读取 9 个字符。
      • 但是,%s 在读取时会遇到空白字符(如空格)停止。
      • input 中,第一个单词是 "This",后面有一个空格,因此 sscanf 读取到 "This" 后停止。

    代码示例:使用正则表达[^\n]式让 sscanf 函数可以读取包括空格的字符串

    #include <stdio.h>int main() 
    {char input[] = "This is a very long string";char str[20]; // 增大缓冲区大小// 读取直到换行符(不包括换行符)sscanf(input, "%19[^\n]", str); printf("读取的字符串: %s\n", str);return 0;
    }
    
  3. 格式匹配format 字符串中的普通字符必须与 str 中的对应字符完全匹配。

    • 例如:format"x = %d",那么 str 必须以 "x = " 开头,后面接着一个整数,否则读取会失败。

sprintf()sscanf() 的区别:

特性sprintf()sscanf()
功能将格式化数据写入字符串从字符串中读取格式化数据
目标字符串 str字符串 str
返回值写入的字符数成功读取的输入项数量
适用场景生成格式化字符串解析格式化字符串

atoi()

函数的介绍:

atoi():用于将字符串转换为整数。

  • atoi()是 C 语言标准库 <stdlib.h> 中的一个函数。
  • 它的名字是 “ASCII to Integer” 的缩写。

atoi() 函数会扫描输入的字符串,跳过前面的空白字符(如:空格、制表符 \t、换行符 \n等),然后尝试将后续的字符序列转换为一个整数。

转换过程会在遇到非数字字符(除了开头的正负号)时停止。

  • 符号处理:字符串开头可以有一个可选的正负号(+-),用于表示整数的正负性。
  • 数字识别:函数会识别字符串中的数字字符(0 - 9),并将其转换为对应的整数值。
  • 终止条件:当遇到非数字字符(除开头的正负号)或字符串末尾时,转换停止。

函数的原型:

int atoi(const char *nptr);
  • nptr:是指向要转换为整数的字符串的指针。

函数的返回值:

  • 若字符串能转换为有效的整数:函数返回转换后的整数值
  • 若字符串不能转换为有效的整数:函数返回 0

函数的使用:

#include <stdio.h>
#include <stdlib.h>int main()
{char str1[] = "1234";char str2[] = "   -567";char str3[] = "12abc";char str4[] = "abc123";int num1 = atoi(str1);int num2 = atoi(str2);int num3 = atoi(str3);int num4 = atoi(str4);printf("str1 转换后的整数: %d\n", num1);printf("str2 转换后的整数: %d\n", num2);printf("str3 转换后的整数: %d\n", num3);printf("str4 转换后的整数: %d\n", num4);return 0;
}

1.处理数字字符

#include <stdio.h>
#include <stdlib.h>int main()
{const char* str1 = "12345";const char* str2 = "-6789";const char* str3 = "   42";int num1 = atoi(str1);int num2 = atoi(str2);int num3 = atoi(str3);printf("str1 转换后的整数: %d\n", num1);printf("str2 转换后的整数: %d\n", num2);printf("str3 转换后的整数: %d\n", num3);return 0;
}

输出:

str1 转换后的整数: 12345
str2 转换后的整数: -6789
str3 转换后的整数: 42

2.处理非数字字符

#include <stdio.h>
#include <stdlib.h>int main()
{const char* str1 = "123abc";const char* str2 = "abc123"; //开头就是非数字字符,无法转换为有效的整数const char* str3 = "12.34";int num1 = atoi(str1);int num2 = atoi(str2);int num3 = atoi(str3);printf("str1 转换后的整数: %d\n", num1);printf("str2 转换后的整数: %d\n", num2);printf("str3 转换后的整数: %d\n", num3);return 0;
}

输出:

str1 转换后的整数: 123
str2 转换后的整数: 0
str3 转换后的整数: 12

3.处理溢出

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>int main()
{const char* str1 = "2147483647";  // INT_MAXconst char* str2 = "2147483648";  // 超出 INT_MAXint num1 = atoi(str1);int num2 = atoi(str2);printf("str1 转换后的整数: %d\n", num1);printf("str2 转换后的整数: %d\n", num2);return 0;
}

输出:

str1 转换后的整数: 2147483647
str2 转换后的整数: 2147483647

函数的注意事项:

  1. 错误处理有限:atoi 函数在遇到无法转换的情况时,只是简单地返回 0,没有提供更详细的错误信息。

    • 因此:无法区分字符串本身表示的就是 0 还是转换失败导致返回 0
  2. 溢出问题:atoi 函数不会检查转换结果是否会导致整数溢出。

    • 如果字符串表示的数值超出了 int 类型的表示范围,可能会得到错误的结果。
      • 例如:尝试将一个非常大的数值字符串转换为 int 时,可能会发生溢出。
  3. 空白字符:atoi 函数会跳过开头的空白字符,但不会跳过字符串中间的空白字符。

👨‍💻 博主正在持续更新C语言基础系列中。
❤️ 如果你觉得内容还不错,请多多点赞。

⭐️ 如果你觉得对你有帮助,请多多收藏。(防止以后找不到了)

👨‍👩‍👧‍👦 C语言基础系列 持续更新中~,后续分享内容主要涉及 C++全栈开发 的知识,如果你感兴趣请多多关注博主。

相关文章:

C语言基础之【指针】(下)

C语言基础之【指针】&#xff08;下&#xff09; 指针和字符串字符指针字符指针做函数参数const修饰的指针变量指针数组做为main函数的形参项目开发常用字符串应用模型while和do-while模型两头堵模型字符串反转模型 字符串处理函数strchr()strrchr()strstr()strtok()strcpy()st…...

Deepseek中的MoE架构的改造:动态可变参数激活的MoE混合专家架构(DVPA-MoE)的考虑

大家好,我是微学AI,今天给大家介绍一下动态可变参数激活MoE架构(Dynamic Variable Parameter-Activated MoE, DVPA-MoE)的架构与实际应用,本架构支持从7B到32B的等多档参数动态激活。该架构通过细粒度难度评估和分层专家路由,实现“小问题用小参数,大问题用大参数”的精…...

【0012】Python函数详解

如果你觉得我的文章写的不错&#xff0c;请关注我哟&#xff0c;请点赞、评论&#xff0c;收藏此文章&#xff0c;谢谢&#xff01; 本文内容体系结构如下&#xff1a; 编写代码往往是为了实现特定的功能&#xff0c;如果需要使用功能多次&#xff0c;也要写同样的代码多次吗…...

Stable Diffusion教程|快速入门SD绘画原理与安装

什么是Stable Diffusion&#xff0c;什么是炼丹师&#xff1f;根据市场研究机构预测&#xff0c;到2025年全球AI绘画市场规模将达到100亿美元&#xff0c;其中Stable Diffusion&#xff08;简称SD&#xff09;作为一种先进的图像生成技术之一&#xff0c;市场份额也在不断增长&…...

鸿蒙应用开发深度解析:API 14核心特性与实战指南

文章目录 一、HarmonyOS API 14架构升级解析1.1 系统架构演进1.2 核心能力对比 二、ArkUI 3.0开发实战2.1 声明式UI完整示例2.2 自定义组件开发 三、分布式能力深度开发3.1 跨设备数据同步流程3.2 分布式数据库操作 四、系统能力扩展开发4.1 后台任务管理4.2 硬件服务调用 五、…...

docker中kibana启动后,通过浏览器访问,出现server is not ready yet

问题&#xff1a;当我在浏览器访问kibana时&#xff0c;浏览器给我报了server is not ready yet. 在网上试了很多方法&#xff0c;都未能解决&#xff0c;下面是我的方法&#xff1a; 查看kibana日志&#xff1a; docker logs -f kibana从控制台打印的日志可以发现&#xff…...

2025年天梯赛第1场选拔赛

目录 A:徐老师的积木山峰 B:徐老师的最长上升子序列 C:徐老师的机器命令 D:徐老师的地下堡 E:徐老师的新鲜羊腿 F:徐老师的黄金矿工 G:徐老师的成绩统计 H:春节糖果 I:幸运函数 J:好坏钥匙 A:徐老师的积木山峰 徐老师有 n 块积木排成一排&#xff0c;从左往右数编号依次为 1∼…...

28-文本左右对齐

给定一个单词数组 words 和一个长度 maxWidth &#xff0c;重新排版单词&#xff0c;使其成为每行恰好有 maxWidth 个字符&#xff0c;且左右两端对齐的文本。 你应该使用 “贪心算法” 来放置给定的单词&#xff1b;也就是说&#xff0c;尽可能多地往每行中放置单词。必要时可…...

SpringBoot校园管理系统设计与实现

在现代校园管理中&#xff0c;一个高效、灵活的管理系统是不可或缺的。本文将详细介绍基于SpringBoot的校园管理系统的设计与实现&#xff0c;涵盖管理员、用户和院校管理员三大功能模块&#xff0c;以及系统的部署步骤和数据库配置。 管理员功能模块 管理员是系统的核心管理…...

thunder bird 配置邮箱

1.配 outlook https://cn.windows-office.net/?p22940 2.配 qq 邮箱 https://blog.csdn.net/lx_ros/article/details/124831850 3.QQ邮箱的授权码在 账号与安全 4.qq 邮箱 更换 foxmail 邮箱名 https://www.yigujin.cn/blog/p10094.html 结语 感觉网上搜到的都不咋好&…...

机器学习中的线性代数:奇异值分解 SVD

线性代数 奇异值分解&#xff08;SVD&#xff09; 参考资料&#xff1a; 超详细&#xff01;彻底搞懂矩阵奇异值分解&#xff08;SVD&#xff09;本质计算应用&#xff01;_哔哩哔哩_bilibili 非常好的视频&#xff0c;本文内容主要来自于该视频&#xff0c;在此表示感谢&#…...

机器学习深度学习基本概念:logistic regression和softmax

逻辑回归用来处理二分类问题 softmax用来处理多分类问题&#xff1a;比如llm在generate的时候&#xff0c;每个batch里面的一个样本的一个一次generate就是softmax生成一个大小为vocab_size的向量的概率分布&#xff0c;然后再采样 逻辑回归&#xff08;logistic regression&…...

机器学习(六)

一&#xff0c;决策树&#xff1a; 简介&#xff1a; 决策树是一种通过构建类似树状的结构&#xff08;颠倒的树&#xff09;&#xff0c;从根节点开始逐步对数据进行划分&#xff0c;最终在叶子节点做出预测结果的模型。 结构组成&#xff1a; 根节点&#xff1a;初始的数据集…...

在 Maven 中使用 <scope> 元素:全面指南

目录 前言 在 Maven 中&#xff0c; 元素用于定义依赖项的作用范围&#xff0c;即依赖项在项目生命周期中的使用方式。正确使用 可以帮助我们优化项目的构建过程&#xff0c;减少不必要的依赖冲突&#xff0c;并提高构建效率。本文将详细介绍 的使用步骤、常见作用范围、代码…...

Manus邀请码如何申请,有哪些办法

Manus是由Monica团队推出的一款通用型AI智能体产品&#xff0c;旨在通过自主任务规划与执行能力&#xff0c;将用户的想法转化为实际成果。它不仅能够理解复杂指令&#xff0c;还能通过调用虚拟环境中的工具&#xff08;如浏览器、代码编辑器、文件处理器等&#xff09;&#x…...

大型WLAN组网部署(Large scale WLAN network deployment)

大型WLAN组网部署 大型WLAN网络关键技术 技术 作用 VLAN Pool 通过VLAN Pool把接入的用户分配到不同的VLAN&#xff0c;可以减少广播域&#xff0c;减少网络中的广播报文&#xff0c;提升网络性能。 DHCP Option 43 & 52 当AC和AP间是三层组网时&#xff0c;AP通过…...

MQ保证消息的顺序性

在消息队列&#xff08;MQ&#xff09;中保证消息的顺序性是一个常见的需求&#xff0c;尤其是在需要严格按顺序处理业务逻辑的场景&#xff08;例如&#xff1a;订单创建 → 支付 → 发货&#xff09;。 一、消息顺序性被破坏的原因 生产者异步/并行发送&#xff1a;消息可能…...

SQL Server查询计划操作符(7.3)——查询计划相关操作符(9)

7.3. 查询计划相关操作符 78)Repartition Streams:该操作符消费多个输入流并产生多个输出流。期间,记录内容与格式保持不变。如果查询优化器使用一个位图过滤(bitmap filter),则输出流中的数据行数将会减少。一个输入流的每行记录被放入一个输出流。如果该操作符保留顺序…...

杨校老师课堂之零基础入门C++备战信息学奥赛-基础篇

零基础快速入门C C学习路线一、基础语法1. C基础框架2. C语言输出3. C 语言输入4. C 数据类型5. C 赋值6. 运算符与表达式7. 控制结构语句7.1 if分支结构语句7.1.1 单分支结构语句7.1.2 双分支结构语句7.1.3 多分支结构语句 7.2 switch开关语句 8. 循环结构语句8.1 for循环8.2 …...

wxWidgets GUI 跨平台 入门学习笔记

准备 参考 https://wiki.wxwidgets.org/Microsoft_Visual_C_NuGethttps://wiki.wxwidgets.org/Tools#Rapid_Application_Development_.2F_GUI_Buildershttps://docs.wxwidgets.org/3.2/https://docs.wxwidgets.org/latest/overview_helloworld.htmlhttps://wizardforcel.gitb…...

Aws batch task 无法拉取ECR 镜像unable to pull secrets or registry auth 问题排查

AWS batch task使用了自定义镜像&#xff0c;在提作业后出现错误 具体错误是ResourceInitializationError: unable to pull secrets or registry auth: The task cannot pull registry auth from Amazon ECR: There is a connection issue between the task and Amazon ECR. C…...

亚信安全发布2024威胁年报和2025威胁预测

在当今数字化时代&#xff0c;网络空间已成为全球经济、社会和国家安全的核心基础设施。随着信息技术的飞速发展&#xff0c;网络连接了全球数十亿用户&#xff0c;推动了数字经济的蓬勃发展&#xff0c;同时也带来了前所未有的安全挑战。2024年&#xff0c;网络安全形势愈发复…...

verb words

纠正correct remedy 修正modify 协商 confer 磋商/谈判 negotiate 通知notice notify *宣布announce 声明declare 宣告 declare *颁布 promulgate /introduce 协调coordinate 评估evaluate assess 撤离evacuate *规定stipulate 参与participate&#xff0c; 涉及refer…...

程序诗篇里的灵动笔触:指针绘就数据的梦幻蓝图<12>

大家好啊&#xff0c;我是小象٩(๑ω๑)۶ 我的博客&#xff1a;Xiao Xiangζั͡ޓއއ 很高兴见到大家&#xff0c;希望能够和大家一起交流学习&#xff0c;共同进步。 目录 一、回调函数二、qsort2.1 使用qsort函数排序整型数据2.2 使用qsort排序结构数据2.3 qsort函数的模…...

视频录像机视频通道是指什么

视频录像机的视频通道是指摄像机在监控矩阵或硬盘录像机设备上的视频输入的物理位置。 与摄像头数量关系&#xff1a;在视频监控系统中&#xff0c;有多少个摄像头就需要多少路视频通道&#xff0c;通道数量决定了视频录像机可接入摄像头的数量&#xff0c;一般硬盘录像机有4路…...

MySQL 实战 4 种将数据同步到ES方案

文章目录 1. 前言2. 数据同步方案 2.1 同步双写2.2 异步双写2.3 定时更新2.4 基于 Binlog 实时同步 3. 数据迁移工具选型 3.1 Canal3.2 阿里云 DTS3.3 Databus3.4 Databus和Canal对比3.4 其它 4. 后记 上周听到公司新同事分享 MySQL 同步数据到 ES 的方案&#xff0c;发现很有…...

sqlserver中的锁模式 | SQL SERVER如何开启MVCC(使用row-versioning)【启用行版本控制减少锁争用】

文章目录 引言锁和隔离级别的关系锁模式之间兼容性I 隔离级别SQLServer默认的隔离级别为:“read commited” (已提交读)在SQLServer2005引入了基于行版本控制的隔离级别。SQL SERVER如何开启MVCC(使用row-versioning)sqlserver开启MVCC后的锁II sqlserver中的锁模式**1、共享…...

拥抱健康养生,开启活力生活

在快节奏的现代生活中&#xff0c;健康养生已成为人们关注的焦点&#xff0c;它不仅是对身体的呵护&#xff0c;更是一种积极的生活态度。 合理饮食是健康养生的基石。我们应秉持均衡膳食的理念&#xff0c;谷物、蔬菜、水果、蛋白质类食物一个都不能少。每天保证足够的蔬菜摄入…...

江科大51单片机笔记【9】DS1302时钟可调时钟(下)

在写代码前&#xff0c;记得把上一节的跳线帽给插回去&#xff0c;不然LCD无法显示 一.DS1302时钟 1.编写DS1302.c文件 &#xff08;1&#xff09;重新对端口定义名字 sbit DS1302_SCLKP3^6; sbit DS1302_IOP3^4; sbit DS1302_CEP3^5;&#xff08;2&#xff09;初始化 因为…...

Python可视化——地理空间型图表(自用)

地图信息可视化的实现就是将不可展开的曲面上的地理坐标信息转化为二维平面进行显示&#xff0c;这个过程也叫地图投影&#xff08;空间三维投影到平面二维&#xff09; 地图投影的要求&#xff1a;等面积、等角度、等距离。总的来说就是映射到二维平面中的任何点通过比例尺放大…...

Python 网络爬虫教程与案例详解

Python 网络爬虫教程与案例详解 在当今数字化时代&#xff0c;数据的价值愈发凸显。Python 作为一门强大的编程语言&#xff0c;在数据获取领域有着广泛的应用&#xff0c;其中网络爬虫便是一项重要的技术。网络爬虫能够自动从网页中提取所需数据&#xff0c;极大地提高了数据…...

最新的前端场景面试题

1、如何实现一个Vue3的弹框组件,你会如何设计? 如果要实现一个 Vue3 的弹框组件,我会从以下几个关键点进行设计: 组件结构:定义组件的基础结构,包括模块(template)、脚本(script)和样式(style);显示和隐藏逻辑:设计和实现弹框的显示和隐藏机制,通常通过传递 pro…...

冲刺高分!挑战7天一篇孟德尔联合meta分析 DAY1-7

Day1 此前我们完成了若干篇关于meta的挑战&#xff0c;这一次挑战想在meta分析基础上进一步创新一些&#xff0c;这一次想要挑战孟德尔联合meta分析的文章&#xff0c;有想学习的师弟师妹跟我们一起完成这波挑战吧&#xff5e; Day1任务收集信息明确选题明确目标期刊精读范文…...

win32汇编环境,对话框中使用树形视图示例二

;运行效果 ;win32汇编环境,对话框中使用树形视图示例二 ;得到树形视图控件Treeview的全路径字符串,这里的方法是由子项向父项挨个找的算法找齐路径 ;直接抄进RadAsm可编译运行。重要部分加备注。 ;下面为asm文件 ;>>>>>>>>>>>>>>&g…...

前端开发10大框架深度解析

摘要 在现代前端开发中&#xff0c;框架的选择对项目的成功至关重要。本文旨在为开发者提供一份全面的前端框架指南&#xff0c;涵盖 React、Vue.js、Angular、Svelte、Ember.js、Preact、Backbone.js、Next.js、Nuxt.js 和 Gatsby。我们将从 简介、优缺点、适用场景 以及 实际…...

tomcat的web管理

进入到conf cd /usr/local/tomcat/conf/备份tomcat-users.xml cp tomcat-users.xml{.,bak}编辑tomcat-users.xml vim tomcat-users.xml增加以下内容 配置tomcat-users.xml <role rolename"manager-gui"/><role rolename"admin-gui"/><use…...

类和对象(上)

1.面向过程与面向对象的初步认识 面向过程&#xff1a;以步骤为中心&#xff0c;适合简单逻辑&#xff0c;但复杂系统易混乱。 面向对象&#xff1a;以对象职责为中心&#xff0c;通过抽象和模块化应对复杂需求。 C语言&#xff1a;面向过程&#xff0c;关注的是过程&#xff0…...

springcloud智慧工地物联网云管理系统源码

智慧工地以物联网云平台为核心&#xff0c;基于智慧工地物联网云平台与现场多个子系统的互联&#xff0c;实现现场各类工况数据采集&#xff0c;存储、分析与应用。通过接入智慧工地物联网云平台的多个子系统板块&#xff0c;根据现场管理实际需求灵活组合&#xff0c;实现一体…...

SLAM评估工具安装及使用EVO(Ubuntu20.04安装evo)--缺少 onnx 库还有Pandas 版本不兼容解决

介绍一下我的是ubuntu20.04.机载电脑是orinnx&#xff0c;通过源码烧写的系统。 首先打开终端&#xff0c;输入 pip install evo --upgrade --no-binary evo 安装过程中出现如下问题 缺少 onnx 库还有Pandas 版本不兼容&#xff0c; ONNX&#xff08;Open Neural Network E…...

K8S学习之基础十五:k8s中Deployment扩容缩容

deployment扩容缩容比较简单&#xff0c;下面介绍两种常用方法 vi deploy-demo.yaml kind: Deployment metadata:name: myapp-v1 spec:replicas: 2selector:matchLabels:app: myappversion: v1template:metadata:labels:app: myappversion: v1spec:containers:- name: myappim…...

ClickHouse 中出现 DB::Exception: Too many parts 错误

在 ClickHouse 中出现 DB::Exception: Too many parts 错误&#xff0c;通常是由于表中数据分片&#xff08;parts&#xff09;数量超过系统限制&#xff0c;导致合并&#xff08;merge&#xff09;操作无法及时处理。以下是逐步解决方案&#xff1a; 1. 理解问题原因 MergeTr…...

PPT 小黑第20套

对应大猫21 Word转PPT 图片也得复制 题目要求两套PPT母板&#xff0c;应用不同版式&#xff08;版式那就可以选&#xff09; 竖排文字...

大模型管理工具:LLaMA-Factory

目录 一、安装与环境配置 二、​启动 Web 界面 三、数据准备 四、模型训练 五、模型评估 七、模型导出 八、API服务部署 LLaMA-Factory 是一个开源的大语言模型&#xff08;LLM&#xff09;微调框架&#xff0c;旨在简化大规模模型的训练、微调和部署流程。它支持多种主…...

【机器人栅格地图】基于鹭鹰算法SBOA实现机器人栅格地图路径规划(目标函数:最短距离)附Matlab代码

基于鹭鹰算法&#xff08;SBOA&#xff09;的机器人栅格地图路径规划实现 一、鹭鹰算法&#xff08;SBOA&#xff09;的基本原理 鹭鹰优化算法&#xff08;Secretary Bird Optimization Algorithm, SBOA&#xff09;是一种新型元启发式算法&#xff0c;灵感源自鹭鹰的捕猎和逃…...

【Linux篇】版本控制器-Git

&#x1f4cc; 个人主页&#xff1a; 孙同学_ &#x1f527; 文章专栏&#xff1a;Liunx &#x1f4a1; 关注我&#xff0c;分享经验&#xff0c;助你少走弯路&#xff01; 文章目录 1.如何理解版本控制&#xff1f;2.Git的操作补充细节问题 1.如何理解版本控制&#xff1f; 版…...

论文阅读:KAM-CoT: Knowledge Augmented Multimodal Chain-of-Thoughts Reasoning

论文来源&#xff1a;AAAI 2024 论文地址&#xff1a;https://ojs.aaai.org/index.php/AAAI/article/view/29844 Abstract LLM通过利用能够逐步思考的思维链在NLP任务中取得了很好的性能&#xff0c;但是为LLM扩展多模态能力时计算成本高&#xff0c;且需要大量的硬件资源。…...

linux内存页块划分及位图存储机制

page_alloc.c - mm/page_alloc.c - Linux source code v5.4.285 - Bootlin Elixir Cross Referencer 一. 什么是页块&#xff08;Pageblock&#xff09;&#xff1f; 定义&#xff1a;页块是物理内存中的一个连续区域&#xff0c;由 2^pageblock_order 个物理页&#xff08;Pag…...

一台云工作站是否能通过共享云桌面让10人流畅进行三维设计

云工作站&#xff0c;作为一种基于云计算技术的远程工作站解决方案&#xff0c;它将高性能的计算资源集中在云端服务器上&#xff0c;用户通过网络访问这些资源&#xff0c;实现高效、灵活的办公和创作环境。而三维设计&#xff0c;尤其是涉及复杂模型、高精度渲染等领域&#…...

安卓应用之服务

服务 服务也是四大组件之一&#xff0c;用于执行长时间运行操作的组件&#xff0c;它与用户界面&#xff08;UI&#xff09;是分开的&#xff0c;因此即使用户切换到其他应用&#xff0c;服务依然可以继续运行。主要用于处理一些不需要用户交互的任务。例如&#xff0c;播放音…...

【Vue CLI脚手架开发】——6.scoped样式

文章目录 一、scoped是什么二、应用案例1.使用代码2.原理3父组件App未添加scoped影响 一、scoped是什么 我们知道vue为了防止css样式污染&#xff0c;在每个组件中提供了 scoped属性进行限定css作用域&#xff1b;当<style>标签有 scoped 属性时&#xff0c;它的 CSS 只…...