странное поведение scanf для краткого int - PullRequest
2 голосов
/ 19 декабря 2011

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

#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 ]

7 голосов
/ 19 декабря 2011

Наиболее вероятная причина того, что 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 на одну ночь!

2 голосов
/ 19 декабря 2011

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

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

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

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

2 голосов
/ 19 декабря 2011

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

PS Чтобы прочитать short с помощью scanf, объявите временную переменную int, прочитайте значение в нее, используя scanf, а затем приведитеэто к short.

1 голос
/ 19 декабря 2011

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

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

1 голос
/ 19 декабря 2011

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

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

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

...