• Как хранится c++ struct в памяти и как определить размер вручную?

    @Mercury13
    Программист на «си с крестами» и не только
    Выяснилось, что на компьютерах с разрядностью 16 и более бит минимальной единицей памяти стоит делать всё равно 8-битный байт: это сильно упрощает работу с узкими типами данных, как в памяти, так и на вводе-выводе. Например, канал RGB8 или кодовая единица UTF-8 — байт. Считаем для простоты, что разрядность 16, порядок Intel.

    Чтобы загрузить 2-байтовое число в 2-байтовый регистр, есть ДВА варианта.
    1. ОЗУ, кэш и другие устройства хранения передают нижний байт данных на нижний байт шины, верхний, соответственно, на верхний.
    2. Байты, чьи номера кратны двум, соединяются только с нижним байтом шины, не кратные — только с верхним. А уж процессор как-то разбирает, что делать, если двухбайтовое число сидит по нечётному адресу и считывается за два обращения к шине.

    Более простым оказался второй подход: он упрощает схемотехнику всех сидящих на шине, кроме процессора. В частности, дешифраторы становятся на 1 бит меньше, и упрощается топология. А процессор — в нём и так микропрограммное управление, и в нём и так до хрена соединений, и если обратился к байту (!) по адресу условно 4, вероятно, скоро потребуется адрес 5 и его один хрен стоит закэшировать.

    Но это значит, что доступ к 2-байтовым числам по нечётному адресу (1-2, 3-4, 5-6) будет медленнее, чем по чётному (0-1, 2-3, 4-5). И потому по умолчанию двухбайтовые числа располагаются именно по ЧЁТНЫМ адресам, и структура { char a; short b; char c; } будет иметь длину 6 байт (0 — a, 1 — выравнивание, 2,3 — b, 4 — c, 5 — выравнивание), а структура { short b; char a; char c; } — всего 4 байта (всё упаковано плотно).

    Но это приводит к двум вещам.
    1. Если нужно сочетать скорость и компактность, надо чётко выравнивать структуры данных. Обычно подобная ручная оптимизация делается для мелких частых структур.
    2. Если кусок памяти подготавливается в памяти, а потом отправляется на устройство, надо принудительно отключить выравнивание. Если в предыдущем примере формат файла так устроен, что байт 0 — a, байты 1 и 2 — b, байт 3 — c, то можно написать
    #include <iostream>
    
    struct Q1 { char a; short b; char c; };
    struct __attribute__ ((packed)) Q2 { char a; short b; char c; };
    
    int main()
    {
        std::cout << sizeof(Q1) << ' ' << sizeof(Q2) << '\n';   // 6 4
    }

    Q1 (или более оптимизированную short-char-char) использовать для хранения в памяти, а Q2 — для записи в файл, раз уж у него такой формат.

    Да, почему компилятор сам этого не делает? Это нарушает совместимость компиляторов. В Си++ до 20 включительно он имел право кое-что переставить, если в struct/class были поля с разными правами доступа (но никто по факту не делал), в 23+ больше не имеет права.

    Ну а алгоритм прост.
    1. Длина := 0, выравнивание := 1
    2. Для каждого поля: позиция := длина, округлённая по выравниванию[i]; длина := позиция+длина[i]; выравнивание := max{выравнивание; выравнивание[i]}
    3. Общая длина := длина, округлённая по общему выравниванию

    Существует очень простой способ уменьшить выравнивание: отсортировать поля по убыванию выравнивания.

    Для того же Q1:
    0. Длина = 0, выравнивание = 1
    1. char a: длина округляется до 0, затем 1, выравнивание = 1
    2. short b: длина округляется до 2, затем 4, выравнивание = 2
    3. char c: длина округляется до 4, затем 5, выравнивание = 2
    4. Общая длина округляется до 6. Ну а выравнивание = 2
    Ответ написан
    Комментировать
  • Нахождение F(x), для которой аргумент (элем. исход) ξ(ω) распределён неравномерно?

    @Mercury13
    Программист на «си с крестами» и не только
    Всё точно так же. Считаем, что φ от −π/2 до π/2!!!!
    Fx(u) = p{x < u} = p{h tg φ < u} = p{φ < arctg(u/h)} = Fφ(arctg(u/h))

    Соответственно, в твоём случае φ от 0 до π, при непрерывном распределении:
    Fx(u) = p{x < u} = p{h ctg φ < u} = [арккотангенс убывает, меняется знак!!] = p{φ > arcctg(u/h)} = 1−Fφ(arcctg(u/h))

    Лучше брать arcctg(x/h), чем arctg(h/x) — не нужно делать скидку на то, что мы сидим на двух стульях кусках ОДЗ.
    655408c964d68977789634.png
    Ответ написан
  • Что и почему лучше подключить в C++? math.h или cmath? stdio.h или cstdio?

    @Mercury13
    Программист на «си с крестами» и не только
    math.h — это для кода, который должен быть одновременно Си и Си++. Также разглючка в некоторых версиях Embarcadero.
    cmath — рекомендуется в Си++.
    Ответ написан
    Комментировать
  • Возможно ли реализовать одноранговый мультиплеер с защитой от жульничества?

    @Mercury13
    Программист на «си с крестами» и не только
    Это не одноранговая игра, это клиент-серверная.

    ИДЕЯ 1. Передавать с сервера картинку. Всё, читерство пресечено на корню. Это те самые Google Stadia и PS Now. Недостатки — в огромной нагрузке на сервер и задержках между управлением и картинкой. Как их решить?

    ИДЕЯ 2. Передавать с сервера некую информацию, по которой клиент будет строить картинку. Нагрузка на сервер меньше — он не занимается рендерингом. Зато появляется первая предпосылка к жульничеству — по этой информации жульническая программа определяет, где находится враг, и наводит оружие. Но остаётся недостаток: всё та же огромная задержка между управлением и картинкой. При этом задержку никак нельзя улучшить — у нас то самое «пространство-время», знакомое из теории относительности.

    О чём я: между Землёй и Луной секунда, и случились два события, на Земле и на Луне. Возможны три варианта: а) свет доходит от Земли до Луны, и событие на Земле заведомо раньше для любого наблюдателя; б) свет доходит от Луны до земли, и событие на Луне заведомо раньше; в) не успевает дойти ни тот, ни другой свет, и хрен его знает, какое раньше — для одних наблюдателей земное, для других лунное. Подобное пространство-время есть и в любом динамичном мультиплеере.

    ИДЕЯ 3. Ну, если задержку не улучшить, купируем её тем, что клиент может как-то предсказывать, чтó остальные будут делать, пока пакет ползёт от сервера. Чего не хватает? 1) Клиенты могут внезапно появляться в поле зрения, и не очень хотелось бы, чтобы мы зашкерились за углом, а враг появился ХЗ где, причём где — это зависит от нашего пинга; 2) Из соображений серверной производительности вопрос «видит ли клиент А клиента Б?» желательно решать очень приблизительно, с запасом — Z-буфер решит эту задачу значительно лучше, чем вычисление на процессоре, видит ли клиент А мизинец клиента Б.

    Есть ещё такая штука: представьте себе, у клиента А пропала связь. Он со своей пропавшей связью начинает крутить мышью, и не хотелось бы, чтобы в момент появления связи этот клиент умер от ХЗ кого, кого он даже не видел. Ну или со связью в порядке, но А очень насобачился работать мышью. Так что вопрос «видит ли один другого» обычно не рассматривает направление взгляда, только геометрию уровня.

    Да, для таких вопросов все эти конструкции типа «запрос-ответ», «доказательство работы» или «доказательство с нулевым знанием» только повышают ту самую противную задержку и вообще не пригодны для динамичной игры.

    Вот, собственно, и причина wallhack’ов: сервер принял решение, что клиент А, возможно, видит клиента Б, и начинает передавать его положение. И потому wallhack’и обычно не работают, если Б далеко: сервер понимает, что Б не виден и в ближайшее время не будет виден.

    Есть ещё такая помощь клиенту, связанная с тем, что есть мощные виды оружия вроде снайперской винтовки, с огромной разницей между «попал» и «промазал». Каждый клиент видит своё в зависимости от работы сети (вспомните фразу «ХЗ, что раньше»), и в момент прихода информации о выстреле сервер пытается восстановить, что именно видел клиент на своём экране, и по этой картинке решает, попал или нет (на этом основан глюк «спрятался за углом, и всё равно убили»). Но эту помощь тоже эксплуатируют: представьте себе, пакеты клиент→сервер, которые идут по штуке за, скажем, 50 мс, отправляют по пачке за 0,3 секунды — больше типичного времени компенсации задержек, но меньше типичного времени реакции. Получается, что персонаж скачет (клиенты, заглядывающие наперёд на 100 мс, отказываются предсказывать дальше — пусть он лучше зависает, чем тыкается в стены), причём скачет так, что в него хрен выстрелишь (пока среагируешь, он перескочит в другое место). А поскольку пакеты сервер→клиент ходят исправно, а сервер руководствуется прикидкой, что видел клиент, клиент всё видит и метко стреляет, а в него никто попасть не может. Если этот трюк исполнять автоматически только при появлении врага на экране, это будет максимально беспалевно — ну у игрока начались перекэширования из-за того, что видеопамяти не хватило. Эту штуку можно сделать даже на приставке, даже не разбирая её — нужен свитч (приставки всё-таки насобачились сообщать, что кто-то физически режет провод), кусок витой пары, моторчик с оптопарой и педалька под ногу. А для некоторых игр хватает свитча и педали, главное не жадничать и отпускать её иногда.

    А теперь вместо «чечни» (я так обозвал Counter-Strike — он появился в те времена, когда война в Чечне превратилась в антитеррористическую операцию) возьмём какой-нибудь преферанс. В чём разница? А в том, что игра не динамичная! И потому тактика «передавать только те данные, что нужны для рендеринга» вдруг становится годной. И если вы в преферансе сможете палить чужие карты — автор разгильдяй!

    Скажу честно, я не знаю, как на всех этих chess.com компенсируют задержки в шахматном суперблице типа «три минуты на партию». Если средняя игра тридцать-сорок ходов, то задержки в 0,1 секунды отъедят 3…4 секунды, а задержки в 0,2 — 6…8. Но из-за шахматного жульничества и далеко ушедшей дебютной теории основной формат игры — именно суперблиц.

    В общем, хрен вы реализуете диплом о защите от жульничества, не понимая, как работает мультиплеер и откуда жульничество берётся. А берётся жульничество из трёх предпосылок: 1) участники мультиплеера живут в релятивистском пространстве-времени; 2) очень хочется экономить вычисления на сервере; 3) жульничество иногда маскируют под обычные для компьютерного мира явления вроде сетевого затора или долгого кадра.

    Да, как реально защищают от жульничества: 1) Не допускают никого постороннего в программу-клиент, чтобы даже пакеты нельзя было расшифровать; 2) Ловят программы-жулики по принципу антивируса; 3) Передают клиентам как можно меньше того, что они знать не обязаны; 4) Ловят признаки компьютерного вмешательства в изображение или управление.
    Ответ написан
    Комментировать
  • Как реализовать побитовый сдвиг чисел, которые записаны как строки ( длинные числа хранятся в строках)?

    @Mercury13
    Программист на «си с крестами» и не только
    Как я понял, вы пишете калькулятор. В десятичной системе, чтобы вычисленное калькулятором совпало с вычисленным на бумажке — на что ещё десятичная система? Есть два варианта.
    1. Преобразовать в двоично-длинный формат (264 не входит в long long), сдвинуть, вернуть.
    2. Получить 232 в десятично-длинном формате, и перемножить.
    3. В зависимости от величины сдвига поступить так или этак: например, сдвиги на 1/2/3 вторым способом, а на 4 и больше — первым.
    Думайте сами, что лучше.
    Ответ написан
  • Как использовать потоки в современном C++ в приложении на основе цикла событий?

    @Mercury13
    Программист на «си с крестами» и не только
    Если вы пишете работу на чистом Си++ — придумайте некий интерфейс (в объектном смысле) на чистом Си++ для связи с интерфейсом (пользовательским, написанным на каком-то Qt): передать состояние, выяснить, хочет ли юзверь чего-то и так далее.

    Я считаю, что лучше использовать механизмы наподобие почтовых ящиков, при этом для передачи информации из потока в интерфейс использовать PostMessage и обёртки над ним вроде Qt QueuedConnection, возможно, с дополнительной структурой данных наподобие atomic<unique_ptr<SomeData>>).

    Если работаете на многопроцессорной системе, потоков (кроме главного) меньше, чем процессоров, задержки интерфейса не важны, но важно вытащить из потока последние крохи скорости — ящик неблокирующей синхронизацией, со стороны интерфейса трясти его таймером.

    А future нужно использовать только при взаимодействии потоков-работников, чтобы один подождал, если ему позарез нужен результат второго.
    Ответ написан
    Комментировать
  • В чём ошибка вычисления бесконечно убывающей прогрессии с точностью до эпсилон?

    @Mercury13
    Программист на «си с крестами» и не только
    У вас две ошибки.
    1. Целочисленное переполнение в знаменателе в prog += (double)1/(i*(i+1));. Вычисляйте знаменатель в double, его длины более чем хватит.
    2. Нельзя брать в счёт аналитический результат, надо высчитывать погрешность по имеющимся данным. Поскольку ещё и ряд сходится ХУдожественно — придётся помучиться, 1/(i(i+1)) < k·eps не катит (катит, если элемент ряда убывает экспоненциально, а у нас всего лишь квадратично).
    Ответ написан
    Комментировать
  • Тонкости Компиляторов. Почему в классах с++ не требуется объявление функции до вызова?

    @Mercury13
    Программист на «си с крестами» и не только
    Подозреваю, что объявление функции после вызова разрешили, потому что оно достаточно легко реализуемо, но даёт изрядную свободу прогеру. Например: несколько открытых конструкторов вызывают private init(), а этот init, как и все скрытые методы, сидит где-то в конце.

    Проходов компиляции может быть много — например, сначала мы говорим, что f(x) — это вызов, а вторым проходом определяем, с какой функцией f имеем дело. К тому же конкретно в Си++ любой код в теле класса — автоматический inline, то есть не создаёт кода, пока кто-то снаружи не вызовет.

    Умножение, приведение типа или ещё какая-то дрянь — это особенность метода разбора. Во многих языках, в том числе в Паскале, используется LL-разбор — сканирование слева направо, опознание слева направо. Ошибки наподобие «ожидалась точка с запятой» — это артефакт примитивных LL-анализаторов. В Си используется LR-разбор — сканирование слева направо, опознание справа налево, а ожидающие опознания лексемы хранятся в стеке.

    И вообще, если пишете собственный крайне специализированный язык, делайте его LL(1), это очень упростит разбор.

    Кстати, в Паскале принят обратный порядок видимости: private → protected → public → published. Последнее — поле доступно в рефлексии времени выполнения.
    Ответ написан
    Комментировать
  • Как называется шрифт?

    @Mercury13
    Программист на «си с крестами» и не только
    Шрифт называется Intro Rust Base, но часть символов тут латинские или вообще ХЗ какие (греческий?) — кириллица дизайнеру не нравится.
    Ответ написан
    1 комментарий
  • Что такое трансцендентные числа?

    @Mercury13
    Программист на «си с крестами» и не только
    Алгебраические числа — это корни многочлена с конечной степенью и целочисленными (ну или рациональными) коэффициентами. Так что пи не алгебраическое — то есть трансцендентное.
    Вот книга про это, добьёте решение в вашей форме? (Ваша чуть отличается от традиционной)
    mathemlib.ru/books/item/f00/s00/z0000027/st057.shtml
    Ответ написан
    2 комментария
  • Scratch задание Алгоритмика, как решить?

    @Mercury13
    Программист на «си с крестами» и не только
    Как я понял, робот смотрит вниз.
    Поверни вправо. Подумай, как надо двигаться, чтобы разрушить четыре блока, стоять в 3-й строке, 2-м столбце и смотреть вниз. Повтори четырежды.
    Ответ написан
    Комментировать
  • Как из SVG сделать шрифт с большим количеством глифов?

    @Mercury13 Автор вопроса
    Программист на «си с крестами» и не только
    Программированием в FontForge на Змее-Питоне.
    file = open('hani-tofu.txt', 'r')
    for line0 in file:
        line = line0.strip()
        if (line != '') and (not line.startswith('#')):
            code = int(line, base=16)
            svgName = "AutoRemade/{}.svg".format(line)
            glyph = font.createChar(code)
            glyph.glyphname = "u" + line.upper()
            glyph.importOutlines(svgName, scale=False)
            glyph.transform(mat)
            glyph.width = 1000
    Ответ написан
    Комментировать
  • Стоит ли использовать типы данных из cstdint?

    @Mercury13
    Программист на «си с крестами» и не только
    Для олимпиадного программирования.
    Если результат до 109 — то int.
    Если до 1018 — то long long!
    Long’ом лучше не пользоваться.
    Можно также использовать int_fast32_t и int_fast64_t.
    Но всё это ТОЛЬКО В СЛУЧАЕ, ЕСЛИ МЫ НЕ ЗАКЛАДЫВАЕМСЯ НА ПЕРЕПОЛНЕНИЕ.
    Если закладываемся — тогда чёткие int32_t и int64_t, без разговоров.
    Ответ написан
    Комментировать
  • Как ограничить скорость разгона?

    @Mercury13
    Программист на «си с крестами» и не только
    Итак, тут у нас задача: каким образом, НАИМЕНЬШЕЙ КРОВЬЮ (игра совершенно условная, верно?) сделать поведение машины в зависимости от оборотов. Опустим сложные материи вроде приёмистости, турбины, торможения двигателем, спойлеров, дифференциала, увода шин и прочего. Считаем, что обороты мотора измеряем в об/мин, остальное (скорости, расстояния…) — в единицах СИ.

    1. Нам нужны передаточные числа коробки в единицах «обороты·c/м·мин». Имея физическую машину в гараже и инструкцию к ней, действуем так: замеряем действующий радиус колеса с учётом смятой шины, находим передаточные числа передач в коробке и главной передачи, и

    об·/метр = (передаточное_число_коробки·передаточное_число_главной_передачи)/(120π·радиус_колеса).

    Ну или просто едем на стабильной скорости, замеряем скорость GPS’ом или километровыми знаками (важно! — спидометр всегда врёт), и заодно видим обороты мотора.

    Если физической нет — находим похожую, узнаём передаточные числа, узнаём, на какой передаче достигается максимальная скорость (часто в народных машинах, не способных ездить за 200, высшая является овердрайвом, служит для экономии топлива и моторесурса, и в экстриме не используется), находим соответствующие обороты мотора, и обороты на скорость — получаем данное число для самой скоростной передачи. Для остальных пропорционально.

    2. Теперь придумываем сопротивление движению в виде квадратного трёхчлена: Fсопр=av²+bv. Цифры a и b находим из двух условий: а) на максимальной скорости Fсопр=Pmax/v; б) линейная часть равна квадратичной где-то около 30 км/ч.

    3. Находим характеристику двигателя: МОЩНОСТЬ от оборотов. Загоняем её в игру в виде ломаной или сплайна.

    4. Прикидываем мощность мотора P в зависимости от положения газа, оборотов и этой кривой.

    5. Ускорение машины: a = (P/v−Fсопр(v))/m, с какой-нибудь аппроксимацией, когда скорость меньше минимальной стабильной (5…10 км/ч в зависимости от машины).

    6. Из скорости через наши «круго-линейные» передаточные числа определяем новые обороты двигателя и ударился ли он в отсечку. Если ударился, ограничиваем скорость.

    7. Если машина вдруг прыгает (хоть одно ведущее колесо в воздухе) — просто считай, что она на нейтрали.

    8. Во время переключения передач ручная машина должна быть на нейтрали, а поскольку игра типа NFS — перехватывай на это время у игрока управление оборотами, чтобы переключение было красивым и без рывков. На автоматической и роботе с двумя сцеплениями какая-то доля мощности приходит на колёса.

    9. На малых скоростях есть ещё один ограничивающий фактор — пробуксовка шин. Если игра условная, то просто коряво рассчитай трение шин и не допускай такого разгона, что шины буксуют.
    Ответ написан
    Комментировать
  • Как понять rvalue ссылки? Когда использовать std::move, а когда std::forward?

    @Mercury13
    Программист на «си с крестами» и не только
    Я их называю «врéменные ссылки» — то есть ссылки на временный безымянный объект, возникший в процессе вычисления значения выражения. Отличается тем, что обычно ссылки на временные объекты принимают заоптимизированные деструктивные функции, которые этот объект готовы выпотрошить в своих целях.

    Функция move предназначена для превращения ссылки (именованной или временной) во временную. Например, чтобы сказать: это именованный объект, но спокойно потрошите его, мне он не нужен.

    У Си++ есть одна фишка:
    void bar(std::string&& x) {}
    
    void foo(std::string&& x) {
      bar(std::move(x));
    }
    
    int main() {
      std::string x = "Test";
      foo (std::move(x));
      return 0;
    }

    То есть снаружи foo требует временную ссылку. Но внутри эта ссылка для ошибкобезопасности обычная именованная! В данном случае, когда функция нешаблонная, мы можем поставить move, но что делать в шаблоне?

    Автоопределение типов тут пасует, потому приходится так.
    template <class T>
    void foo(T&& x) {
      bar(std::forward<T>(x));
    }

    Да, я написал «ошибкобезопасность», хотя функция типа усложняет код. Дело в том, что потеря данных всегда хуже, чем неоптимальный код.

    Таким образом, move — это превращение ссылки (обычной или временной) во временную. Что-то вроде: «Потрошите этот объект спокойно, он мне не нужен».

    Forward — это прикидка, по какой ссылке (обычной или временной) мы передавали объект в функцию, и передача по цепочке таким же образом. Используется только в шаблонах.

    Вот моё описание всего этого.
    https://mercury13-kiev.livejournal.com/96961.html
    Ответ написан
    Комментировать
  • Qt: как сделать, чтобы пункт меню и горячая клавиша делали чуть разное?

    @Mercury13 Автор вопроса
    Программист на «си с крестами» и не только
    Всё оказалось просто. Создаём QAction и QShortcut с одной кнопкой. В ситуации, когда QAction недоступен, QShortcut делаем доступным, и наоборот.
    Ответ написан
    Комментировать
  • Как сделать чтобы программа не запускала процес на каждый аргумент, а запускала один процес на все аргументы?

    @Mercury13
    Программист на «си с крестами» и не только
    Под Windows это делается через DDE.
    Штука сильно устаревшая, однако осталась именно на это — поддержку нескольких файлов через файловый менеджер.
    Ответ написан
    2 комментария
  • Как заменить мышь, и установить чувствительность как было раньше?

    @Mercury13
    Программист на «си с крестами» и не только
    X — это DPI мыши. Я сам чувствителен к этому самому DPI, но я не настолько крутой геймер и мне ±15% вполне покатит.

    Купи геймерскую мышь с плавной установкой DPI и установи такой же, какой был.

    Кроме того, есть ещё один скрытый параметр — качество сенсора. В 2006 было время: покупаешь мышку, а она время от времени устраивает срыв: курсор уходит ХЗ куда. Разумеется, это недопустимо, но умные люди подключили методы решения некорректно поставленных задач, и мыши перестали срываться. Главная фишка этого метода: чем меньше решение по модулю, тем оно предпочтительнее. Это приводит к т.н. отрицательному ускорению мыши: если мышь походит А→Б быстро, курсор пройдёт меньшее расстояние, чем если бы её провели медленно. Если старая (или новая) мышь не очень качественная, отклик будет немножко не такой.
    Ответ написан
    Комментировать
  • Работа с рисованием в Qt. Почему не получается рисовать после оператора if?

    @Mercury13
    Программист на «си с крестами» и не только
    Дело тут вот в чём. События рисования могут проскакивать когда угодно по любому чиху, и потому механизмы перерисовки надо делать такие, что когда угодно этот paintEvent() проскочит — изображение в окне будет корректно восстановлено.

    Например:
    enum class Shape { NONE, LINE, ELLIPSE, RECTANGLE };
    
    ... в форме...
    Shape shareToDraw = Shape::NONE;

    …соответственно в коде нажатия кнопок устанавливаем эту shapeToDraw,
    а paintEvent реагирует на неё.

    У вас же, как только paintEvent() повторно проскочит, ни одно событие не будет отмечено, и он ничего не сделает. А ведь он может дважды проскочить, чтобы нарисовать непрямоугольную область. Или если оконный менеджер что-то попросил.
    Ответ написан
  • Почему поведение fscanf ( stdin, "%c", &c ) различается при чтении EOF в msvc и gcc?

    @Mercury13
    Программист на «си с крестами» и не только
    В таком случае, когда есть подозрение на конец файла, надо проверять возвращаемое значение функции fscanf:
    • EOF — файл закончился;
    • 0 — не прочитали (в данном случае, когда читаем символ такого явно не будет);
    • 1 — прочитали.

    Если во втором случае MSVC возвращает EOF, но заполняет — поведение некузявое, но допустимое.
    Если во втором случае MSVC возвращает 1 — поведение дрянь.

    Вообще для такого дела fscanf — большая пушка, лучше использовать fgetc.
    Ответ написан