Rtthread kservice中的系列函数

Rtthread kservice中的系列函数

/* global errno in RT-Thread */
static volatile int __rt_errno;

/*
 * This function will get errno
 *
 * @return errno
 */
//errno获取函数
rt_err_t rt_get_errno(void)
{
    rt_thread_t tid;
    if (rt_interrupt_get_nest() != 0)
    {
        /* it's in interrupt context */
        return __rt_errno;
    }
    tid = rt_thread_self();
    if (tid == RT_NULL)
        return __rt_errno;
    return tid->error;
}
/*
 * This function will set errno
 *
 * @param error the errno shall be set
 */
void rt_set_errno(rt_err_t error)
{
    rt_thread_t tid;
    if (rt_interrupt_get_nest() != 0)
    {
        /* it's in interrupt context */
        __rt_errno = error;

        return;
    }
    tid = rt_thread_self();
    if (tid == RT_NULL)
    {
        __rt_errno = error;

        return;
    }
    tid->error = error;
}
/**
 * This function returns errno.
 *
 * @return the errno in the system
 */
int *_rt_errno(void)
{
    rt_thread_t tid;
    if (rt_interrupt_get_nest() != 0)
        return (int *)&__rt_errno;
    tid = rt_thread_self();
    if (tid != RT_NULL)
        return (int *) & (tid->error);
    return (int *)&__rt_errno;
}
/**
 * This function will set the content of memory to specified value
 *
 * @param s the address of source memory
 * @param c the value shall be set in content
 * @param count the copied length
 *
 * @return the address of source memory
 */
void *rt_memset(void *s, int c, rt_ubase_t count)
{
#define LBLOCKSIZE      (sizeof(long)) //32BIT 平台long为4字节
#define UNALIGNED(X)    ((long)X & (LBLOCKSIZE - 1)) //没有4字节对齐为true
#define TOO_SMALL(LEN)  ((LEN) < LBLOCKSIZE)//小于4字节

    unsigned int i;
    char *m = (char *)s;
    unsigned long buffer;
    unsigned long *aligned_addr;
    unsigned int d = c & 0xff;  /* To avoid sign extension, copy C to an
                                unsigned variable.  */
    if (!TOO_SMALL(count) && !UNALIGNED(s))// count >= 4 && 4align
    {
        /* If we get this far, we know that n is large and m is word-aligned. */
        aligned_addr = (unsigned long *)s;

        /* Store D into each char sized location in BUFFER so that
         * we can set large blocks quickly.
         */
        if (LBLOCKSIZE == 4)
        {//buffer == [d][d][d][d]
            buffer = (d << 8) | d;
            buffer |= (buffer << 16);
        }
        else //sizeof long != 4
        {
            buffer = 0;
            for (i = 0; i < LBLOCKSIZE; i ++)
                buffer = (buffer << 8) | d;
        }
        while (count >= LBLOCKSIZE * 4)
        {//一次给 4字节地址赋值,效率最高
            *aligned_addr++ = buffer;
            *aligned_addr++ = buffer;
            *aligned_addr++ = buffer;
            *aligned_addr++ = buffer;
            count -= 4 * LBLOCKSIZE;
        }
        while (count >= LBLOCKSIZE)//不足4*LBLOCKSIZE 但是 >= LBLOCKSIZE
        {
            *aligned_addr++ = buffer;
            count -= LBLOCKSIZE;
        }
        /* Pick up the remainder with a bytewise loop. */
        m = (char *)aligned_addr;// m 
    }
    while (count--) //剩下不满LBLOCKSIZE的字节赋值
    {
        *m++ = (char)d;
    }
    return s;
#undef LBLOCKSIZE
#undef UNALIGNED
#undef TOO_SMALL
}

/**
 * This function will copy memory content from source address to destination
 * address.
 *
 * @param dst the address of destination memory
 * @param src  the address of source memory
 * @param count the copied length
 *
 * @return the address of destination memory
 */
void *rt_memcpy(void *dst, const void *src, rt_ubase_t count)
{
#define UNALIGNED(X, Y) \
    (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1)))
