C字符串和字符串函数(二)

C字符串和字符串函数(二)

字符串输入

声明字符串的第一件事:

  • 分配存储空间
  • 存储稍后要读入的字符串

注意指针声明的字符串: -> 本质上是要注意指针的特性

示例:

char *name; scanf("%s", name);

注意:

  • name可能会擦写掉程序中的数据或代码,因为name是未初始化指针

  • scanf()函数是把信息拷贝至参数指定的地址上

  • name未曾初始化,所以可能会指向任何地方

  • scanf()智能读取一个单词,整行读取需要用到函数gets()

整行读取示例代码:

/**
 * @Author: Lucifer
 * @Date: 5/21/2023, 7:34:03 PM
 * @LastEditors: Lucifer
 * @LastEditTime: 5/21/2023, 7:34:03 PM
 * Description: 正行读取字符串函数 - gets() puts()
 * Copyright: Copyright (©)}) 2023 Your Name. All rights reserved.
 */
# include<stdio.h>
# define STLEN 81

int main(void)
{
    char words[STLEN];

    puts("输入一串字符串:");
    gets(words);
    printf("你输入的字符串是:");
    printf("- print %s.\n", words);
    puts(words);
    puts("Done.");

    getchar();

    return 0;
}

注意:

  • gets()函数唯一参数是words,无法检查数组是否装得下输入行
  • 由于数组名会转换成数组首元素地址,所以只知道开始并不知道数组有多少元素

带来的问题:

  • 缓冲区溢出 -> 多余的字符超出了指定的目标空间
  • 长期以往会占用未使用的内存

替代品fgets()

fgets()

函数特点:

  • 第2个参数指明了读入字符的最大数量,读取到n-1个字符,n是形参
  • fgets()读取到换行符会存储换行符
  • fgets()第三个参数指明要读入的文件,如果是从键盘读入数据的话,那么以stdin(标准输入)作为参数

示例代码:

/**
 * @Author: Lucifer
 * @Date: 5/21/2023, 7:59:47 PM
 * @LastEditors: Lucifer
 * @LastEditTime: 5/21/2023, 7:59:47 PM
 * Description: fgets()函数初体验,关注fgets()函数存储的特性
 * Copyright: Copyright (©)}) 2023 Your Name. All rights reserved.
 */
# include<stdio.h>
# define STELN 14

int main(void)
{
    char words[STELN];

    puts("请输入一个字符串:");
    fgets(words, STELN, stdin);
    printf("字符串 (puts(), then fputs()):");
    puts(words); // 第一行输入比fgets()读入的整行输入短,所以apple pie\n\0被存储在数组中
    fputs(words, stdout);
    puts("输入字符串:");
    fgets(words, STELN, stdin);
    printf("字符串 (puts(), then fputs()):");
    puts(words);
    fputs(words, stdout);
    puts("Done.\n");

    getchar();

    return 0;
}

注意:

  • fgets()函数返回指向char的指针,该函数返回的地址与传入的第一个参数相同
  • 函数读到文件末尾,返回一个特殊指针(空指针) -> 需要处理这种特殊情况

处理空指针示例代码:

/**
 * @Author: Lucifer
 * @Date: 5/21/2023, 8:12:06 PM
 * @LastEditors: Lucifer
 * @LastEditTime: 5/21/2023, 8:12:06 PM
 * Description: fgets()函数的使用
 * Copyright: Copyright (©)}) 2023 Your Name. All rights reserved.
 */
# include<stdio.h>
# define STELN 10

int main(void)
{
    char words[STELN];

    puts("输入字符串(q退出):");
    while (fgets(words, STELN, stdin) != NULL && words[0] != '\n')
        fputs(words, stdout);
    puts("Done.");

    getchar();

    return 0;
}

注意:

  • 每次读取的字符数量是STELN - 1个字符

  • 输入的内容是By the way, the gets() functyion -> 读取到tion\n时将其存储为tion\n\0fputs()打印出

  • 因为存储了一个换行符,fputs()打印出该字符串,光标被下移两行

  • 系统使用了缓冲IO,用户在按下Return键之前,输入都被存储在临时存储区,按下Return键之后在输入中加入换行符发送给fputs()

处理换行符示例代码:

