Обсуждаем контроллеры компании Atmel.
Ответить

Re: Нескольно простых вопросов о программировании AVR на Си.

Сб мар 28, 2020 20:26:51

Так процитируйте спецификацию Си! То место, где говорится о типизации и её строгости (или сильности). Хватит уже собственных интерпретаций!


Ничего не понял.

Я в третий (третий?) раз повторяю: в теме классификации языков программирования, вариантов "типизаций" и прочего растекания теорией по древу нет ничего, кроме чьих-то "собственных интерпретаций".

Что же касается стандарта, я же ясно сказал выше (процитирую самого себя):

Ну в первую очередь надо спросить у вас, что вы имели в виду под "нестрогой типизацией" в конкретном случае. Вы ведь вели речь о "ворнингах", то есть имели в виду конкретные ситуации в коде, а не какой-то огульный холивор. Приводите примеры. А далее - стандарт языка С. Авторитетнее некуда.


Ждем конкретных примеров. А вы продолжаете гнать какой-то дым. Не буду же я вываливать сюда все скопом все ограничения/требования типизации языка С? Зачем? Давайте конкретные примеры, а я вам с радостью продемонстрирую, что о них говорит стандарт языка.

Re: Нескольно простых вопросов о программировании AVR на Си.

Сб мар 28, 2020 20:46:26

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

Такое определение вас устроит? Вроде как во всем мире именно его используют, когда говорят об этом. Почему вам это не известно, хотя вы беретесь об этом говорить, я не понимаю.

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

Ошибка - это недопускаемое синтаксисом языка действие.

Тут тоже определения вроде как общепринятые, и с чего вы стали их по-своему интерпретировать, тоже вопрос интересный...

Добавлено after 4 minutes 45 seconds:
В контексте варнингов и ошибок строгая типизация означает, что варнингов по поводу неявного преобразования типов в принципе быть не может - это однозначно ошибка. Язык самой-самой НЕ строгой типизации вообще никак не информирует о неявном преобразовании типов.

Как-то так...

Re: Нескольно простых вопросов о программировании AVR на Си.

Вс мар 29, 2020 05:39:04

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


Это - прекрасное определение. Я ничего не имею против него.

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


Дискуссии о том, что считать строгой типизацией, а что нет, обычно фокусируются на различиях между "object context" (типизация представления объекта в памяти) и "value context" (типизация абстрактного значения, без привязки к памяти).

Я придерживаюсь мнения о том, что понятие строгой типизации стоит применять только к объектным контекстам. То есть строгая типизация - это когда язык не позволяет вам производить доступ к представлению в памяти объекта типа `T`, как к объекту типа `U`. В терминах С и/или С++ строгая типизация - это запрет на неявное преобразование указателей типа `T *` и `U *` друг к другу (или ссылок `T &` и `U &` друг к другу) с целью последующей "переинтерпретации" указуемой памяти. Языки С и С++ на декларативном уровне запрещают такое использование (т.наз. правила strict aliasing), а также принимают ряд меры для того, чтобы заставить вас использовать явные преобразования в таких ситуациях, если вам вдруг приспичит эти преобразования выполнить. Эти меры имеют кое-какие обходные "дыры", но и их использование требует более-менее явных усилий.

Вот это я считаю строгой типизацией.

А нарушения типизации в "value context", т.е. возможность неявно преобразовать `short` к `int` и даже `int` к `unsigned int` и `double` к `int` я нарушениями строгой типизации не считаю. Тем не менее, современный С++ сделал шаги к затягиванию гаек в этом направлении (см. narrowing conversions).

---

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

Тут тоже определения вроде как общепринятые, и с чего вы стали их по-своему интерпретировать, тоже вопрос интересный...


Нет, нет, нет... Вы в общем и целом близки к истине, но все таки определения дали очень и очень неточные! Как вы сами правильно заметили, существуют общепринятые определения. Давайте их уточним.

Общепринятым определением понятия "ошибка" в языке С называется то, что описывается в разделе 5.1.1.3 стандарта языка С. А именно: нарушение синтаксиса и нарушения ограничений (constraints). Вот эту вторую часть - нарушения ограничений - вы потеряли. В результате у вас получилось неправильное определение "ошибки", ничего общего не имеющее с общепринятым.

Проиллюстрирую примером:

Код:
double d = 42.0;
switch (d)
{
case 42:;
}


