printf 格式化输出踩坑


categories: 解决方案
tags:

  • C
  • cpp
    date: 2024-03-20 16:09:03

关于 printf 格式化输出与预期不符的分析

缺陷问题详细描述

printf 和 cout 的值不一致。

重现方法

/*******************************************************************************
 * @copyright Copyright (c) 2024
 *
 * @author Wu Wei
 * @version 0.1
 * @date 2024-03-19, created.
 *
 * @file test_print_cast.cpp
 * @brief 测试printf 打印强转的问题
 * @ref C/C++中%d、%ld、%lld的含义和区别是什么? - BetaCat的回答 - 知乎
 *      https://www.zhihu.com/question/283460749/answer/432742267
 ******************************************************************************/

/**
 * 输出:
 * (%ld)d: 140722676870488
 * (%ld)(long)d: 3
 */

#include <iostream>

int main(int argc, char const *argv[])
{
    double d = 10 * 1.0 / 3;
    printf("(%%ld)d: %ld\n", d);
    printf("(%%ld)(long)d: %ld\n", (long)d);
    return 0;
}

原因分析

printf是通过 “可变长参数函数(Variable Argument Functions)” 来实现读取任意类型,数量的参数的。

printf的函数原型是

int printf(const char* format,...);

而可变长参数函数的实际实现方式是将可变长部分(对应…部分),按照顺序,直接在内存中首尾相连的连接起来。

printf(“test %d %ld”,(int)1,(long)2);

此时 format 指向一段内存,开头是一个字符串,然后跟着一段int,随后是long

然而 printf 不知道他应该如何分别这些内存段,哪个是哪个(字符串可以用\0来找),所以这个时候就需要程序猿在提供 format 字符串的时候,同时提供随后参数列表中参数的对应长度!因而 %d%ld 等的混用会导致 printf 使用错误的长度去读取数据!严重的话会造成溢出,轻微的话会把数据读乱。

而cpp中提供了自定义的类转换,因此在cout<<的时候,其实cout接受的是string,转换已经自动发生了,因而没有区别。

作者:BetaCat
链接:https://www.zhihu.com/question/283460749/answer/432742267
来源:知乎

解决方案

先进行类型转换,再使用 printf 输出。

<think>好的,我现在需要帮助用户了解如何使用VOFA工具调试PID控制器。首先,我需要回忆一下VOFA的基本功能和PID调试的常见方法。VOFA是一个数据可视化工具,支持多种通信协议,比如串口、TCP/IP等,能够实时显示数据曲线,这对于观察PID控制器的输入输出非常有用。 用户的问题是如何使用VOFA调试PID参数或进行校准。我应该分步骤来回答,首先可能需要介绍VOFA的基本设置,比如如何连接硬件,发送数据格式。然后,结合PID调试的一般流程,比如调整Kp、Ki、Kd参数,观察系统响应,如超调量、稳态误差等,这些都可以通过VOFA的曲线来直观展示。 接下来,我需要考虑用户可能遇到的难点,例如数据格式是否正确,VOFA支持的协议类型,如何确保数据正确传输。根据用户提供的引用,VOFA的使用需要注意数据格式,同的编译器可能有差异,比如在Keil和CLion中的区别。因此,在步骤中可能需要提醒用户检查数据发送的格式是否正确,确保与VOFA的解析方式匹配。 另外,用户提到的引用[1]中提到了fputc的使用,可能涉及到如何通过串口发送数据。因此,在调试PID时,用户需要将PID的输出、设定值、误差等变量通过串口发送到VOFA,这可能需要编写相应的代码,将数据打包成VOFA支持的格式,比如FireWater格式,每个变量用逗号分隔,最后加上换行符。 还需要考虑PID调试的具体步骤,比如先比例,后积分,再微分的调整顺序,以及如何利用VOFA的图形界面观察每个参数调整后的效果。例如,当增大Kp时,系统响应加快,但可能出现振荡,这时候可以通过曲线判断是否超调过大,从而调整参数。 可能还需要提到实时调整参数的方法,比如在代码中允许通过串口接收新的PID参数,然后应用到控制循环中,这样用户可以在VOFA中发送调整后的参数,实现动态调参,而无需重新烧录固件。 最后,总结常见问题,比如数据无法显示,可能检查波特率、数据格式、连接是否正确;曲线稳定,可能需要检查传感器噪声或控制周期等。同时,参考用户提供的引用,确保提到同编译器可能的影响,比如CLion与Keil的区别,避免用户。 总结起来,回答应该分步骤,包括VOFA设置、数据发送格式、PID调试流程、动态调参方法和常见问题排查。需要确保语言清晰,结构合理,帮助用户一步步实现调试目标。</think>### 如何使用VOFA调试PID控制器? VOFA(Visual Open Field for Automation)是一款支持实时数据可视化的调试工具,通过图形化界面帮助用户观察PID控制器的输入、输出及误差变化。以下是具体步骤: --- #### **1. VOFA环境配置** - **硬件连接**:确保控制器(如STM32、Arduino)与PC通过串口/USB连接,并安装对应驱动。 - **协议选择**:VOFA支持`FireWater`、`JustFloat`等协议。以`FireWater`为例,数据格式为`变量1,变量2,变量3\n`(逗号分隔,换行符结尾)[^1]。 - **软件设置**:在VOFA中新建工程,选择对应串口号和波特率,并绑定数据变量名称(如`设定值,实际值,输出值`)。 --- #### **2. 数据发送代码实现** 在控制器代码中,将PID相关变量(设定值、反馈值、输出值、误差等)通过串口发送,需注意数据格式与VOFA协议匹配。 **示例代码(C语言)**: ```c // 假设使用串口发送函数 void send_to_vofa(float setpoint, float feedback, float output) { printf("%.2f,%.2f,%.2f\n", setpoint, feedback, output); // FireWater格式 } ``` > 注意:同编译器对浮点数的支持可能同,需验证数据解析正确性。 --- #### **3. PID调试流程** - **步骤1:观察开环响应** 关闭PID闭环,发送固定输出值,观察系统响应曲线,确定系统大致延迟和增益。 - **步骤2:比例(Kp)调节** 逐步增大Kp,观察实际值跟踪设定值的速度和超调量。若振荡明显,需降低Kp。 - **步骤3:积分(Ki)调节** 引入Ki以消除稳态误差。若曲线出现周期性波动,说明Ki过大。 - **步骤4:微分(Kd)调节** 添加Kd抑制超调,但过大的Kd会放大噪声影响。 --- #### **4. 动态调参(可选)** 通过VOFA发送新参数至控制器,实现无需重启的动态调整。 **实现方法**: 1. 在控制器代码中解析串口接收的指令(如`KP=1.2,KI=0.5,KD=0.1`)。 2. 更新PID参数并重新计算输出。 --- #### **常见问题排查** | 问题现象 | 可能原因 | 解决方案 | |---------|----------|----------| | 数据无显示 | 波特率匹配 | 检查设备与VOFA的波特率设置 | | 曲线混乱 | 数据格式错误 | 验证结尾是否为`\n`,分隔符是否匹配协议[^1] | | 响应延迟 | 控制周期过长 | 缩短PID计算周期或降低数据发送频率 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

AWei_i_i

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

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

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

打赏作者

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

抵扣说明:

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

余额充值