/**
 * @Author: Lucifer
 * @Date: 5/21/2023, 8:27:46 PM
 * @LastEditors: Lucifer
 * @LastEditTime: 5/21/2023, 8:27:46 PM
 * Description: 处理fgets()函数保存的最后一行的换行符\n
 * Copyright: Copyright (©)}) 2023 Your Name. All rights reserved.
 */
# include<stdio.h>
# define STLEN 10

int main(void)
{
    char words[STLEN];
    int i;

    puts("请输入字符串(空行退出):");
    while (fgets(words, STLEN, stdin) != NULL && words[0] != '\n')
    {
        i = 0;
        while (words[0] != '\n' && words[i] != '\0')
            i++;
        if (words[i] == '\n')
            words[i] = '\0';
        else // word[i] == '\0' 执行这部分代码
            while (getchar() != '\n')
                continue;
        puts(words);
    }
    puts("Done.");

    return 0;
}

注意:

  • 处理方法是删除存储在字符串中换行符

空字符串和空指针二者区别

首先观测容易混淆的原因:

  • 都可以用数值0表示

区别:

  • 空字符串('\0')用于标记C字符串末尾的字符,字符编码是0,其他字符的编码不可能是0
  • 空指针(NULL)有一个值,该值不会与任何数据的有效地址对应 -> 函数使用它返回一个有效地址,表示某些特殊情况的发生,如遇到文件结尾或未能按预期执行
  • 空字符是整数类型,占用的字节是1字节的
  • 空指针是指针类型,返回值是地址,通用占用4字节

gets_s()函数

作用:

处理最后一行的换行符

特点:

  • gets_s()函数只从标准输入中读取数据,不需要第三个参数stdin
  • gets_s()读取到换行符会丢弃,而不是存储
  • 如果gets_s()读到最大字符数都没有读到换行符,那么会:
    • 把目标数组中的首字符设置为空字符
    • 读取并丢弃随后的输入,直至读到换行符或文件结尾 -> 返回空指针
    • 调用依赖实现的"处理函数" -> 中止或者退出程序

为什么要丢弃过长输入行中的余下字符?

  • 输入行中多出来的字符会被留在缓冲区当中,成为下一条语句的输入 -> |符号可以使用的由来
  • 如果下一条语句读取的是double类型的值,那么可能导致程序崩溃

fgets()函数的处理函数:

实现:

  • 用空字符替代换行符
  • 字符串中遇到空字符丢弃该输入行的其余字符

示例代码:

char * sgets(char * st, int n)
{
    char * ret_val;
    int i = 0;

    ret_val = fgets(st, n, stdin);
    if (ret_val)
    {
        while (st[i] != '\n' && st[i] != '\0') // 没遇到换行符,继续读取
            i++;
        if (st[i] == '\n')
            st[i] = '\0'; // 换行符用空字符替换
        else
            while (getchar() != '\n')
                continue;
    }
    
    return ret_val;
}

scanf()函数

scanf()函数与gets()fgets()函数的区别:

  • 如何确定字符串末尾

scanf()函数确定字符串末尾的方式:

  • 未指定字段宽度(如%s) -> 遇到第一个非空白字符作为字符串开始,开始之后的第一个空白字符作为字符串结束
  • 指定字段宽度(如%10s) -> scanf()函数将读取10个字符,或者读到第一个空白字符终止 -> 先满足的条件就是结束输入的条件

示例代码:

/**
 * @Author: Lucifer
 * @Date: 5/21/2023, 9:13:46 PM
 * @LastEditors: Lucifer
 * @LastEditTime: 5/21/2023, 9:13:46 PM
 * Description: scanf()函数处理字符串结束的方式
 * Copyright: Copyright (©)}) 2023 Your Name. All rights reserved.
 */
# include<stdio.h>

int main(void)
{
    char name1[11], name2[11];
    int count;

    printf("输入两个英文名:.\n");
    count = scanf("%5s %10s", name1, name2);
    printf("读取到 %d 个名字, 分别是 %s, %s.\n", count, name1, name2);

    getchar();

    return 0;
}

注意:

举例说明:

接收: sacnf("%5s", name)

input: Ann Ular -> 中间有空白字符

output: Ann为第一个结束点,因为第一个空白字符先进入,虽然声明了字段宽度,但是先满足的条件就是输入的结束条件 -> 后面是接着从结束点读取,所以下一个输出是 Ular有空白字符

posted @   俊king  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示