Передача беззнакового целого числа в функцию приводит к потере битов?

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

Я написал некоторый код для поворота беззнакового целого числа; то есть функция bitRotate() (код ниже).

Функция работает очень хорошо, когда вместо printfs и scanfs я напрямую помещаю литералы, которые хочу использовать, например. bitRotate(0xabcdef00,8);в основной функции.

Однако, когда я передаю x в качестве аргумента, как в следующем коде, abcdef00, который был получен от пользователя, x искажается до ab000000. Я проверял, дважды проверял и отлаживал свой код несколько раз, и я почти уверен, что ошибка именно в этой части, но я не понимаю, почему.

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#define WIDTH sizeof(unsigned int)*CHAR_BIT

unsigned int bitRotate(unsigned int, char );

int main()
{

    unsigned int x;
    char n;
    while(1)
    {
        printf("Enter x: ");
        scanf("%x", &x);
        printf("Enter n: ");
        scanf("%d", &n);
        printf("%x\n", bitRotate(x,n));
    }

    return 0;
}

unsigned int bitRotate(unsigned int value, char n)
{

    char un = abs(n);
    unsigned int fallen = ~(0u);

    if(un == WIDTH)
        return value;
    else if (un < WIDTH)
    {

        if (n < 0)
        {
            fallen >>= (WIDTH - n);
            fallen = value & fallen;
            fallen <<= (WIDTH - n);
            value >>= n;
        }
        else
        {
            fallen <<= (WIDTH - n);
            fallen = value & fallen;
            fallen >>= (WIDTH - n);
            value <<= n;
        }
        value |= fallen;
        return value;

    }
    else
        return 0;

}

person user401445    schedule 24.10.2019    source источник
comment
"%d" является неправильным спецификатором формата для ввода char через scanf. Вам нужно использовать "%c", а для использования пробела новой строки из предыдущей записи, я думаю, вам нужно использовать " %c" (с начальным пробелом). Этот вопрос может быть полезен.   -  person yano    schedule 24.10.2019
comment
По крайней мере, добавьте int response = getchar(); if(response == 'q') break; в конец цикла.   -  person ryyker    schedule 24.10.2019
comment
Как вы убедились, что прочитали правильные числа? Вы не проверяете возвращаемое значение scanf и не печатаете свои числа. Как бы вы заметили ошибку, прежде чем вызывать функцию вращения?   -  person Gerhardh    schedule 24.10.2019
comment
Скорее всего, ваш недопустимый спецификатор формата для scanf приводит к повреждению числа. Хранение 4 или 8 байтов в одной символьной переменной не подходит.   -  person Gerhardh    schedule 24.10.2019
comment
@yano Я каждый раз менял тип n на int, но ничего не изменилось. Я просто разместил здесь исходный код без правок, которые я сделал, чтобы найти ошибку.   -  person user401445    schedule 24.10.2019
comment
@Gerhardh, я поставил «printf («введенный вами номер был ...»). Но, как я уже сказал, я разместил здесь только код без каких-либо методов отладки, которые я использовал.   -  person user401445    schedule 24.10.2019
comment
@ryyker хорошо, да, но я нашел некоторые источники, в которых говорится, что это лучше «из соображений переносимости», поэтому я просто выразился так .. Может быть, в некоторых системах байт не 8 бит? Я не уверен, почему они рекомендовали это сделать   -  person user401445    schedule 24.10.2019
comment
@user401445 user401445 Верно. Я никогда не работал в системе, в которой не было 8-битного байта, но они существуют.   -  person yano    schedule 24.10.2019
comment
Проверка возвращаемого значения не является методом отладки. Вы должны напечатать x и n после второго scanf. Кстати, какой точный ввод для scanf?   -  person Gerhardh    schedule 24.10.2019
comment
@ user401445 - Да, я давно не видел CHAR_BIT, я удалил комментарий, и да, его правильно использовать для того, что вы делаете. Извините за путаницу.   -  person ryyker    schedule 24.10.2019
comment
@ user401445 Я изменил тип n на int в каждом случае, но ничего не изменилось: я вам не верю.   -  person Jabberwocky    schedule 24.10.2019
comment
char un = abs(n); может быть проблемой. это должно быть: int un = abs(n);   -  person ryyker    schedule 24.10.2019
comment
@ryyker не уверен, почему char un = abs(n) может быть проблемой, за исключением случаев, когда n равно › 255, но для больших значений n вращение в любом случае не имеет особого смысла.   -  person Jabberwocky    schedule 24.10.2019
comment
Код if (n < 0) основан на том, что char является подписанным типом, который может применяться или не применяться. Вместо этого вы должны использовать signed char.   -  person Gerhardh    schedule 24.10.2019


Ответы (3)


Функция bitRotate хороша, как и то, как вы ее называете.

Настоящий виновник здесь:

