Здравствуйте, Marty, Вы писали:
V>>Гарвардская модель удобна для встраиваемых решений, т.к. программы сидят в ПЗУ на кристалле. M>В фон-немановской STM32 программы точно также лежат во флешке на кристалле.
ARM-ы бывают и гарвардские.
M>В упор не вижу, причем тут архитектура Г или фН.
Для 8-16 бит было удобно, т.к. это удвоение суммарного адресного пространства кода+данных.
Для 32 бит уже не принципиально, ес-но, могут делить одно логическое адресное пространство.
M>А есть ещё извраты, когда прошивка лежит во внешней флешке и МК с ней общается по SPI, но и тут архитектура не причем
Это не то.
В гарварде прога непосредственно исполняется из другого адресного пула, где обычно внутреннее ПЗУ (программируемое или прошитое при изготвлении чипа).
Здравствуйте, Евгений Музыченко, Вы писали:
M>>ничего не понял, что за проблема, о каких извращения ты говоришь
ЕМ>Ну вот нужно в зависимости от типа МК определить не макрос, а функцию. Если ее просто определить в одном из стандартных заголовков, то она будет определена в каждом модуле, включающем этот заголовок, и обычный линкер этого не переварит. Поэтому нужен либо линкер с оптимизацией множественных определений, либо готовый набор нужных функций в libc на все случаи жизни, для которых в заголовке под видом общего имени функции будет определяться макрос.
В плюсах та же проблема будет, если ты будешь не inline код писать в заголовках. Ничего, как-то справляемся
M>>>>Сортировка — std::sort M>>Она не нуждается в CRT, и помещается по месту
ЕМ>Она и есть та часть CRT, которая реализована в заголовках в виде исходников, а не в libc в виде объектного кода.
Это не CRT
M>>Умеет, но даже десяток имён файлов легко съедят килобайт
ЕМ>Имен — не съедят, а вот путей — да. Именно поэтому было бы полезно или управлять раскрытием __FILE__, или иметь отдельный макрос только для имени.
Какая разница, суть ты понял. И никто не предоставляет средств управления раскрытием этого макроса
ЕМ>>>А если нужно выводить текст на экран?
M>>Чрезвычайно редкий кейс
ЕМ>Производители миллионов гаджетов на МК с экранами смотрят на Вас с недоумением.
Тем не менее, устройств без экрана на порядки больше. У нас в роботе легко могло быть несколько десятков плат с контроллерами, и ни одной с экраном
M>>я каждый раз лезу в доки по printf, потому что все нюансы форматной строки не держу в голове.
ЕМ>Чего там держать-то для типовых форматов (d, u, x, s)? Это для плавучки там куча нюансов, а для целых и строк — раз-два и обчелся.
Это если ты не хочешь вывести строку определенной длины с определенным выравниванием
M>>это не фетиш, а удобство
ЕМ>Я сильно сомневаюсь, что, если бы во всех руководствах по C++ не утверждалось, что только потоки — истинно правильный способ, а *printf — древнее и опасное убожество, большинство пользовалось бы потоками.
Не знаю, что там во всех руководствах написано, я сам в итоге к такому пришел
M>>обсёры с форматной строкой встречаются регулярно
ЕМ>Гораздо реже, чем логические ошибки, приводящие к тем же последствиям — просто потому, что *sprintf в типовой программе гораздо меньше, чем других конструкций, чреватых неконтролируемыми ошибками.
Только из-за этого, да. В процентах на количество использований printf — он впереди всех других способов обосраться
M>>современные компиляторы умеют о таких проблемах предупреждать
ЕМ>По уму, надо было изначально сделать эти предупреждения, а не пихать потоки везде, куда можно технически.
Мне потоки нравяться гораздо больше, чем printf. Реализация конечно так себе, но идея неплохая.
Здравствуйте, vdimas, Вы писали:
V>Это не то. V>В гарварде прога непосредственно исполняется из другого адресного пула, где обычно внутреннее ПЗУ (программируемое или прошитое при изготвлении чипа).
Я в курсе, что такое гарвардская архитектура. Я не понял, к чему ты её приплёл
Здравствуйте, Marty, Вы писали:
V>>Если пользуешь Си, то для оперирования объявленными статическими данными (структурами, массивами) необходим CRT. M>Что именно из CRT нужно для этого?
Двухфазная инициализация глобальных данных.
Причём, при внешнем загрузчике CRT обыгрывает только вторую фазу (динамической инициализации), а в гарварде CRT обыгрывает обе фазы.
Упреждая пинг-понг вопросов-ответов:
int a = 42;
int b = calcB();
Здесь в бинарном образе в сегменте данных по адресу a будет число 42, по адресу b будет 0.
Внешний загрузчик грузит образ в память, переменная a уже инициализирована (это первая фаза), а переменная b будет инициализирована из глобального связанного списка инициализации, где каждый узел примерно такого вида:
CRT пробегается по этому списку, вызывая код динамической инициализации — это вторая фаза стандартной инициализации С/С++.
Для инициализации модуля, содержащего динамическую инициализацию (здесь глобальной переменной b), будет порождена безымянная ф-ия примерно такого вида:
void __some_init_xxx() {
b = calcB();
}
Указатель на эту ф-ию будет в одном из узлов __init_module в глобальном списке динамической инициализации.
Для микриков обычно нет связанного списка, динамическая инициализация представляет собой развернутый (типа заинлайненный) код, но это всё-равно лишний код, который почти всегда не нужен, т.к. любые начальные данные или не нужны, или являются константами, тогда им не место в ОЗУ.
Т.е., статические константы в embedded никогда не используются (я давал выше по ветке условный код, а не реальный), т.к. программист не может знать — будет ли под эти константы выделена память или компилятор обойдётся без этого.
На деле всё программируется через #define константы, либо через enum, т.е. через те техники, где "воздушность" констант гарантируется.
V>>Просто напоминаю, что почти всегда микроконтроллеры выполнены по гарвардской ахитектуре, а не по фон-неймановской, с которой ты писал этот пост. M>Весьма спорное утверждение.
Это реальность.
M>Из гарвардских видел только клоны MCS-51, хотя, не скажу, что большой опыт с различными семействами
У меня опыт достаточный, более десятка лет.
Для справки, мейнстримом для встраиваемых решений ARM не является до сих пор, не смотря на их засилье в смартфонах/планшетах, хотя гарвардские АРМ-ы тоже порой в ходу.
Повально там PIC-и, AVR-RISC, 51-я архитектура, MIPS-семейства (именно в гарварде, если ПЗУ на борту кристалла, т.е. почти всегда), недавно кучно пошли RISC-V, которые прямо по спецификации есть в исполнении для архитектуры общего назначения (фон-Нейман) и для встраиваемых (Гарвард): https://www.sbir.gov/node/1654311
Практически все DSP-процы (включая якобы "аппаратные" микросхемы-кодеки звука или видео, кои есть в т.ч. на твоей материнке и видюхе — на самом деле не аппаратные кодеки, конечно, а программные — это просто ядро какого-нить мейнстримого DSP с заводской прошивкой) — это всё исключительно Гарвард.
И так будет всегда во встраиваемых решениях, потому что унутре любого проца всегда Гарвард при обращении к внутреннему кешу (а минимальный кеш, опять же, есть всегда при наличии конвейера, т.е. в любом современном RISC).
Поддержка фон Неймана при таких раскладах требует существенного удорожания архитектуры чипа с мультиплексированием шины, т.е. еще повышенным требованием к пропускной способности шины памяти. Плюс еще вопрос безопасности — как исключить исполнение данных (разновидность атаки).
В итоге, Гарвард проще/дешевле/эффективней, т.к. шины данных и кода работают независимо.
И еще Гарвард не подвержен атакам экзекутора данных.
M>Ничего не понял. Причем тут фон-неймановская архитектура?
В этой архитектуре программа может храниться прямо с данными инициализации, как она хранится для виндов и обычных линухов, например, где загрузку сегментов данных выполняет внешний загрузчик, являющийся неотъемлимой частью ОС.
M>В любом случае, в ОЗУ инициализированные переменные должны как-то попасть, от архитектуры тут ничего не зависит.
Да не должны, в этом суть.
Редко (почти никогда) требуется инициализировать переменные значениями, известными на момент компиляции.
Тем более, во встраиваемых решениях почти всегда есть watch-dog, в т.ч. сугубо программный, который в любой момент обновит состояние системы до начального.
Вот ты можешь в любой момент в С/С++ перевызвать статическую и динамическую инициализацию модуля?
Нет.
Это только ручками.
И тогда инициализация, данная языком+CRT изкаробки, резко становится ненужной. ))
По-сути, такая якобы "необходимость" в дефолтной инициализации глобальных переменных выдаёт недостаточную продуманность графа обработки данных.
И ты не придумаешь пример, где бы я не избавился от такой "необходимости" на раз-два.
Например, в паре проектов мы измеряли тактовую конкретной платы с точностью до 7-го знака и от этой частоты вычисляли некую константу-поправку (прецизионная техника была).
И эта константа шла в код 24 бита фиксированной запятой, а не в переменную в ОЗУ.
То бишь, известные на момент компиляции данные оформляются в виде констант компиляции, но ни в коем случае не переменными.
M>Да, вспомнил, про STM32. Там у них уже есть кое-какой прошитый код, который собственно, и занимается всей инициализацией для запуска прошивки.
Не сравнивай. ))
Эта балалайка изначально позиционировалась как система общего назначения, т.е. можно разрабатывать на её основе мини-компьютеры, интеллектуальные терминалы, подключать бесконечное разнообразие сложной периферии и т.д.
В то время как встраиваемый микрик — это всегда непременно SoC с некоторой периферией на борту.
(их не зря в одной серии десятки разнообразных вариантов, где варианты отличаются не только объемом ОЗУ/ПЗУ, но как раз набором периферии)
При таких раскладах в каждой конкретной конфигурации SoC фактор "общего назначения" отсутствует как класс. ))
M>Ещё этот встроенный бутлоадер анализирует пины BOOT0/BOOT1, и в зависимости от их состояния может переходить в режим общения по UART, через который можно обращаться к памяти читать/писать, или даже залить прошивку, без какого-либо ST-LINK'а
Только нафига это в микроволновке, стиралке, цифровом приёмнике или рации?
Здравствуйте, Marty, Вы писали:
V>>В гарварде прога непосредственно исполняется из другого адресного пула, где обычно внутреннее ПЗУ (программируемое или прошитое при изготвлении чипа). M>Я в курсе, что такое гарвардская архитектура. Я не понял, к чему ты её приплёл
Приплёл к embedded, где она абсолютным мейнстримом.
Здравствуйте, Евгений Музыченко, Вы писали:
V>>Двухфакторная инициализация — статическая и динамическая. ЕМ>CRT не является безусловно необходимым для этого.
Тогда будет UB. ))
Т.е. ты тогда не можешь полагаться на спецификации языка.
ЕМ>Он нужен, когда невозможно загрузить инициализированные данные вместе с программой в готовом виде. Если платформа позволяет это делать (с помощью стандартного загрузчика, отображения памяти, или еще как), CRT не требуется.
V>>Гарвардская модель удобна для встраиваемых решений, т.к. программы сидят в ПЗУ на кристалле. ЕМ>Так ПЗУ не является необходимым элементом гарвардской архитектуры.
Ес-но.
ТУт зависимость другая: есть ПЗУ — удобней Гарвард.
И кристаллу удобней (подробности тоже по ссылке выше, почему кристаллу всегда удобней Гарвард), и программисту удобнее, когда численно совпадающие адреса данных и программы на самом деле смотрят в разную память.
ЕМ>В общем случае эта архитектура никак не мешает загрузке в память заранее подготовленных данных.
Разумеется.
Но часто делает ненужным саму эту операцию.
Считай, что это чуть другой образ мышления.
ЕМ>И даже в тех случаях, когда такой возможности нет, именно CRT не требуется. Для одномодульной программы компилятор может складывать код инициализации в заранее известную функцию, которую нужно будет явно вызвать до использования статических переменных.
Так и делается.
Одно плохо — тебе недоступна возможность явного вызова этой ф-ии, этим занимается CRT.
А в случае watch-dog таймеров иногда эту ф-ию надо бы вызывать явно.
А никак!
И вот ты целиком отказываешься от этого раздела спецификации языка и пользуешь другие решения, которые, к тому же, оказываются резко эффективней, бо каждое такое решение выходит "по-месту".
Тут во всей красе проявляется то правило инженерии, что самое общее решения является наихудшим для каждого конкретного случая. ))
ЕМ>Для многомодульных программ это делается складыванием кода в секции, для обработки которых также могла бы генерироваться служебная функция, вызываемая явно. ЕМ>По сути, это часть CRT, но поднятая чуть выше, вроде явной инициализации вместо умолчания.
Да нет такой возможности в языке, забудь. ))
Только описать такую инициализацию явно, но тогда конструкции языка объявления+инициализации глобальных переменных получаются нерабочими.
Взять простой пример — некая табличная ф-ия, например, кодировка из PCM в u-law.
По классике если, ты опишешь эту таблицу констант в данных, т.е. в ОЗУ, и будешь извлекать из таблицы константы простым обращением к глобальному массиву.
А в микриках другая техника.
Там обычно достаточно большое ПЗУ, но небольшое ОЗУ.
Занимать это ОЗУ большой таблицей — ересь, заслужить линейкой по пальцам.
И ты прошиваешь эти константы в ПЗУ, т.е. по адресу кода.
Причём, если архитектура не позволяет непосредственно читать байты из адресного пространства команд, то ты через макросы прошиваешь команду сложения рабочего регистра с константой (или загрузки константы в регистр) + операция ret.
Тогда обращение к таблице вырождается в call c предвычисленным смещением.
И никакого ОЗУ. Ву а ля! ))
Здравствуйте, Евгений Музыченко, Вы писали:
V>>в гарвардской архитектуре инициализация "внутренней" памяти тоже нифига не автоматическая, требует некоторого кода. ЕМ>Но не обязательно в CRT. Это может быть тот же загрузчик — хоть примитивный в МК, хоть навороченный в ОС.
Нет никакой ОС за ненадобностью.
После reset программа тупо исполняется с некоторого адреса.
V>>Допустим, не в БД, а в неоднородном NUMA-окружении, в неких суперкомпах. V>>Под это есть свои Си-подобные диалекты, позволяющие декларативно описывать неоднородную память, дабы сохранять переносимость программ в исходниках. ЕМ>Такое надо делать на плюсах, заворачивая в классы.
Повторю, для этого есть специальные языки для NUMA, которые выглядят как расширения к мейнстримовым.
Увы, весь современный мейнстрим создан для SMP-архитектур, и это одна из причин, почему в С++ не было библиотек для потоков — потому что SMP окончательно победила всех и вся только в нулевые, когда даже суперкомпы стали делать на обычных процах от персоналок.
V>>На асме, однако, приходилось писать критичные вещи, где надо расчитывать код с точностью до такта. V>>Ну и еще для уменьшения вложенности вызова подпрограмм, т.к. на асме легко сделать в одну подпрограмму несколько точек входа ЕМ>Все это успешно делается на "расширенных сях" с псевдопеременными, intrinsic-функциями, ассемблерными вставками и т.п.
Учитывая изкаробочную развитую модульность в Си/С++, ассемблерные вставки не всегда нужны.
Полноценный асм (макро-асм) на порядки удобнее убогих "вставок". ))
ЕМ>Если под архитектуру есть только "канонический" C, то вряд ли она сильно популярна.
Под любую архитектуру микриков, в первую очередь, есть асм.
Порой тулчейны не генерят непосредственно код из Си, а работают через промежуточный асм — оно зачастую и для отладки удобно.
V>>многие переменные могли покрывать адресное пространство друг друга в разные моменты работы программы), а компилятор справляется с этим плохо, т.к. не способен полностью трассировать программу. ЕМ>Не понял, для чего здесь "трассировка". Эта задача решается сочетанием struct/union.
Зачем?
Вот у тебя есть локальные переменные.
В обычной архитектуре локальные переменные живут в стеке, а в микриках часто стек обслуживает только возвраты, а локальные данные живут во вручную организуемом стеке данных в ОЗУ.
Так вот, эта техника далека от эффективности.
В простейших случаях (и в отсутствии рекурсии) компиляторы способны статически распределить в ОЗУ локальные переменные с перекрытием.
Например, код верхнего уровня вызывает в разные моменты разные ф-ии, тогда компилятор статически распределит в ОЗУ адреса локальных переменных процедуры верхнего уровня и вызываемых по цепочке процедур. У вызываемых процедур адреса локальных переменных будут пересекаться, что не страшно, т.к. они никогда не используются одновременно.
(компилятор в т.ч. распределит с перекрытием переменные внутри одной ф-ии, если увидит, что время жизни у них не пересекается, где время жизни POD-значений условно ограничивается последним к нему обращением)
Но когда идёт комбинаторика вызовов, и компилятор не способен оттрасировать статическое распределение локальных переменных, он начинает организовывать динамические фреймы "ручного стека", что тяжело и дорого. И тогда программист закатывает рукава и выполняет такое распределение сам, если знает, что некие ф-ии не вызывают друг друга (даже сколь угодно косвенно) ни в каком из сценариев.
В этом случае идёт отказ от локальных переменных в пользу перекрывающихся глобальных.
Да, можно через union, но с той оговоркой, что ты не объявляешь такую глобальную переменную, а высчитываешь ее адрес "в уме", как показывал выше, где адрес будет константой времени компиляции.
Здравствуйте, Marty, Вы писали:
V>>Или на микроконтроллере мало памяти, что всё лишнее выметается, т.е. даже подменяется CRT. M>Не очень понятно, в чем смысл подмены CRT?
Заменить код начальной инициализации, отказаться от инициализации глобальных переменных.
В том числе от инициализации скрытых глобальных переменных самой CRT.
M>Я вот как-то и не припомню, что из CRT я использовал. Разве что mem* функции
CRT в любом случае для встраиваемых решений всегда идёт в т.ч. в виде исходников, можно подключить любую библиотечную часть из неё.
mem* и str* функции да, порой удобны.
А ввод/вывод, математика и начальная инициализация — обычно на помойку.
Оно громоздкое и почти всегда не нужное.
Здравствуйте, ononim, Вы писали:
O>>>... обнуляется ядром — потому что в ином случае это была бы утечка данных из какого нить соседнего процесса. 7>>Офигеть шпиономания. А как наш процесс мог этим воспользоваться? O>Пароль спереть, ваш кэп.
Как пароль может оказаться в данных прикладной программы? Речь идёт о том, что при запуске программы она может оказаться там, где ранее работала другая программа. И если выделенные ей данные не инициализировать, то программа получает к ним доступ. И вот представь себе, что она должна делать, какой анализ проводить, чтобы осмыслить эти данные и извлечь из них что-нибудь полезное?
Здравствуйте, Marty, Вы писали:
M>В плюсах та же проблема будет, если ты будешь не inline код писать в заголовках. Ничего, как-то справляемся
Вот и интересно, как с этим справляются стандартные заголовки. Или они просто никогда не определяют функций?
M>Это не CRT
Да — строго говоря, это "стандартная библиотека", как и mem*, str* и прочие. И не все шаблоны, определенные в стандартных заголовках C++, самодостаточны, они могут тянуть функции из библиотеки — те же mem*.
M>Это если ты не хочешь вывести строку определенной длины с определенным выравниванием
А если хочу, то через потоки это делается интуитивно, в голове ничего держать не нужно, и в документацию лезть — тоже?
7>Как пароль может оказаться в данных прикладной программы?
Вот ты когда в браузере в окошке вводишь пароль, он именно там и оказывается
7>Речь идёт о том, что при запуске программы она может оказаться там, где ранее работала другая программа. И если выделенные ей данные не инициализировать, то программа получает к ним доступ. И вот представь себе, что она должна делать, какой анализ проводить, чтобы осмыслить эти данные и извлечь из них что-нибудь полезное?
Погрепать по слову password и отправить все рядом располагающееся своим хозяевам. Это если действовать тупо. А если знать точно что искать то — искать более конкретные сигнатуры.
Как много веселых ребят, и все делают велосипед...
Здравствуйте, vdimas, Вы писали:
V>>>Двухфакторная инициализация — статическая и динамическая. ЕМ>>CRT не является безусловно необходимым для этого.
V>Тогда будет UB. ))
Да и хрен бы с ним.
V>ты тогда не можешь полагаться на спецификации языка.
Уж где-где, а во встроенных системах, где без регулярной ревизии порождаемого кода можно словить разнообразных глюков, полагаться на общие спецификации языка — последнее дело.
V>Для динамической фазы инициализации CRT требуется
Если Вы об инициализации неконстантным выражением, то да. Я даже не подумал, что кто-то в здравом уме может использовать такое на МК.
V>программисту удобнее, когда численно совпадающие адреса данных и программы на самом деле смотрят в разную память.
Далеко не всегда удобнее — с теми же табличными функциями.
V>Считай, что это чуть другой образ мышления.
Я сам почти никогда не использую статических переменных, требующих инициализации во время выполнения. Но константные таблицы использовать люблю.
V>Одно плохо — тебе недоступна возможность явного вызова этой ф-ии, этим занимается CRT.
Во многих реализациях ее вполне себе можно вызвать, объявив прототип. Но лучше бы такие вещи включать в спецификацию и давать возможность управления. Разбить все служебные функции CRT на группы, выделить каждой определенные функции, тогда весь стартап сведется к их последовательному вызову, и нетрудно будет заменить как любую из функций, так и стартап.
V>Тут во всей красе проявляется то правило инженерии, что самое общее решения является наихудшим для каждого конкретного случая. ))
Я об этом уже который год толкую местным ревнителям чистоты и стандартности, но у них такие идеи порождают лишь неподдельное изумление.
V>нет такой возможности в языке, забудь. ))
"Жопа есть, а слова — нет?"
V>если архитектура не позволяет непосредственно читать байты из адресного пространства команд, то ты через макросы прошиваешь команду сложения рабочего регистра с константой (или загрузки константы в регистр) + операция ret. Тогда обращение к таблице вырождается в call c предвычисленным смещением.
Здравствуйте, vdimas, Вы писали:
V>Полноценный асм (макро-асм) на порядки удобнее убогих "вставок". ))
Когда на нем нужно написать нечто обширное — безусловно. Но, если некое сочетание МК и реализации C/C++ под него, с одной стороны, не вызывает желания написать все на ассемблере, а с другой — вызывает необходимость писать на ассемблере не вставки по нескольку команд, а полноценные функции, то с таким сочетанием что-то не в порядке.
V>В обычной архитектуре локальные переменные живут в стеке, а в микриках часто стек обслуживает только возвраты, а локальные данные живут во вручную организуемом стеке данных в ОЗУ.
Какой в этом смысл, если область стека нормально адресуется? Разве только в крайних вариантах экономии ОЗУ?
V>В простейших случаях (и в отсутствии рекурсии) компиляторы способны статически распределить в ОЗУ локальные переменные с перекрытием.
Они это делают в точности по тому же принципу, что и распределение памяти для вложенных друг в друга struct/union.
V>тогда программист закатывает рукава и выполняет такое распределение сам, если знает, что некие ф-ии не вызывают друг друга (даже сколь угодно косвенно) ни в каком из сценариев.
Вот я и не понимаю, для чего тут "закатывать рукава", ежли достаточно определить соответствующую комбинацию struct/union, в которой каждой структуре будет соответствовать ее собственный набор локальных переменных. Если есть рекурсия, то один общий union распадается на несколько, достаточно лишь двигать указатель по тому ручному стеку. Ну а дальше напрашивается только запихать все это в общий стек.
V>В этом случае идёт отказ от локальных переменных в пользу перекрывающихся глобальных.
V>Да, можно через union, но с той оговоркой, что ты не объявляешь такую глобальную переменную, а высчитываешь ее адрес "в уме"
В упор не понимаю, зачем что-то "высчитывать в уме". Если нет рекурсии, то общий/корневой union, в который вложено все остальное, тупо размещается по известному адресу — хоть по нулевому, если возможно. Если рекурсия есть, то добавляется переменная-указатель этого "ручного стека".
Здравствуйте, 777777w, Вы писали:
7>что она должна делать, какой анализ проводить, чтобы осмыслить эти данные и извлечь из них что-нибудь полезное?
Это далеко не всегда настолько сложно, как может показаться. Атаки всегда проводятся адресно — на известные приложения, используемые ими библиотеки (системные или сторонние), на определенные последовательности операций. Это почти всегда создает в памяти определенную "картинку", на которую всего лишь нужно правильно посмотреть.
Здравствуйте, Евгений Музыченко, Вы писали:
M>>В плюсах та же проблема будет, если ты будешь не inline код писать в заголовках. Ничего, как-то справляемся
ЕМ>Вот и интересно, как с этим справляются стандартные заголовки. Или они просто никогда не определяют функций?
В заголовках — только объявления. Определения — в сишниках. Ты что, правда не знаешь, заче м нужны заголовки?
M>>Это не CRT
ЕМ>Да — строго говоря, это "стандартная библиотека", как и mem*, str* и прочие. И не все шаблоны, определенные в стандартных заголовках C++, самодостаточны, они могут тянуть функции из библиотеки — те же mem*.
Тянуть могут, но это не CRT
M>>Это если ты не хочешь вывести строку определенной длины с определенным выравниванием
ЕМ>А если хочу, то через потоки это делается интуитивно, в голове ничего держать не нужно, и в документацию лезть — тоже?
Ха стандартные потоки не скажу, а у меня это будет так:
using namespace omanip;
out << left << width(20) << str;
Здравствуйте, Евгений Музыченко, Вы писали:
7>>что она должна делать, какой анализ проводить, чтобы осмыслить эти данные и извлечь из них что-нибудь полезное?
ЕМ>Это далеко не всегда настолько сложно, как может показаться. Атаки всегда проводятся адресно
Что значит "атака"? Программа здесь совершенно пассивна, если повезёт — то ей будут доступны данные каких-то программ (и она даже не знает каких именно), а не повезёт — будет пустая память.
Здравствуйте, vsb, Вы писали:
vsb>Вроде общепринятая точка зрения, что C ближе всего к железу, ну если не считать языков ассемблера.
И ты ее разделяешь что ли? С — абстрактная машина, описанная в стандарте. Много какое железо плохо с этой абстрактной машиной согласуется. На нем С близким к железу быть перестает.