ZeroMemory vs xxx = { 0 };
От: Дарней Россия  
Дата: 11.06.04 12:05
Оценка:
Признаюсь честно, привык писать обнуление структур в таком стиле:

WNDCLASSEX wcx = { 0 };

Немного нагляднее и короче, чем ZeroMemory. Хоть это и небольшой хак
Интересно — может быть, есть какие-то аргументы против такой практики?
Всех излечит, исцелит
добрый Ctrl+Alt+Delete
Re: ZeroMemory vs xxx = { 0 };
От: Аноним  
Дата: 11.06.04 12:28
Оценка:
Здравствуйте, Дарней, Вы писали:

Д>Признаюсь честно, привык писать обнуление структур в таком стиле:


Д> WNDCLASSEX wcx = { 0 };


Д>Немного нагляднее и короче, чем ZeroMemory. Хоть это и небольшой хак

Д>Интересно — может быть, есть какие-то аргументы против такой практики?
Почему хак?
Re: ZeroMemory vs xxx = { 0 };
От: _FRED_ Черногория
Дата: 11.06.04 13:12
Оценка:
Здравствуйте, Дарней, Вы писали:

Д>Признаюсь честно, привык писать обнуление структур в таком стиле:

Д> WNDCLASSEX wcx = { 0 };
Д>Немного нагляднее и короче, чем ZeroMemory. Хоть это и небольшой хак
Д>Интересно — может быть, есть какие-то аргументы против такой практики?

Против не скажу, но мне достаточно того, что в своих книгах Рихтер нередко
именно к такому способ и обращается.
Help will always be given at Hogwarts to those who ask for it.
Re: ZeroMemory vs xxx = { 0 };
От: Kapany3 Россия  
Дата: 11.06.04 13:16
Оценка:
Здравствуйте, Дарней, Вы писали:

Д>Признаюсь честно, привык писать обнуление структур в таком стиле:


Д> WNDCLASSEX wcx = { 0 };


Д>Немного нагляднее и короче, чем ZeroMemory. Хоть это и небольшой хак

Д>Интересно — может быть, есть какие-то аргументы против такой практики?

Смотрим что генерит компилятор:


mov         dword ptr [ebp-7Ch],0
mov         ecx,1Dh
xor         eax,eax
lea         edi,[ebp-78h]
rep stos    dword ptr [edi]
stos        word ptr [edi]
stos        byte ptr [edi]


Т.е. просто напросто вставляется код, который нулями забивает участок памяти длиной размера структуры.

для вызова ZeroMemory вроде call будет, а потом примерно такой же код с начальными проверками на валидность указателей, а тут уже сразу известно что указатели валидны, т.е. получается что {0} будет немного быстрее.
Re[2]: ZeroMemory vs xxx = { 0 };
От: maq Россия http://www.maqdev.com
Дата: 11.06.04 14:05
Оценка:
K>для вызова ZeroMemory вроде call будет, а потом примерно такой же код с начальными проверками на валидность указателей, а тут уже сразу известно что указатели валидны, т.е. получается что {0} будет немного быстрее.

Вряд ли потому что ZeroMemory определен при помощи DEFINE как memset((Destination),0,(Length)),
а memset в свою очередь инлайнится компилятором.
... << Rsdn@Home 1.1.4 beta 1 >>
Re: ZeroMemory vs xxx = { 0 };
От: Аноним  
Дата: 12.06.04 01:46
Оценка:
Д>Признаюсь честно, привык писать обнуление структур в таком стиле:

Д> WNDCLASSEX wcx = { 0 };


Д>Немного нагляднее и короче, чем ZeroMemory. Хоть это и небольшой хак

Д>Интересно — может быть, есть какие-то аргументы против такой практики?
По стандарту, если хотя бы один элемент массива инициализирован, то все остальные элементы будут установлены в ноль (для встроенных типов). Для элементов структур — не знаю, но все-таки мне кажется, что запись выше должна инициализировать только первый элемент структуры, а ассемблерный код, что привели выше — по-моему это из серии "the standard is what my compiler supports!" То есть, генерируется код, который совсем не должен там быть, но это только мое ИМХО.

Лично мне такая запись не нравится по причине названной выше и очень не нравится, что Рихтер делает также (может заметили, что у него и другие ошибки есть?). Ричард Стивенс, чей уровень намного выше Рихтера никогда не позволяет себе так обнулять структуру — только через memset() и bzero() — и я также делаю как и он.

