Преобразование массива Char в Long in C error - PullRequest
0 голосов
/ 12 мая 2018

Я пытаюсь преобразовать unsigned long int в char, но у меня появляется ошибка

int main(void)
{
    unsigned char pdest[4];
    unsigned long l=0xFFFFFFFF;
    pdest[0] = l         & 0xFF;
    pdest[1] = (l >>  8) & 0xFF;
    pdest[2] = (l >> 16) & 0xFF;
    pdest[3] = (l >> 24) & 0xFF;

    unsigned long int l1=0;
    l1 |= (pdest[0]);
    l1 |= (pdest[1] << 8);
    l1 |= (pdest[2] << 16);
    l1 |= (pdest[3] << 24);

    printf ("%lu",l1);
}

и вывод

18446744073709551615

не 4294967295?

Как сделатьэто правильно?

Ответы [ 7 ]

0 голосов
/ 12 мая 2018

Прежде всего, чтобы назвать тип шириной ровно 32 бита, используйте uint32_t, а не unsigned long int. unsigned long int обычно имеет ширину 64 бита в 64-битных * никсах (так называемых LP64), тогда как в Windows это 32 бита (LLP64).

Так или иначе, проблема в целочисленных акциях . Операнд в арифметической операции с рангом преобразования меньше int или unsigned int будет конвертирован в int или unsigned int, в зависимости от того, в какой диапазон он входит. Поскольку все unsigned char s представимы как signed int s, pdest[3] преобразуется в signed int, а результат pdest[3] << 24 также имеет тип signed int !. Теперь, если для него установлен самый значимый бит, этот бит смещается в знаковый бит целого числа, и его поведение соответствует стандарту C undefined .

Однако GCC определил поведение для этого случая; там результат - просто отрицательное целое число с представлением дополнения 2; следовательно, результат (unsigned char)0xFF << 24 равен (int)-16777216. Теперь для операции | ее необходимо повысить до ранга другого операнда , который равен unsigned. Преобразование без знака происходит, как будто путем многократного сложения или вычитания на единицу больше, чем максимум (то есть многократного сложения или вычитания 2⁶⁴) до тех пор, пока значение не попадет в диапазон значения. Поскольку unsigned long на вашей платформе составляет 64 бита, результатом этого преобразования будет 2 ^ 64 - 16777216 или 18446744073692774400, что равно ORred с битами из предыдущих шагов.

как исправить? Легко, непосредственно перед сменой, приведите каждое смещенное число к uint32_t. Печать с помощью макроса PRIu32:

#include <inttypes.h>

...

uint32_t l1=0;
l1 |= (uint32_t)pdest[0];
l1 |= (uint32_t)pdest[1] << 8;
l1 |= (uint32_t)pdest[2] << 16;
l1 |= (uint32_t)pdest[3] << 24;
printf ("%" PRIu32, l1);
0 голосов
/ 12 мая 2018

Проблема с вашим кодом заключается в неявном преобразовании типов беззнакового символа в int, а затем в беззнаковый длинный с расширением со знаком для побитовой операции или операции, соответствующие значения для каждой строки такие, как прокомментировано ниже

l1 |= (pdest[0]);   //dec = 255 hex = 0xFF
l1 |= (pdest[1] << 8);   //dec = 65535 hex = 0xFFFF
l1 |= (pdest[2] << 16);  //dec = 16777215 hex =0xFFFFFF
l1 |= (pdest[3] << 24);  //here is the problem

В последней строке pdest[3] << 24 = 0xFF000000, что эквивалентно -16777216 из-за неявного преобразования в int. Он снова преобразуется в unsigned long для побитовой операции или операции, где расширение со знаком происходит в l1 |= (pdest[3] << 24), что эквивалентно 0x0000000000FFFFFF | 0xFFFFFFFFFF000000.

Как многие предлагали, вы можете использовать явное преобразование типов или использовать приведенный ниже фрагмент кода,

l1 = (l1 << 0) | pdest[3];
l1 = (l1 << 8) | pdest[2];
l1 = (l1 << 8) | pdest[1];
l1 = (l1 << 8) | pdest[0];

Надеюсь, это решит вашу проблему и объяснит столь огромный результат.

0 голосов
/ 12 мая 2018

Длина без знака не должна быть 4 байта.

#include <stdio.h>
#include <stdint.h>

int main(void) {
    int index;
    unsigned char pdest[sizeof(unsigned long)];
    unsigned long l=0xFFFFFFFFUL;

    for(index = 0; index < sizeof(unsigned long); index++)
    {
        pdest[index] = l & 0xff;
        l >>= 8;
    }

    unsigned long l1=0;
    for(index = 0; index < sizeof(unsigned long); index++)
    {
        l1 |= (unsigned long)pdest[index] << (8 * index);

    }

    printf ("%lx\n",l1);
}
0 голосов
/ 12 мая 2018

Проблема в смещении символа. Вы должны форсировать преобразование в unsigned задолго до того, чтобы перейти за пределы 8 битов символа. Более того, знак char будет влиять на результат.

Попробуйте

unsigned long int l1=0;
l1 |= ((unsigned long)pdest[0]);
l1 |= ((unsigned long)pdest[1] << 8); 
l1 |= ((unsigned long)pdest[2] << 16); 
l1 |= ((unsigned long)pdest[3] << 24); 

Обратите внимание на использование приведения, чтобы заставить компиляторы преобразовывать символ в неподписанный задолго до того, как произойдет сдвиг.

0 голосов
/ 12 мая 2018

pdest [3] << 24 имеет тип со знаком int. </p>

Измените код на

unsigned long int l1=0;
l1 |= (pdest[0]);
l1 |= (pdest[1] << 8); 
l1 |= (pdest[2] << 16);
l1 |= ((unsigned int)pdest[3] << 24);
0 голосов
/ 12 мая 2018

Вы должны написать последние 4 строки в виде:

l1 |= ((unsigned long) pdest[0]);
l1 |= (((unsigned long) pdest[1]) << 8);
l1 |= (((unsigned long) pdest[2]) << 16);
l1 |= (((unsigned long) pdest[3]) << 24);

Поскольку вы должны преобразовать байт в беззнаковый задолго до сдвига.

0 голосов
/ 12 мая 2018

Читать это: https://en.wikipedia.org/wiki/C_data_types

... Длинный целочисленный тип без знака.Может содержать не менее в диапазоне [0, 4 294 967 295];

...