Это код не содержит нарушений синтаксиса языка С. Однако он грубейше ошибочен. Он содержит нарушение constraint 6.4.8.2/1, которое говорит, что значение в `switch` обязано быть целочисленным.

Еще один пример:

Код:
void foo()
{
  goto abc;
}


Это код не содержит нарушений синтаксиса языка С. Однако он ошибочен. Он содержит нарушение constraint 6.8.6.1/1, которое говорит, что `goto` должно ссылаться на метку, располагающуюся в той же функции.

Еще один пример:

Код:
void bar()
{
  const int a = 42;
  ++a;
}


Это код не содержит нарушений синтаксиса языка С. Однако он ошибочен. Он содержит нарушение constraint 6.5.3.1/1, которое говорит, что операнд унарного `++` должен быть модифицируемым lvalue.

Да и в стандарте по ссылке приведен пример

Код:
{
  char i;
  int i;
}


Это код не содержит нарушений синтаксиса языка С. Ну вы поняли...

Теперь, надеюсь, вам понятно, что ваше "ошибка - это недопускаемое синтаксисом языка действие" - это не более чем детский лепет?

Давайте теперь составим настоящие, серьезные определения общепринятых понятий "ошибка" и "варнинг":

"Ошибка" - это нарушение синтаксиса языка или нарушение ограничений (constraints) языка. Язык С требует, чтобы при возникновении "ошибки" компиляторы выдавали диагностическое сообщение. Язык С не накладывает никаких требований на формат этого диагностического сообщения (не требуется наличия в нем слова "error"). Язык С не требует прекращения трансляции поле выдачи такого сообщения.

"Варнинг" - это все остальное. Это когда компилятор решил проинформировать пользователя о каких-то странностях в коде, для которых стандарт языка НЕ требует выдачи диагностического сообщения. Например, нарушение семантических правил языка. Или, например, код, вызывающий неопределенное поведение. Такие ситуации формально не требуют никакой диагностики со стороны компилятора. Однако продвинутый компилятор имеет право обнаруживать такие отклонения и сообщать о них дополнительными диагностическими сообщениями. Как и имеет право сообщать вообще о чем угодно.

Вот это то, что общепринято называется "ошибками" и "варнингами".

---

А теперь, когда мы ввели правильные определения, я еще раз повторю тот ключевой момент, который я уже приводил выше:

Среди малоопытных пользователей языка С существует наивное верование, что вышеприведенное деление на "ошибки" и "варнинги" совпадает с делением на сообщения вида "error: ..." (с прекращением трансляции) и "warning: ..." (без прекращения трансляции) в современных мейнстримовых компиляторах С. Это не так. Практически все мейнстримовые компиляторы С в конфигурации по умолчанию рапортуют некоторые "ошибки" через сообщения типа "warning: ...". Это зачастую приводит к тому, что малоопытные пользователи неверно полагают, что перед ними "варнинг", когда на самом деле пред ними "ошибка", отрапортованная со словом "warning".

Например

Код:
int main()
{
  int *a = 0;
  char *b = a;
}


Перед вами грубое нарушение constraints 6.5.16.1/1, то есть то, что общепринято называется "ошибкой". Однако компилятор GCC в конфигурации по умолчанию отреагирует на этот код сообщением со словом "warning...".

То есть некоторые сообщения "warning..." являются "варнингами", а некоторые сообщения "warning..." являются "ошибками" с точки зрения языка С.

Как вы сами понимаете (и как я говорил выше) в такой ситуации отличить первые от вторых можно
1) на основе знания стандарта языка С, или
2) путем "затягивания гаек" в настройках компилятора, чтобы заставить компилятор рапортовать "ошибки" как "error...".

Но малоопытные пользователи языка С, как правило, не умеют ни того, ни другого. В результате рождаются мифы об "отсутствии контроля типов в С" и прочая подобная чушь.

Re: Нескольно простых вопросов о программировании AVR на Си.

Вс мар 29, 2020 09:10:47

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

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

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

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

приведенные вами примеры, естественно, понятны, но я никогда и не мог себе представить, что они демонстрируют разные требования языка! для меня все это является тем или иным проявлением синтаксиса Си!

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

изготовители компиляторов, видя в стандартно нестрогой типизации Си источник проблем (хотя как посмотреть - в том же паскале процесс идет в обратном направлении - прочь от строгой типизации), вводят в свои компиляторы опции, позволяющие как-то ситуацию скорректировать, отсюда и варнинги и, порой, прекращение сборки, как при ошибке. но это полумеры все равно. если все эти "расширения" отключить в Си и паскале, мы получим то самое, что я и говорил: алгоритм "буква в букву" переписаный с Си на паскаль с вероятностью более 80% компилятором паскаля не будет собран именно по причине более строгой типизации паскаля.