Лучше использовать ZeroMemory(), ну а если кого-то смущает, что это лишний вызов, то пусть переходят на ассемблер. Это не та часть кода где нужно считать циклы ЦПУ!
Re[2]: ZeroMemory vs xxx = { 0 };
От: Павел Кузнецов  
Дата: 12.06.04 03:04
Оценка:
> Д> WNDCLASSEX wcx = { 0 };
>
> Д>Немного нагляднее и короче, чем ZeroMemory. Хоть это и небольшой хак
> Д>Интересно — может быть, есть какие-то аргументы против такой практики?

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


Стандарт не разделяет инициализацию массивов и структур, а говорит об инициализации агрегатов вообще:

8.5.1/7 If there are fewer initializers in the list than there are members in the aggregate, then each member not explicitly initialized shall be value-initialized (8.5). [Example:

   struct S { int a; char* b; int c; };
   S ss = { 1, "asdf" };

initializes ss.a with 1, ss.b with "asdf", and ss.c with the value of an expression of the form
int(), that is, 0. ]


> Лучше использовать ZeroMemory()


Отнюдь, использовать ZeroMemory() значительно хуже: не для всех типов заполнение нулями создает сколько-нибудь "осмысленное" значение. Среди них как типы, определяемые пользователем, так и некоторые встроенные (например, плавающие или указатели на члены).
Posted via RSDN NNTP Server 1.9 alpha
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[3]: ZeroMemory vs xxx = { 0 };
От: Аноним  
Дата: 12.06.04 16:11
Оценка:
ПК>Отнюдь, использовать ZeroMemory() значительно хуже: не для всех типов заполнение нулями создает сколько-нибудь "осмысленное" значение. Среди них как типы, определяемые пользователем, так и некоторые встроенные (например, плавающие или указатели на члены).
Перечитайте еще раз основное сообщение и увидите, что речь шла изначально об обнулении структур (то есть о забитии участка памяти нулями а не об инициализации как таковой). Имелись в виду структуры с примитивными типами данных — char, short, int, long и их знаковые и беззнаковые версии. Никто НЕ будет инициализировать структуру с типами, определенными пользователем, через ZeroMemory(), и, более того, что делать если у такого типа нету конструктора по умолчанию? Правильно, нужно будет написать свой конструктор для "общей" структуры и там уже вызывать имеющийся конструктор, а имея хотя бы один конструктор, уже нельзя инициализировать структуру через фигурные скобки!

Такой подход, как Вы и сами знаете, порядком используется в сетевом коде — обнуляют просто структуру целиком а затем уже устанавливают нужные элементы, типы данных которых уже приведены выше, и где Вы там видели числа с правающей точкой не говоря уже о user-defined data types? Зачем лезть в "дебри ООП?"
Re[4]: ZeroMemory vs xxx = { 0 };
От: Lorenzo_LAMAS  
Дата: 12.06.04 16:30
Оценка:
Это тебе не мешало бы перечитать исходное сообщение, свое, и ответ Павла.
А когда говоришь про стандарт, старайся все таки на самом деле заглянуть в него, чтоб не сесть в лужу.
int аrr[10] = {};

Если следовать твоему "стандарту", то, видимо, никакой инициализации тут нет.
Кстати, для "примитивных" типов нулевое значение в какой-то реализации может быть вовсе не набором нулевых битов.
Of course, the code must be complete enough to compile and link.
Re[5]: ZeroMemory vs xxx = { 0 };
От: Аноним  
Дата: 12.06.04 16:54
Оценка:
L_L>Это тебе не мешало бы перечитать исходное сообщение, свое, и ответ Павла.
И что мне там перечитать? То, что мне Павел говорит, что нехорошо "инициализировать" обьекты через ZeroMemory()? Это я и так знаю.

L_L>А когда говоришь про стандарт, старайся все таки на самом деле заглянуть в него, чтоб не сесть в лужу.

L_L>
L_L>int аrr[10] = {}; 
L_L>

L_L>Если следовать твоему "стандарту", то, видимо, никакой инициализации тут нет.
Сейчас нет ни книги не компилятора под рукой. Приведи этот пункт, будь добр.

L_L>Кстати, для "примитивных" типов нулевое значение в какой-то реализации может быть вовсе не набором нулевых битов.

Это я тоже знаю. Я НЕ говорил о том, что при забитии структуры нулями, например char * будет NULL! Все что я сказал, это то, что участок памяти отведенной под структуру будет забит нулями! Все! Дальше уже дело программиста правильно установить этот указатель. Где ты вычитал, что я, якобы, говорил, что для примитивных типов значение ноль всегда соответсвует набору нулевых битов?
Re[4]: ZeroMemory vs xxx = { 0 };
От: Павел Кузнецов  
Дата: 12.06.04 17:07
Оценка:
> Имелись в виду структуры с примитивными типами данных — char, short, int, long и их знаковые и беззнаковые версии.

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

