Вопросы по С/С++ (СИ)

Если ваш вопрос не влез ни в одну из вышеперечисленных тем, вам сюда.
Аватара пользователя
baron_P
Нашел транзистор. Понюхал.
Сообщения: 183
Зарегистрирован: Вт сен 14, 2010 23:07:10
Откуда: Ростов

Re: Вопросы по С/С++ (СИ)

Сообщение baron_P »

Спасибо.
Т.е. lcd_write(*str++) означает, что *str (символ-константа) отправится на обрабоку в функцию, а str (переменная-адрес) инкрементируется и идет дальше на проверку условия перед след. итераций цикла while. Тогда еще глупые вопросы: почему в функцию отправляется не конечный результат выражения *str++, а промежуточный (ведь все выражение стоит в качестве аргумента)?

const char *str - переменная-указатель на символ-константу
const *char str - указатель-константа на символ переменную
Логику в этих выражениях не уловил, постараюсь просто запомнить.

О состоянии str сложно написано, не понял в чем возможны проблемы с этим выражением. Я передаю *str в качестве аргумента функции lcd_write. Что она там с ним делает не важно - после выхода из функции эта переменная перестает существовать, ведь локальная. Внутри функции lcd_puts указатель str в явном виде инкрементирует и почему кто-то (кроме меня, еще не вкурившего возню с указателями) должен подумать, что там останется старое значение, непонятно. А вне функции lcd_puts переменная опять же бессмыслена, т.к. локальная.
Но еще один вопрос вызывает использование класса static. Он тут применен для фукнции которая ничего не возвращает (void же), зачем это сделано?
We do what we must because we can (c) GLaDOS
Реклама
Аватара пользователя
WiseLord
Друг Кота
Сообщения: 4905
Зарегистрирован: Чт апр 11, 2013 11:19:59
Откуда: Минск
Контактная информация:

Re: Вопросы по С/С++ (СИ)

Сообщение WiseLord »

baron_P писал(а):еще глупые вопросы: почему в функцию отправляется не конечный результат выражения *str++, а промежуточный (ведь все выражение стоит в качестве аргумента)?
Есть понятие преинкремента и постинкремента. Например:

Код: Выделить всё

char ch = 'a';
lcd_write(ch++);
lcd_write(ch);
- на дисплее отобразится "ab"

Код: Выделить всё

char ch = 'a';
lcd_write(++ch);
lcd_write(ch);
- на дисплее отобразится "bb"

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

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

Так и здесь - при lcd_write(*str++) в функцию отправится число, находящееся по старому адресу, и лишь потом адрес изменяется.
Реклама
uk8amk
Поставщик валерьянки для Кота
Сообщения: 2222
Зарегистрирован: Вт ноя 27, 2007 11:32:06
Откуда: Tashkent

Re: Вопросы по С/С++ (СИ)

Сообщение uk8amk »

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

К static функциям обращаться можно внутри одного файла-модуля C. Внешне они недоступны.
Аватара пользователя
Siarzhuk
Потрогал лапой паяльник
Сообщения: 353
Зарегистрирован: Вс янв 19, 2014 22:41:55

Re: Вопросы по С/С++ (СИ)

Сообщение Siarzhuk »

baron_P писал(а):Тогда еще глупые вопросы: почему в функцию отправляется не конечный результат выражения *str++, а промежуточный (ведь все выражение стоит в качестве аргумента)?
Результат выражения и есть символ по исходному указателю. Инкремент-же указателя - можете считать "побочным" эффектом.
baron_P писал(а):const *char str - указатель-константа на символ переменную
Окровенно говоря тип "*char" встречаю впервые - хотелось бы развёрнутый пример применения. Подозреваю имелось ввиду char* const str - т.е. указатель-константа - который нельзя изменить в отличие от переменной-указателя на константу менять который можно.

Попробую пояснить кодом:

Код: Выделить всё

const char* str1 = "const char* str1\n"; // указатель-переменная
const char* str2 = "const char* str2\n"; // указатель-переменная
char* const cstr = "const char* cstr\n"; //  указатель-константа


