Здравствуйте, vsb, Вы писали:
vsb>1. Язык С гарантирует, что все глобальные переменные инициализируются нулями. vsb>2. Компилятор кладёт этот буфер в секцию `.bss`. vsb>3. Стартап код на ассемблере обнуляет секцию `.bss` сразу после запуска контроллера. vsb>4. _start из libc ещё раз обнуляет секцию `.bss` (видимо для надёжности).
Согласно Талмуду руки мыть нужно 3 раза, так что все верно.
vsb>5. Итого наш хиленький процессорчик после подачи питания просто так потратил порядка сотни тысяч тактов, гоняя нули. vsb>6. Наш буфер вообще никак не использует этот факт своего обнуления.
А что если туда каким-то образом попадет вредоносный код?
Здравствуйте, Shmj, Вы писали:
S>А что если туда каким-то образом попадет вредоносный код?
Это просто буфер. Я туда данные записываю, которые надо отправить и в отдельной процедуре отправляю. Пока данные туда не записались, значение этого буфера никак не используется.
Здравствуйте, vsb, Вы писали:
vsb>Вроде общепринятая точка зрения, что C ближе всего к железу, ну если не считать языков ассемблера.
vsb>Очень забавно за всем этим наблюдать, конечно.
никто не заставляет тебя размещать буфер в bss
можешь сделать свою секцию
Здравствуйте, night beast, Вы писали:
vsb>>Вроде общепринятая точка зрения, что C ближе всего к железу, ну если не считать языков ассемблера.
vsb>>Очень забавно за всем этим наблюдать, конечно.
NB>никто не заставляет тебя размещать буфер в bss NB>можешь сделать свою секцию
Я так и сделал. Но это уже не C. В C нет никаких секций. Это уже что-то вроде GCC-C.
Меня просто сам подход удивил. С одной стороны вроде как топят за то, что язык ничего не делает сам по себе. Даже локальные переменные не инициализирует, хотя тут какой вред, вообще сложно представить, с локальной переменной компилятор прекрасно видит — инициализировали ли её перед использованием. А с глобальными логика инвертировалась.
Здравствуйте, vsb, Вы писали:
vsb>Вроде общепринятая точка зрения, что C ближе всего к железу, ну если не считать языков ассемблера.
vsb>2. Компилятор кладёт этот буфер в секцию `.bss`.
vsb>3. Стартап код на ассемблере обнуляет секцию `.bss` сразу после запуска контроллера.
vsb>4. _start из libc ещё раз обнуляет секцию `.bss` (видимо для надёжности).
vsb>6. Наш буфер вообще никак не использует этот факт своего обнуления.
1. Очевидно двойная инициализация это баг. Баги есть везде, непонятно какое это отношение
имеет к языку C.
2. По стандарту статические переменные без инициализатора обнуляются.
Если это не нужно, то можно использовать статический указатель, и инициализировать его в "main",
или присвоить этому статическому указателю конкретное значение, если речь идет об embedded,
где используют кастомные скрипты линковщика и подобные хитрости.
vsb>Меня просто сам подход удивил. С одной стороны вроде как топят за то, что язык ничего не делает сам по себе. Даже локальные переменные не инициализирует, хотя тут какой вред, вообще сложно представить, с локальной переменной компилятор прекрасно видит — инициализировали ли её перед использованием. А с глобальными логика инвертировалась.
В "больших" операционках, память, выделяемая на уровне ОС (то есть не malloc, а mmap какой нить или VirtualAlloc) обнуляется ядром — потому что в ином случае это была бы утечка данных из какого нить соседнего процесса. Ну и соответственно bss всякие загрузчик в линуксе не обнуляет — они сразу мапятся в процесс обнуленными. Возможно решили использовать этот факт в стандарте.
Как много веселых ребят, и все делают велосипед...
Здравствуйте, vsb, Вы писали:
vsb>3. Стартап код на ассемблере обнуляет секцию `.bss` сразу после запуска контроллера.
vsb>4. _start из libc ещё раз обнуляет секцию `.bss` (видимо для надёжности).
Ну, руки-крюки у кого-то.
Я в свое время для TurboC 2.0 писал минималистичный стартап на асме. Получалось строк типа 20, что ли. Как раз, в основном, обнуление _bss и инициализация стека (не уверен, что с моим стартапом работал malloc() и зависящие от него функции, но не всякой же программе они нужны, правда?).
vsb>5. Итого наш хиленький процессорчик после подачи питания просто так потратил порядка сотни тысяч тактов, гоняя нули.
Он после подачи питания сколько-то там миллисекунд, если не десятков, ждет просто, пока тактовый генератор раскрутится и застабилизируется. Неужели на этом фоне эти обнуления заметны? Особенно если учесть, что к хиленьким процессорам редко когда большой кусок памяти приделывают.
Здравствуйте, vsb, Вы писали:
vsb>Меня просто сам подход удивил. С одной стороны вроде как топят за то, что язык ничего не делает сам по себе. Даже локальные переменные не инициализирует, хотя тут какой вред, вообще сложно представить, с локальной переменной компилятор прекрасно видит — инициализировали ли её перед использованием. А с глобальными логика инвертировалась.
Я думаю, в том древнем UNIX, в котором C родился, неинициализированные данные зануляло ядро, а вовсе даже не сишный стартап. И это, с одной стороны, убирало утечку данных между программами через мусор в памяти, а с другой — было дешевле, чем в каждой программе явно инициализировать глобальные переменные,
Здравствуйте, vsb, Вы писали:
vsb>Самый низкоуровневый язык, ага vsb>Вроде общепринятая точка зрения, что C ближе всего к железу, ну если не считать языков ассемблера.
1. Си это язык программирования высокого уровня абстракций.
2. Ассемблер это язык программирования низкого уровня абстракций.
Даже у ассемблера есть абстракции, но их там мало и они в основном носят организационный характер. Что касается Си, то он очень далёк от машинных кодов. А все абстракции строятся именно поверх машинных кодов.
На сегодняшний день развитие компиляторов и усложнение процессоров привело к тому, что вручную написанный ассемблерный код (кроме разве что очень коротких программ) практически не выигрывает по сравнению с кодом, генерируемым компиляторами, при этом Си продолжает оставаться одним из наиболее эффективных языков высокого уровня.
Здравствуйте, Maniacal, Вы писали:
vsb>>1. Язык С гарантирует, что все глобальные переменные инициализируются нулями.
M>Нет, только статические обнуляются.
Все голабальные переменные — статические.
Но Си, действительно, обнуляет все статические переменные, а не только глобальные:
int foo; // Глобальная переменная; будет обнулена
static int bar; // Неглобальная, но статическая. Тоже будет обнулена
void f(void)
{
static int c; // И эта тоже;
. . .
}
Здравствуйте, vsb, Вы писали:
vsb>5. Итого наш хиленький процессорчик после подачи питания просто так потратил порядка сотни тысяч тактов, гоняя нули.
Пример в студию
<объединил цитирование>
vsb>Я так и сделал. Но это уже не C. В C нет никаких секций. Это уже что-то вроде GCC-C.
>3. Стартап код на ассемблере обнуляет секцию `.bss` сразу после запуска контроллера.
>4. _start из libc ещё раз обнуляет секцию `.bss` (видимо для надёжности).
Равно как нет и никакого стартап кода и функции _start тоже нет.
Так что не путай язык и его реализацию. Язык лишь определяет, что они должны быть занулены. Как это будет сделано — он не определяет.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Равно как нет и никакого стартап кода и функции _start тоже нет.
PD>Так что не путай язык и его реализацию. Язык лишь определяет, что они должны быть занулены. Как это будет сделано — он не определяет.
Про двойное обнуление это так, к слову. Может и не везде это, в STM32 стартапе я это увидел и в libc которая линкуется через gcc с сайта arm я это тоже увидел.
А про язык я написал потому, что считаю требование обнулять глобальные переменные без явной инициализации — глупостью. Кому нужны нули, тот напишет int n = 0; char s[123] = {}, зря что-ли специальный синтаксис выдумали.
Здравствуйте, vsb, Вы писали:
vsb>А про язык я написал потому, что считаю требование обнулять глобальные переменные без явной инициализации — глупостью. Кому нужны нули, тот напишет int n = 0; char s[123] = {}, зря что-ли специальный синтаксис выдумали.
Если ты так в embedded напишешь, то линкер эти твои нули засунет в ПЗУ, занимая там место, а стартап-код будет заботливо копировать в ОЗУ. Возможно дважды, как в STM32
vsb>Если всмотреться, что происходит при этом, то можно заметить следующее:
vsb>1. Язык С гарантирует, что все глобальные переменные инициализируются нулями.
Разве? Я думал, что чтобы произошла инициализация нулями, надо написать что-то типа
byte buf[1024] = { 0 };
А если написать
byte buf[1024];
то инициализации не будет
vsb>2. Компилятор кладёт этот буфер в секцию `.bss`.
Я не помню, что такое .bss. Есть секции для инициализированных переменных (и вроде не обязательно нулями), и для не инициализированных. Для последних вроде никто обнуление не делает
vsb>3. Стартап код на ассемблере обнуляет секцию `.bss` сразу после запуска контроллера.
vsb>4. _start из libc ещё раз обнуляет секцию `.bss` (видимо для надёжности).
Не совсем понял, в чем отличие стартап кода и _start из libc? Я вообще не уверен, что в прошивке для МК есть какие-то секции.
vsb>5. Итого наш хиленький процессорчик после подачи питания просто так потратил порядка сотни тысяч тактов, гоняя нули.
Или нет
vsb>6. Наш буфер вообще никак не использует этот факт своего обнуления.
vsb>Очень забавно за всем этим наблюдать, конечно.
Не наблюдай. Не инициализируй переменные там, где это не нужно. Перепиши стартап.
Здравствуйте, Sergei I. Gorelkin, Вы писали:
vsb>>А про язык я написал потому, что считаю требование обнулять глобальные переменные без явной инициализации — глупостью. Кому нужны нули, тот напишет int n = 0; char s[123] = {}, зря что-ли специальный синтаксис выдумали.
SIG>Если ты так в embedded напишешь, то линкер эти твои нули засунет в ПЗУ, занимая там место, а стартап-код будет заботливо копировать в ОЗУ. Возможно дважды, как в STM32
Это понятно но это текущая кривая реализация. Ничего не мешает такие переменные сувать в bss, а неинициализированные ещё куда.