> Никто НЕ будет инициализировать структуру с типами, определенными пользователем, через ZeroMemory()


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

Но, вообще, дело не в том, что будут или не будут делать изначально. Член "неправильного" типа вполне может появиться позднее, при модификации структуры. И в этом случае memset покажет себя не с лучшей стороны.

> и, более того, что делать если у такого типа нету конструктора по умолчанию?


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

> Такой подход, как Вы и сами знаете, порядком используется в сетевом коде — обнуляют просто структуру целиком а затем уже устанавливают нужные элементы, типы данных которых уже приведены выше, и где Вы там видели числа с правающей точкой не говоря уже о user-defined data types?


Обнуление структур используется много где. И в том числе, например, в работе с API Windows, пример чего был в исходном сообщении, или в работе с DirectX. И там, и там в структурах присутствуют члены типа float.
Posted via RSDN NNTP Server 1.9 alpha
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[6]: ZeroMemory vs xxx = { 0 };
От: Павел Кузнецов  
Дата: 12.06.04 17:10
Оценка:
> Это я тоже знаю. Я НЕ говорил о том, что при забитии структуры нулями, например char * будет NULL!

Строго говоря, это относится не только к указателям, но и к целочисленным типам.

> Все что я сказал, это то, что участок памяти отведенной под структуру будет забит нулями! Все!


И каков смысл такого "обнуления"?
Posted via RSDN NNTP Server 1.9 alpha
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[5]: ZeroMemory vs xxx = { 0 };
От: Аноним  
Дата: 12.06.04 17:55
Оценка:
>> Имелись в виду структуры с примитивными типами данных — char, short, int, long и их знаковые и беззнаковые версии.
ПК>В оригинальном сообщении ничего не было о подобных ограничениях. В противном случае, приведи цитату, пожалуйста.
Давайте вначале разберемся с экземлярами классов, а до перечислений дойдем позже. Допустим у нас в структуре есть обьект класса. Теперь скажите, как часто в своей практике Вы инициализируете подобную структуру через фигурные скобки? Думаю, что все-таки не часто.

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

Более того, сам Дарней пишет, что "привык писать обнуление структур в таком-то виде," но в том-то и дело, что что значит "обнуление" для обьектов пользовательских типов? Согласитесь, этот низко-уровневый термин не очень-то применим к высоко-уровневым обьектам, что в свою очередь значит, обычное низко-уровневое забивание участка памяти нулями (и которое ни в коем случае НЕ является инициализацией). Так получается, что семантика этого низко-уровнего забивания памяти соответствует инициализации нулем для большинства примитивных типов, как Вы уже это и заметили.


>> Никто НЕ будет инициализировать структуру с типами, определенными пользователем, через ZeroMemory()

ПК>Структура или перечислимый тип — варианты типов, определяемых пользователем. Таким образом ты говоришь, что обнулять структуры с членами-структурами или перечислениями тоже никто не будет?
Согласитесь, класс — более высокоуровневая конструкция чем обычный enum. Что нам мешает забить этот enum нулями (даже если переменная после забития будет иметь illegal state) а затем установить уже легальное значение для этого перечисления?

ПК>Но, вообще, дело не в том, что будут или не будут делать изначально. Член "неправильного" типа вполне может появиться позднее, при модификации структуры. И в этом случае memset покажет себя не с лучшей стороны.

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

>> и, более того, что делать если у такого типа нету конструктора по умолчанию?

ПК>Тем более хороший аргумент. В случае использования инициализатора компилятор сразу сообщит о неверном "обнулении". В случае же с memset, тебе придется полагаться на провидение.
Да никакого провидения! Это ошибка если используется memset() на "неправильном" типе.

>> Такой подход, как Вы и сами знаете, порядком используется в сетевом коде — обнуляют просто структуру целиком а затем уже устанавливают нужные элементы, типы данных которых уже приведены выше, и где Вы там видели числа с правающей точкой не говоря уже о user-defined data types?


ПК>Обнуление структур используется много где. И в том числе, например, в работе с API Windows, пример чего был в исходном сообщении, или в работе с DirectX. И там, и там в структурах присутствуют члены типа float.

