Перейти к содержанию
    

Плавный переход C -> C++ под МК

2 часа назад, jcxz сказал:

Да ладно!

А в чем противоречие? Я думаю, @esaulenka просто имел ввиду то, что вне зависимости от последовательности инициализации в явно определенном списке инициализации конструктора (о как сказал!) программист должен не забывать, что порядок реальной инициализации (порядок выполнения инструкций присвоения элементам значений) будет происходить в соответствии со списком в определении членов класса/структуры. Потому как программист может написать что-то типа

class A {
  int a;
  int b;
  int c;
  int d;
  
  public:
    A(int b, int d, int a, int c) :
      b(b), d(d), a(a), c(c) {}
};

и по своей человеческой натуре наивно предполагать, что порядок инициализации будет точь-в-точь как указан в initializer list конструктора. Облом, однако - порядок инициализации членов будет таким, каким они объявлены в классе: сначала значение получит 'a', затем 'b', после этого 'c' и в конце 'd'.

Ваш пример на чистом компиляторе C++ не должен компилироваться, т.к. назначенная инициализация до C++20 языком не поддерживается. А компилируется он, скорее всего, из-за каких-то документированных расширений компилятора. У меня Keil тоже компилирует такой код, но в настройках у меня выбраны галки обработки исходников для двух языков независимо: для Си можно выбрать версию Си (C11, например), для C++, соответственно, свою. Думаю, это как раз неспроста. Но Keil выдает на такую инициализацию законное предупреждение, мол, что назначенная инициализация поддерживается только в Си.
 

3 часа назад, byRAM сказал:

Недавно поступил на предприятие  HEX-код от программиста из Москвы...

Звучит как начало чего-то, похожего на ржачную и поучительную байку про то, как высокоэффективные менеджеры из Москвы приехали в региональный филиал одной крупной компании наладить рекламу... а в итоге развалили всю компанию. Пока что без пруфов за саботаж смахивает эта история, ИМХО:biggrin:
 

1 час назад, jcxz сказал:

Имхо: Компоновщик каждого конкретного компилятора, создаёт список инициализации, соответствующий возможностям си-стартап кода данного компилятора.

Не знаю как в Си, но порядок инициализации C++ объектов (и даже POD-структур) очень жестко закреплен стандартом C++.
 

Цитата

Как-то просматривал код какого-то си-стартапа, так там список инициализации состоял из множества таких записей:

{базовый_адрес_исполняемый_блока_данных, длина_блока_данных, блок_данных(N_байт)}

И стартап код просто последовательно обрабатывал эти записи, копируя блоки данных в их исполняемые адреса.

Так они (стартапы) все так работают. Линковщик создает один или несколько регионов (в зависимости от файла скрипта), из которых он копирует RW-данные в ОЗУ (в том числе, выполняя декомпрессию данных при необходимости). В коде можно написать миллион и один способ инициализации одного и того же массива, членов класса и т.д. ИМХО, этот зоопарк способов невозможно описать единым алгоритмом, чтобы затем скомпилировать его и включать библиотечным бинарником в набор инструментов сборки. Иначе это должен быть какой-то скрипт, который будет анализировать исходники юзера-программиста, на основе них строить код инициализации Си-окружения... что, во-первых, способствует росту граблей и багов в геометрической прогрессии, а во-вторых, требует чуть большего расхода памяти программы. Проще "разом" пройтись по списку регионов и "раздать" переменным их значения по порядку, друг за другом.
 

Цитата

Но я допускаю, что завтра может выйти более умная версия компилятора, которая для:

struct Name1 { char m0[50], m1, m2[2]; } static body1 = { .m1 = 1 };

вместо того, чтобы как предыдущие, менее умные версии, создавать запись инициализации с блоком данных длиной = 53 байта, поместит body1 в секцию нуль-инициализируемых данных, обнулит, а  затем часть стартап-кода, инициализирующая инициализируемые переменные, поверх скопирует в m1 один байт == 1.

