【问题标题】:Casting QByteArray to `long` outputs different result for same input将 QByteArray 转换为 `long` 为相同的输入输出不同的结果
【发布时间】:2016-08-14 10:00:45
【问题描述】:

编辑:添加了完整的 MCV 示例项目。

我有一个奇怪的问题,相同的代码和相同的输入产生不同的输出值。

代码的目的是测试一个函数,该函数接受一个打包成 4 个字节的值,并将其解压缩成一个 32 位的值。 test_unpack() 中的 value1value2value3 的预期值为 2018915346(即 0x78563412,因为 little-endian 解包)。我从another answer 得到了这种解包方法。下面是一个 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() &lt;&lt; "garbage";,则生成的所有 3 个值都是正确的。

【问题讨论】:

  • 看看值的内存表示,可能会提供一个线索(我没有 qt) - 另一个想法:为什么不使用简单的联合来做到这一点?
  • @slashmais 感谢您的提示。 “内存中的表示”是指我在设置断点时在调试器中看到的变量的值吗?这些值与qDebug() 输出相同。我不确定在这种情况下如何使用联合。
  • 我运行你的代码,我得到了所有三个值的相同结果。
  • 你的系统上long 的大小是多少?
  • 内存中我的意思是您查看包含值/变量的内存地址 - 可能有助于您发现 ecatmur 的建议。

标签: c++ qt casting qt5.6


【解决方案1】:

您在long 为8 个字节的系统上编译和运行此程序,但您的QByteArray 只有4 个字节。这意味着,当您将数组别名为 long(使用 *((long*)a.data()))时,您正在将数组末尾的 4 个字节读取到未初始化的堆存储中。

解决方法是使用保证大小为 4 字节的类型,例如std::int32_t.

顺便说一句,不能保证使用*((long*)[...]) 对内存进行别名处理,这主要是因为对齐问题,但也(在一般情况下)因为别名仅支持与charsigned 等效的类型或unsigned 变体。更安全的技术是使用memcpy

std::uint32_t value1;
assert(a.size() == sizeof(value1));
memcpy(&value1, a.data(), a.size());

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-30
    • 1970-01-01
    相关资源
    最近更新 更多