Ага! Про ДиректХ ничего не знаю, но позволю себе заметить, что НИ в одной структуре Вин АПИ нету обьектов классов! Поэтому можно и использовать memset(). На счет флоутов — Вы уверены, что ноль флоута не является нулевым набором битов для Интеловской архитектуры? Или может быть все-таки в некоторых вызовах значения этих флоутов не принимаются во внимание?

Уфф... Чувствую себя так, словно lawyer проводящий защиту! Перечитывать уже не хочу ничего.
Re[7]: ZeroMemory vs xxx = { 0 };
От: Аноним  
Дата: 12.06.04 18:11
Оценка:
ПК>Строго говоря, это относится не только к указателям, но и к целочисленным типам.
Раз уж мы вдались в дебри юриспруденции языков программирования, то можете привести ссылку на этот "факт?" Насколько я знаю, на всех машинах int 0 будет всегда нулевым набором бит (другое дело разница между big-endian и little-endian архитектурами, но это совсем не то). 0 float или double может не быть нулевым набором бит (нужно читать стандарт IEEE 754 ) точно также как 0 void * может не быть нулевым набором бит.

>> Все что я сказал, это то, что участок памяти отведенной под структуру будет забит нулями! Все!

ПК>И каков смысл такого "обнуления"?
Смысл в том, что для многих встроенных типов семантика такого обнуления равна семантике инициализации нулем. Вместого того, чтобы писать = 0; для кучи переменных типа int проще сразу забить всю структуру нулями а затем уже подкоректировать "некоторые оставшиеся" переменные, дав им нужные значения. Все! Это я и имел в виду!
Re[5]: ZeroMemory vs xxx = { 0 };
От: Аноним  
Дата: 12.06.04 18:17
Оценка:
L_L>А когда говоришь про стандарт, старайся все таки на самом деле заглянуть в него, чтоб не сесть в лужу.
L_L>
L_L>int аrr[10] = {}; 
L_L>

L_L>Если следовать твоему "стандарту", то, видимо, никакой инициализации тут нет.
Ок. Должен частично признать свою ошибку. gcc скомпилировал этот код без проблем, и все элементы действительно были равны нулю. Visual C++ 6.0 SP 5 отказался компилировать. Поставив один элемент между скобками, код скомпилировался. Нулевой элемент был равен установленому значению, а все оставшиеся были равны нулю.
Re[5]: ZeroMemory vs xxx = { 0 };
От: Шахтер Интернет  
Дата: 12.06.04 19:26
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>Обнуление структур используется много где. И в том числе, например, в работе с API Windows, пример чего был в исходном сообщении, или в работе с DirectX. И там, и там в структурах присутствуют члены типа float.


Ничего страшного. И float и double пердставляют положительный нуль нулевой цепочкой байт. Это, конечно, особенность архитектуры, но эта особенность соответствует стандарту IEEE 754.
Вообще, с чисто практической точки зрения системы, где фундаментальные типы данных имеют нулевые значения, представимые ненулевой цепочкой байт, менее юзабельны. Я, кстати, ни одной современной такой системы не знаю.

Ну, например. Перед началом программы, все статические неинициализировные явно переменные должны быть обнулены. Проще всего это сделать, собрав их в одну секцию и натравив на неё memset.
Типично, компилятор собирает такие переменные в секцию .bss. Эта секция не имеет тела -- соответственно, не занимает памяти в объектнике/экзешнике, не нужно грузить её в память при загрузке и.т.п.
Просто стартап-код вытирает её в начале работы программы -- и всё. Просто, эффективно. Ну а поскольку человек -- существо ленивое ... Вообщем лень -- двигатель не только прогресса, но и стандартизации.
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[6]: ZeroMemory vs xxx = { 0 };
От: Павел Кузнецов  
Дата: 12.06.04 19:37
Оценка:
> ПК> дело не в том, что будут или не будут делать изначально. Член "неправильного" типа вполне может появиться позднее, при модификации структуры. И в этом случае memset покажет себя не с лучшей стороны.

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


Ч.т.д.: "обнуление" с помощью memset в ряде случаев некорректно, при этом "обнуление" инициализатором либо делает, что ожидается, либо приводит к ошибке компиляции. Что непонятно-то?

> ПК>Тем более хороший аргумент. В случае использования инициализатора компилятор сразу сообщит о неверном "обнулении". В случае же с memset, тебе придется полагаться на провидение.


> Да никакого провидения! Это ошибка если используется memset() на "неправильном" типе.


Только никто об этой "ошибке" тебе не скажет. Например: была когда-то структура, в которой, скажем, был член const char*, и кто-то "обнулял" ее с помощью memset, а впоследствии член был заменен, скажем, на std::string.

