将 QByteArray 转换为“long”输出相同输入的不同结果
Casting QByteArray to `long` outputs different result for same input
编辑:添加了完整的 MCV 示例项目。
我有一个奇怪的问题,相同的代码和相同的输入产生不同的输出值。
该代码的目的是测试一个函数,该函数将一个值打包成 4 个字节,并将其解包为一个 32 位值。 value1
、value2
和 value3
在 test_unpack()
中的预期值为 2018915346(即 0x78563412,因为小端解包)。我从那里得到了这种解包方法。下面是一个 MCV 示例,您可以轻松构建它并亲自查看问题。请注意,如果您注释掉 test1()
的正文,test_unpack()
会神奇地传递正确的值。
test_canserialcomm.cpp
#include "test_canserialcomm.h"
#include <QtTest/QtTest>
#include <QByteArray>
long unpack() noexcept
{
quint8 a_bytes[] = {0x12, 0x34, 0x56, 0x78};
QByteArray a = QByteArray(reinterpret_cast<char*>(a_bytes), 4);
long value1 = *((long*)a.data());
qDebug() << value1; // outputs "32651099317351442" (incorrect value)
quint8 b_bytes[] = {0x12, 0x34, 0x56, 0x78};
QByteArray b = QByteArray(reinterpret_cast<char*>(b_bytes), 4);
long value2 = *((long*)b.data());
qDebug() << value2; // outputs "2018915346" (correct value)
quint8 c_bytes[] = {0x12, 0x34, 0x56, 0x78};
QByteArray c = QByteArray(reinterpret_cast<char*>(c_bytes), 4);
long value3 = *((long*)c.data());
qDebug() << value3; // outputs "2018915346" (correct value)
return value1;
}
void TestCanSerialComm::test1()
{
QCOMPARE("aoeu", "aoeu"); // If you comment this line, the next test will pass, as expected.
}
void TestCanSerialComm::test_unpack()
{
long expected {0x78563412};
QCOMPARE(unpack(), expected);
}
test_canserialcomm.h
#ifndef TEST_CANSERIALCOMM_H
#define TEST_CANSERIALCOMM_H
#include <QtTest>
class TestCanSerialComm: public QObject
{
Q_OBJECT
private slots:
void test1();
void test_unpack();
};
#endif // TEST_CANSERIALCOMM_H
test_main.cpp
#include <QtTest>
#include "test_canserialcomm.h"
#include <QCoreApplication>
int main(int argc, char** argv) {
QCoreApplication app(argc, argv);
TestCanSerialComm testCanSerialComm;
// Execute test-runner.
return QTest::qExec(&testCanSerialComm, argc, argv); }
tmp.pro
QT += core \
testlib
QT -= gui
CONFIG += c++11
TARGET = tmp
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
TARGET = UnitTests
HEADERS += test_canserialcomm.h
SOURCES += test_canserialcomm.cpp \
test_main.cpp
尽管代码和输入相同,test_unpack()
中 value1
的输出是错误的。奇怪的是,如果我删除 qDebug()
调用并设置断点,调试器表达式计算器现在会显示 value2
有错误的值。
知道为什么会这样吗?或者甚至如何进一步解决这个问题?
附加说明:如果我在函数顶部添加一行 qDebug() << "garbage";
,则生成的所有 3 个值都是正确的。
您正在 运行 系统上编译此程序,其中 long
是 8 个字节,但您的 QByteArray
只有 4 个字节。这意味着当您将数组别名为 long
(使用 *((long*)a.data())
)时,您正在读取数组末尾后的 4 个字节,进入未初始化的堆存储。
解决方法是使用保证大小为 4 字节的类型,例如std::int32_t
.
顺便说一句,使用 *((long*)[...])
来别名内存不能保证有效,主要是因为对齐问题,而且(在一般情况下)因为别名只支持等同于 [=17= 的类型] 或 signed
或 unsigned
变体。更安全的技术是使用 memcpy
:
std::uint32_t value1;
assert(a.size() == sizeof(value1));
memcpy(&value1, a.data(), a.size());
编辑:添加了完整的 MCV 示例项目。
我有一个奇怪的问题,相同的代码和相同的输入产生不同的输出值。
该代码的目的是测试一个函数,该函数将一个值打包成 4 个字节,并将其解包为一个 32 位值。 value1
、value2
和 value3
在 test_unpack()
中的预期值为 2018915346(即 0x78563412,因为小端解包)。我从test1()
的正文,test_unpack()
会神奇地传递正确的值。
test_canserialcomm.cpp
#include "test_canserialcomm.h"
#include <QtTest/QtTest>
#include <QByteArray>
long unpack() noexcept
{
quint8 a_bytes[] = {0x12, 0x34, 0x56, 0x78};
QByteArray a = QByteArray(reinterpret_cast<char*>(a_bytes), 4);
long value1 = *((long*)a.data());
qDebug() << value1; // outputs "32651099317351442" (incorrect value)
quint8 b_bytes[] = {0x12, 0x34, 0x56, 0x78};
QByteArray b = QByteArray(reinterpret_cast<char*>(b_bytes), 4);
long value2 = *((long*)b.data());
qDebug() << value2; // outputs "2018915346" (correct value)
quint8 c_bytes[] = {0x12, 0x34, 0x56, 0x78};
QByteArray c = QByteArray(reinterpret_cast<char*>(c_bytes), 4);
long value3 = *((long*)c.data());
qDebug() << value3; // outputs "2018915346" (correct value)
return value1;
}
void TestCanSerialComm::test1()
{
QCOMPARE("aoeu", "aoeu"); // If you comment this line, the next test will pass, as expected.
}
void TestCanSerialComm::test_unpack()
{
long expected {0x78563412};
QCOMPARE(unpack(), expected);
}
test_canserialcomm.h
#ifndef TEST_CANSERIALCOMM_H
#define TEST_CANSERIALCOMM_H
#include <QtTest>
class TestCanSerialComm: public QObject
{
Q_OBJECT
private slots:
void test1();
void test_unpack();
};
#endif // TEST_CANSERIALCOMM_H
test_main.cpp
#include <QtTest>
#include "test_canserialcomm.h"
#include <QCoreApplication>
int main(int argc, char** argv) {
QCoreApplication app(argc, argv);
TestCanSerialComm testCanSerialComm;
// Execute test-runner.
return QTest::qExec(&testCanSerialComm, argc, argv); }
tmp.pro
QT += core \
testlib
QT -= gui
CONFIG += c++11
TARGET = tmp
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
TARGET = UnitTests
HEADERS += test_canserialcomm.h
SOURCES += test_canserialcomm.cpp \
test_main.cpp
尽管代码和输入相同,test_unpack()
中 value1
的输出是错误的。奇怪的是,如果我删除 qDebug()
调用并设置断点,调试器表达式计算器现在会显示 value2
有错误的值。
知道为什么会这样吗?或者甚至如何进一步解决这个问题?
附加说明:如果我在函数顶部添加一行 qDebug() << "garbage";
,则生成的所有 3 个值都是正确的。
您正在 运行 系统上编译此程序,其中 long
是 8 个字节,但您的 QByteArray
只有 4 个字节。这意味着当您将数组别名为 long
(使用 *((long*)a.data())
)时,您正在读取数组末尾后的 4 个字节,进入未初始化的堆存储。
解决方法是使用保证大小为 4 字节的类型,例如std::int32_t
.
顺便说一句,使用 *((long*)[...])
来别名内存不能保证有效,主要是因为对齐问题,而且(在一般情况下)因为别名只支持等同于 [=17= 的类型] 或 signed
或 unsigned
变体。更安全的技术是使用 memcpy
:
std::uint32_t value1;
assert(a.size() == sizeof(value1));
memcpy(&value1, a.data(), a.size());