int _tmain(int argc, _TCHAR* argv[])
{
	printf(str1);
	printf(str2);
	printf(cstr);

	str2 = str1; // можно присваивать
	printf(str2);

	str1 = cstr; // можно присваивать
	printf(str1);

	cstr = "just a char*"; // error C3892: 'cstr' : you cannot assign to a variable that is const
	printf(cstr);

	return 0;
}
baron_P писал(а):Внутри функции lcd_puts указатель str в явном виде инкрементирует и почему кто-то (кроме меня, еще не вкурившего возню с указателями) должен подумать, что там останется старое значение, непонятно.
Вы инструктаж по технике безопасности регулярно проходите? Тем не менее никого не удивляет что ноги-руки-пальцы регулярно отрезаются, отвёртки-молотки пробивают-калечат подушечки пальцев а киловольты исправно жгут тела нарушителей ТБ. Вот и считайте это своего рода инструктажем. Долгоживущий код имеет тенденцию к разбуханию и функция в процессе разработки и поддержки может принимать такие причудливые формы что то, что было очевидно исходному писателю просто теряется в глубине спагетти-кода. Разобрать даже собственный код по прошествии времени требует усилий - потому как оно всегда и бывает - лепим по быстрому, пробный прогон - косяк, отладчик - мать-мать-мать - минус десять минут жизни коорые можно было сохранить соблюдая правила. Посему если оптимизация не довлеет - писать нужно ясно и понятно и не только для себя самого.
baron_P писал(а):Но еще один вопрос вызывает использование класса static. Он тут применен для фукнции которая ничего не возвращает (void же), зачем это сделано?
Класс static для глобальных объектов - это гарантия того, что нигде кроме как в этом модуле этот объект не используется - в общем такая-же "техника безопасности".
Одновременным нажатием LIGHT и POWER, РП Sangean ATS-909X (ver 1.29) превращается в ATS-909XR! ;-)
Реклама
Эиком - электронные компоненты и радиодетали
Аватара пользователя
baron_P
Нашел транзистор. Понюхал.
Сообщения: 183
Зарегистрирован: Вт сен 14, 2010 23:07:10
Откуда: Ростов

Re: Вопросы по С/С++ (СИ)

Сообщение baron_P »

Спасибо.

Не сразу дошло, что *str++ и *(str+1) не одно и то же.

По static-функциям вроде понятно.

А с костантами-указателями путаница.
const char *str;
const char* str;
Это одно и тоже или нет? К чему тут применяется разыменование указателя?

Код: Выделить всё

const char* str1 = "const char* str1\n";
Я правильно понял, что здесь str1 - это адрес первого элемента строки и *srt1 == 'c'?
Т.е. *str1 это константа, а str1 - переменная, за счет изменения которой я и могу считать несколько констант, составляющих строку?

Код: Выделить всё

char* const cstr = "const char* cstr\n";
А здесь костанты две - cstr и *cstr. Т.е. я не могу ни изменить записанный в cstr адрес, ни поменять записанный в *cstr символ. Так?
We do what we must because we can (c) GLaDOS
Реклама
Аватара пользователя
GARMIN
Держит паяльник хвостом
Сообщения: 954
Зарегистрирован: Вс дек 02, 2012 16:58:33
Откуда: от туда
Контактная информация:

Re: Вопросы по С/С++ (СИ)

Сообщение GARMIN »

baron_P писал(а): const char *str;
const char* str;
Это одно и тоже или нет? К чему тут применяется разыменование указателя?
Это одно и то же. Первая запись более правильная, так как указывает, что str является указателем.
Как пример, показывающий разницу в использовании:
const char* str1, str2;
const char *str1, str2;
в первом примере не видно, что str2 не указатель, а символ.
baron_P писал(а):

Код: Выделить всё

const char* str1 = "const char* str1\n";
Я правильно понял, что здесь str1 - это адрес первого элемента строки и *srt1 == 'c'?
Т.е. *str1 это константа, а str1 - переменная, за счет изменения которой я и могу считать несколько констант, составляющих строку?

Код: Выделить всё

char* const cstr = "const char* cstr\n";
А здесь костанты две - cstr и *cstr. Т.е. я не могу ни изменить записанный в cstr адрес, ни поменять записанный в *cstr символ. Так?
здесь есть строка без имени "const char* cstr\n", расположенная где-то в памяти, и константный указатель на неё cstr. Строка может располагаться в ОЗУ, тогда её можно изменить. Явно это не указывается, зависит от контекста.
Реклама
Аватара пользователя
Siarzhuk
Потрогал лапой паяльник
Сообщения: 353
Зарегистрирован: Вс янв 19, 2014 22:41:55

