memcpy с инициализированной переменной и отрицательными числами с приведением

#c #c #qt #casting #memcpy

#c #c #qt #Кастинг #memcpy

Вопрос:

У меня есть

     QByteArray bytes    // Fullfilled earlier   
    char id_c = bytes[7];   
    int _id;
    _id = 0; // If I comment this result would be different
    memcpy(amp;_id, amp;id_c, 1); 
    int result = _id;
  

У меня есть переменная _id, и если я прокомментирую переменную результата «_id = 0», результат будет отличаться с отрицательным числом. Почему? Почему инициализация _id с 0 будет отличаться ?!
Как я могу сделать это альтернативно с тем же результатом, что и при использовании «_id = 0», но без memcpy и нежелательных приведений?

Это не мой код. Мне интересно, как правильно получить тот же результат без глупых кастингов.

Комментарии:

1. вы копируете один байт на что-то, что (вероятно) составляет 4 байта. Если вы не инициализируете остальные 3 байта, вы получите мусор (технически неопределенное поведение). Есть ли причина, по которой вы не можете просто сделать _id = id_c; ?

2. Я бы попробовал это и сообщил, потому что я пытался, но забыл!

3. Результат зависит от последовательности. Хотя я полагаю, что переносимый код в любом случае невозможен, поскольку вы используете «кроссплатформенный» QT.

4. Планируете ли вы передавать / передавать данные по какой-либо сети? bytes

5. Нет. Я бы не стал отправлять их через TX / RS. Я могу использовать пакеты QTcpSocket и ethernet

Ответ №1:

Правильно.

Потому что это утверждение:

 memcpy(amp;_id, amp;id_c, 1); 
  

Копирует только один байт из amp;id_c в адрес, представляющий 4-байтовое целое число, amp;_id . Только первый байт памяти, занятой, _id получает что-либо скопированное в него. Без нулевого инициализации _id first оставшиеся три байта этого значения остаются неопределенными (предположительно, случайные значения мусора из стека).

Что не так с «нежелательным приведением»? Это так же хорошо, и компилятор генерирует наиболее эффективный код.

 QByteArray bytes    // Fullfilled earlier   
int _id = (int)(bytes[7]);
int result = _id;
  

Если вы хотите подписать расширенный результат скопированного беззнакового байта _id , то это:

 int _id = (signed char)(bytes[7]);
  

Комментарии:

1. это лучше, чем _id=0; _id = id_c? Почему это самый эффективный код?

2. Действительно сложно превзойти оптимизацию компилятора для присвоения значений между переменными. memcpy лучше всего подходит для массового копирования и выполнения некоторых очень эзотерических действий между адресами в памяти.

3. Также memcpy полезен для очень явных операций сериализации из объектов (структур) в байтовые массивы и из них. Итак, это может быть полезно, если вы делаете что-то сложное, например, инициализируете структуру из массива байтов. Вы могли memcpy бы перевести каждый отдельный элемент структуры в / из его ожидаемой позиции в массиве байтов. Но в приведенном выше примере вы имели дело всего с одним байтом, поэтому это становится намного более простой проблемой.

Ответ №2:

_id = 0 вызывается присвоением 0 значения переменной _id , если вы прокомментируете это, тогда мы не можем быть уверены, что в ней хранится _id , и вы обновляете только один байт из этого, поскольку он имеет тип int , размер которого превышает один байт.

Комментарии:

1. Это объясняет причину проблемы, но на самом деле не отвечает на вопрос OP.

2. Я использовал _id = static_cast<int>(байты [7]), и это было неправильно. Я получаю код так, как он есть, и, полагаю, мой вопрос не точен

Ответ №3:

Вы можете попробовать эти преобразования порядка байтов сети / хоста:

в Linux

в Windows

единственное отличие заключается в используемом файле заголовка; Вы можете использовать приемы препроцессора для определения платформы и выбора правильного заголовка, если предполагается кроссплатформенное программирование. Лучшим подходом является использование функции C 20 std::endian . Но вам нужно обработать преобразование самостоятельно:

 #include <bit>
#include <climits>
int int_cvt(int x){
    if constexpr (endian::native==endian::big)
       return x;
    y=0;
    while(x){
        unsigned char c=x;
        x>>=std::CHAR_BIT;
        y<<=std::CHAR_BIT;
        y =c;
    };
    return y;
};
  

приветствую,
FM.

Комментарии:

1. Спасибо! Но нет C 2020

2. поэтому вам необходимо включить соответствующий заголовок, зависящий от ОС.

3. Существует QT с VS2008 IDE и компилятором и подключенной базой данных firebird. Мне нужно время, чтобы восстановить все.

4. так что ссылка на Windows должна делать.