собственно, только об этом я и говорил всю дорогу.

и ссылки на безусловных авторитетов типа Кернигана, Страуструпа или кого-либо еще с рассужденими на тему строгости типизации языков программирования вы так и не привели. зато многократно упоминали, что частные мнения не должны быть основополагающими... однако, собственное частное мнение в качестве аргумента, тем не менее, приводите.

Добавлено after 7 minutes 48 seconds:
KorbenDallas писал(а):Как вы сами понимаете (и как я говорил выше) в такой ситуации отличить первые от вторых можно
1) на основе знания стандарта языка С, или
2) путем "затягивания гаек" в настройках компилятора, чтобы заставить компилятор рапортовать "ошибки" как "error...".

Но малоопытные пользователи языка С, как правило, не умеют ни того, ни другого. В результате рождаются мифы об "отсутствии контроля типов в С" и прочая подобная чушь.
как бы отличить ошибку просто: проект не собирается. или вы можете привести пример, как можно получить exe-шник для кода, в котором имеются участки, вами же приведенные в качестве ошибок "ограничений"?

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

Добавлено after 1 minute 12 seconds:
и да, в Си имеется контроль типов. обратного я не утверждал.

Re: Нескольно простых вопросов о программировании AVR на Си.

Вс мар 29, 2020 10:19:30

KorbenDallas писал(а):Как вы сами понимаете (и как я говорил выше) в такой ситуации отличить первые от вторых можно
1) на основе знания стандарта языка С, или
2) путем "затягивания гаек" в настройках компилятора, чтобы заставить компилятор рапортовать "ошибки" как "error...".

Но малоопытные пользователи языка С, как правило, не умеют ни того, ни другого. В результате рождаются мифы об "отсутствии контроля типов в С" и прочая подобная чушь.
как бы отличить ошибку просто: проект не собирается. или вы можете привести пример, как можно получить exe-шник для кода, в котором имеются участки, вами же приведенные в качестве ошибок "ограничений"?


Вы, я вижу, невнимательно читали то, что я написал. Я уже приводил пример такого кода, когда компилятор выдает "warning" на то, что на самом деле является "ошибкой" согласно приводившемуся определению. При этом компилятор продолжает компиляцию и порождает выполнимый файл.

Пожалуйста еще раз

Код:
#include <stdio.h>
int main(void)
{
   const int *a = "Hello World";
   char *b = a;
   printf("%s\n", b);
}


Такой код прекрасно "скомпилируется" компилятором GCC (в конфигурации по умолчанию) с диагностическими сообщениями типа "warning". Получится выполнимый файл, который имеет "ожидаемое" поведение (с "пионэрской" точки зрения).

В реальности все эти диагностические сообщения являются сообщениями об "ошибках". Данная программа содержит constraint violations и не является валидной программой на языке С. Эта программа ошибочна, т.е. содержит именно "ошибки" как мы определили их раньше. Спецификация языка С не признает эту программу программой на языке С и не дает никаких гарантий ее поведения. Поведение этой программы - целиком и полностью самодеятельность вашего компилятора, к языку С никакого отношения не имеющая.

что касаестся гаек, и тут не соглашусь. если все варнинги превратить в ошибки (т.е. запретить сборку абсолютно в таких ситуациях)


О чем вы? Почему вдруг зашла речь о "все варнинги превратить в ошибки"? Кто сказал про "все"? Все не надо! Речь идет лишь о том, что часть диагностических сообщений типа "warning" является на самом деле "ошибками". Просто на них решили смотреть сквозь пальцы ради совместимости с унаследованным нестандартным/достандартным кодом. Речь идет, повторяю, лишь о части сообщений "warning" - именно и только о тех, которые на самом деле являются "ошибками".

Приведу пример

Код:
void foo(char *p, struct S *q)
{
  int a = p;
}


Компиляция такого кода в GCC в конфигурации по умолчанию приведет к выдаче двух диагностических сообщений:

Код:
warning: 'struct S' declared inside parameter list will not be visible outside of this definition or declaration
warning: initialization of 'int' from 'char *' makes integer from pointer without a cast [-Wint-conversion]


