Странное поведение 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).
Не обманывайте свой компилятор - даже случайно. Он получит свой собственный обратно.