同一代码在不同设备上的不同行为
Different behaviour of same code on different devices
我很困惑。我有一个相同的程序正在上传到两个不同的Arduino板。它在C++中。
这是一个大得多的程序,但我将把它减少到只有问题的部分。基本上,我有一个"主机"Arduino和一个"漫游者"Arduio无线通信。火星车有多个单元,但问题只发生在其中一个单元上。漫游者有需要校准的电机,所以我在Motor
命名空间中有静态变量来保存这些校准值。为了防止每次校准时都必须更改源代码中的这些值,重新编译并重新部署,我使用无线系统允许主机在运行时向漫游者发送校准值。
问题是:在一个漫游者上,如果我调用ChangeSpeed
方法,则不会更新值,但如果我直接修改变量,则会更新值。
让我强调一下,它在五分之四的漫游车上运行良好。问题正好发生在一个漫游者身上。上传到每个漫游者的代码是相同的。
以下代码导致问题:
电机h:
namespace Motor
{
static unsigned char left_speed = 0;
static unsigned char right_speed = 0;
void ChangeSpeed(unsigned char, unsigned char);
}
Motor.cpp:
void Motor::ChangeSpeed(unsigned char l_speed, unsigned char r_speed)
{
left_speed = l_speed;
right_speed = r_speed;
soft.println("Change speed: " + String(left_speed) + ", " + String(right_speed));
}
Main.cpp:
void UpdateSpeedValuesBad(unsigned char l_speed, unsigned char r_speed)
{
Motor::ChangeSpeed(l_speed, r_speed);
soft.println("Motor write: " + String(l_speed) + ", " + String(r_speed));
}
void UpdateSpeedValuesGood(unsigned char l_speed, unsigned char r_speed)
{
Motor::left_speed = l_speed;
Motor::right_speed = r_speed;
soft.println("Motor write: " + String(l_speed) + ", " + String(r_speed));
}
void ReturnSpeedValues()
{
soft.println("Motor read: " + String(Motor::left_speed) + ", " + String(Motor::right_speed));
}
情况1:
在坏漫游器上,主机调用UpdateSpeedValuesBad(5, 5)
,然后调用ReturnSpeedValues
。输出为:
Change speed: 5, 5
Motor write: 5, 5
Motor read: 0, 0
情况2:
在坏漫游器上,主机调用UpdateSpeedValuesGood(5, 5)
,然后调用ReturnSpeedValues
。输出为:
Motor write: 5, 5
Motor read: 5, 5
情况3:
在良好漫游者上,主机调用UpdateSpeedValuesBad(5, 5)
,然后调用ReturnSpeedValues
。输出为:
Change speed: 5, 5
Motor write: 5, 5
Motor read: 5, 5
我是不是做错了什么?我有C#背景,所以C++对我来说很陌生。我不知道我是否在做一些行为不明确的事情。
编辑:如果我把所有内容都放在一个文件中,它就可以正常工作。只有当我将它拆分为一个头文件和一个cpp文件时,它才会失败。
Main.cpp:
#include <SoftwareSerial.h>
SoftwareSerial soft(9, 10);
namespace Motor
{
static int left_speed = 0;
void ChangeSpeed(unsigned char);
}
void Motor::ChangeSpeed(unsigned char l_speed)
{
left_speed = l_speed;
soft.println("Change speed: " + String(left_speed));
}
void setup()
{
soft.begin(9600);
soft.println("Before: " + String(Motor::left_speed));
Motor::ChangeSpeed(5);
soft.println("Bad attempt: " + String(Motor::left_speed));
Motor::left_speed = 5;
soft.println("Good attempt: " + String(Motor::left_speed));
}
void loop()
{
}
输出:
Before: 0
Change speed: 5
Bad attempt: 5
Good attempt: 5
编辑2:我深入程序集,发现这是针对坏情况的。它使用不同的内存地址,这取决于我是调用ChangeSpeed
还是直接更新值。有人知道为什么会这样吗?是编译器错误还是不能保证地址相同?
000000a8 <setup>:
{
Motor::ChangeSpeed(5, 6);
a8: 85 e0 ldi r24, 0x05 ; 5
aa: 66 e0 ldi r22, 0x06 ; 6
ac: 0e 94 5f 00 call 0xbe ; 0xbe <_ZN5Motor11ChangeSpeedEhh>
Motor::left_speed = 5;
b0: 85 e0 ldi r24, 0x05 ; 5
b2: 80 93 00 01 sts 0x0100, r24
Motor::right_speed = 6;
b6: 86 e0 ldi r24, 0x06 ; 6
b8: 80 93 01 01 sts 0x0101, r24
}
bc: 08 95 ret
000000be <_ZN5Motor11ChangeSpeedEhh>:
void Motor::ChangeSpeed( unsigned char l_speed, unsigned char r_speed )
{
left_speed = l_speed;
be: 80 93 02 01 sts 0x0102, r24
right_speed = r_speed;
c2: 60 93 03 01 sts 0x0103, r22
c6: 08 95 ret
您不应该使这些变量成为静态的。静态全局变量意味着该变量是编译单元(通常是正在编译的.cpp
文件)的本地变量,因此,如果在头文件中声明了静态变量,并将该头文件包含在单独编译的3个不同的.cpp
文件中,则该变量将有3个独立版本,每个.cpp
文件一个。
相反,在头文件中将它们声明为
namespace Motor {
extern unsigned char left_speed;
extern unsigned char right_speed;
void ChangeSpeed(unsigned char, unsigned char);
}
这告诉编译器某些文件将为这些变量提供一个定义,并使用该公共共享定义。
然后,由于变量只需要定义一次(这被称为一个定义规则),您应该将定义添加到Motor.cpp
:
unsigned char Motor::left_speed = 0;
unsigned char Motor::right_speed = 0;
我选择Motor.cpp
来保存定义,因为这就是ChangeSpeed
函数的定义所在
在C++中,static
关键字的工作方式与C#大不相同。当在类定义中使用时,它可能有点相似,但这就是相似之处的结束。
通过声明变量static
,可以将它们的范围限制为当前代码单元。换句话说,通过在.h
中包含static
变量,可以使Motor.cpp
和Main.cpp
具有这两个变量的单独副本。
案例1修改这些变量的Motor.cpp
副本,同时输出Main.cpp
中的副本。案例2仅适用于Main.cpp
副本,因此可以按预期工作。如果你把所有的东西都塞进一个文件,你只会得到这些变量的一个副本。
您应该:
- 在头中声明变量为
extern unsigned char left_speed, right_speed;
,然后在其中一个.cpp
文件中声明值为unsigned char left_speed = 0;
- 直接在其中一个
.cpp
文件(例如Rotor.cpp
)中声明静态变量,并使用函数获取它们的值,就像使用函数设置它们一样
- C++我的数学有什么问题,为什么我的代码不能正确循环
- 代码在main()中运行,但在函数中出现错误
- 在VS代码中交叉编译Windows与Linux上的MinGW的SDL程序
- 编译包含字符串的代码时遇到问题
- 我在c++代码中生成了一个运行时#3异常
- 如何在linux终端中同时编译和运行c++代码
- 为cl.exe(Visual Studio代码)指定命令行C++版本
- 在Linux for Windows上编译C++代码时出错
- 我的字符计数代码计算错误.为什么
- 孤立代码块在结构中引发异常
- 在编译C++代码(具有dlib和opencv)到WASM时面临问题
- 为什么我的C#代码在调用回C++COM直到Task时会暂停.等待/线程.加入
- 处理小于cpu数据总线的数据类型.(c++转换为机器代码)
- 此代码是否违反一个定义规则
- 为什么我的代码在输出中增加了93天
- 我的简单if-else语句是如何无法访问的代码
- 使用动态分配的数组会导致代码分析发出虚假的C6386缓冲区溢出警告
- 为什么在这个代码结束循环中没有得到结束
- 在c代码之间共享数据的最佳方式
- 这个指针和内存代码打印是什么?我不知道是打印垃圾还是如何打印我需要的值