Как видите, оба сообщения представлены как "warning". И первое сообщение действительно не является "ошибкой" - это именно честный "варнинг". Стандарт языка не имеет никаких претензий к тому, что тип `struct S` объявлен в списке параметров. Однако это странная ситуация, которая в 99 случаев из 100 является ненамеренной и ставит в тупик малоопытных пользователей языка. Поэтому компилятор, чисто по собственной инициативе, решил на всякий случай выдать здесь "warning". Это действительно классический "варнинг", как мы его определили раньше.

А вот второе сообщение является именно "ошибкой". Полноценной, грубой, во все воронье горло, "ошибкой".

Компилятор GCC специально предоставляет вам ключ `-pedantic-errors`, который заставляет его рапортовать "ошибки", как "error". Этот ключ не превращает "все варнинги в ошибки" (!), а лишь те, которые на самом деле являются "ошибками" с точки зрения нашего определения. Попробуем скомпилировать тот же код с `-pedantic-errors`. И получим

Код:
warning: 'struct S' declared inside parameter list will not be visible outside of this definition or declaration
error: initialization of 'int' from 'char *' makes integer from pointer without a cast [-Wint-conversion]


Как видите, первый "warning" остался "warning", потому что это действительно просто "варнинг". А вот второй "warning" превратился в "error", потому что он всегда был именно "ошибкой". При этом, как видите, тут нет никакого "все варнинги превратить в ошибки". Об этом речи не идет и никогда не шло.

В режиме `-pedantic-errors` компилятор GCC старается соответствовать нашему определению "ошибки" и "варнинга" и рапортовать первые как "error", а вторые как "warning". Это функциональность еще далека от идеала, но она в принципе существует.

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


Отнюдь. В профессиональном программировании уже давно существует практика бить по руками за внесение "ошибок" в код, даже если GCC соглашается такой код компилировать. За "warning" принято наказывать так же как и за "error". Вы не увидите нигде в современных проектах

Код:
const int *a = "Hello World";
char *b = a;


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

Код:
const int *a = (const int *) "Hello World";
char *b = (char *) a;


то есть использует явное приведение типа, что сделает код формально корректным с точки зрения языка С. Вот и все.

А примеры с "Линуксом и Виндосвом" не вполне корректны. Для написания ОС на каком то уровне придется взаимодействовать с сущностями, которые никак не вписываются в концепции С. Другим словами, невозможно написать ОС на С. Можно лишь использовать компилятор С как удобное средство интеграции компонентов, многие из которых к С не имеют никакого отношения.

Re: Нескольно простых вопросов о программировании AVR на Си.

Вс мар 29, 2020 10:29:31

Вся проблема лишь в том, что я не считаю ошибкой то, что лишь соответствует формальному определению. Ошибка - это непреодолимое препятствие на пути сборки проекта. Если разработчики стандарта почему-то решили, что можно делать то, что нельзя, я в этом не виноват. Но если проект собирается, значит, препятствий тому в коде нет, то есть код, возможно, и кривой, но должен называться "не содержащий ошибок".

Ещё раз: любое определение верно тогда, когда исключений из него нет или их пренебрежимо мало. Ещё раз: стандарт Си содержит массу исключений из оговоренных им же требований. Исторически Си является лоскутным одеялом... Но это беспокоит лишь педантов.
Если из стандарта Си убрать противоречия, это будет уже не Си...

Однако, на сегодня Си остаётся языком с не сильной типизацией...

Re: Нескольно простых вопросов о программировании AVR на Си.

Чт май 21, 2020 18:13:46

Привет всем! Мужики, прошу прощения, что я дебил в МК и Си полный (как и вообще по жизни тоже). )))
Пните в нужном направлении пожалуйста.

Мне надо из массива выкидывать данные размером в 16 бит в порт последовательно. На один вывод. Для 595 регистров, которых 2 штуки включенных последовательно.

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

Понимаю, что надо тоже типа сдвигать, потом сравнивать с маской и делать тоже самое, но не понимаю.... :facepalm: :oops:

Re: Нескольно простых вопросов о программировании AVR на Си.

Чт май 21, 2020 18:28:43

Что-то вроде
Код:
void send(uint16_t data)
{
    for (uint8_t i = 0; i < 16; i++) {
        if (data & 0x0001) {
            PORTB |= (1<<3);    // set bit 1
        } else {
            PORTB &= ~(1<<3);   // set bit 0
        }
        // strob or something else
       
        data >> 1; // shift data
    }
}