Re: Вопросы по С/С++ (СИ)

Сообщение Siarzhuk »

baron_P писал(а):А с костантами-указателями путаница.
const char *str;
const char* str;
Это одно и тоже или нет? К чему тут применяется разыменование указателя?
Это лишь разные стили объявления одного и того-же типа переменной - указателя на неизменяемые данные. И как водится по поводу положения этой звёздочки идут бесконечные битвы остроконечников с тупоконечниками - одни говорят, что звёздочка принадлежит типу и кошернее писать "char*" а другие моментально двигают им под нос вот такой код:

Код: Выделить всё

const char* str2 = "str2", str3 = "str3";  // ошибка!
const char* str2 = "str2", *str3 = "str3"; // ОК - но некрасиво-же!
const char *str2 = "str2", *str3 = "str3"; // И ОК и красиво!
После чего обычно приходит местный проектенфюрер и прописывает в кодестайл гайдлайнах проекта тот стиль, что ему больше нравится.
baron_P писал(а):str1 - переменная, за счет изменения которой я и могу считать несколько констант, составляющих строку?
Да
baron_P писал(а):

Код: Выделить всё

char* const cstr = "const char* cstr\n";
А здесь костанты две - cstr и *cstr. Т.е. я не могу ни изменить записанный в cstr адрес, ни поменять записанный в *cstr символ. Так?
Верно. Мне пожалуй стоило сразу сделать свой пример нагляднее добавлением вот такого такого копирования:

Код: Выделить всё


char data[10] = { 0 };
char* const cstr = data;

[...]

 for (int i = 0; i < 9; i++)
    cstr[i] = str1[i]; // ага.. работает!

  cstr = "just a char*"; // error C3892: 'cstr' : you cannot assign to a variable that is const
PS: Поправил выделение cstr. :-)
Одновременным нажатием LIGHT и POWER, РП Sangean ATS-909X (ver 1.29) превращается в ATS-909XR! ;-)
Аватара пользователя
baron_P
Нашел транзистор. Понюхал.
Сообщения: 183
Зарегистрирован: Вт сен 14, 2010 23:07:10
Откуда: Ростов

Re: Вопросы по С/С++ (СИ)

Сообщение baron_P »

Спасибо, вроде понял, но ваше дополнение вызвало к жизни еще пару вопросов.

Код: Выделить всё

for (int i = 0; i < 9; i++)
    cstr[i] = str1[i]; // ага.. работает!
А почему работает? cstr и str1 это ведть не массивы и не строки, а только только одно число (адрес первого сивола соотв. строки). Разве можно к не заданному явно массиву обращаться по индексам?

Код: Выделить всё

cstr = "just a char*"
Я правильно понял, что тут, если б cstr не было константой, в него б записался адрес первого символа строки?
We do what we must because we can (c) GLaDOS
Аватара пользователя
Siarzhuk
Потрогал лапой паяльник
Сообщения: 353
Зарегистрирован: Вс янв 19, 2014 22:41:55

Re: Вопросы по С/С++ (СИ)

Сообщение Siarzhuk »

baron_P писал(а):Спасибо, вроде понял, но ваше дополнение вызвало к жизни еще пару вопросов.

Код: Выделить всё

for (int i = 0; i < 9; i++)
    cstr[i] = str1[i]; // ага.. работает!
А почему работает? cstr и str1 это ведть не массивы и не строки, а только только одно число (адрес первого сивола соотв. строки). Разве можно к не заданному явно массиву обращаться по индексам?
Указатель хранит лишь адрес им указываемого объекта. cstr и str1 - это соответственно константа и переменная, хранящие адрес на первые байты соответствующих данных. Содержимое по указателю появляется лишь как результат разыменования указателя. Тип указателя (char*, uint16_t*, uint32_t*) определяет размер элемента в виртуальном массиве на начало которого и указывает указатель. Т.е. к нему равноправно можно обращаться как разыменованием так и доступом а-ля массив [индекс]. Индекс массива - суть смещение от начала массива. Это смещение - на размер элемента!

Код: Выделить всё