Эта структура по стандарту C++ итак сначала обнулится (мы ведь предполагаем, что body1 это глобальный объект?) , и только потом выполнит свой конструктор, который в данном случае установит 'm1' в 1. Я не так давно приводил похожий пример, где в классе у меня были жирные по объему массивы, которые я не хотел инициализировать нулем до вызова конструктора, объявляя глобальный объект такого класса. Нет же, пришлось для сокращения времени старта программы объявлять указатель на такой массив, а конструктору передавать параметром адрес отдельно объявленного массива, причем, уже размещенного в NOINIT-секции.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

13 minutes ago, Arlleex said:

Нет же, пришлось для сокращения времени старта программы объявлять указатель на такой массив, а конструктору передавать параметром адрес отдельно объявленного массива, причем, уже размещенного в NOINIT-секции.

Увы, костыли, куда уж без них :(

Я обхожу это отложенной инциализацией. Т.е. конструктор любого такого "жирного" класса у меня практически или полупустой или его вообще нет, но всегда есть метод initialize(). Например, в классах задач и таймеров он вообще virtual, т.е. по сути рекомендованный к реализации в каждом потомке. 

 

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

Например, периферию НИКОГДА не инициализирую в конструкторах классов, которые создаются статически. За исключением классов портов/пинов (хотя при нужде ничто не мешает создать экземпляр любого порта/пина в реалтайме так сказать).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

23 минуты назад, Forger сказал:

Я обхожу это отложенной инциализацией...

А как отложенная инициализация влияет на так называемую zero-initialization, которая выполняется априори до любых других видов инициализаций? Или Вы весь объект такого класса объявляете в секции NOINIT? Пока что только в этом вижу возможность отложенной инициализации (врукопашную).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

34 минуты назад, Arlleex сказал:

Я думаю, @esaulenka просто имел ввиду то, что вне зависимости от последовательности инициализации в явно определенном списке инициализации конструктора (о как сказал!) программист должен не забывать, что порядок реальной инициализации (порядок выполнения инструкций присвоения элементам значений)

А где в моём примере Вы нашли "выполнение инструкций присвоения элементам значений"?  :shok:

Присмотритесь внимательнее - там есть const.

37 минут назад, Arlleex сказал:

Не знаю как в Си, но порядок инициализации C++ объектов (и даже POD-структур) очень жестко закреплен стандартом C++.

Вызовов конструкторов, если таковые есть - возможно. Я же говорил про инициализацию объектов без конструкторов. Тем более - встроенных типов.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

20 minutes ago, Arlleex said:

А как отложенная инициализация влияет на так называемую zero-initialization, которая выполняется априори до любых других видов инициализаций?

Достаточно лишь пометить объекты, которые недопустимо класть в секцию zero-initialization, соотв. аттрибутом в стиле NO_INIT: __attribute__ ((section (".bss.noinit"))), которая у всех компиляторов разная.

Но это не работает для отдельных полей класса, однако, есть простое решение: объявлять такие жирные массивы с "припиской" static, внутри реализации класса, закрыв максимально их область видимости. А внутри класса объявлять лишь указатели на такие объекты.

Саму инциализацию таких массивов/объектов (через указатели или напрямую) делать уже вручную когда это нужно и нужно ли вообще.

Все это желательно закрывать глубоко внутри реализации класса. Это - костыли, поэтому они должны быть аккуратно спрятаны.

 

Конечно, есть более радикальный метод - дин. память, как крайний случай )))

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

11 минут назад, jcxz сказал:

А где в моём примере Вы нашли "выполнение инструкций присвоения элементам значений"?

Ну там, поскольку объект глобальный, да еще и POD, данные сразу "легли" в нужные места во Flash. Все равно не понял, в чем противоречие сказанному

Цитата

Также в C++ есть ограничение, что члены структуры должны инициализироваться в том же порядке, как они расположены в самой структуре...

