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

news2025/3/9 23:34:16

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); // 输出:hello
          
        return 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);
           // 后续调用,传入 NULL
           token = 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字节给\0
    dest[sizeof(dest) - 1] = '\0';        // 手动添加终止符

    printf("p: %s\n", p);    // 输出:Hello, Wo
    printf("dest: %s\n", dest); // 输出:Hello, Wo

    return 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 拷贝给dest
    for (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, Wor

    return 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);  // 输出:0
    printf("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个字符,留一个位置给字符串结尾的空字符 \0
        printf("读取的字符串: %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_MAX
    const char* str2 = "2147483648";  // 超出 INT_MAX
   
    int 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++全栈开发 的知识,如果你感兴趣请多多关注博主。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2311082.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis--Hash类型

目录 一、引言 二、介绍 三、操作 1.HSET,HGET,HEXISTS,HDEL 2.HKEYS&#xff0c;HVALS 3.HGETALL&#xff0c;HMGET&#xff0c;HSAN 4.HLEN,HSETNX,HINCRBY,HINCRBYFLOAT 四、编码方式 1.ziplist&#xff08;压缩列表&#xff09; 2.hashtable&#xff08;哈希表&am…

大型语言模型训练的三个阶段:Pre-Train、Instruction Fine-tuning、RLHF (PPO / DPO / GRPO)

前言 如果你对这篇文章可感兴趣&#xff0c;可以点击「【访客必读 - 指引页】一文囊括主页内所有高质量博客」&#xff0c;查看完整博客分类与对应链接。 当前的大型语言模型训练大致可以分为如下三个阶段&#xff1a; Pre-train&#xff1a;根据大量可获得的文本资料&#…

共享模型之管程(悲观锁)

共享模型之管程&#xff08;悲观锁&#xff09; 文章目录 共享模型之管程&#xff08;悲观锁&#xff09;一、常见线程安全的类二、对象头三、Monitor&#xff08;监视器 / 管程&#xff09;四、偏向锁偏向锁的实现原理撤销偏向锁 五、轻量级锁轻量级锁的释放 六、重量级锁七、…

零基础C语言学习日志22(自定义类型:联合和枚举)

目录 联合体 联合体类型的声明 联合体的特点 相同成员联合体和结构体的对比 联合体大小的计算 例子 枚举类型 枚举类型的声明 枚举类型的优点 枚举类型的使用 联合体 联合体类型的声明 像结构体一样&#xff0c;联合体也是由一个或者多个成员构成&#xff0c;这些成…

ROS2 Rviz 实战:给 panda 机械臂场景塞个圆柱体

视频讲解 ROS2 Rviz 实战&#xff1a;给 panda 机械臂场景塞个圆柱体 创建add_cylinder的package ros2 pkg create add_cylinder --build-type ament_cmake --dependencies rclcpp control_msgs moveit_ros_planning_interface 在src中添加add_cylinder.cpp&#xff0c;如下 #…

DeepSeek+知识库+鸿蒙,助力鸿蒙高效开发

不知道你们发现没有&#xff0c;就是鸿蒙开发官网&#xff0c;文档也太多太多了&#xff0c;对于新手来说确实头疼&#xff0c;开发者大多是极客&#xff0c;程序的目的是让世界更高效&#xff01;看文档&#xff0c;挺头疼的&#xff0c;毕竟都是理科生。 遇到问题不要慌&…

从零开始在Windows使用VMware虚拟机安装黑群晖7.2系统并实现远程访问

文章目录 前言1.软件准备2. 安装VMware17虚拟机3.安装黑群晖4. 安装群晖搜索助手5. 配置黑群晖系统6. 安装内网穿透6.1 下载cpolar套件6.2 配置群辉虚拟机6.3 配置公网地址6.4 配置固定公网地址 总结 前言 本文主要介绍如何从零开始在Windows系统电脑使用VMware17虚拟机安装黑…

【LeetCode101】对称二叉树

题目描述 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 思路与算法 对称&#xff1a;左右子树互为镜像 这很显然暗示了一种递归方法 确定base case&#xff08;s&#xff09; 如果 left 和 right 都是 None &#xff0c;那么它们是镜像的&#xff08;对称&…

K8s 1.27.1 实战系列(四)验证集群及应用部署测试

一、验证集群可用性 1、检查节点 kubectl get nodes ------------------------------------------------------ NAME STATUS ROLES AGE VERSION k8s-master Ready control-plane 3h48m v1.27.1 k8s-node1 Ready <none> …

【STM32】STM32系列产品以及新手入门的STM32F103

&#x1f4e2; STM32F103xC/D/E 系列是一款高性能、低功耗的 32 位 MCU&#xff0c;适用于工业、汽车、消费电子等领域&#xff1b;基于 ARM Cortex-M3&#xff0c;主频最高 72MHz&#xff0c;支持 512KB Flash、64KB SRAM&#xff0c;适合复杂嵌入式应用&#xff0c;提供丰富的…

pycharm找不到conda可执行文件

conda 24.9.2 在pycharm的右下角就可以切换python解释器了

自注意力机制的演进-从Transformer架构到DeepSeek-R1模型的深度语义理解革新

2025年&#xff0c;我国发布了开创性且高性价比的大语言模型-DeepSeek-R1&#xff0c;推动了AI领域的重大变革。本章节回顾了LLM的发展历程&#xff0c;其起点可追溯至2017年Transformer架构的提出&#xff0c;该架构通过自注意力机制(Self-Attention)彻底革新了自然语言处理技…

动态内存管理的了解及使用

目录 1.什么是动态内存&#xff1f; 2.为什么要使用动态内存分配空间&#xff1f; 3.动态内存开辟函数malloc&#xff0c;calloc&#xff0c;realloc 3.1 malloc 3.2 calloc 3.3 realloc 3.4 头文件包含 4.动态内存释放函数free 5.动态内存的基本常见错误 5.1 对NULL…

pom.xml配置(mybatisplus增删改查实现;PageInfo分页实现;JSONObject实现)

一、mybatisplus增删改查实现 pom.xml <!-- mybatis-plus --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.2</version></dependency>在mapper文件里…

【运维笔记】Navicat中删除mongo 某个时间之前的数据

【运维笔记】Navicat中删除mongo 某个时间之前的数据 一、场景与需求1.1、场景1.2、需求 二、解决方案三、实战3.1、【Navicat】使用sql语句 &#xff08;推荐&#xff09;Step 1&#xff1a;使用查询窗口 - 查询Step 2&#xff1a;确认第一步的数据是否是需要删除的数据Step 3…

BUUCTF逆向刷题笔记(1-12)

easyre、内涵的软件、xor、不一样的flag&#xff1a; buuctf reverse部分题解&#xff08;实时更新&#xff09;_reverse 题解-CSDN博客 请见小库里的blog。 reverse1 查壳发现没有&#xff0c;而且是64位 粗略改一下部分函数名&#xff0c;看看主要逻辑。 第一个for循环暂…

如何改变怂怂懦弱的气质(2)

你是否曾经因为害怕失败而逃避选择&#xff1f;是否因为不敢拒绝别人而让自己陷入困境&#xff1f;是否因为过于友善而被人轻视&#xff1f;如果你也曾为这些问题困扰&#xff0c;那么今天的博客就是为你准备的。我们将从行动、拒绝、自我认知、实力提升等多个角度&#xff0c;…

【DeepSeek】5分钟快速实现本地化部署教程

一、快捷部署 &#xff08;1&#xff09;下载ds大模型安装助手&#xff0c;下载后直接点击快速安装即可。 https://file-cdn-deepseek.fanqiesoft.cn/deepseek/deepseek_28348_st.exe &#xff08;2&#xff09;打开软件&#xff0c;点击立即激活 &#xff08;3&#xff09;选…

算法之二维装水问题

目录 1. 题目2. 解释3. 思路4. 代码5. 总结 1. 题目 给定一个数组arr&#xff0c;已知其中所有的值都是非负的&#xff0c;将这个数组看作一个容器&#xff0c;请返回容器能装多少水 比如&#xff0c;arr {3&#xff0c;1&#xff0c;2&#xff0c;5&#xff0c;2&#xff0c…

[C语言日寄] 字符串操作函数的使用及其拓展

【作者主页】siy2333 【专栏介绍】⌈c语言日寄⌋&#xff1a;这是一个专注于C语言刷题的专栏&#xff0c;精选题目&#xff0c;搭配详细题解、拓展算法。从基础语法到复杂算法&#xff0c;题目涉及的知识点全面覆盖&#xff0c;助力你系统提升。无论你是初学者&#xff0c;还是…