uint32_t  data = 0x11223344;
uint16_t* p =  (uint16_t*)&data;

// оба вызовы равнозначны - выведут "верхнее" и "нижнее" слова - нюансы endian-ности опустим для простоты ;-)
pritnf( "%04x %04x\n", p[0], p[1]);
 pritnf( "%04x %04x\n", *p, *(p + 1));
baron_P писал(а):Я правильно понял, что тут, если б cstr не было константой, в него б записался адрес первого символа строки?
Разъяснение смотрите выше. Указатель хранит лишь адрес указуемого объекта.
Одновременным нажатием LIGHT и POWER, РП Sangean ATS-909X (ver 1.29) превращается в ATS-909XR! ;-)
Аватара пользователя
WiseLord
Друг Кота
Сообщения: 4905
Зарегистрирован: Чт апр 11, 2013 11:19:59
Откуда: Минск
Контактная информация:

Re: Вопросы по С/С++ (СИ)

Сообщение WiseLord »

Siarzhuk писал(а):cstr и str1 это ведть не массивы и не строки, а только только одно число
cstr - это не число, это указатель. Указатель константый, то есть он измениться не может. Поэтому ему нельзя присвоить значение, он может указывать только в одно место, т.е. хранить постоянный адрес. Но данные, на которые он указывает, менять никто не мешает.

А по поводу обращения по индексам - запись a абсолютно эквивалентна записи *(a + i); Обращаться можно как угодно.
Например, код

Код: Выделить всё

char a;
char *b;
b = &a;
a = b[7];
вполне допустим. В результате в a будет записано значение, которое раньше было в байте на 7 ячеек правее.

Другое дело, что на компьютере такая программа хоть и скомпилируется, но, скорее всего, будет завершаться с ошибкой, так как операционная система обычно отслеживает, чтобы программа не копалась в областях памяти, которые выделены не ней, и прибивает её в таких случаях.
Аватара пользователя
baron_P
Нашел транзистор. Понюхал.
Сообщения: 183
Зарегистрирован: Вт сен 14, 2010 23:07:10
Откуда: Ростов

Re: Вопросы по С/С++ (СИ)

Сообщение baron_P »

Спасибо, кажется понял.
До разъяснения думал, что указатель все же хранит некоторое число, которое и есть адрес в памяти той ячейки, на которую он указывает. И с этим числом можно работать как с обычной переменной соотвествующего типа. Но все оказалось таки сложнее.
We do what we must because we can (c) GLaDOS
Аватара пользователя
Siarzhuk
Потрогал лапой паяльник
Сообщения: 353
Зарегистрирован: Вс янв 19, 2014 22:41:55

Re: Вопросы по С/С++ (СИ)

Сообщение Siarzhuk »

WiseLord писал(а):

Код: Выделить всё

char a;
char *b;
b = &a;
a = b[7];
вполне допустим. В результате в a будет записано значение, которое раньше было в байте на 7 ячеек правее.

Другое дело, что на компьютере такая программа хоть и скомпилируется, но, скорее всего, будет завершаться с ошибкой, так как операционная система обычно отслеживает, чтобы программа не копалась в областях памяти, которые выделены не ней, и прибивает её в таких случаях.
Позволю себе дополнить - если переменная 'а' выделена локально на стеке функции - при чтении ничего страшного не произойдёт, при записи - новое значение уйдёт в одну из переменных которая была объявлена за 'b' (если компилятор их не перетасовал) либо просто в неиспользуемую часть стека функции. При выделении в глобальной области - изменится одна из соседних переменных, либо, если 'a' выделена буквально в самом конце сегмента данных и в 7-ми байтах "правее" находится невыделенная область памяти - произойдёт исключение нарушения доступа - обработчик которого и завершит программу. Но всё конечно же зависит от конкретной архитектуры и того куда эту переменную "поселил" компилятор.
Одновременным нажатием LIGHT и POWER, РП Sangean ATS-909X (ver 1.29) превращается в ATS-909XR! ;-)
Аватара пользователя
Siarzhuk
Потрогал лапой паяльник
Сообщения: 353
Зарегистрирован: Вс янв 19, 2014 22:41:55

Re: Вопросы по С/С++ (СИ)

Сообщение Siarzhuk »

