Поведение символов и коротких типов данных при чтении через спецификатор формата %d.

#c #struct #bit-manipulation #scanf #union

#c #структура #манипулирование битами #scanf #объединение

Вопрос:

Я пытаюсь выполнить операцию «БИТОВОЙ МАНИПУЛЯЦИИ», используя struct и union(без каких-либо побитовых операторов). При этом я столкнулся с некоторой незначительной ошибкой, то есть, если я использую типы данных «char» и «short», я не могу получить желаемый результат, вместо этого, если я использую тип данных «int», я получаю желаемый результат. Итак, я не понял, что происходит в scanf() при чтении переменной типа char или short с помощью %d . здесь переменная ‘num1’ будет хранить введенный пользователем номер, переменная ‘pos’, которая хранит позицию бита, а ‘val’ — это переменная, которая хранит значение ‘0’ или ‘1’, которое необходимо заменить.

При использовании типа данных char / short считывается правильное значение позиции, которое вводится пользователем, но внутри оператора ‘switch’ оно переходит в состояние «по умолчанию». Но когда я использую тип данных ‘int’, внутри оператора switch он исправит метку регистра.

код:

  int main()
{
    union bit_operations
    {
        unsigned char num;
        struct bit_field
        {
        unsigned char b1:1;
        unsigned char b2:1;
        unsigned char b3:1;
        unsigned char b4:1;
        unsigned char b5:1;
        unsigned char b6:1;
        unsigned char b7:1;
        unsigned char b8:1;
        }p;

}u;
    
    
    unsigned char num1;
    char pos,val;
    printf("enter the numn");
    scanf("%x",amp;num1);
    u.num=num1;
    printf("value entered is %xn",u.num);
    
    while(1){
    printf("enter bit position(in range 1-8)n");
    scanf("%d",amp;pos);
    
    printf("pos value is %dn",pos);
    
    printf("For SET->1nFor REST->0n");
    scanf("%d",amp;val);
        
switch(pos)
{
    case 1: u.p.b1=val;
            printf("0X%Xn",u.num);
    break;

    case 2: u.p.b2=val;
            printf("0X%Xn",u.num);
    break;
    
    case 3: u.p.b3=val;
            printf("0X%Xn",u.num);
    break;

    case 4: u.p.b4=val;
            printf("0X%Xn",u.num);
    break;
    
    case 5: u.p.b5=val;
            printf("0X%Xn",u.num);
    break;

    case 6: u.p.b6=val;
            printf("0X%Xn",u.num);
    break;

    case 7: u.p.b7=val;
            printf("0X%Xn",u.num);
    break;

    case 8: u.p.b8=val;
            printf("0X%Xn",u.num);
    break;

    default:printf("entered wrong bit position");
            
}
    
    
}
return 0;
}
 

вот вывод:-

 enter the num
ff
value entered is ff
enter bit position(in range 1-8)
4
pos value is 4
SET->1
REST->0
0
entered wrong bit position
enter 10 to EXIT
 or
enter bit position(in range 1-8)
 

Заранее спасибо…

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

1. Прямо здесь unsigned char num1; scanf("%x",amp;num1); просто недопустимо. Разве вы не получаете предупреждающее сообщение от своего компилятора?

2. Вы не можете использовать %x и %d для char , а только для int . Изменить char pos,val; на int pos,val; . Использование scanf с типами, не соответствующими спецификаторам формата, приводит к неопределенному поведению.

3. @KamilCuk на самом деле я не получил никакого предупреждения во время компиляции. Я выбираю unsigned char , потому что я выполняю операцию с 1-байтовыми данными, поэтому, если я возьму int , я выделю 4 байта памяти, а оставшиеся 3 байта останутся неиспользованными.

Ответ №1:

Как определено здесь: https://en.cppreference.com/w/c/io/fscanf

Если спецификация преобразования недопустима, поведение не определено.

И спецификатор преобразования %d или %x , предназначен для

подписанный int* или unsigned int*

т.е. не для char* , т.Е. недопустимо для него.

Таким образом, «Поведение типа данных char и short при чтении через спецификатор формата %d» не определено.

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

1. но я узнал, что char также является типом данных int размером 1 байт, в котором хранится значение от 127 до 128 или 0-255. Если мы используем %c, он считывает / печатает символ для соответствующего десятичного значения, а если мы используем %d, он выводит десятичное значение.

2. Это целочисленный тип данных или может использоваться как таковой, но он просто не относится к тем типам, для которых предназначены спецификаторы.

Ответ №2:

Чтобы scanf unsigned char вам нужно было использовать соответствующий модификатор длины внутри спецификатора формата:

 unsigned char num1;
char pos,val;
int r = scanf("%hhx", amp;num1);
if (r) abort(); // scanf _may_ fail. Do not forget to handle errors.
r = scanf("%hhd", amp;pos);
if (r) abort(); // scanf _may_ fail. Do not forget to handle errors.
r = scanf("%hhd", amp;val);
if (r) abort(); // scanf _may_ fail. Do not forget to handle errors.
 

Модификатор hh длины выполняет scanf сканирование для unsigned char спецификатора или signed char в зависимости от него.

Обратите внимание , что вам не нужно hh для printf . Это особенность языка Си, потому что каждый аргумент внутри списка переменных аргументов подвергается неявному целочисленному продвижению.

Лучший способ — это вообще не заботиться и заботиться о предупреждениях компилятора. Всегда старайтесь включить все предупреждения в вашей компиляции. Ваш код при компиляции с помощью gcc -Wall -Wextra на godbolt выдает:

 <source>: In function 'main':
<source>:26:13: warning: format '%x' expects argument of type 'unsigned int *', but argument 2 has type 'unsigned char *' [-Wformat=]
   26 |     scanf("%x",amp;num1);
      |            ~^  ~~~~~
      |             |  |
      |             |  unsigned char *
      |             unsigned int *
      |            %hhx
<source>:32:13: warning: format '%d' expects argument of type 'int *', but argument 2 has type 'char *' [-Wformat=]
   32 |     scanf("%d",amp;pos);
      |            ~^  ~~~~
      |             |  |
      |             |  char *
      |             int *
      |            %hhd
<source>:37:13: warning: format '%d' expects argument of type 'int *', but argument 2 has type 'char *' [-Wformat=]
   37 |     scanf("%d",amp;val);
      |            ~^  ~~~~
      |             |  |
      |             |  char *
      |             int *
      |            %hhd
Compiler returned: 0
 

Я не вижу особого смысла в том , чтобы писать профсоюз , а затем передавать его большому жиру switch . Код, по сути, просто выполняет:

 if (1 <= pos amp;amp; pos <= 7) {
   u.num = 1 << (pos - 1);
   // or maybe you want MSB order, like:
   // u.num = 1 << (8 - pos);
   printf("0X%Xn", u.num);
} else {
    // wrong pos;
}
 

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

1. спасибо за ответ и разъяснения. До сих пор я не знаю %hhx , %hhd Я ценю ваш вклад и заставляю меня изучать новые вещи. Могу кратко рассказать о спецификаторе формата%hh, в чем разница между %x и%hhx и почему нам нужно пойти на это

2. Разница в том, что %x принимает unsigned int аргумент и %hhx принимает unsigned char аргумент. printf и scanf (и любая переменная функция, если на то пошло) должны знать, какие аргументы вы им передаете, чтобы они знали, что получить из стека. Если вы передадите неправильный спецификатор формата для типа аргумента, данные в стеке будут интерпретироваться иначе, чем они есть.