Странное поведение scanf для краткого int

Код выглядит следующим образом:

#include <stdio.h>
main()
{
    int m=123;
    int n = 1234;
    short int a;
    a=~0;
    if((a>>5)!=a){
        printf("Logical Shift\n");
        m=0;
    }
    else{
        printf("Arithmetic Shift\n");
        m=1;
    }
    scanf("%d",&a);
    printf("%d\n", m);
}

после линии scanf("%d",&a); значение m становится 0.

Я знаю, что это может быть вызвано scanf: тип a короткий, а тип ввода int. Но как это может повлиять на значение m?

Большое спасибо!

5 ответов

Решение

Наиболее вероятная причина m являющийся 0 в вашем фрагменте, потому что вы назначаете m чтобы иметь это значение в теле вашего оператора if, но так как код содержит неопределенное поведение, никто не может сказать это наверняка.


Вероятная история про прохождение short* когда scanf ожидает int*

Если предположить, sizeof(short) = 2 а также sizeof(int) == 4,

При вводе вашей основной функции стек, в котором находятся переменные, обычно будет выглядеть примерно так:

  _
 |short int (a)   : scanf will try to read an int (4 bytes).
 |_ 2 bytes       : This part of memory will most
 |int       (n)   : likely be overwritten
 |                :..
 |
 |_ 4 bytes
 |int       (m)
 |
 |
 |_ 4 bytes

Когда вы читаете %d (т.е. int) в переменную a это не должно влиять на переменную m, хоть n скорее всего, его части будут перезаписаны.


Неопределенное поведение

Хотя это все игра в догадки, так как вы используете то, что мы обычно называем "неопределенным поведением" при использовании вашего выражения scanf.

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

Никто не может гарантировать, что мы доживем до того дня, когда появится UB.


Как читать short int с помощью scanf

использование %hdи обязательно передайте short*.. У нас было достаточно UB на одну ночь!

Ты прав, %d ожидает и пишет int, Если вы введете значение меньше 65535вписывается в байты снаружи shortтак что вы видите 0 когда вы печатаете a назад. Я пытался читать short и распечатывать его обратно; Я вошел 65536123и получил 123, что имеет смысл (65536 занимает ровно 16 бит; вы видите оставшиеся 123 через два байта short). Это поведение опасно, потому что два других байта short в конечном итоге в "переменной по соседству" к shortчто очень и очень плохо. Я надеюсь, что это должно убедить вас не делать этого.

PS читать short с scanfобъявить временным int переменная, считайте значение в нее, используя scanf, а затем бросить его short,

При условии, что int а также short четыре - и двухбайтовые целые числа, соответственно, на вашей платформе (что является вероятным предположением, но не гарантировано стандартом), вы спрашиваете scanf читать в целое число и хранить его в четырех байтах: два байта bи любые два байта следуют за ним в памяти. (Ну, технически это неопределенное поведение, и никакого конкретного поведения не гарантируется; но это то, что он может сделать.) Очевидно, ваш компилятор использует два байта после b как первые два байта m, Что немного удивительно - я, конечно, не ожидал b а также m быть смежным, и это скорее означает, что ваш компилятор не выравнивается shortс и intДо начала четырехбайтовых блоков - но совершенно законно.

Вы можете лучше видеть, что происходит, если вы добавите

printf("&a: %08X\n&m: %08X\n", (int)&a, (int)&m);

который покажет вам, где a а также m хранятся относительно друг друга. (Я имею в виду просто тест. Вы не хотели бы этого в "реальном" коде.)

Вы вызываете Undefined Behavior при передаче указателя на не-int в scanf%d.

Вероятно, компилятор вводит байты заполнения для выравнивания, и значения сохраняются в байтах заполнения, а не в "полезных" байтах.

Тем не менее, компилятор может делать все, что угодно, от повышения уровня нарушения прав доступа / доступа до вызова носовых демонов.

Если вы действительно использовали переменную nто, вероятно, это был бы тот, кто был забит, а не m, Так как вы не использовали nкомпилятор оптимизировал его, а это значит, что он был m что было забито scanf() запись 4 байта (потому что было сказано, что он получил указатель на (4-байтовое) целое число) вместо 2 байтов. Это зависит от множества деталей вашего оборудования, таких как порядковый номер и выравнивание (если int должен был быть выровнен по 4-байтовой границе, вы не увидите проблемы; Я думаю, что вы находитесь на машине Intel, а не, скажем, PowerPC или SPARC).

Не обманывайте свой компилятор - даже случайно. Он получит свой собственный обратно.

Другие вопросы по тегам