Что-то мы плохо разъясняем. ;-)
baron_P писал(а):До разъяснения думал, что указатель все же хранит некоторое число, которое и есть адрес в памяти той ячейки, на которую он указывает.
Именно так - там хранится адрес.
baron_P писал(а): И с этим числом можно работать как с обычной переменной соотвествующего типа.
Именно так © - лишь компилятор будет ворчать на несоответствия типов. Это число N можно использовать чтобы "отсчитать" N-ое количество байт от начала памяти и прочитать содержимое указуемой переменной:

Код: Выделить всё

int a = 1234567890;
int* pa = &a;

char* p = (char*)0; // указатель на "начало" памяти ;-)
char* pc = &p[(int)pa]; // используем 'pa' - как индекс в памяти, приведение типа - чтобы компилятор не ругался
int* pi = (int*)pc; // мы ведь хотим прочитать оттуда не один байт ( т.е. sizeof(char)) а целых sizeof(int) байт, верно?

printf("%d или %d\n", *pa, *pi); // и что нам покажет smoke test? ;-)
Одновременным нажатием LIGHT и POWER, РП Sangean ATS-909X (ver 1.29) превращается в ATS-909XR! ;-)
Аватара пользователя
WiseLord
Друг Кота
Сообщения: 4905
Зарегистрирован: Чт апр 11, 2013 11:19:59
Откуда: Минск
Контактная информация:

Re: Вопросы по С/С++ (СИ)

Сообщение WiseLord »

baron_P писал(а):До разъяснения думал, что указатель все же хранит некоторое число, которое и есть адрес в памяти той ячейки, на которую он указывает. И с этим числом можно работать как с обычной переменной соотвествующего типа.
Так и есть. Указатель - это именно число, которое есть некий адрес, на который он указывает. Но с этим числом-указателем можно работать только в ограниченном диапазоне операций - например, прибавлять-отнимать некое значение (перемещаться по памяти), разыменовывать указатель (*p - получить значение). Можно обращаться как к массиву (и получать значения из соседних с указываемой ячеек). Но нельзя, например, сложить два указателя, или умножить на что-то.

В AVR8 любой указатель занимает 2 байта, не важно на что он указывает. Хоть на char, хоть на long long.

В машинах с архитектурой фон Неймана уже другие размеры указателей: в x86 - 32 байта, в x86_64 - 64 байта. Собственно, разрядность системы и определяет размер указателя.

Отсюда, кстати, и принципиальная невозможность тех же 32битных архитектур адресовать больше 4Гб памяти. Потому что 2³² = 4 294 967 296, и указатель в принципе не может хранить большее число. Если очень нужно - приходится извращаться и вводить дополнительные абстракции типа страниц памяти, и тому подобных.
Аватара пользователя
Chip115
Сверлит текстолит когтями
Сообщения: 1132
Зарегистрирован: Пт фев 16, 2007 14:18:20
Откуда: Новосибирск
Контактная информация:

Re: Вопросы по С/С++ (СИ)

Сообщение Chip115 »

Всем привет! Подскажите, пожалуйста, как реализовать не блокирующею функцию задержки.

В общем, вот псевдокод тела функции, которая, на ряду с остальными, вызывается в общем цикле

Код: Выделить всё

u8Funk_1 ();
vFunk_2(a);
vFunk_2(b);

delay_ms(1000);

u8Funk_1 ();
vFunk_2(a);
vFunk_2(c);

some_variable1 = 1;
some_variable2= 2;
some_variable3 = 3;
some_variable4 = 4;
Есть затык в виде delay_ms, но в этом месте надо выдержать 1 сек.
Нужна ф-ция, которая не вешает проц. ибо подобных мест в программе будет достаточно и если в каждой будем тупить, то будет плохо.
Можете подсказать решение проблемы?
В гугле пытался найти "не блокирующая функция задержки". Выдает всякую фигню.
Ставить FreeRTOS не охота.
Теория — это когда все известно, но ничего не работает. Практика — это когда все работает, но никто не знает почему. Мы же объединяем теорию и практику: ничего не работает… и никто не знает почему!
© Альберт Эйнштейн
Аватара пользователя
WiseLord
Друг Кота
Сообщения: 4905
Зарегистрирован: Чт апр 11, 2013 11:19:59
Откуда: Минск
Контактная информация:

Re: Вопросы по С/С++ (СИ)

