Casting QByteArray to `long` outputs different result for same input

≡放荡痞女 提交于 2019-12-13 07:36:56

问题


EDIT: Added full MCV example project.

I have a strange problem where the same code and same input produce different output values.

The purpose of the code is to test a function that takes a value packed into 4 bytes, and unpack it into a single 32bit value. The expected value of value1, value2 and value3 in test_unpack() is 2018915346 (i.e. 0x78563412 because of little-endian unpacking). I got this method of unpacking from another answer. Below is an MCV example that you can easily build and see the problem for yourself. Note that if you comment out the body of test1() test_unpack() magically passes with the correct value.

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

The output of value1 in test_unpack() is wrong, despite the same code and same inputs. Strangely, if I remove the qDebug() calls and set a breakpoint, the debugger expression evaluator now shows that value2 has the wrong value.

Any idea why this is happening? Or even how to troubleshoot this further?

Additional Notes: If I add a line qDebug() << "garbage"; at the top of my function, all 3 values produced are correct.


回答1:


You're compiling and running this program on a system where long is 8 bytes, but your QByteArray has only 4 bytes. That means that when you alias the array as a long (using *((long*)a.data())) you're reading 4 bytes past the end of the array, into uninitialized heap storage.

The fix is to use a type that is guaranteed to be 4 bytes in size, e.g. std::int32_t.

As an aside, using *((long*)[...]) to alias memory is not guaranteed to work, primarily because of alignment issues but also (in the general case) because aliasing is only supported for types equivalent to char or a signed or unsigned variant. The safer technique is to use memcpy:

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


来源:https://stackoverflow.com/questions/36765576/casting-qbytearray-to-long-outputs-different-result-for-same-input

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!