РЕДАКТИРОВАТЬ: Добавлен полный 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 значения верны.
Вы компилируете и запускаете эту программу в системе, где 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());
Других решений пока нет …