C语言学习:字符函数和字符串函数(内容丰富且易懂)

1、 字符函数

一、字符分类函数(ctype.h 头文件)

这些函数用于判断字符的类型使用前需包含头文件 #include <ctype.h>

函数 功能说明
iscntrl 判断是否为控制字符
isspace 判断是否为空白字符(空格、换页\f、换行\n、回车\r、制表符\t、垂直制表符\v
isdigit 判断是否为十进制数字 '0'~'9'
isxdigit 判断是否为十六进制数字(含 0-9a-fA-F
islower 判断是否为小写字母 a-z
isupper 判断是否为大写字母 A-Z
isalpha 判断是否为字母(a-zA-Z
isalnum 判断是否为字母或数字(a-zA-Z0-9
ispunct 判断是否为标点符号(可打印的非字母、非数字字符)
isgraph 判断是否为图形字符(除空格外的可打印字符)
isprint 判断是否为可打印字符(含图形字符和空格)

💡 小细节:这些函数的参数是 int 类型 ,必须传入**unsigned char 类型的值或 EOF**,否则行为未定义。

如果满足功能要求 就会返回一个非0值 如果不满足就会返回0

例如 用isdigit判断是否为十进制数

练习: 将字符串中的小写字母转成大写,其他字符不变

cpp 复制代码
//练习 将字符串中的小写字母转成大写,其他字符不变
#include<stdio.h>
#include<ctype.h>
int main()
{
	int i = 0;
	char arr[] = "I am a Student!";
	while (arr[i] != '\0')
	{
		if (islower(arr[i]))//islower判断字符是否为小写字母
		{
			arr[i] -= 32;//转换成大写
		}
		printf("%c", arr[i]);
		i++;
	}
	return 0;
}

运行结果为I AM A STUDENT!

二、字符转换函数(ctype.h 头文件)

C 语言标准库提供了两个专门用于字符大小写转换的函数:

复制代码
// 功能:将大写字母转为小写
// 说明:如果传入的不是大写字母,会原样返回
int tolower(int c);

// 功能:将小写字母转为大写
// 说明:如果传入的不是小写字母,会原样返回
int toupper(int c);

核心说明

  • 两个函数的参数和返回值 都是 int 类型 ,这是为了兼容 EOF(通常为 -1)的情况。
  • 使用前必须包含头文件 #include <ctype.h>
  • 相比直接用 c -= 32 这种硬编码方式 ,使用标准库函数可读性更强、兼容性更好(不受字符集影响)。

1. 示例代码解析(小写转大写)

复制代码
#include <stdio.h>
#include <ctype.h>

int main()
{
    int i = 0;
    char str[] = "Test String.\n";
    char c;
    while (str[i]) // 遍历字符串,直到遇到 '\0'
    {
        c = str[i];
        if (islower(c)) // 判断是否为小写字母
            c = toupper(c); // 转为大写
        putchar(c); // 输出字符
        i++;
    }
    return 0;
}

运行结果

复制代码
TEST STRING.

优化说明

  • 这段代码也可以去掉 islower 判断 ,直接写 c = toupper(c); ,因为**toupper 对非小写字母的字符会原样返回。**

  • 完整等价写法:

    复制代码
    while (str[i])
    {
        putchar(toupper(str[i]));
        i++;
    }

2、字符串函数

一、strlen 函数基础

1. 函数原型

复制代码
#include <string.h>
size_t strlen(const char *str);
  • 头文件<string.h>
  • 功能 :统计字符串中 '\0' 之前的字符个数(不包含 '\0' 本身)。
  • 参数str 是指向要统计的字符串的指针。
  • 返回值size_t 类型无符号整数 ),表示字符串长度。

2. 代码演示

复制代码
#include <stdio.h>
#include <string.h>

int main()
{
    const char* str = "abcdef";
    printf("%zd\n", strlen(str)); // 输出:6
    return 0;
}

3. 使用注意事项

  1. 字符串必须以 '\0' 结尾,否则 strlen 会一直往后找**,导致未定义行为** (越界访问)。

  2. 返回值 size_t无符号整数这是一个高频易错点

    复制代码
    if (strlen("bbb") - strlen("abcdef") > 0) {
        printf("bbb 更长\n");
    } else {
        printf("abcdef 更长\n");
    }

    上面的代码会永远输出 bbb 更长 ,因为无符号数相减的结果 永远是非负数不会得到负数。


二、strlen 的三种模拟实现

方式 1:计数器法(最直观)

复制代码
int my_strlen(const char *str)
{
    int count = 0;
    assert(str != NULL); // 防止传入空指针
    while (*str != '\0')
    {
        count++;
        str++;
    }
    return count;
}

方式 2:递归法(不使用临时变量)

复制代码
int my_strlen(const char *str)
{
    assert(str != NULL);
    if (*str == '\0')
        return 0;
    else
        return 1 + my_strlen(str + 1);
}

方式 3:指针 - 指针法(指针运算)

复制代码
int my_strlen(const char *str)
{
    assert(str != NULL);
    const char *p = str;
    while (*p != '\0')
    {
        p++;
    }
    return p - str;
}

💡 原理:两个指向同一数组的指针相减 ,结果是它们之间的元素个数。

三、strcpy 基础介绍

1. 函数原型

复制代码
#include <string.h>
char* strcpy(char* destination, const char* source);
  • 头文件<string.h>
  • 功能 :把 source 指向的字符串,完整拷贝destination 指向的内存中,直到遇到 '\0' 为止,并且会把 '\0' 也拷贝过去
  • 返回值 :返回**destination** 的起始地址(方便链式调用)。

2. 代码演示

复制代码
#include <stdio.h>
#include <string.h>

int main()
{
    char arr1[10] = {0};
    char arr2[] = "hello";
    strcpy(arr1, arr2);
    printf("%s\n", arr1); // 输出:hello
    return 0;
}

四、使用注意事项(高频考点)

  1. 源字符串必须以 '\0' 结尾 :如果 source 没有结束符strcpy 会一直拷贝导致越界访问
  2. 会拷贝 '\0' :目标数组的末尾也会被自动加上 '\0',保证拷贝后是合法字符串。
  3. 目标空间必须足够大destination 的大小 必须能容纳 source 的所有字符 (包括 '\0'),否则会缓冲区溢出。
  4. 目标空间必须可修改不能传入字符串常量 (如 char* p = "abc"; strcpy(p, "def"); 会崩溃)

五、strcpy 的模拟实现

复制代码
#include <stdio.h>
#include <assert.h>

char* my_strcpy(char* dest, const char* src)
{
    // 保存目标地址起始位置,因为后面dest会移动
    char* ret = dest;
    // 空指针检查
    assert(dest != NULL);
    assert(src != NULL);

    // 核心拷贝逻辑
    // *dest++ = *src++ 会先赋值,再自增
    // 赋值结果就是被拷贝的字符,当拷贝到'\0'时,表达式结果为0,循环结束
    while ((*dest++ = *src++))
    {
        // 循环体为空,所有操作都在条件里完成
    }
    return ret;
}

int main()
{
    char arr1[10] = {0};
    char arr2[] = "hello";
    my_strcpy(arr1, arr2);
    printf("%s\n", arr1); // 输出:hello
    return 0;
cpp 复制代码
//模拟实现strcpy
#include<assert.h>
char* my_strlen(char* dest, char* src)
{
	char* ret = dest;
	assert(dest && src);
	while (*src != '\0')
	{
		*dest = *src;
		dest++;
		src++;
	}
	*dest = *src;//\0也要传入
	return ret;
} 
int main()
{
	char arr1[] = "abcdef";
	char arr2[20] = "--------";
	char*pr = my_strlen(arr2, arr1);
	printf("%s\n", pr);
	printf("%s\n", arr2);
}

一. strcat 是什么?

strcatC 标准库<string.h> )提供的字符串拼接函数 ,作用是把一个字符串追加到另一个字符串的末尾

1. 函数原型

复制代码
char *strcat(char *destination, const char *source);
  • 参数说明
    • destination目标字符串的起始地址必须是可修改的内存空间,且有足够大的容量
    • source要追加的源字符串const 修饰 ,保证内容不会被修改
  • 返回值 :返回 destination 的起始地址,方便链式调用。

2. 代码演示解析

复制代码
#include <stdio.h>
#include <string.h>

int main()
{
    char arr1[20] = "hello ";
    char arr2[] = "world";
    strcat(arr1, arr2);
    printf("%s\n", arr1); // 输出:hello world
    return 0;
}
  • 执行逻辑:
    1. strcat 先找到 arr1 字符串末尾的 '\0'
    2. '\0' 的位置开始 ,把**arr2 的字符逐个复制过去** ,直到遇到 arr2'\0'
    3. 最终**arr1 里的内容变成 "hello world\0"**。

3. 使用注意事项(重点⚠️)

  1. 源字符串必须以 '\0' 结尾 :否则 strcat 不知道复制到哪里停止,会一直向后访问内存 ,导致越界访问(野指针问题)。
  2. 目标字符串也必须包含 '\0'strcat 是从目标字符串的**'\0' 位置开始追加的** ,如果目标字符串没有 '\0'就无法确定追加的起始位置。
  3. 目标空间必须足够大 :目标数组的容量必须能装下原字符串 + 追加的字符串 + 最后的 '\0',否则会发生缓冲区溢出,导致程序崩溃或内存错误。
  4. 目标空间必须可修改 :不能给 strcat****传入字符串常量 (比如 char *arr = "hello";),因为常量字符串存储在只读内存区,无法修改。

4. 模拟实现 my_strcat 逐行解析

复制代码
#include <stdio.h>
#include <assert.h>

char* my_strcat(char *dest, const char* src)
{
    // 保存目标字符串的起始地址,最后要返回它
    char *ret = dest;
    // 断言:防止传入空指针,提高代码健壮性
    assert(dest != NULL);
    assert(src != NULL);

    // 1. 让 dest 移动到目标字符串的末尾 '\0' 处
    while(*dest)
    {
        dest++;
    }

    // 2. 从 '\0' 位置开始,复制 src 的字符到 dest,直到遇到 src 的 '\0'
    while((*dest++ = *src++))
    {
        ;
    }

    // 返回目标字符串的起始地址
    return ret;
}

int main()
{
    char arr1[20] = "hello ";
    char arr2[] = "world";
    my_strcat(arr1, arr2);
    printf("%s\n", arr1);
    return 0;

一、 strcmp 是什么?

strcmp 是 C 标准库 <string.h> 提供的字符串比较函数 ,用来按 ASCII 码值逐字符比较两个字符串的大小。

1. 函数原型

复制代码
int strcmp(const char *str1, const char *str2);
  • 参数说明
    • str1:要比较的第一个字符串
    • str2:要比较的第二个字符串
    • 两个参数都用 const 修饰保证函数内部不会修改原字符串
  • 返回值 (标准规定):
    • > 0str1 大于 str2
    • == 0str1 等于 str2
    • < 0str1 小于 str2注意:不同编译器的具体返回值可能不同(比如有的返回 1/-1,有的返回差值),但符号是固定的

2. 代码演示解析

复制代码
#include <stdio.h>
#include <string.h>

int main()
{
    char arr1[] = "abcdef";
    char arr2[] = "abq";
    int ret = strcmp(arr1, arr2);
    printf("%d\n", ret);

    if(ret > 0)
        printf("arr1 > arr2\n");
    else if(ret == 0)
        printf("arr1 == arr2\n");
    else
        printf("arr1 < arr2\n");

    return 0;
}
  • 执行逻辑
    1. 逐字符比较 arr1arr2
      • 第 1 个字符 'a' == 'a',继续比较下一个;
      • 第 2 个字符 'b' == 'b',继续比较下一个;
      • 第 3 个字符**'c''q'** :'c' 的 ASCII 码值(99)小于 'q'(113),比较停止。
    2. 函数返回一个小于 0 的值 ,所以最终会输出**arr1 < arr2。**

3. 模拟实现 my_strcmp 逐行解析

复制代码
#include <stdio.h>
#include <assert.h>

int my_strcmp(const char *str1, const char *str2)
{
    assert(str1 != NULL); // 断言防止空指针
    assert(str2 != NULL);

    // 当两个字符相等且都不是 '\0' 时,继续比较下一个
    while(*str1 == *str2)
    {
        if(*str1 == '\0') // 两个字符串同时到末尾,说明完全相等
            return 0;
        str1++;
        str2++;
    }

    // 遇到不相等的字符,返回两者的 ASCII 差值
    return *str1 - *str2;
}

int main()
{
    char arr1[] = "abcdef";
    char arr2[] = "abq";
    int ret = my_strcmp(arr1, arr2);
    printf("%d\n", ret);
    return 0;
}

4. 核心注意事项 & 易错点

  1. 比较的是 ASCII 码值,不是字符串长度 很多新手会误以为长字符串一定更大,比如 "abcd""ab" 比较,实际上第 3 个字符 'c' > '\0' 所以 "abcd" 更大和长度无关。

  2. 必须用 strcmp 比较字符串,不能用 ==

    复制代码
    // 错误写法
    if(arr1 == arr2) { ... }

    这里的 == 比较的是两个数组的地址 ,而不是字符串内容,永远不会相等

  3. 字符串必须以 '\0' 结尾 如果字符串没有结束符,strcmp 会一直向后访问内存导致越界访问,结果不可预测。

一、 strncpy 是什么?

strncpy 是 C 标准库 <string.h> 提供的安全版字符串拷贝函数相比 strcpy它多了一个参数可以限制拷贝的字符数,避免缓冲区溢出。

1. 函数原型

复制代码
char *strncpy(char *destination, const char *source, size_t num);
  • 参数说明
    • destination:目标空间的起始地址(必须可修改,且空间足够)
    • source:源字符串的起始地址(被 const 修饰,不可修改)
    • num:最多从 source 拷贝的字符个数
  • 返回值 :返回**destination 的起始地址**,方便链式调用。

2. 代码演示解析

复制代码
#include <stdio.h>
#include <string.h>

int main()
{
    char arr1[20] = {0};  // 初始化为全 0
    char arr2[] = "abcdefghi";
    char* str = strncpy(arr1, arr2, 5);
    
    printf("%s\n", arr1);  // 输出:abcde
    printf("%s\n", str);   // 输出:abcde
    
    return 0;
}
  • 执行逻辑:
    1. arr2拷贝前 5 个字符 'a' 'b' 'c' 'd' 'e'arr1
    2. 注意:strncpy 不会自动在末尾添加 '\0' !因为**arr1 初始化时是全 0** ,所以第 6 个位置本来就是**'\0'** ,所以能正常打印 。如果 arr1 没有初始化,这里可能会出现乱码。

3. strncpy vs strcpy 关键区别

特性 strcpy strncpy
拷贝终止条件 遇到 '\0' 才停止 拷贝满 num 个字符就停止,或遇到 '\0' 提前停止
安全问题 目标空间不足时会越界,导致缓冲区溢出 num 限制,不会超过目标空间大小,更安全
自动补 '\0' 会自动拷贝源字符串的 '\0' 只有当 num 大于源字符串长度时,才会补 '\0' ;否则不补

4. 核心注意事项(⚠️ 新手必看)

  1. 目标空间必须手动确保以 '\0' 结尾 因为 strncpy 不会自动追加结束符,拷贝完成后最好手动加上 '\0'

    复制代码
    strncpy(arr1, arr2, 5);
    arr1[5] = '\0'; // 手动补结束符,防止乱码
  2. num 大于源字符串长度时 strncpy 会把源字符串的**'\0' 也拷贝过去** ,并且会用额外的 '\0' 填充剩余空间,直到填满 num 个字符。

  3. 目标空间必须可修改 不能传入字符串常量(如 char *arr = "hello";),否则会导致程序崩溃。


5. 模拟实现 my_strncpy

复制代码
#include <stdio.h>
#include <assert.h>

char* my_strncpy(char *dest, const char *src, size_t num)
{
    assert(dest != NULL);
    assert(src != NULL);
    
    char *ret = dest;
    // 先拷贝最多 num 个字符
    while(num-- && (*src != '\0'))
    {
        *dest++ = *src++;
    }
    // 如果 src 提前结束,剩下的用 '\0' 填充
    while(num--)
    {
        *dest++ = '\0';
    }
    return ret;
}

一、strncat 是什么?

strncat 是 C 标准库 <string.h> 提供的安全版字符串追加函数 ,是 strcat 的升级版本通过限制追加的字符数,避免缓冲区溢出。

1.函数原型

复制代码
char *strncat(char *destination, const char *source, size_t num);
  • 参数说明
    • destination:目标字符串的起始地址(必须可修改,且空间足够)
    • source:要追加的源字符串(被 const 修饰,函数内不会修改它)
    • num:最多追加的字符个数
  • 返回值 :返回**destination 的起始地址**,方便链式调用。

2. 代码演示解

复制代码
#include <stdio.h>
#include <string.h>

int main()
{
    char arr1[20] = "hello ";
    char arr2[] = "world";
    char* str = strncat(arr1, arr2, 5);
    
    printf("%s\n", arr1); // 输出:hello world
    printf("%s\n", str);  // 输出:hello world
    
    return 0;
}
  • 执行逻辑:
    1. strncat 先找到**arr1 末尾的** '\0'
    2. '\0' 位置开始 ,从 arr2 中追加最多 num 个字符 (这里是 5 个字符 'w' 'o' 'r' 'l' 'd')。
    3. 关键细节 :追加完成后,strncat自动在末尾补上 '\0',所以不需要手动处理结束符。

3. strcat vs strncat 对比

特性 strcat strncat
追加长度 无限制,直到源字符串的 '\0' num 限制,最多追加 num 个字符
结束符处理 会自动追加源字符串的 '\0' 追加 num 个字符后,一定会自动补上 '\0'
安全风险 目标空间不足时会缓冲区溢出 num 限制,更安全,避免溢出
源字符串要求 必须以 '\0' 结尾 即使没有 '\0' ,也只会追加 num 个字符

4. 核心注意事项(⚠️ 必看)

  1. 目标空间必须足够大 虽然 strncat 限制了追加的字符数,但目标数组的大小必须能装下原字符串 + num 个追加字符 + 最后的 '\0'否则依然会溢出
  2. num 的取值技巧 通常 num 设为目标数组剩余的空间大小减 1这样可以确保追加后还有位置放 '\0'避免越界
  3. strncat 一定会补 '\0' 不管追加了多少个字符,strncat 都会在追加完成后自动加上 '\0'这一点和 strncpy 不同。

5. 补充拓展:模拟实现 my_strncat

复制代码
#include <stdio.h>
#include <assert.h>

char* my_strncat(char *dest, const char *src, size_t num)
{
    assert(dest != NULL);
    assert(src != NULL);
    
    char *ret = dest;
    // 1. 先找到 dest 末尾的 '\0'
    while(*dest)
    {
        dest++;
    }
    // 2. 追加最多 num 个字符
    while(num-- && (*src != '\0'))
    {
        *dest++ = *src++;
    }
    // 3. 自动补上结束符
    *dest = '\0';
    return ret;
}

一、 strncmp 是什么?

strncmp 是 C 标准库 <string.h> 提供的安全版字符串比较函数 ,是 strcmp 的升级版本,可以限制比较的字符个数,避免越界访问。

1.函数原型

复制代码
int strncmp(const char *str1, const char *str2, size_t num);
  • 参数说明
    • str1:要比较的第一个字符串
    • str2:要比较的第二个字符串
    • num:最多比较的字符个数
  • 返回值 (和 strcmp 完全一致):
    • > 0str1 大于 str2
    • == 0str1 等于 str2(在 num 个字符内)
    • < 0str1 小于 str2

2. 代码演示解析

复制代码
#include <stdio.h>
#include <string.h>

int main()
{
    char arr1[] = "abcdef";
    char arr2[] = "abcqw";

    int ret1 = strncmp(arr1, arr2, 3);
    printf("%d\n", ret1); // 输出:0

    int ret2 = strncmp(arr1, arr2, 4);
    printf("%d\n", ret2); // 输出:负数(因为 'd' < 'q')

    return 0;
}
  • 执行逻辑:
    1. num=3 时,只比较前 3 个字符 'a' 'b' 'c'两者完全相同返回 0
    2. num=4 时,比较第 4 个字符 'd''q''d' 的 ASCII 码值小于 'q'返回负数。

3. strcmp vs strncmp 对比

特性 strcmp strncmp
比较长度 无限制,直到遇到 '\0' num 限制,最多比较 num 个字符
安全风险 字符串无结束符时会越界访问 num 限制,不会越界,更安全
适用场景 比较完整字符串是否相等 比较字符串前缀(如版本号、文件头)

4. 模拟实现 strncmp

复制代码
#include <stdio.h>
#include <assert.h>

// 模拟实现 strncmp
int my_strncmp(const char* str1, const char* str2, size_t n)
{
    // 断言:保证指针不为空
    assert(str1 != NULL);
    assert(str2 != NULL);

    // 最多比较 n 个字符
    while (n--)
    {
        // 字符不相等 → 返回差值
        if (*str1 != *str2)
        {
            return *str1 - *str2;
        }
        // 字符相等,但已经到末尾 \0 → 两个字符串完全相等
        else if (*str1 == '\0')
        {
            return 0;
        }
        
        // 继续比较下一个字符
        str1++;
        str2++;
    }

    // 比完 n 个字符都一样 → 相等
    return 0;
}

// 测试
int main()
{
    char arr1[] = "abcdef";
    char arr2[] = "abq";

    // 比较前 3 个字符
    int ret1 = my_strncmp(arr1, arr2, 3);
    printf("%d\n", ret1);  // <0,因为 'c' < 'q'

    // 比较前 2 个字符
    int ret2 = my_strncmp(arr1, arr2, 2);
    printf("%d\n", ret2);  // 0,ab == ab

    return 0;
}

一、strstr 是什么?

strstr 是 C 标准库 <string.h> 提供的字符串查找函数用来在一个主字符串中查找子字符串第一次出现的位置

1. 函数原型

复制代码
char *strstr(const char *str1, const char *str2);
  • 参数说明
    • str1被查找的主字符串
    • str2要查找的子字符串
  • 返回值
    • 找到时:返回子字符串在主字符串中第一次出现起始地址
    • 找不到时:返回 NULL
    • 特殊情况:如果**str2 是空字符串("")** ,直接返回 str1 的起始地址

2. 代码演示解析

复制代码
#include <stdio.h>
#include <string.h>

int main()
{
    char str[] = "This is a simple string";
    char *pch;
    pch = strstr(str, "simple");
    
    if (pch != NULL)
        printf("%s\n", pch); // 输出:simple string
    else
        printf("查找的字符串不存在\n");
    
    return 0;
}
  • 执行逻辑:
    1. str 中查找 "simple" 子串,找到它在第 10 个字符的位置。
    2. strstr 返回 "simple"首字符的地址 ,所以 pch 指向 "simple string"
    3. printf 打印 pch 时,会从 "simple" 开始一直打印到字符串末尾。

3. 模拟实现 my_strstr 逐行解析(暴力模拟法 效率较低)

复制代码
#include <stdio.h>
#include <assert.h>

char *my_strstr(const char *str1, const char *str2)
{
    // 1. 处理空指针和特殊情况:str2 是空字符串时,直接返回 str1
    assert(str1 != NULL);
    assert(str2 != NULL);
    if (*str2 == '\0')
        return (char *)str1;

    // 2. 遍历主字符串的每个位置,尝试匹配子串
    const char *cp = str1; // cp 记录当前尝试匹配的起始位置
    while (*cp != '\0')
    {
        const char *s1 = cp;
        const char *s2 = str2;

        // 3. 从当前位置开始,逐个字符匹配子串
        while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
        {
            s1++;
            s2++;
        }

        // 4. 如果 s2 走到了末尾,说明子串匹配成功
        if (*s2 == '\0')
        {
            return (char *)cp; // 返回匹配成功的起始地址
        }

        // 5. 匹配失败,主字符串的起始位置后移一位
        cp++;
    }

    // 6. 遍历完主字符串都没找到,返回 NULL
    return NULL;
}

关键逻辑拆解

  1. 特殊情况处理 :如果**str2 是空字符串,直接返回 str1。**
  2. 双重循环匹配
    • 外层循环:遍历主字符串的每个位置 ,作为匹配的起点 cp
    • 内层循环:cp 开始逐个字符和子串 str2 比较。
  3. 匹配成功判断 :如果内层循环结束时***s2 == '\0'** ,说明子串的所有字符都匹配上了 ,返回 cp
  4. 匹配失败处理 :如果当前位置匹配失败 ,主字符串的起点**cp 后移一位,** 继续下一轮匹配。

4. 核心注意事项(⚠️ 必看)

  1. 子字符串必须以 '\0' 结尾 否则 strstr 会一直向后访问内存,导致越界访问,结果不可预测。
  2. 返回的是地址,不是索引 比如上面的例子中,pch 不是 "simple"str 中的下标,而是指向它的指针 。你可以用 pch - str 来计算它的下标。
  3. 只返回第一次出现的位置 如果主字符串中存在多个相同的子串,strstr 只会返回第一个的地址。

一、 strtok 是什么?

strtok 是 C 标准库 <string.h> 提供的字符串分割函数 ,它能根据你指定的分隔符把一个字符串拆分成多个子串

1. 函数原型

复制代码
char *strtok(char *str, const char *delim);
  • 参数说明
    • str:要分割的字符串。首次调用时传字符串地址,后续调用传 NULL,表示继续分割同一个字符串。
    • delim分隔符集合 (比如 ". " 表示用 . 和空格作为分隔符)。
  • 返回值
    • 成功时:返回当前分割出来的子串的指针
    • 分割结束时返回 NULL

2. 核心工作原理(必懂)

strtok 有两个关键行为

  1. 破坏性修改原字符串 :它会直接把分隔符替换成 '\0'让每个子串都变成独立的字符串
  2. (特殊性)内部静态变量记录位置 :它会记住上一次分割结束的位置 ,所以后续调用传 NULL它能接着往下分割

3. 代码演示解析

复制代码
#include <stdio.h>
#include <string.h>

int main()
{
    char arr[] = "192.168.6.111";
    const char* sep = ".";
    char* str = NULL;

    // 为了不破坏原字符串,先拷贝一份到 buf
    char buf[30] = {0};
    strcpy(buf, arr);

    // 循环分割
    for (str = strtok(buf, sep); str != NULL; str = strtok(NULL, sep))
    {
        printf("%s\n", str);
    }

    return 0;
}
  • 执行过程:
    1. 第一次调用 strtok(buf, sep):找到第一个 .,把它改成 '\0'返回 "192" 的地址
    2. 第二次调用 strtok(NULL, sep)从上次结束的位置继续找下一个 .,改成 '\0'返回 "168" 的地址。
    3. 重复这个过程,直到找不到分隔符返回 NULL,循环结束。

输出结果:

复制代码
192
168
6
111

4. 必须注意的坑(⚠️ 必看)

  1. 会修改原字符串 分隔符会被替换成 '\0' ,原字符串会被破坏。如果需要保留原数据,一定要先拷贝一份 再分割,像例子里的**buf 一样**。
  2. 不能分割字符串常量 比如 char* arr = "192.168.6.111"; 这种常量字符串,存储在只读内存区,strtok 要修改它会直接导致程序崩溃
  3. 连续分隔符会被当成一个 比如 "a..b". 分割,只会返回 "a""b" ,中间的连续分隔符会被跳过,不会返回空字符串。

一、 strerror 是什么?

strerror 是 C 标准库 <string.h> 提供的错误码转字符串函数它能把一个数字错误码转换成人类能看懂的错误信息字符串

1.函数原型

复制代码
char *strerror(int errnum);
  • 参数说明errnum 是错误码,通常直接传 errno(一个全局变量,记录标准库函数调用后的错误码)。
  • 返回值返回错误信息字符串的首地址。

核心背景:errno 变量

  • errno 是一个全局变量 ,定义在**<errno.h> 中。**
  • 当标准库函数(如 fopenmalloc调用失败时 ,会把对应的错误码写入 errno
  • 错误码是一个数字 ,比如 2 代表 "文件不存在"直接打印很难理解,所以需要 strerror 把它转成文字。

2. 代码演示解析

示例 1:打印所有错误码对应的信息
复制代码
#include <errno.h>
#include <string.h>
#include <stdio.h>

int main()
{
    int i = 0;
    for (i = 0; i <= 10; i++) {
        printf("%d: %s\n", i, strerror(i));
    }
    return 0;
}
  • 输出结果(Windows 环境):

    复制代码
    0: No error
    1: Operation not permitted
    2: No such file or directory
    3: No such process
    4: Interrupted function call
    5: Input/output error
    ...
示例 2:实际场景(文件打开失败)(后续会学习文件指针)
复制代码
#include <stdio.h>
#include <string.h>
#include <errno.h>

int main()
{
    FILE *pFile = NULL;
    pFile = fopen("unexist.ent", "r");
    if (pFile == NULL)
    {
        printf("错误信息是: %s\n", strerror(errno));
        return 1;
    }
    return 0;
}
  • 执行逻辑:
    1. fopen 尝试打开一个不存在的文件调用失败,就会返回一个空指针 并且会设置错误码
    2. errno自动设置为 2(对应 "文件不存在" 的错误码)。
    3. strerror(errno) 把错误码 2 转换成字符串 "No such file or directory"
    4. 最终输出:错误信息是: No such file or directory

一、perror 是什么?

perror<stdio.h> 提供的一站式错误打印函数它会自动读取 errno并把错误信息打印到控制台。

1. 函数原型

复制代码
void perror(const char *str);
  • 参数说明str 是你自定义的提示信息 ,函数会先打印这个字符串再打印一个冒号和空格最后打印错误信息
  • 返回值无返回值,直接输出到屏幕。

2. 代码演示(和上面 strerror 的例子等价)

复制代码
#include <stdio.h>
#include <string.h>
#include <errno.h>

int main()
{
    FILE *pFile = NULL;
    pFile = fopen("unexist.ent", "r");
    if (pFile == NULL)
    {
        perror("错误信息是");
        return 1;
    }
    return 0;
}
  • 输出结果:错误信息是: No such file or directory

3. strerror vs perror 对比

特性 strerror perror
功能 只返回错误信息字符串不直接打印 直接把提示信息 + 错误信息打印到控制台
使用方式 printf("错误: %s", strerror(errno)); perror("错误");
适用场景 需要把错误信息存起来、传给其他函数,或者格式化输出 快速打印错误信息,调试用
头文件 <string.h> + <errno.h> <stdio.h>

4. 核心注意事项(⚠️ 必看)

  1. errno 是全局变量,会被后续函数调用覆盖 比如 fopen 失败后,errno 被设为 2,但如果你接下来调用了 printf 出错,errno 会被改成新的错误码。所以要立即读取并保存 errno 的值
  2. strerror 返回的字符串是只读的,不能修改 它指向的是库函数内部的静态缓冲区多次调用可能会被覆盖
  3. perror 只能输出到标准输出 如果你想把错误信息写入文件,就只能用**strerror。**
相关推荐
jllllyuz1 小时前
VC++ 读写 Excel 文件实现
开发语言·c++·excel
小小编程能手1 小时前
C++文件从操作:
开发语言·c++
czxyvX1 小时前
5-Qt系统相关
开发语言·qt
茉莉玫瑰花茶1 小时前
C++ 17 详细特性解析(6)
开发语言·c++
froginwe111 小时前
Bootstrap 标签页
开发语言
12.=0.1 小时前
【stm32_8】IIC内部集成电路——IIC的时序、利用IO口模拟IIC的时序、IIC通信器件的读写使用、半导体存储器的基本概述
c语言·stm32·单片机·嵌入式硬件
代码地平线1 小时前
【排序】C语言实现八大排序算法(含完整源码与性能测试)
c语言·算法·排序算法
东方.既白1 小时前
QML与C++炫酷界面交互DEMO
开发语言·c++·交互
声网1 小时前
OpenAI 的 WebRTC 秘密架构:没有 SFU?没有问题!丨 Voice Agent 学习笔记
学习·架构·webrtc