char n;
scanf("%d", &n);  // <<<<<

Вы указываете неправильный спецификатор формата для scanf, что приводит к неопределенному поведению. Ваш компилятор, скорее всего, предупредил вас об этом.

Спецификатор формата %d нуждается в указателе на int (который обычно занимает 32 бита), но вы предоставляете указатель на char, который занимает 8 бит. Поэтому scanf скорее всего затирает соседнюю с адресом n память.

Вы хотите это:

int n;
scanf("%d", &n);
person Jabberwocky    schedule 24.10.2019
comment
Ну ладно, был один символ, который я не менял. Вы правы, код сработал. Но у меня вопрос, а что, если мне не нужны все биты int?? Мне нужно сохранить целочисленное значение (т. е. не ASCII, если я введу 8 в качестве значения для n на консоли, я хочу, чтобы оно воспринималось как 8, а не как «8»), поэтому %c не будет работать. Что я могу сделать вместо этого? - person user401445; 24.10.2019
comment
@user401445 user401445 Используйте %hhd для спецификатора формата. Это ожидает адрес char. - person dbush; 24.10.2019
comment
@dbush Здравствуйте, я работал над новой проблемой и использовал предложенный вами %hhd, но компилятор (mingw в Windows 10) выдал мне предупреждение. Я немного погуглил и нашел это. Я попробовал ту же идею с scanf (т.е. _mingw_scanf()), но оказалось, что функции с таким именем нет. У вас есть идея, как я могу решить эту проблему? - person user401445; 12.11.2019
comment
@ user401445, пожалуйста, задайте другой вопрос. - person Jabberwocky; 12.11.2019

Ваш код имеет неопределенное поведение.

В этом звонке

scanf("%d", &n);

вы используете неправильный спецификатор преобразования с объектом типа char. Кроме того, тип char может вести себя либо как signed char, либо как unsigned char в зависимости от опции компилятора.

В функции используется неправильное выражение, когда n отрицательно

fallen >>= (WIDTH - n);
                 ^^^
fallen <<= (WIDTH - n);
                 ^^^

Я думаю, ты имеешь в виду

fallen >>= (WIDTH + n);
                 ^^^
fallen <<= (WIDTH + n);
                 ^^^

В любом случае функцию можно написать проще. Вот демонстрационная программа.

#include <stdio.h>
#include <limits.h>

unsigned int bitRotate(unsigned int value, int n )
{
    const int Width = sizeof( unsigned int ) * CHAR_BIT;

    n %= Width;

    if ( n < 0 )
    {
        value = ( value >> -n ) | ( value << ( Width + n ) );
    }
    else if ( n > 0 )
    {
        value = ( value << n ) | ( value >> ( Width - n ) );

    }

    return value;
}

int main(void) 
{
    while ( 1 )
    {
        unsigned int x;

        printf( "Enter a hexadecimal value of x (0 - exit): " );

        if ( scanf( "%x", &x ) != 1 || x == 0 ) break;

        int n;

        printf( "Enter a negative or positive value of n (0 - exit): " );
        if ( scanf( "%d", &n ) != 1 || n == 0 ) break;

        printf( "\n%#x shifted %d is %#x\n", x, n, bitRotate( x, n ) );
    }

    return 0;
}

Его вывод может выглядеть как

Enter a hexadecimal value of x (0 - exit): 0xabcdef00
Enter a negative or positive value of n (0 - exit): 16

0xabcdef00 shifted 16 is 0xef00abcd

Enter a hexadecimal value of x (0 - exit): 0xabcdef00
Enter a negative or positive value of n (0 - exit): -16

0xabcdef00 shifted -16 is 0xef00abcd

Enter a hexadecimal value of x (0 - exit): 0xabcdef00
Enter a negative or positive value of n (0 - exit): 32

0xabcdef00 shifted 16 is 0xabcdef00

Enter a hexadecimal value of x (0 - exit): 0
person Vlad from Moscow    schedule 24.10.2019

Этот код показывает, имеет ли значение знаковый бит или нет:

#include <stdio.h>
#include <stdlib.h>

int main(void) {
    const int arr[7] = {0x80000000u, -2, -1, 0, 1, 2, 0x7fffffffu};
    unsigned int i, j, signVal, uiSiz;

    uiSiz = sizeof(unsigned int);

    for (i = 0, j = 7; i < 7; i++) {
        signVal = ((unsigned int) arr[i] & (0x1u << ((8 * uiSiz) - 1))) >> ((8 * uiSiz) - 1);
        fprintf(stdout, "arr[%u]: %i, uiSiz: %u, signVal: %u;\n", i, arr[i], uiSiz, signVal);
    }

    return 0;
}

ССЫЛКА @[ https://ideone.com/rWiSrF ]

person C. R. Ward    schedule 24.10.2019