#define BIGBLOCKSIZE    (sizeof (long) << 2) //4 << 2 == 16
#define LITTLEBLOCKSIZE (sizeof (long)) // 4
#define TOO_SMALL(LEN)  ((LEN) < BIGBLOCKSIZE) // LEN < 16

    char *dst_ptr = (char *)dst;
    char *src_ptr = (char *)src;
    long *aligned_dst;
    long *aligned_src;
    int len = count;

    /* If the size is small, or either SRC or DST is unaligned,
    then punt into the byte copy loop.  This should be rare. */
    if (!TOO_SMALL(len) && !UNALIGNED(src_ptr, dst_ptr))//len >= 16 && src_ptr, dst_ptr都是4align
    {
        aligned_dst = (long *)dst_ptr;
        aligned_src = (long *)src_ptr;

        /* Copy 4X long words at a time if possible. */
        while (len >= BIGBLOCKSIZE)//len >= 16
        {
            *aligned_dst++ = *aligned_src++;
            *aligned_dst++ = *aligned_src++;
            *aligned_dst++ = *aligned_src++;
            *aligned_dst++ = *aligned_src++;
            len -= BIGBLOCKSIZE;
        }

        /* Copy one long word at a time if possible. */
        while (len >= LITTLEBLOCKSIZE) // len >= 4
        {
            *aligned_dst++ = *aligned_src++;
            len -= LITTLEBLOCKSIZE;
        }

        /* Pick up any residual with a byte copier. */
        dst_ptr = (char *)aligned_dst;//剩下未被操作的地址
        src_ptr = (char *)aligned_src;
    }

    while (len--)
        *dst_ptr++ = *src_ptr++;

    return dst;
#undef UNALIGNED
#undef BIGBLOCKSIZE
#undef LITTLEBLOCKSIZE
#undef TOO_SMALL
}

/**
 * This function will move memory content from source address to destination
 * address.
 *
 * @param dest the address of destination memory
 * @param src  the address of source memory
 * @param n the copied length
 *
 * @return the address of destination memory
 */
void *rt_memmove(void *dest, const void *src, rt_ubase_t n)
{
    char *tmp = (char *)dest, *s = (char *)src;
    // s<tmp 并且 s与tmp指向的内存空间重叠
    if (s < tmp && tmp < s + n)
    {
        tmp += n;
        s += n;
        //从地址尾部拷贝
        while (n--)
            *(--tmp) = *(--s);
    }
    else
    {
       //s < tmp 并且两块内存空间不重叠,或者s > tmp
        while (n--)
            *tmp++ = *s++;
    }
    return dest;
}

/**
 * This function will compare two areas of memory
 *
 * @param cs one area of memory
 * @param ct another area of memory
 * @param count the size of the area
 *
 * @return the result
 */
rt_int32_t rt_memcmp(const void *cs, const void *ct, rt_ubase_t count)
{
    const unsigned char *su1, *su2;
    int res = 0;
    //遍历每一个字节,对比其大小
    for (su1 = (const unsigned char *)cs, su2 = (const unsigned char *)ct; 0 < count; ++su1, ++su2, count--)
        if ((res = *su1 - *su2) != 0)
            break;

    return res;
}

/**
 * This function will return the first occurrence of a string.
 *
 * @param s1 the source string
 * @param s2 the find string
 *
 * @return the first occurrence of a s2 in s1, or RT_NULL if no found.
 */
char *rt_strstr(const char *s1, const char *s2)
{
    int l1, l2;

    l2 = rt_strlen(s2);
    if (!l2)
        return (char *)s1;
    l1 = rt_strlen(s1);
    //l1 > l2时
    while (l1 >= l2)
    {
        l1 --;
        if (!rt_memcmp(s1, s2, l2))//从s1中查找子字符串s2首地址,找到rt_memcmp返回0
            return (char *)s1;
        s1 ++;
    }

    return RT_NULL;
}

/**
 * This function will compare two strings while ignoring differences in case
 *
 * @param a the string to be compared
 * @param b the string to be compared
 *
 * @return the result
 */
 //忽略大小写比较字符串
rt_int32_t rt_strcasecmp(const char *a, const char *b)
{
    int ca, cb;
    do
    {
        ca = *a++ & 0xff;
        cb = *b++ & 0xff;
        if (ca >= 'A' && ca <= 'Z')
            ca += 'a' - 'A';
        if (cb >= 'A' && cb <= 'Z')
            cb += 'a' - 'A';
    }
    while (ca == cb && ca != '\0');
    return ca - cb;
}