> НИ в одной структуре Вин АПИ нету обьектов классов! Поэтому можно и использовать memset().


Хорошо, уговорил. Используй Только другим не навязывай.

> На счет флоутов — Вы уверены, что ноль флоута не является нулевым набором битов для Интеловской архитектуры? Или может быть все-таки в некоторых вызовах значения этих флоутов не принимаются во внимание?


А причем здесь Интел? Я тебе просто привел относительно типичные API, где часто используется "обнуление". Просто в ответ на твоё заявление о том, что, дескать, такой код используется в каком-то сетевом коде. Такая же петрушка есть на любой платформе, в т.ч. и на тех, где побайтное обнуление для плавающих типов не означает ничего осмысленного.
Posted via RSDN NNTP Server 1.9 alpha
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[6]: ZeroMemory vs xxx = { 0 };
От: Павел Кузнецов  
Дата: 12.06.04 19:39
Оценка:
> Ничего страшного. И float и double пердставляют положительный нуль нулевой цепочкой байт. Это, конечно, особенность архитектуры, но эта особенность соответствует стандарту IEEE 754.

Да, конечно. Никто и не утверждал обратного.
Posted via RSDN NNTP Server 1.9 alpha
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[8]: ZeroMemory vs xxx = { 0 };
От: Шахтер Интернет  
Дата: 12.06.04 19:46
Оценка:
Здравствуйте, <Аноним>, Вы писали:

ПК>>Строго говоря, это относится не только к указателям, но и к целочисленным типам.

А>Раз уж мы вдались в дебри юриспруденции языков программирования, то можете привести ссылку на этот "факт?" Насколько я знаю, на всех машинах int 0 будет всегда нулевым набором бит (другое дело разница между big-endian и little-endian архитектурами, но это совсем не то). 0 float или double может не быть нулевым набором бит (нужно читать стандарт IEEE 754 )

Будет. Вот по IEEE 754 и будет.

А> точно также как 0 void * может не быть нулевым набором бит.


Обычно, тоже будет.

>>> Все что я сказал, это то, что участок памяти отведенной под структуру будет забит нулями! Все!

ПК>>И каков смысл такого "обнуления"?
А>Смысл в том, что для многих встроенных типов семантика такого обнуления равна семантике инициализации нулем. Вместого того, чтобы писать = 0; для кучи переменных типа int проще сразу забить всю структуру нулями а затем уже подкоректировать "некоторые оставшиеся" переменные, дав им нужные значения. Все! Это я и имел в виду!
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[7]: ZeroMemory vs xxx = { 0 };
От: Аноним  
Дата: 12.06.04 23:16
Оценка: -1
ПК>Только никто об этой "ошибке" тебе не скажет. Например: была когда-то структура, в которой, скажем, был член const char*, и кто-то "обнулял" ее с помощью memset, а впоследствии член был заменен, скажем, на std::string.
Ок. У меня Visual C++ SP 5. Думаете он скомпилирует следущий код если убрать комментарий?
#include <iostream>
#include <string>

using namespace std;

struct SomeStruct
{
    int    i;
    string    s;
};

int main()
{
    SomeStruct ss /* = { 100 } */ ;

    ss.i = 5;
    cout << "ss.i:          " << ss.i       << endl
         << "ss.s.length(): " << ss.s.length() << endl;

    return 0;
}

Ответ: нет! Не знаю по стандарту это или нет и даже не хочу вникать! Я бы никогда так не инициализировал такой обьект!

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

Вот встречный вопрос: если же мы говорим тут об инициализации обьектов (высоко-уровневой операции) а не о забивании куска памяти нулями (низко-уровневой операции), зачем вообще зашла речь о ZeroMemory()?

ПК>Хорошо, уговорил. Используй Только другим не навязывай.

Мне это не надо! Сколько бы мы тут не говорили, каждый из нас останется при своем мнении. Только Вы говорите о высокоуровневых обьектах, для которых ZeroMemory() неприемлем, а я же и автор начального топика говорим о низкоуровневом обнулении куска памяти. Подумайте об этом!

PS. Меня поражает как люди бросаются ссылками на стандарт по синтаксису, словно задались задачей знать каждую синтаксическую конструкцию языка. Только синтаксис-то и знают, а вот в вопросе является ли creat() стандартной функцией или все-таки системным вызовом тормозят. Точно также был вопрос по макросам тут из stdarg.h, так что-то не особо бросались знаниями в этом направлении.

PPS. У Вас, Павел, указан неверный автор цитаты!

Censored. П. 6. -- ПК
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.