Приведение QByteArray к `long` выводит другой результат для одного и того же ввода

РЕДАКТИРОВАТЬ: Добавлен полный 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

Выход из value1 в test_unpack() неправильно, несмотря на тот же код и те же входные данные. Странно, если я уберу qDebug() вызывает и устанавливает точку останова, теперь оценщик выражений отладчика показывает, что value2 имеет неправильное значение.

Есть идеи, почему это происходит? Или даже как устранить эту проблему дальше?

Дополнительные примечания: Если я добавлю строку qDebug() << "garbage"; в верхней части моей функции все 3 значения верны.

0

Решение

Вы компилируете и запускаете эту программу в системе, где long 8 байт, но ваш QByteArray имеет только 4 байта. Это означает, что когда вы используете псевдоним массива как long (с помощью *((long*)a.data())) вы читаете 4 байта после конца массива в неинициализированную кучу.

Исправление заключается в использовании типа, размер которого гарантированно составляет 4 байта, например std::int32_t,

В сторону, используя *((long*)[...]) к псевдониму не гарантируется работа памяти, в первую очередь из-за проблем с выравниванием, но также (в общем случае), потому что псевдонимы поддерживаются только для типов, эквивалентных char или signed или же unsigned вариант. Более безопасная техника заключается в использовании memcpy:

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

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]