/**
 * This function will copy string no more than n bytes.
 *
 * @param dst the string to copy
 * @param src the string to be copied
 * @param n the maximum copied length
 *
 * @return the result
 */
char *rt_strncpy(char *dst, const char *src, rt_ubase_t n)
{
    if (n != 0)
    {
        char *d = dst;
        const char *s = src;

        do
        {
            if ((*d++ = *s++) == 0)//已经拷贝到src的尾部
            {
                /* NUL pad the remaining n-1 bytes */
                while (--n != 0) //剩下的空间全部置0
                    *d++ = 0;
                break;
            }
        } while (--n != 0);
    }

    return (dst);
}

/**
 * This function will compare two strings with specified maximum length
 *
 * @param cs the string to be compared
 * @param ct the string to be compared
 * @param count the maximum compare length
 *
 * @return the result
 */
rt_int32_t rt_strncmp(const char *cs, const char *ct, rt_ubase_t count)
{
    register signed char __res = 0;

    while (count)
    {
        if ((__res = *cs - *ct++) != 0 || !*cs++)//字符不相等或者*cs 为0 退出
            break;
        count --;
    }

    return __res;
}

/**
 * This function will compare two strings without specified length
 *
 * @param cs the string to be compared
 * @param ct the string to be compared
 *
 * @return the result
 */
rt_int32_t rt_strcmp(const char *cs, const char *ct)
{
    while (*cs && *cs == *ct)// *cs != '\0' and *cs == *ct
    {
        cs++;
        ct++;
    }
    return (*cs - *ct); // 非0则两个字符串不相等
}

/**
 * The  strnlen()  function  returns the number of characters in the
 * string pointed to by s, excluding the terminating null byte ('\0'),
 * but at most maxlen.  In doing this, strnlen() looks only at the
 * first maxlen characters in the string pointed to by s and never
 * beyond s+maxlen.
 *
 * @param s the string
 * @param maxlen the max size
 * @return the length of string
 */
rt_size_t rt_strnlen(const char *s, rt_ubase_t maxlen)
{
    const char *sc;

    for (sc = s; *sc != '\0' && (rt_ubase_t)(sc - s) < maxlen; ++sc) /* nothing */
        ;

    return sc - s;
}

/**
 * This function will return the length of a string, which terminate will
 * null character.
 *
 * @param s the string
 *
 * @return the length of string
 */
rt_size_t rt_strlen(const char *s)
{
    const char *sc;

    for (sc = s; *sc != '\0'; ++sc) /* nothing */
        ;

    return sc - s;
}

#ifdef RT_USING_HEAP
/**
 * This function will duplicate a string.
 *
 * @param s the string to be duplicated
 *
 * @return the duplicated string pointer
 */
 //将s 的内容复制到新的一块申请的内存中
char *rt_strdup(const char *s)
{
    rt_size_t len = rt_strlen(s) + 1;
    char *tmp = (char *)rt_malloc(len);

    if (!tmp)
        return RT_NULL;

    rt_memcpy(tmp, s, len);

    return tmp;
}

rt_inline int divide(long *n, int base)
{
    int res;

    /* optimized for processor which does not support divide instructions. */
    if (base == 10)
    {
        res = (int)(((unsigned long)*n) % 10U);
        *n = (long)(((unsigned long)*n) / 10U);
    }
    else
    {
        res = (int)(((unsigned long)*n) % 16U);
        *n = (long)(((unsigned long)*n) / 16U);
    }

    return res;
}
rt_inline int skip_atoi(const char **s)
{
    register int i = 0;
    while (_ISDIGIT(**s))
        i = i * 10 + *((*s)++) - '0';

    return i;
}

static char *print_number(char *buf,
                          char *end,
                          long  num,
                          int   base,
                          int   s,
                          int   precision,
                          int   type)
{
    char c, sign;
#ifdef RT_PRINTF_LONGLONG
    char tmp[32];
#else
    char tmp[16];
#endif
    int precision_bak = precision;//记录精度
    const char *digits;
    static const char small_digits[] = "0123456789abcdef";
    static const char large_digits[] = "0123456789ABCDEF";
    register int i;
    register int size;

    size = s; //输出长度
  
    digits = (type & LARGE) ? large_digits : small_digits;//大小写由字符0x/0X决定
    if (type & LEFT) //LEFT type 将ZEROPAD清零
        type &= ~ZEROPAD;

    c = (type & ZEROPAD) ? '0' : ' ';//ZEROPAD 用来将空格补0

    /* get sign */
    sign = 0;
    if (type & SIGN) //有符号
    {
        if (num < 0)//显示符号
        {
            sign = '-';
            num = -num;
        }
        else if (type & PLUS)//字符串中的+号
            sign = '+';
        else if (type & SPACE)//空格
            sign = ' ';
    }

#ifdef RT_PRINTF_SPECIAL
    if (type & SPECIAL) //特殊字符#
    {
        if (base == 16)//添加0x
            size -= 2;
        else if (base == 8)//添加O
            size--;
    }
#endif

    i = 0;
    if (num == 0)//形参num的值==0
        tmp[i++] = '0';
    else //形参num的值!=0
    {
        while (num != 0)
            tmp[i++] = digits[divide(&num, base)];//将long类型的数据分别转换成base指定的字符,顺序反的
    }

#ifdef RT_PRINTF_PRECISION
    if (i > precision) //i此时是该Num转换成char后的长度
        precision = i;
    size -= precision;//size为空格的长度
#else
    size -= i;
#endif
    //type 既不是左对齐也不需要补0
    if (!(type & (ZEROPAD | LEFT)))
    {
        if ((sign) && (size > 0))
            size--;

        while (size-- > 0)//左边补空格
        {
            if (buf < end)
                *buf = ' ';
            ++ buf;
        }
    }

    if (sign)//有符号
    {
        if (buf < end)
        {
            *buf = sign;//显示符号
        }
        -- size;//符号占用一个字节
        ++ buf;
    }

#ifdef RT_PRINTF_SPECIAL
    if (type & SPECIAL)//#号
    {
        if (base == 8)//8进制
        {
            if (buf < end)
                *buf = '0';//0开头
            ++ buf;
        }
        else if (base == 16)
        {
            if (buf < end)
                *buf = '0';
            ++ buf;
            if (buf < end)
            {
                *buf = type & LARGE ? 'X' : 'x';//0x还是0X
            }
            ++ buf;
        }
    }
#endif

    /* no align to the left */
    if (!(type & LEFT))//非左对齐,但是ZEROPAD标志为true,左边补字符c('0')
    {
        while (size-- > 0)
        {
            if (buf < end)
                *buf = c;
            ++ buf;
        }
    }

#ifdef RT_PRINTF_PRECISION
    while (i < precision--)//输出精度不够,补0
    {
        if (buf < end)
            *buf = '0';
        ++ buf;
    }
#endif

    /* put number in the temporary buffer */
    while (i-- > 0 && (precision_bak != 0))//将tmp中的字符填到buf中
    {
        if (buf < end)
            *buf = tmp[i];
        ++ buf;
    }

    while (size-- > 0)//左对齐的情况,剩下长度填空格
    {
        if (buf < end)
            *buf = ' ';
        ++ buf;
    }

    return buf;
}

rt_int32_t rt_vsnprintf(char       *buf,
                        rt_size_t   size,
                        const char *fmt,
                        va_list     args)
{
    rt_uint32_t num;
    int i, len;
    char *str, *end, c;
    const char *s;

    rt_uint8_t base;            /* the base of number */
    rt_uint8_t flags;           /* flags to print number */
    rt_uint8_t qualifier;       /* 'h', 'l', or 'L' for integer fields */
    rt_int32_t field_width;     /* width of output field */

#ifdef RT_PRINTF_PRECISION
    int precision;      /* min. # of digits for integers and max for a string */
#endif

    str = buf;
    end = buf + size;

    /* Make sure end is always >= buf */
    if (end < buf)
    {
        end  = ((char *) - 1);
        size = end - buf;
    }

    for (; *fmt ; ++fmt) //遍历字串参数
    {
        if (*fmt != '%')//取出非格式化字符放到str中
        {
            if (str < end)
                *str = *fmt;
            ++ str;
            continue;
        }

        /* process flags */
        flags = 0;
        //[标志][输出最少宽度][.精度][长度]类型 
        while (1) //获取标志
        {
            /* skips the first '%' also */
            ++ fmt;
            if (*fmt == '-') flags |= LEFT; //左对齐
            else if (*fmt == '+') flags |= PLUS; //右对齐
            else if (*fmt == ' ') flags |= SPACE; //空格:若符号为正,则显示空格,负则显示"-" 
            else if (*fmt == '#') flags |= SPECIAL;//对o类输出时加前缀o;对x类输出时加前缀0x;
            else if (*fmt == '0') flags |= ZEROPAD;//0:有0表示指定空位填0,如省略表示指定空位不填
            else break;
        }

        /* get field width */
        field_width = -1; //输出格式总长度
        // *fmt是数字则取出数字长度,并且fmt += field_width 
        //printf("%*.*s\n",m,n,ch);前边的*定义的是总的宽度,后边的定义的是输出的个数。
        //分别对应外面的参数m和n
        if (_ISDIGIT(*fmt)) field_width = skip_atoi(&fmt);
        else if (*fmt == '*')//否则*fmt == *
        {
            ++ fmt;
            /* it's the next argument */
            field_width = va_arg(args, int);
            if (field_width < 0)//形参指定的输出宽度小于0的话,则表示左对齐
            {
                field_width = -field_width;
                flags |= LEFT;
            }
        }

#ifdef RT_PRINTF_PRECISION
        /* get the precision */
        precision = -1;
        if (*fmt == '.')
        {
            ++ fmt;
            if (_ISDIGIT(*fmt)) precision = skip_atoi(&fmt);//输出的个数(n)
            else if (*fmt == '*')
            {
                ++ fmt;
                /* it's the next argument */
                precision = va_arg(args, int);
            }
            if (precision < 0) precision = 0;//不能小于0
        }
#endif
        /* get the conversion qualifier */
        qualifier = 0;
        //l h要搭配d/u等使用
        if (*fmt == 'h' || *fmt == 'l')//l对应为long, h对应位short
        {
            qualifier = *fmt;
            ++ fmt;
#ifdef RT_PRINTF_LONGLONG
            if (qualifier == 'l' && *fmt == 'l')
            {
                qualifier = 'L';
                ++ fmt;
            }
#endif
        }

        /* the default base */
        base = 10;//默认十进制

        switch (*fmt)//判断控制符
        {
        case 'c'://字符
            if (!(flags & LEFT))//非左对齐
            {
                while (--field_width > 0)//前面先输出空格,最后一个位置给字符
                {
                    if (str < end) *str = ' ';
                    ++ str;
                }
            }

            /* get character */
            c = (rt_uint8_t)va_arg(args, int);//获取字符对应的实参的值
            if (str < end) *str = c;
            ++ str;

            /* put width */
            while (--field_width > 0)//如果是左对齐才会进这里,c[空格]
            {
                if (str < end) *str = ' ';
                ++ str;
            }
            continue;

        case 's':
            s = va_arg(args, char *);
            if (!s) s = "(NULL)";
            //获取要显示字串的长度    
            for (len = 0; (len != field_width) && (s[len] != '\0'); len++);
#ifdef RT_PRINTF_PRECISION
            //指定显示字串的位数大于0且小于字串的长度,那么将字串的长度改成precision
            if (precision > 0 && len > precision) len = precision;
#endif
            if (!(flags & LEFT))//非左对齐
            {
                while (len < field_width--)//len之外的字串填空格
                {
                    if (str < end) *str = ' ';
                    ++ str;
                }
            }
            //将Len长的字串填入Buff
            for (i = 0; i < len; ++i)
            {
                if (str < end) *str = *s;
                ++ str;
                ++ s;
            }

            while (len < field_width--)//左对齐的时候
            {
                if (str < end) *str = ' ';
                ++ str;
            }
            continue;

        case 'p':
            if (field_width == -1)//没指定输出长度
            {
                field_width = sizeof(void *) << 1;//8
                flags |= ZEROPAD;//填‘0’标志
            }
#ifdef RT_PRINTF_PRECISION
            str = print_number(str, end,
                               (long)va_arg(args, void *),
                               16, field_width, precision, flags);
#else
            str = print_number(str, end,
                               (long)va_arg(args, void *),
                               16, field_width, flags);
#endif
            continue;

        case '%':
            if (str < end) *str = '%';
            ++ str;
            continue;

        /* integer number formats - set up the flags and "break" */
        case 'o':
            base = 8;
            break;

        case 'X':
            flags |= LARGE;
        case 'x':
            base = 16;
            break;

        case 'd':
        case 'i':
            flags |= SIGN;
        case 'u':
            break;

        default://没有匹配到正确的格式,则%为一个字符
            if (str < end) *str = '%';
            ++ str;

            if (*fmt)
            {
                if (str < end) *str = *fmt;
                ++ str;
            }
            else//检测到'\0'(%'\0')说明到字串尾部了,防止越界
            {
                -- fmt;
            }
            continue;
        }
//能走到下面的有d,i,u,o,x
#ifdef RT_PRINTF_LONGLONG
        if (qualifier == 'L') num = va_arg(args, long long);
        else if (qualifier == 'l')
#else
        if (qualifier == 'l')//long
#endif
        {
            num = va_arg(args, rt_uint32_t); //long unsigned int
            if (flags & SIGN) num = (rt_int32_t)num;//long int
        }
        else if (qualifier == 'h')//short
        {
            num = (rt_uint16_t)va_arg(args, rt_int32_t);// unsigend short
            if (flags & SIGN) num = (rt_int16_t)num;// signed short
        }
        else
        {
            num = va_arg(args, rt_uint32_t);//unsigend int
            if (flags & SIGN) num = (rt_int32_t)num; //signed int
        }
#ifdef RT_PRINTF_PRECISION
        str = print_number(str, end, num, base, field_width, precision, flags);
#else
        str = print_number(str, end, num, base, field_width, flags);
#endif
    }

    if (size > 0)
    {
        if (str < end) *str = '\0';
        else
        {
            end[-1] = '\0';
        }
    }

    /* the trailing null byte doesn't count towards the total
    * ++str;
    */
    return str - buf;
}
/**
 * This function will fill a formatted string to buffer
 *
 * @param buf the buffer to save formatted string
 * @param size the size of buffer
 * @param fmt the format
 */
rt_int32_t rt_snprintf(char *buf, rt_size_t size, const char *fmt, ...)
{
    rt_int32_t n;
    va_list args;

    va_start(args, fmt);
    n = rt_vsnprintf(buf, size, fmt, args);
    va_end(args);

    return n;
}
/**
 * This function will fill a formatted string to buffer
 *
 * @param buf the buffer to save formatted string
 * @param arg_ptr the arg_ptr
 * @param format the format
 */
rt_int32_t rt_vsprintf(char *buf, const char *format, va_list arg_ptr)
{
    return rt_vsnprintf(buf, (rt_size_t) - 1, format, arg_ptr);
}

/**
 * This function will fill a formatted string to buffer
 *
 * @param buf the buffer to save formatted string
 * @param format the format
 */
rt_int32_t rt_sprintf(char *buf, const char *format, ...)
{
    rt_int32_t n;
    va_list arg_ptr;

    va_start(arg_ptr, format);
    n = rt_vsprintf(buf, format, arg_ptr);
    va_end(arg_ptr);

    return n;
}
/**
 * This function will print a formatted string on system console
 *
 * @param fmt the format
 */
void rt_kprintf(const char *fmt, ...)
{
    va_list args;
    rt_size_t length;
    static char rt_log_buf[RT_CONSOLEBUF_SIZE];

    va_start(args, fmt);
    /* the return value of vsnprintf is the number of bytes that would be
     * written to buffer had if the size of the buffer been sufficiently
     * large excluding the terminating null byte. If the output string
     * would be larger than the rt_log_buf, we have to adjust the output
     * length. */
    length = rt_vsnprintf(rt_log_buf, sizeof(rt_log_buf) - 1, fmt, args);
    if (length > RT_CONSOLEBUF_SIZE - 1)
        length = RT_CONSOLEBUF_SIZE - 1;
#ifdef RT_USING_DEVICE
    if (_console_device == RT_NULL)
    {
        rt_hw_console_output(rt_log_buf);
    }
    else
    {
        rt_uint16_t old_flag = _console_device->open_flag;

        _console_device->open_flag |= RT_DEVICE_FLAG_STREAM;
        rt_device_write(_console_device, 0, rt_log_buf, length);
        _console_device->open_flag = old_flag;
    }
#else
    rt_hw_console_output(rt_log_buf);
#endif
    va_end(args);
}
#endi

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tony++

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值