хоть, и немного неточно сказанному. Трактовать эту строчку надо так: в C++ члены класса будут инициализированы в списке инициализации или в конструкторе по-умолчанию в порядке, соответствующему порядку объявления членов. Не более.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

52 минуты назад, Arlleex сказал:

Эта структура по стандарту C++ итак сначала обнулится (мы ведь предполагаем, что body1 это глобальный объект?) , и только потом выполнит свой конструктор, который в данном случае установит 'm1' в 1.

Это не так. Или назовите компилятор в котором работает столь странно и неоптимально?

body1 - глобальная.

Те компиляторы, которые я смотрел, располагали инициализированные 0-ями данные в одной секции, инициализированные не0-ями - в другой. Затем по отдельности - первую секцию заполняли 0, вторую - копированием из const-секции (содержащей инициализаторы).

Вот как сиё выглядит в IAR for MSP430: cstart_msp430.png.a6e936935106359d8af6c3a4b8102af0.png

Тот массив (body1) инициализируется внутри __data16_memcpy(). И никак не модифицируется внутри __data16_memset(). Что и логично.

Скорей всего в других компиляторах - аналогично.

 

PS: Про случаи с конструкторами я ничего не говорил. И в моих примерах их нет.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Я бы для начала хотел узнать, как мне трактовать два противоречащих друг другу поста

2 часа назад, jcxz сказал:

Но я допускаю, что завтра может выйти более умная версия компилятора, которая для:

struct Name1 { char m0[50], m1, m2[2]; } static body1 = { .m1 = 1 };

вместо того, чтобы как предыдущие, менее умные версии, создавать запись инициализации с блоком данных длиной = 53 байта, поместит body1 в секцию нуль-инициализируемых данных, обнулит, а  затем часть стартап-кода, инициализирующая инициализируемые переменные, поверх скопирует в m1 один байт == 1.

на что я написал

1 час назад, Arlleex сказал:

Эта структура по стандарту C++ итак сначала обнулится (мы ведь предполагаем, что body1 это глобальный объект?) , и только потом выполнит свой конструктор, который в данном случае установит 'm1' в 1. Я не так давно приводил похожий пример, где в классе у меня были жирные по объему массивы, которые я не хотел инициализировать нулем до вызова конструктора, объявляя глобальный объект такого класса. Нет же, пришлось для сокращения времени старта программы объявлять указатель на такой массив, а конструктору передавать параметром адрес отдельно объявленного массива, причем, уже размещенного в NOINIT-секции.

на что Вы написали уже

9 минут назад, jcxz сказал:

Это не так. Или назовите компилятор в котором работает столь странно и неоптимально?


Так оптимальный/умный компилятор то как в итоге сделает?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

18 минут назад, Arlleex сказал:

Я бы для начала хотел узнать, как мне трактовать два противоречащих друг другу поста

В чём Вы видите противоречие?

Вы сказали что:

1 час назад, Arlleex сказал:

Эта структура по стандарту C++ итак сначала обнулится (мы ведь предполагаем, что body1 это глобальный объект?) , и только потом выполнит свой конструктор, который в данном случае установит 'm1' в 1.

Я привёл Вам конкретный пример существующего сейчас компилятора (да и в других видел аналогичное поведение), который не обнуляет сначала инициализируемые не0-ями данные. А значит одно из 2-х:

1) или эти компиляторы не соответствуют этому стандарту;

2) или такого требования в стандарте нет. Инициализируемые не0-ями данные могут сразу инициализироваться этими данными, без предварительного обнуления.

И я думаю что - второе. Почему и написал, что Вы не правы.

18 минут назад, Arlleex сказал:

Так оптимальный/умный компилятор то как в итоге сделает?

А здесь я предполагаю, что в будущем возможно появится компилятор (или он уже где-то существует), в котором к указанным двум секциям инициализации (скриншот выше), добавится 3-я. В которую компилятор будет помещать объекты, по большей части заполненные 0-ями, с отдельными элементами не равными 0. И компилятор в эту секцию будет помещать некоторые объекты, если решит что так будет оптимальнее.

Но опять же - никакой стандарт, имхо, этой возможности не противоречит.

 

1 час назад, Arlleex сказал:

Я думаю, @esaulenka просто имел ввиду то, что вне зависимости от последовательности инициализации в явно определенном списке инициализации конструктора (о как сказал!) программист должен не забывать, что порядок реальной инициализации (порядок выполнения инструкций присвоения элементам значений)

34 минуты назад, Arlleex сказал:

Ну там, поскольку объект глобальный, да еще и POD, данные сразу "легли" в нужные места во Flash. Все равно не понял, в чем противоречие сказанному

А я думаю - Вы пытаетесь "натянуть сову на глобус".  :unknw:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 hours ago, byRAM said:

Шутка, не шутка, но моя одноклассница, начальница отдела программистов, уже натерпелась уже от быдлокодеров и попросила моей помощи

Пусть введёт пятничные порки. На улице, с привязыванием к столбу.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

3 минуты назад, jcxz сказал:

2) или такого требования в стандарте нет. Инициализируемые не0-ями данные могут сразу инициализироваться этими данными, без предварительного обнуления.

Все, теперь понял, о чем Вы говорите. Тем не менее, синтаксисом C++ так инициализировать объекты (через точку выборочно) - по стандарту нельзя.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

...начальница отдела программистов, уже натерпелась

от быдлокодеров и попросила моей помощи в избавлении от такого мусора


Бейсбольная бита над её столом, для начала. (((-8Ж

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, Forger сказал:

Но это не работает для отдельных полей класса, однако, есть простое решение: объявлять такие жирные массивы с "припиской" static, внутри реализации класса, закрыв максимально их область видимости. А внутри класса объявлять лишь указатели на такие объекты.

А объекты то сами как объявлять, если их будет > 1? Понятно, когда объект один такой в своем роде, то все жирные массивы можно, конечно же, объявить внутри .cpp-реализации как static. Только вот когда я создаю два, три, пять, да сколько угодно объектов - мне нужно при создании все равно будет каким-то образом связать объект с отдельным массивом. Через конструктор можно передавать номер массива в уже заготовленном "пуле" таких статических буферов, но, ИМХО, не красиво...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

30 minutes ago, Arlleex said:

А объекты то сами как объявлять, если их будет > 1?

Есть постой вариант - создается некий класс диспетчер/менеджер, он по сути синглтон. 

А уже через него создаются новые объекты в любом количестве, которое ограничено изначально в коде через какой-нить define. 

Это что-то типа пула памяти, но пул не просто неких данных, а пул целых классов. Пул по сути самодельный (хотя можно использовать штатную встроенную дин память или сторонний диспетчер дин. памяти).

 

Увы, но отложенная инициализация потребует контроля за отложенным созданием массивов или что там нужно для создаваемых однотипных классов.

Контроль потребуется в реалтайме, а не средствами компилятора. Это перекладывает ответственность с компилятора на прогера. 

 

Тут суть простая - мы создаем интерфейс для созданий неких объектов, а не сами объекты. По сути - это "самопальная" реализация паттерна "фабрика объектов".

Вариаций на эту тему может быть масса, реализация зависит в конкретного проекта. 

Обычно, я стараюсь избегать использовать дин. память, вообще заблокировать ее через соотв. прагмы.

Но, если использую, то только на создание malloc (new), причем лишь на этапе запуска приложения. При этом никогда оттуда ничего не освобождая по известным причинам.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 3/19/2022 at 8:31 AM, Arlleex said:

А C++, за счет более строгой типизации, не дает так сделать.

Вот объяснение, почему так нельзя делать в C++: https://isocpp.org/wiki/faq/ctors#ctor-initializer-order

Там, правда, почему-то сказано, что "компилятор может выдавать предупреждение". Проверил на gcc нескольких версий и на MSVC - все отказываются это компилировать.

 

В случае C - да, конечно, компилятору проще сделать константный массив и в момент инициализации memcpy'ить его в нужное место.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...