Здравствуйте, vsb, Вы писали:
vsb>Блин, сто раз уже написали — нет в С неинициализированных глобальных переменных. Только локальные.
Кто написал? И почему сотни написанного должны что-то поменять в реальности?
Я тебе ещё раз повторю. .data — сегмент инициализированных данных, .bss — сегмент неинициализированных данных. Компилятор хоть C, хоть C++, хоть форта или фортрана — раскладывает глобальные переменные по этим сегментам. Всё. Дальше идёт работа линкера по сборке исполняемого файла из объектных файлов и работа системы по загрузке исполняемого файла.
vsb>bss обнуляется на старте, точка.
Экий ты категоричный. А теперь подумай о том, что твои проблемы инспирированы только твоим тулчэеном, а не языком.
vsb>Ты вроде в С спец, должен это лучше меня знать.
Я не спец в C, но кое-что, похоже, знаю лучше тебя
vsb>Можно удалить из стартапа этот код обнуления, можно не вызывать libc _start, тогда не будет обнуляться, но это уже не соответствует тому, что требует стандарт.
Давай ты приведёшь цитаты из стандарта, где он это требует?
vsb>Под vscode. Не совсем под STM, под её китайский перепев (GD32).
Я думаю, проблема в VSCode и в тулчейне, который он использует, под Кейлом никакой такой фигни нет
ЗЫ Ты бы разобрался сначала, кто за что отвечает и кто что гарантирует и тд и тп, а потом бы уже с шашкой махаться выходил
Здравствуйте, vdimas, Вы писали:
M>>Это для работы с периферией, она там обычно прикидывается памятью. Для обычного кода зачем что-то распределять в уме?
V>Чтобы уменьшить размер образа, оставив в нём только код.
Здравствуйте, Marty, Вы писали:
vsb>>bss обнуляется на старте, точка.
M>Экий ты категоричный. А теперь подумай о том, что твои проблемы инспирированы только твоим тулчэеном, а не языком.
Тулчейн у меня официальный, с сайта ARM, ничего официальней не существует. И проблем у меня нет, у меня есть претензии к кривому дизайну языка.
vsb>>Можно удалить из стартапа этот код обнуления, можно не вызывать libc _start, тогда не будет обнуляться, но это уже не соответствует тому, что требует стандарт.
M>Давай ты приведёшь цитаты из стандарта, где он это требует?
N2176 5.1.2 All objects with static storage duration shall be initialized (set to their initial values) before program startup.
Здравствуйте, Евгений Музыченко, Вы писали:
vsb>>А при чём тут POSIX и C? Это как бы разные вещи.
ЕМ>Вы таки или крестик снимите, или трусы наденьте. То у Вас "в C нет никаких секций, это уже не C", то "при чем тут POSIX?". Есть спецификация языка, есть стандарты, которые соблюдаются в канонических "пользовательских" реализациях, но почти никогда не соблюдаются в ограниченных "системных". Если это "все-таки C", то и "C с секциями" — "тоже C".
Я не языковой юрист, поэтому однозначно говорить не могу, но вроде в C есть т.н. freestanding environment. Прямо в стандарте прописано. И подразумевается, что он как раз для ядер ОС, прошивок и тд. И там нет требования, что все функции должны быть доступны. Так что я бы не стал утверждать, что есть printf нет, то это не С. А вот то, что секции к С не относятся — это смею утверждать, это уже расширение С.
При этом я, конечно, рад, что эти секции есть, и я могу реализовать что хочу и как хочу. Но я бы предпочёл, чтобы этой проблемы не было в принципе.
Здравствуйте, sambl74, Вы писали:
vsb>>Вроде общепринятая точка зрения, что C ближе всего к железу, ну если не считать языков ассемблера.
S>Если ты хочешь это опровергнуть — приведи просто пример языка, который ещё ближе к железу — а вот это всё что ты написал для этого не нужно
Я такого не знаю. Вообще было бы неплохо иметь язык, который уровнем чуть выше ассемблера. Ну чтобы там были if/for/while и тд, чтобы без goto простейшие вещи писать. Чтобы какая-то минимальная типизация была (ну к примеру signed/unsigned и компилятор знал, какие инструкции для сравнения генерировать). Чтобы функции писать и вызывать по-человечески, а не запоминать, сколько там аргументов и какого типа идут в регистры. Но в то же время чтобы вопросы "а не оптимизирует ли компилятор вечный цикл", "а можно ли передавать невалидный указатель в memcpy", "а можно ли разыменовывать 0?" даже смысл не имели. Чтобы каждая конструкция понятно и предсказуемо компилировалась в одну или несколько машинных инструкций.
PS я понимаю, что программы на таком языке будут в плане производительности или размера проигрывать программам на С.
Здравствуйте, Marty, Вы писали:
M>ЗЫ Ты бы разобрался сначала, кто за что отвечает и кто что гарантирует и тд и тп, а потом бы уже с шашкой махаться выходил
Здравствуйте, vdimas, Вы писали:
V>Чтобы уменьшить размер образа, оставив в нём только код.
Подозреваю, что не существует настолько примитивных средств промышленной (а не игрушечной) разработки, которые не позволяли бы распределять рабочее пространство, не занимая место в файле/прошивке.
И даже если они существуют, такое распределение можно организовать более адекватными методами — хотя бы через struct/union.
vsb>Я такого не знаю. Вообще было бы неплохо иметь язык, который уровнем чуть выше ассемблера. Ну чтобы там были if/for/while и тд, чтобы без goto простейшие вещи писать. Чтобы какая-то минимальная типизация была (ну к примеру signed/unsigned и компилятор знал, какие инструкции для сравнения генерировать).
Это C. Не путай с С++. vsb>PS я понимаю, что программы на таком языке будут в плане производительности или размера проигрывать программам на С.
Здравствуйте, vsb, Вы писали:
vsb>в C есть т.н. freestanding environment. Прямо в стандарте прописано.
Правильно — это и есть то самое "разумное подмножество", которое сперва формировалось стихийно, под нужды конкретных применений, а потом закрепилось в стандарте.
vsb>я бы предпочёл, чтобы этой проблемы не было в принципе.
А это возможно? Сумеете сочинить стандарт сколько-нибудь серьезного ЯП, который строго соблюдался бы для сотен совершенно разнородных платформ и применений, и при этом не требовал десятков лет на вхождение?
Здравствуйте, vsb, Вы писали:
vsb>было бы неплохо иметь язык, который уровнем чуть выше ассемблера.
Для этого не нужно иметь отдельный язык. Вполне достаточно любого более-менее строгого языка, имеющего дополнительные атрибуты для прямого указания нужных особенностей. Вашу "проблему" можно было бы решить введением атрибута "не инициализировать", но во всей массе создаваемых программ он использовался в ничтожно малой доле, поэтому ее издавна принято решать на уровне реализации, а не самого языка.
vsb>чтобы вопросы "а не оптимизирует ли компилятор вечный цикл", "а можно ли передавать невалидный указатель в memcpy", "а можно ли разыменовывать 0?" даже смысл не имели
Если посмотреть на историю C/C++, то хорошо видно, что подобные вопросы когда-то не обсуждались с таким накалом страстей, как в последнее время. В основном потому, что этими языками пользовались главным образом программисты, неплохо понимающие, как работают аппаратура и ПО на всех уровнях, и к любым рискам относившиеся оценочно "насколько велик риск и насколько тяжелы его возможные последствия"? Они понимали, что идеалы чистоты недостижимы, что абстракция должна иметь разумные пределы, что программист и компилятор должны действовать сообща, а не воевать друг с другом, и т.п.
А за последние два-три десятка лет наросло достаточно большая доля программистов, переносящих подход "программирование есть запись алгоритма средствами языка" с более абстрактных ЯВУ на C/C++. От этого идут и войны с малейшими отступлениями от стандарта ради удобства или эффективности, и расширение свободы компилятора без возможности контроля за нею, и ряд других косяков.
Ваша позиция здесь весьма двойственна. Если для Вас первично то, какой получается код, и как он работает, и реализация имеет средства для управления этим, то нет смысла акцентировать внимание на том, что стандарт самого языка такого не позволяет. Если первичен стандарт, то Вам не должно быть дела до требуемых объема памяти и количества тактов, ибо "компилятор умнее, он сам решит". То есть, между крестиком и трусами выбирать таки придется.
Здравствуйте, Евгений Музыченко, Вы писали:
V>>Чтобы уменьшить размер образа, оставив в нём только код. ЕМ>Подозреваю, что не существует настолько примитивных средств промышленной (а не игрушечной) разработки, которые не позволяли бы распределять рабочее пространство, не занимая место в файле/прошивке.
Что ты понимаешь под "промышленной" разработкой?
Вот есть некие семейства микроконтроллеров, на них тулчейн — это ассемблер, Си, эмулятор, программатор и отладочные платы.
Если пользуешь Си, то для оперирования объявленными статическими данными (структурами, массивами) необходим CRT.
Но, выкинув CRT, можно взять камень с меньшим объёмом ПЗУ/ОЗУ, более дешевый, что даёт профит как раз при масовом выпуске некоего конечного изделия.
CRT почти всегда выкидывали, поэтому.
ЕМ>И даже если они существуют, такое распределение можно организовать более адекватными методами — хотя бы через struct/union.
Еще скажи через сишную инициализацию глобальных переменных вида:
SomeStruct s = { X, { Y, Z } };
Просто напоминаю, что почти всегда микроконтроллеры выполнены по гарвардской ахитектуре, а не по фон-неймановской, с которой ты писал этот пост.
Поэтому, такая инициализация в гарвардской бессмысленна, требует работы CRT в сочетании со специально-сгенерированным кодом на каждую такую инициализацию.
Например, в фон-неймановской архитектуре, проинициализированная глобальная переменная не требует лишнего кода, потому что в бинарном образе уже хранятся данные ровно так, как описаны в сниппете выше, т.е. загрузку этих данных выполняет внешний загрузчик. Но в микроконтроллере инициализацию переменной s должен выполнять код, прошитый в микрик — и ситуация резко меняется, не так ли?
Здравствуйте, Евгений Музыченко, Вы писали:
vsb>>было бы неплохо иметь язык, который уровнем чуть выше ассемблера. ЕМ>Для этого не нужно иметь отдельный язык. Вполне достаточно любого более-менее строгого языка, имеющего дополнительные атрибуты для прямого указания нужных особенностей. Вашу "проблему" можно было бы решить введением атрибута "не инициализировать"
И это всё отностельно свежее, при том, что на Си под микрики активно пишут примерно с 93/94-х, выкручиваясь уникально для каждого случая.
До этого писали сугубо на асмах.
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Для этого не нужно иметь отдельный язык. Вполне достаточно любого более-менее строгого языка, имеющего дополнительные атрибуты для прямого указания нужных особенностей. Вашу "проблему" можно было бы решить введением атрибута "не инициализировать", но во всей массе создаваемых программ он использовался в ничтожно малой доле, поэтому ее издавна принято решать на уровне реализации, а не самого языка.
Это просто один из примеров. C отвратительный язык для этого, по крайней мере в текущей инкарнации. К примеру gcc может заменить цикл копирования на memcpy. Причём он умудрился это сделать в моей реализации memcpy, когда я рассматривал вариант выкинуть libc. Такой вот рекурсивный memcpy. Да, я знаю, как сделать неинициализируемый буфер. Я теперь знаю, каким флагом отключить у gcc эти выкрутасы с memcpy/memset. Я теперь знаю, что вечный цикл валидный в С и не валидный в С++. Это, конечно, очень весело — прыгать между граблей, заботливо разложенных разработчиками компилятора. Но я не уверен, что это очень продуктивно в итоге.
ЕМ>А за последние два-три десятка лет наросло достаточно большая доля программистов, переносящих подход "программирование есть запись алгоритма средствами языка" с более абстрактных ЯВУ на C/C++. От этого идут и войны с малейшими отступлениями от стандарта ради удобства или эффективности, и расширение свободы компилятора без возможности контроля за нею, и ряд других косяков.
Собственно "корень зла" тут компилятор. А точней стандарт. Я не могу обвинять тех, кто пишет компилятор по стандарту. Пользователи разные. Одним нужно одно, другим другое. Наверное существуют и те, кто обоснует, зачем выкидывать вечный цикл, честно признаюсь — мне интересно было бы послушать.
Компилятор такой, какой есть. Я могу переписать стартап. Я могу обойтись без libc и написать memcpy в одну строчку, может быть он не будет таким быстрым, как libc-шный, ну и пофиг. Но компилятор переписать я не могу, это уже то, что дано свыше.
ЕМ>Ваша позиция здесь весьма двойственна. Если для Вас первично то, какой получается код, и как он работает, и реализация имеет средства для управления этим, то нет смысла акцентировать внимание на том, что стандарт самого языка такого не позволяет. Если первичен стандарт, то Вам не должно быть дела до требуемых объема памяти и количества тактов, ибо "компилятор умнее, он сам решит". То есть, между крестиком и трусами выбирать таки придется.
Моя позиция простая. Я не хочу бороться с компилятором. А мне приходится. Прямо сейчас у меня в прошивке bss на несколько килобайтов и я с этим ничего не могу сделать. Этот bss приезжает из китайской библиотеки, в которой 18 мегабайтов кода на C, и если я начну её переписывать, что было бы мне, конечно, очень интересно, но проект тогда я в этом году точно не закончу. Если я не буду обнулять bss, я на 100% уверен, что китайская библиотека будет глючить, т.к. ни один китаец не рассчитывал на то, что глобальные переменные не обнуляются. И я на те же 100% уверен, что львиная доля из этого bss в инициализации на самом деле не нуждается. И простого выхода тут нет, я просто смиряюсь с тем, что процессор крутит лишние циклы. Но правильным считать это не могу.
Здравствуйте, vdimas, Вы писали:
V>Что ты понимаешь под "промышленной" разработкой?
Разработку ПО для реальных задач. Которое систематически применяется в соответствующих сферах, а не только лежит в репозиториях, демонстрируя сам факт своего наличия.
V>Если пользуешь Си, то для оперирования объявленными статическими данными (структурами, массивами) необходим CRT.
Как именно он необходим для оперирования ими?
V>выкинув CRT, можно взять камень с меньшим объёмом ПЗУ/ОЗУ
Ну так понятно же, что стандартный CRT в любой реализации всегда будет заточен первым делом под максимальную простоту использования, а не максимальную эффективность. И преимущество C/C++ в том, что выкинуть/переделать его CRT в среднем проще, чем для любого другого ЯВУ. А еще лучше было бы иметь хоть в стандарте, хоть в документации конкретной реализации, внятную спецификацию CRT, чтоб можно было им управлять осмысленно, а не наугад.
V>Еще скажи через сишную инициализацию глобальных переменных вида
Не скажу.
V>почти всегда микроконтроллеры выполнены по гарвардской ахитектуре, а не по фон-неймановской, с которой ты писал этот пост.
Это Вы с чего взяли?
V>в фон-неймановской архитектуре, проинициализированная глобальная переменная не требует лишнего кода, потому что в бинарном образе уже хранятся данные ровно так, как описаны в сниппете выше, т.е. загрузку этих данных выполняет внешний загрузчик. Но в микроконтроллере инициализацию переменной s должен выполнять код, прошитый в микрик — и ситуация резко меняется, не так ли?
Если именно так, как Вы написали, то да. Но я, разумеется, подразумевал не это. Коль Вы знакомы с разными реализациями, то должны знать, как это делается в норме.
При чем здесь вообще внешняя память? А то, может, еще попеняем C/C++ за отсутствие встроенной поддержки переменных, размещенных в файлах и БД?
V>на Си под микрики активно пишут примерно с 93/94-х, выкручиваясь уникально для каждого случая. До этого писали сугубо на асмах.
Сугубо на асмах писали просто потому, что первые МК имели совсем уж смешные объемы памяти, поэтому впихнуть туда сколько-нибудь сложную программу было физически невозможно, а на размерах порядка тысяч команд преимущество C над ассемблерами весьма условно. Как только стало достаточно для реализации преимуществ, так и начали переходить на C.