https://godbolt.org/z/_KPHZU

А если надо передавать старший бит первым, то маска 0x8000 вместо 0x0001 и сдвигать вправо

Re: Нескольно простых вопросов о программировании AVR на Си.

Чт май 21, 2020 19:32:27

или аппаратно через SPI

Re: Нескольно простых вопросов о программировании AVR на Си.

Чт июн 04, 2020 11:13:28

Здравствуйте, подскажите пожалуйста как реализовать передачу данных по кадрам, в моей задаче каждый бит в байте является определенной командой и хотелось не посылать каждый раз 0b00000010 и 0b00000100 сразу 0b0000011 надеюсь понятно спросил.

Re: Нескольно простых вопросов о программировании AVR на Си.

Чт июн 04, 2020 11:55:26

Непонятно. Как из 0b00000010 и 0b00000100 получить 0b0000011, я вижу после объединения по ИЛИ получается 0b00000110. Каким кадрам, по какому интерфейсу ? Синхронно - асинхронно ?Ну сразу вспомнилось Чеховское "на деревню дедушке" .

Re: Нескольно простых вопросов о программировании AVR на Си.

Чт июн 04, 2020 12:32:14

Jack_A, передача через rs-232, преобразователь будет подключен по usart, длинна блока данных 8 бит, формат кадра: начало кадра (0х02 ), длинна данных ( включает контрольную сумму и конец), данные (8 бит), контрольная сумма, конец (0х33). Так вот данные из себя представляют таблицу в которой, по первому байту мы определяем какой режим, а последующие используются для передачи соответствующих команд.

Re: Нескольно простых вопросов о программировании AVR на Си.

Чт июн 04, 2020 12:57:53

Yarikkasl, если есть стартовый байт и поле длины данных, то байт "конца" пакета не требуется.

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

Re: Нескольно простых вопросов о программировании AVR на Си.

Чт июн 04, 2020 13:08:51

ARV, здравствуйте, это приложение из протокола обмена который не я придумал, о нужности байта конец не неизвестно, вопрос следующий у меня есть команда i и команда j они соответствуют разным байтам как мне их просто записать atmel, я просто не пойму как записать эти переменные в код.

Re: Нескольно простых вопросов о программировании AVR на Си.

Чт июн 04, 2020 13:11:23

Yarikkasl, вы вообще непонятно что спрашиваете.

Код:
#define i 0b00000010
#define j 0b00000100

Об этом что-ли речь?

Re: Нескольно простых вопросов о программировании AVR на Си.

Чт июн 04, 2020 13:26:48

NStorm, ARV, Jack_A, Изображение и ещё Изображение

Re: Нескольно простых вопросов о программировании AVR на Си.

Чт июн 04, 2020 13:31:25

Yarikkasl, во-первых фото практически нечитаемое. Во-вторых, оно нихрена не объясняет, что вы в итоге спрашиваете. В чем вопрос?! Нормально напишите что вы делаете и что не получается. В коде конкретно.

Re: Нескольно простых вопросов о программировании AVR на Си.

Чт июн 04, 2020 13:34:18

NStorm, да, но как их передать одним кадром, если я их передам разными то получается что значения команды то есть то нет. А не получается потому что ещё кода нет, есть кадр в нем 8 бит, и каждый бит это отдельная команда, вопрос как их одновременно отправить, если я переменные завел в виде как у вас в примере

Re: Нескольно простых вопросов о программировании AVR на Си.

Чт июн 04, 2020 13:44:06

Yarikkasl, каждый бит или байт отдельные команды?
Если каждый бит, то просто логическое или. Но под ваш текст выше это не подходит.

Добавлено after 2 minutes 37 seconds:
Блин! Ну понятно из-за чего никто вас не понимает:
0b00000010 и 0b00000100 сразу 0b0000011

А ничего, что в конце в нолик пропустили?
Вам из 0b00000010 и 0b00000100 нужно полувчить 0b00000110? Тогда ИЛИ делается вот так (0b00000010 | 0b00000100). И если заведены #define, как я выше написал, то можно писать (i | j).

Re: Нескольно простых вопросов о программировании AVR на Си.

Чт июн 04, 2020 13:45:09

NStorm, каждый бит, тоесть я просто посылаю побитно 8 команд и в зависимости от их состояния у меня либо 0 либо 1. Чуть стало понятнее спасибо!
Ответить