您的位置:首页 > 编程语言 > C语言/C++

c语言笔记 - 标准库函数 - 持续更新

2020-03-05 14:33 1086 查看

文章目录

  • 2 `char *gets(char *str) `
  • 3 `char *fgets(char *str, int n, FILE *stream) `
  • 4 `int scanf(const char *format, ...)`
  • 5 `void qsort (void * base, size_t num, size_t size, int ( * comparator ) ( const void *, const void * ) )`
  • 1
    int getc (FILE* __F) && int getchar (void)

    标准库中的函数实现

    可以看见 getc 与 getchar 的唯一区别就是 getchar 默认 __F 参数为 stdin

    int getc (FILE* __F)
    {
    return (--__F->_cnt  >= 0)
    ? (int) (unsigned char) *__F->_ptr++
    : _filbuf (__F);
    }
    
    int getchar (void)
    {
    return (--stdin->_cnt >= 0)
    ?  (int) (unsigned char) *stdin->_ptr++
    : _filbuf (stdin);
    }

    功能

    从标准输入流(getc还包括从指定文件)中读取一个字符

    输入结束标志

    换行即结束

    返回值

    • 若输入成功:返回用户输入的第一个字符的ASCII码;
      例如:什么都不输入直接按 “Enter” 键,因 “Enter” 键包含 ‘\r’ 和 ‘\n’ 两个字符,又遇到 ‘\n’ 结束,故返回 ‘\r’ 即 10
    • 其他:则返回EOF;例如输入组合按键 ctrl+z

    2
    char *gets(char *str)

    功能

    从标准输入流中读取一串字符,并把它存储在str所指向的字符串中

    输入结束标志

    换行即结束

    返回值

    若输入成功,则返回str;其他情况,返回NULL

    说明

    遇换行符结束后,最终输入的值是:在输入有效字符的基础上丢弃换行符,末尾添加 nul (’\0’字符)

    缺陷

    gets可以从标准输入无限读取,不会判断上限,以回车结束读取,而gets()函数只知道buffer的首地址,并不知道有多少个元素。若输入字符串过长,则会导致溢出,访问其他地址空间,若这些空间未被使用,则暂时不会出现问题,若这些空间已被使用,则会覆盖原有内容,导致不可估量的后果。因此,不建议使用此函数,为了避免上述缺陷,建议使用 fgets() 代替 gets()

    3
    char *fgets(char *str, int n, FILE *stream)

    char *fgets( char *str, int n, FILE *stream )
    {
    register int   c;
    register char  *cs;
    
    cs = str;
    
    while( --n>0 && (c = getc(stream))!= EOF )
    {
    if( (*cs++ = c) =='\n' )
    break;
    }
    
    *cs ='\0';
    
    return (c == EOF && cs == str) ? NULL : str;
    }

    功能

    获取一行长度为 n 的字符串(包含 ‘0’ 符)

    输入结束标志

    换行即结束

    返回值

    若输入成功,则返回str;其他情况,返回NULL

    参数 int n

    1.输入字符串的长度 < n-1 :保留换行符,在最后添加 ‘0’
    例:fgets(str,4,stdin); 输入:12 则实际为:12\n + ‘0’
    这种情况下得到的字符串中包含了换行符 ‘\n’,故在输出时会进行自动换行
    2.输入字符串的长度 >= n-1:保留 n-1 个字符,在最后添加 ‘0’;下一次调用会继续读该行的剩余部分
    例:fgets(str,4,stdin); 输入:123 则实际为:123 + ‘0’

    参数 char *str

    参数 str 可以是一个字符数组或字符指针,若是字符指针,一定要进行初始化,分配内存空间

    char str1[100];
    可以进行传递
    char *str2;
    不能进行传递,并没有为它分配内存空间
    char *str3 = (char*)malloc(100*sizeof(char));
    可以进行传递,已分配动态内存空间

    参数 FILE *stream

    1.从标准设备读数据:该参数为 stdin
    2.从文件流读取数据:fopen()、fwrite()、fread()函数使用说明与示例

    4
    int scanf(const char *format, ...)

    函数实现

    (想了解具体文件请访问:关于输入输出的3个文件

    #define	INBUFSIZE 1024
    
    static char g_pcInBuf[INBUFSIZE];
    
    int scanf(const char * fmt, ...)
    {
    int i = 0;
    unsigned char c;
    
    va_list args;
    
    //获取输入的内容
    while(1)
    {
    c = getc(stdin);
    
    //此处可以看到输入流中遇见 '\r', '\n' 即结束
    if((c == 0x0d) || (c == 0x0a))
    {
    g_pcInBuf[i] = '\0';
    break;
    }
    else
    {
    g_pcInBuf[i++] = c;
    }
    }
    
    va_start(args,fmt);
    i = vsscanf(g_pcInBuf,fmt,args); //将g_pcInBuf缓冲区反格式化为参数列表
    va_end(args);
    
    return i;
    }

    功能

    格式输入函数,从标准输入 stdin 读取指定格式化输入;

    可变参数列表

    请看:可变参数列表

    返回值

    成功:成功匹配和赋值的个数;其他:EOF

    刨析 vsscanf(g_pcInBuf,fmt,args)

    scanf格式字符串中的统一格式 :%[*][width][qualifier]type],如%3d,包含对width, type的格式说明
    下面函数省略 * 和 qualifier,以及对具体匹配部分的具体转换,函数实现如下:(包含解析)

    // buf:输入缓冲
    // fmt:缓冲区格式
    // args:可变参数列表首地址
    int vsscanf(const char * buf, const char * fmt, va_list args)
    {
    /*...*/
    
    //!nul
    while (*fmt && *str)
    {
    //跳过格式中的任何空白,格式中的空白匹配输入中任何数量的空白(即空格,回车...)
    if (isspace(*fmt))
    {
    while (isspace(*fmt)) //检测空白字符
    ++fmt;
    while (isspace(*str))
    ++str;
    }
    
    //任何非转换的内容都必须完全匹配
    if (*fmt != '%' && *fmt)
    {
    if (*fmt++ != *str++)
    break;
    continue;
    }
    
    //到达转换符%位置
    if (!*fmt)
    break;
    ++fmt;
    
    /*...*/
    
    //获取字段宽度
    field_width = -1;
    if (isdigit(*fmt)) //检查字符是一个十进制数字字符(0-9)
    field_width = skip_atoi(&fmt); //将数字字符转换为相应数字,如将'1'->1
    
    /*...*/
    
    switch(*fmt++)
    {
    //%nc
    case 'c':
    {
    char *s = (char *) va_arg(args,char*);
    //n:1		:输入一个字符
    if (field_width == -1)
    field_width = 1;
    //n:2-9		:输入多个字符,组成固定长度字符串(包含空格)
    do
    {
    *s++ = *str++;
    } while (--field_width > 0 && *str);
    num++;
    }
    continue;
    
    //%s
    case 's':
    {
    char *s = (char *) va_arg(args, char *);
    if(field_width == -1)
    field_width = INT_MAX;	//字符串长度为最大值,否则按限定长度读取
    
    //忽略字符串前的所有白字符
    while (isspace(*str))
    str++;
    
    //存储知道下一个空白符之前
    while (*str && !isspace(*str) && field_width--)
    {
    *s++ = *str++;
    }
    
    //加入nul
    *s = '\0';
    num++;
    }
    continue;
    
    //%n 返回到目前为止读取的字符数(长度按字节算)
    case 'n':
    {
    int *i = (int *)va_arg(args,int*);
    *i = str - buf;
    }
    continue;
    
    //%o
    case 'o':
    base = 8;
    break;
    
    //%x
    case 'x':
    case 'X':
    base = 16;
    break;
    
    //%d
    case 'i':		//old
    base = 0;
    case 'd':		//new
    is_sign = 1;
    case 'u':		//%u
    break;
    
    //%%
    case '%':
    /* looking for '%' in str */
    if (*str++ != '%')
    return num;
    continue;
    
    default:
    return num;
    }
    
    //跳过缓冲区中的空白符
    while (isspace(*str))
    str++;
    
    /*...*/
    }
    return num;
    }

    总结

    1. 格式字符串中的所有空白均无效

    2. 任何非转换的内容都必须完全匹配,格式中的空白匹配输入中任何数量的空白

    3. 对于width的举例

      scanf("%4d", &a); printf("%d", a);
      将输入得到一串0-9的字符直接合并转换为数字

      输入为"12345" / 输出为1234

      scanf("%4c", str); printf("%s", str);
      %nc可进行带空白符的固定长度的字符串输出

      str必须为一段已开拓的空间的首地址:数组/分配的动态内存的首地址
      输入为"12 345" / 输出为"12 3"

      scanf("%4s", str); printf("%s", str);
      %ns与%nc类似,但遇到空白符即终止

      输入为"12 345" / 输出为"12"

    4. 匹配/过滤特定字符

    5
    void qsort (void * base, size_t num, size_t size, int ( * comparator ) ( const void *, const void * ) )

    功能

    对数组元素进行排序

    参数 void * base

    数组的首地址

    参数 size_t num

    数组的元素个数

    参数 size_t size

    元素的字节大小

    参数 int ( *comparator ) ( const void *, const void * )

    指向比较函数的函数指针,决定了排序的顺序;示例如下:dir决定排序方式

    #define dir -1
    
    //method1
    int comparator (void const *a, void const *b)
    {
    int *pa = (int *)a;
    int *pb = (int *)b;
    
    return (*pa > *pb) ? dir : ((*pa < *pb) ? -dir : 0);
    }
    
    //method2
    int compare (const void * a, const void * b)
    {
    return ( *(int*)a - *(int*)b ) * dir;
    }
    • 点赞
    • 收藏
    • 分享
    • 文章举报
    清风_杰sir 发布了7 篇原创文章 · 获赞 0 · 访问量 153 私信 关注
    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: