printf函数

printf函数详解

该文章printf函数原型来自CooCox CoIDE内部组件,为精简版,不能处理浮点数。

  • 首先printf函数将格式字符串以及可变参数列表va_list做为参数传递给vprintf函数。
/**
 * @brief  Outputs a formatted string on the DBGU stream, using a variable number of
 *         arguments.
 *
 * @param  pFormat  Format string.
 */
signed int printf(const char *pFormat, ...)
{
    va_list ap;
    signed int result;

    /* Forward call to vprintf */
    va_start(ap, pFormat);
    result = vprintf(pFormat, ap);
    va_end(ap);

    return result;
}

  • vprintf函数调用vfprintf函数并将标准输出做为其输出流,将上述格式字符串以及参数列表传递给vfprintf
/**
 * @brief  Outputs a formatted string on the DBGU stream. Format arguments are given
 *         in a va_list instance.
 *
 * @param pFormat  Format string.
 * @param ap  Argument list.
 */
signed int vprintf(const char *pFormat, va_list ap)
{
    return vfprintf(stdout, pFormat, ap);
}

  • vfprintf主要调用两个函数vsprintf和fputs,前者用于解析格式字符串并将解析后的字符串填充到输出字符数组中,后者用于输出输出字符数组到指定的输出流。
/**
 * @brief  Outputs a formatted string on the given stream. Format arguments are given
 *         in a va_list instance.
 *
 * @param pStream  Output stream.
 * @param pFormat  Format string
 * @param ap       Argument list. 
 */
signed int vfprintf(FILE *pStream, const char *pFormat, va_list ap)
{
    char pStr[MAX_STRING_SIZE]; // 将处理后的格式字符串填充到该数组里
    char pError[] = "stdio.c: increase MAX_STRING_SIZE\n\r";

    /* Write formatted string in buffer */
    if (vsprintf(pStr, pFormat, ap) >= MAX_STRING_SIZE) { // 将处理后的格式化字符串写入到输出数组里

        fputs(pError, stderr);
        while (1); /* Increase MAX_STRING_SIZE */
    }

    /* Display string */
    return fputs(pStr, pStream);    // 显示字符串
}

  • 以下函数请参考注释阅读
/**
 * @brief  Stores the result of a formatted string into another string. Format
 *         arguments are given in a va_list instance.
 *
 * @param pString  Destination string.
 * @param length   Length of Destination string.
 * @param pFormat  Format string.
 * @param ap       Argument list.
 *
 * @return  The number of characters written.
 */
signed int vsprintf(char *pString, const char *pFormat, va_list ap)
{
   return vsnprintf(pString /* 解析后的字符串 */, 
                    MAX_STRING_SIZE /* 填充字符数组最大长度 */,      
                    pFormat /* 原始格式字符串 */, 
                    ap /* 参数列表 */);
}

/**
 * @brief  Stores the result of a formatted string into another string. Format
 *         arguments are given in a va_list instance.
 *
 * @param pStr    Destination string.
 * @param length  Length of Destination string.
 * @param pFormat Format string.
 * @param ap      Argument list.
 *
 * @return  The number of characters written.
 */
signed int vsnprintf(char *pStr, size_t length, const char *pFormat, va_list ap)
{
    char          fill  /* 填充字符一般为空格 */;
    unsigned char width /* 输出宽度,如%5d输出宽度为5 */;
    signed int    num = 0 /* 按格式解析后所占位数 */;
    signed int    size = 0/* 解析后字符串长度 */;

    /* Clear the string */
    if (pStr) {
        *pStr = 0;
    }

    /* Phase string 开始解析字符串*/
    while (*pFormat != 0 && size < length) {

        /* Normal character */
        if (*pFormat != '%') {  // 普通字符直接填充
            *pStr++ = *pFormat++; 
            size++;
        }
        /* Escaped '%' */
        else if (*(pFormat+1) == '%') { // 如果是连着的%除最后一个其他的原样填充
            *pStr++ = '%';
            pFormat += 2;
            size++;
        }
        /* Token delimiter 开始解析特定格式*/
        else {
            fill = ' '/* 初始化填充字符为空格 */;
            width = 0;
            pFormat++;

            /* Parse filler 若为0则将填充字符设置为0*/
            if (*pFormat == '0') {
                fill = '0';
                pFormat++;
            }

            /* Parse width 解析宽度*/
            while ((*pFormat >= '0') && (*pFormat <= '9')) {
                width = (width*10) + *pFormat-'0';
                pFormat++;
            }

            /* Check if there is enough space 检查数组是否越界*/
            if (size + width > length) {
                width = length - size;
            }

            /* Parse type 解析类型*/
            switch (*pFormat) {
            // 注意va_arg返回值为指定类型的下一个参数的值,注意是下一个
            // 解析有符号整形
            case 'd': 
            case 'i': num = PutSignedInt(pStr, fill, width, va_arg(ap, signed int)); break; 
            // 解析无符号整形
            case 'u': num = PutUnsignedInt(pStr, fill, width, va_arg(ap, unsigned int)); break; 
            // 十六进制小写
            case 'x': num = PutHexa(pStr, fill, width, 0, va_arg(ap, unsigned int)); break; 
            // 十六进制大写
            case 'X': num = PutHexa(pStr, fill, width, 1, va_arg(ap, unsigned int)); break; 
            // 字符串
            case 's': num = PutString(pStr, va_arg(ap, char *)); break; 
            // 字符
            case 'c': num = PutChar(pStr, va_arg(ap, unsigned int)); break; 
            default:
                return EOF;
            }

            pFormat++;
            pStr += num;
            size += num;
        }
    }

    /* NULL-terminated (final \0 is not counted) */
    if (size < length) {
        *pStr = 0;  // 填充结尾字符
    }
    else {
        *(--pStr) = 0;
        size--;
    }

    return size;
}

  • 各个解析函数
/**  %c 
 * @brief  Writes a character inside the given string. Returns 1.
 *
 * @param  pStr Storage string.
 * @param  c    Character to write.
 */
signed int PutChar(char *pStr, char c)
{
    *pStr = c;
    return 1;
}

/**  %s
 * @brief  Writes a string inside the given string.
 *
 * @param  pStr     Storage string.
 * @param  pSource  Source string.
 * @return  The size of the written
 */
signed int PutString(char *pStr, const char *pSource)
{
    signed int num = 0;

    while (*pSource != 0) {
        *pStr++ = *pSource++;
        num++;
    }

    return num;
}


/**  %u
 * @brief  Writes an unsigned int inside the given string, using the provided fill &
 *         width parameters.
 *
 * @param  pStr  Storage string.
 * @param  fill  Fill character.
 * @param  width  Minimum integer width.
 * @param  value  Integer value.   
 */
signed int PutUnsignedInt(
    char *pStr,
    char fill,
    signed int width,
    unsigned int value)
{
    signed int num = 0;

    /* Take current digit into account when calculating width */
    width--;

    /* Recursively write upper digits */
    if ((value / 10) > 0) {

        num = PutUnsignedInt(pStr, fill, width, value / 10);
        pStr += num;
    }

    /* Write filler characters */
    else {

        while (width > 0) {

            PutChar(pStr, fill);
            pStr++;
            num++;
            width--;
        }
    }

    /* Write lower digit */
    num += PutChar(pStr, (value % 10) + '0');

    return num;
}

/** %d
 * @brief  Writes a signed int inside the given string, using the provided fill & width
 *         parameters.
 *
 * @param pStr   Storage string.
 * @param fill   Fill character.
 * @param width  Minimum integer width.
 * @param value  Signed integer value.
 */
signed int PutSignedInt(
    char *pStr,
    char fill,
    signed int width,
    signed int value)
{
    signed int num = 0; // 已填充数字位数
    unsigned int absolute; // 绝对值

    /* Compute absolute value */
    if (value < 0) {
        absolute = -value;
    }
    else {
        absolute = value;
    }

    /* Take current digit into account when calculating width */
    width--; // 注意每进入该函数一次就减一

    /* Recursively write upper digits 以递归的方式写从高位开始填充*/
    if ((absolute / 10) > 0) {
        if (value < 0) {
            num = PutSignedInt(pStr, fill, width, -(absolute / 10));// 递归调用
        }
        else {
            num = PutSignedInt(pStr, fill, width, absolute / 10);
        }
        pStr += num; // 输出字符串指针偏移
    }
    else {
        /* Reserve space for sign 为符号位保留一个宽度*/
        if (value < 0) {
            width--;
        }

        /* Write filler characters */
        while (width > 0) { // 如果解析出来的字符宽度小于指定宽度则填充填充字符
            PutChar(pStr, fill);
            pStr++;
            num++;
            width--;
        }

        /* Write sign */
        if (value < 0) {  // 写符号位
            num += PutChar(pStr, '-');
            pStr++;
        }
    }

    /* Write lower digit */
    num += PutChar(pStr, (absolute % 10) + '0'); // 写入到输出字符串里
    return num;
}

/** %x
 * @brief  Writes an hexadecimal value into a string, using the given fill, width &
 *         capital parameters.
 *
 * @param pStr   Storage string.
 * @param fill   Fill character.
 * @param width  Minimum integer width.
 * @param maj    Indicates if the letters must be printed in lower- or upper-case.
 * @param value  Hexadecimal value.
 *
 * @return  The number of char written
 */
signed int PutHexa(
    char *pStr,
    char fill,
    signed int width,
    unsigned char maj/* 判断大小写 */,
    unsigned int value)
{
    signed int num = 0;

    /* Decrement width */
    width--;

    /* Recursively output upper digits */
    if ((value >> 4) > 0) { // 先写高位,正好1位16进制可以表示4位二进制

        num += PutHexa(pStr, fill, width, maj, value >> 4);
        pStr += num;
    }
    /* Write filler chars */
    else {

        while (width > 0) {

            PutChar(pStr, fill);
            pStr++;
            num++;
            width--;
        }
    }

    /* Write current digit */
    if ((value & 0xF) < 10) {

        PutChar(pStr, (value & 0xF) + '0');
    }
    else if (maj) {

        PutChar(pStr, (value & 0xF) - 10 + 'A');
    }
    else {

        PutChar(pStr, (value & 0xF) - 10 + 'a');
    }
    num++;

    return num;
}

  • 注意以下函数和硬件平台有关,我的硬件平台为STM32故将STM32的串口做为其输出流。

/**
 * @brief  Implementation of fputs using the DBGU as the standard output. Required
 *         for printf().
 *
 * @param pStr     String to write.
 * @param pStream  Output stream.
 *
 * @return  Number of characters written if successful, or -1 if the output
 *          stream is not stdout or stderr.
 */
signed int fputs(const char *pStr, FILE *pStream)
{
    signed int num = 0;

    while (*pStr != 0) {

        if (fputc(*pStr, pStream) == -1) {

            return -1;
        }
        num++;
        pStr++;
    }

    return num;
}

/**
 * @brief  Implementation of fputc using the DBGU as the standard output. Required
 *         for printf().
 *
 * @param c        Character to write.
 * @param pStream  Output stream.
 * @param The character written if successful, or -1 if the output stream is
 *        not stdout or stderr.
 */
signed int fputc(signed int c, FILE *pStream)
{
    if ((pStream == stdout) || (pStream == stderr)) {

        PrintChar(c);

        return c;
    }
    else {

        return EOF;
    }
}

/**
 * @brief  Transmit a char, if you want to use printf(), 
 *         you need implement this function
 *
 * @param  pStr Storage string.
 * @param  c    Character to write.
 */
void PrintChar(char c)
{
    // 将字符输出到串口,UART4为STM32的一个串口
    USART_SendData(UART4, (unsigned char)c);
    while (USART_GetFlagStatus(UART4, USART_FLAG_TC) == RESET);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值