• get_num.c源码分析


    源代码

    源代码来自《Linux/Unix系统编程手册》的get_num.c

    /* Listing 3-6 */
    
    /* get_num.c
    
       Functions to process numeric command-line arguments.
    */
    #include 
    #include 
    #include 
    #include 
    #include 
    #include "get_num.h"
    
    /* Print a diagnostic message that contains a function name ('fname'),
       the value of a command-line argument ('arg'), the name of that
       command-line argument ('name'), and a diagnostic error message ('msg'). */
    static void
    gnFail(const char *fname, const char *msg, const char *arg, const char *name)
    {
        fprintf(stderr, "%s error", fname);
        if (name != NULL)
            fprintf(stderr, " (in %s)", name);
        fprintf(stderr, ": %s\n", msg);
        if (arg != NULL && *arg != '\0')
            fprintf(stderr, "        offending text: %s\n", arg);
    
        exit(EXIT_FAILURE);
    }
    
    /* Convert a numeric command-line argument ('arg') into a long integer,
       returned as the function result. 'flags' is a bit mask of flags controlling
       how the conversion is done and what diagnostic checks are performed on the
       numeric result; see get_num.h for details.
    
       'fname' is the name of our caller, and 'name' is the name associated with
       the command-line argument 'arg'. 'fname' and 'name' are used to print a
       diagnostic message in case an error is detected when processing 'arg'. */
    
    static long
    getNum(const char *fname, const char *arg, int flags, const char *name)
    {
        long res;
        char *endptr;
        int base;
    
        if (arg == NULL || *arg == '\0')
            gnFail(fname, "null or empty string", arg, name);
    
        base = (flags & GN_ANY_BASE) ? 0 : (flags & GN_BASE_8) ? 8
                                       : (flags & GN_BASE_16)  ? 16
                                                               : 10;
    
        errno = 0;
        res = strtol(arg, &endptr, base);
        if (errno != 0)
            gnFail(fname, "strtol() failed", arg, name);
    
        if (*endptr != '\0')
            gnFail(fname, "nonnumeric characters", arg, name);
    
        if ((flags & GN_NONNEG) && res < 0)
            gnFail(fname, "negative value not allowed", arg, name);
    
        if ((flags & GN_GT_0) && res <= 0)
            gnFail(fname, "value must be > 0", arg, name);
    
        return res;
    }
    
    /* Convert a numeric command-line argument string to a long integer. See the
       comments for getNum() for a description of the arguments to this function. */
    
    long getLong(const char *arg, int flags, const char *name)
    {
        return getNum("getLong", arg, flags, name);
    }
    
    /* Convert a numeric command-line argument string to an integer. See the
       comments for getNum() for a description of the arguments to this function. */
    
    int getInt(const char *arg, int flags, const char *name)
    {
        long res;
    
        res = getNum("getInt", arg, flags, name);
    
        if (res > INT_MAX || res < INT_MIN)
            gnFail("getInt", "integer out of range", arg, name);
    
        return res;
    }
    

    NULL、0、‘0’、‘\0’、“\0”

    NULL的定义如下:

    #ifndef __cplusplus
    #define NULL ((void *)0)
    #else   /* C++ */
    #define NULL 0
    #endif  /* C++ */
    #endif  /* G++ */
    #endif	/* NULL not defined and  or need NULL.  */
    

    NULL在数值上为0,在C语言中表示空指针,类型为void*,在C++中表示0;
    '0’是字符0,ASCII码值即数值上为48;
    0在数值上为0;
    '\0’是空字符(null character),数值上为0,C语言用于标记字符串的结尾;
    "\0"为字符串,包含两个空字符。

    位操作

    flags & GN_ANY_BASE利用位操作实现掩码操作,在get_num.h文件中定义如下:

    #define GN_NONNEG       01      /* Value must be >= 0 */
    #define GN_GT_0         02      /* Value must be > 0 */
    #define GN_ANY_BASE   0100      /* Can use any base - like strtol(3) */
    #define GN_BASE_8     0200      /* Value is expressed in octal */
    #define GN_BASE_16    0400      /* Value is expressed in hexadecimal */
    

    0开头表示8进制,0100、0200、0400转换为2进制显示后,三个数分别为:

    0000 0100 0000
    0000 1000 0000
    0001 0000 0000
    

    三者在bit位上没有重叠,仅有1位为1,通过按位&操作可以检查flags的某个bit是否为1,如果该bit为1,则flags & GN_ANY_BASE的结果为1,如果该bit不为1,则flags & GN_ANY_BASE的结果为0。

    嵌套条件运算符

    代码中使用了嵌套的条件运算符:

    base = (flags & GN_ANY_BASE) ? 0 : (flags & GN_BASE_8) ? 8
                                    : (flags & GN_BASE_16)  ? 16
                                                            : 10;
    

    条件运算符?:是C语言中唯一的三目运算符。条件运算符遵循从右至左的结合律,因此上述代码等同于:

    base = (flags & GN_ANY_BASE) ? 0 : ((flags & GN_BASE_8) ? 8 : ((flags & GN_BASE_16) ? 16 : 10));
    

    运算顺序与结合顺序不是同一个概念,条件运算符先计算flags & GN_ANY_BASE的结果是否为0,再根据结果进入相应的分支。

    strtol()函数

    函数功能,将字符串转换为长整型数。
    函数原型:

    long int strtol (const char* str, char** endptr, int base);
    
    • str    待转换的字符串
    • endptr   指向第一个不可转换的字符位置的指针
    • base    基数,表示转换为几进制的数

    当 base 的值为 0 时,默认为10进制转换,但如果遇到 ‘0x’ / ‘0X’ 前置字符则会使用 16 进制转换,遇到 ‘0’ 前置字符则会使用8进制转换。
    当endptr为NULL,则表示该参数无效,或不使用该参数,若不为NULL,会返回不符合base的字符的指针,举例来说,base为10,则str字符串为"1223214ABC"中,从’A’开始不符合base条件,则(*endptr)指向’A’的地址。

    参考链接

    【1】https://zhuanlan.zhihu.com/p/79210633
    【2】https://www.cnblogs.com/xiehongfeng100/p/4010802.html
    【3】https://www.cnblogs.com/grandyang/p/4966132.html

  • 相关阅读:
    一篇文章讲清楚 MySQL 的聚簇 / 联合 / 覆盖索引、回表、索引下推
    你还只知道测试金字塔?
    【OAuth2】十六、Spring Authorization Server如何生成并发放token的
    三分钟实战手写Spring Boot Starter
    Linux 之 Firewalld
    Unity-WebGL基于JS实现网页录音
    vue中用xlsx、xlsx-style、file-saver插件实现用表格原始数据打印excel文件
    go语言|数据结构:二叉树可视化(制作svg格式树形图)
    Springboot +spring security,解决跨域问题
    Docker安装Rabbitmq并挂载宿主机数据目录
  • 原文地址:https://blog.csdn.net/qq_37775990/article/details/127109281