Сообщение WiseLord »

Не совсем задача корректно поставлена, но попробую пояснить, как я подобное делал для термодатчика DS18B20.
Суть там в том, измерение он производит в течение 750мс. То есть, после команды на измерение надо выждать 750мс, и только тогда можно вызвать функцию, которая запросит результат у датчика.
Решение в лоб - такое:

Код: Выделить всё

measureTemp();
_delay_ms(750);
temp = getTemp();
Но возникает та же проблема, что Вы описали - МК висит в цикле ожидания. Не самое лучшее решение.

Идея обойти в этом - запустить функцию измерения температуры раз в 750мс по таймеру, по прерыванию. И тогда можно будет сделать такой набор функций:

Код: Выделить всё

updateTemp() { // вызывать не чаще раза в 750мс по таймеру
  _temp = getTemp(); // узнаём у датчика последнее значение температуры
  measureTemp(); // запускаем новое измерение
}
int getTemp() {
  return _temp;
}
А в основном цикле температуру узнавать как

Код: Выделить всё

temp = getTemp();
Главное - не вызывать updateTemp() чаще, чем надо.

Не знаю, для чего нужна задержка Вам, но принцип может быть похожим.
Аватара пользователя
ks0
Прорезались зубы
Сообщения: 238
Зарегистрирован: Чт фев 28, 2013 14:16:10

Re: Вопросы по С/С++ (СИ)

Сообщение ks0 »

Так а че процессору делать если не висеть в цикле? Вот конкретно в вашем примере?
Если у него есть другая работа, то используются таймеры или прерывания. Тогда и вся структура программы другая будет. И RTOS для таких простых МК нафиг не уперлась.
Аватара пользователя
uldemir
Друг Кота
Сообщения: 7360
Зарегистрирован: Пт авг 28, 2009 21:34:30
Откуда: 845-й км.

Re: Вопросы по С/С++ (СИ)

Сообщение uldemir »

Подскажите гуру, как убрать предупреждения компилятора.
В общем, пишу знакогенератор:

Код: Выделить всё

#define seg_a        ((uint8_t)(1<<7))
#define seg_b        ((uint8_t)(1<<5))
#define seg_c        ((uint8_t)(1<<3))
#define seg_d        ((uint8_t)(1<<0))
#define seg_e        ((uint8_t)(1<<1))
#define seg_f        ((uint8_t)(1<<6))
#define seg_g        ((uint8_t)(1<<4))
#define seg_h        ((uint8_t)(1<<2))
#include "chars7.c"
----теперь фрагмент инклюда:
#define    invertmask    ~
#define let_1 invertmask(seg_b | seg_c)
#define let_2 invertmask(seg_a|seg_b|seg_d|seg_e|seg_g)
---- итд.

const uint8_t charset[16] = {let_0, let_1, let_2, let_3, \
                                         let_4, let_5, let_6, let_7, \
                                         let_8, let_9, let_A, let_b, \
                                         let_c, let_d, let_E, let_  };
 
Собственно, проблема в предупреждении:

Код: Выделить всё

main.c(17): warning:  #69-D: integer conversion resulted in truncation  const uint8_t charset[16] = {let_0, let_1, let_2, let_3, \
В каком месте мне нужно поставить неопределённый артикль... Кхм, в каком месте тут всплывает int, если аргументы 8-ми разрядные, беззнаковые и операции побитные?
И очень не хотелось бы править инклюд, потому как этот инклюд, планируется использовать с любой шириной данных. В последнем проекте там был, вроде, вообще 32-х разрядный регистр.
Аватара пользователя
ks0
Прорезались зубы
Сообщения: 238
Зарегистрирован: Чт фев 28, 2013 14:16:10

Re: Вопросы по С/С++ (СИ)

Сообщение ks0 »

В #define invertmask он и всплывает
Аватара пользователя
uldemir
Друг Кота
Сообщения: 7360
Зарегистрирован: Пт авг 28, 2009 21:34:30
Откуда: 845-й км.

Re: Вопросы по С/С++ (СИ)

Сообщение uldemir »

Спасибо. Действительно, стоит убрать, как предупреждений нет. осталось придумать как побитно инвертировать, чтобы не было больно.
Ответить

Вернуться в «Разные вопросы по МК»