IPB

Добро пожаловать, гость ( Вход | Регистрация )

9 Страниц V « < 4 5 6 7 8 > »   
Reply to this topicStart new topic
> Delphi, Asm, C, WinAPI, PHP, ..., FAQ
Siberian GRemlin
Sep 18 2014, 09:26
Сообщение #101


Advanced Member
***

Группа: CTPAX-X
Сообщений: 533
Регистрация: 4-February 08
Пользователь №: 2
Спасибо сказали: 221 раз(а)



Кто подскажет как отловить что пишется.
Суть такая: после завершения программы на экран выводится сообщение «Except...», но выводится оно на доли секунды. Но проблема в том, что выводится оно после завершения приложения, т.е. после «end.».
CODE
  Readln;
end.

И если запустить прогу с параметром, то в файле будет пусто либо то что выводим мы сами.
CODE
test.exe >err.txt

Переписав всё в графическое приложение мы разумеется вообще ничего (сообщения об ошибке) не увидим. Как грамотный программист должен поступить в данном случае? Всё работает штатно, но это мелькание ошибки меня смущает. Есть подозрения, что шелудит «ActiveX».
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
Axsis
Sep 18 2014, 14:15
Сообщение #102


Advanced Member
***

Группа: CTPAX-X
Сообщений: 121
Регистрация: 6-February 08
Пользователь №: 374
Спасибо сказали: 149 раз(а)



Если не ошибаюсь, оператор перенаправления вывода ">" по-умолчанию перенаправляет только поток STDOUT, а ошибки выводятся в STDERR.
Попробуй так:
CODE
test.exe >>err.txt 2>&1

Это перенаправит STDERR в STDOUT, а его, в свою очередь, в файл err.txt
Подробности:
_ttp://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/redirection.mspx?mfr=true


Спасибо сказали:
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Sep 20 2014, 22:53
Сообщение #103


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,353
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 311 раз(а)



Почитываю блог The Old New Thing, который ведёт Raymond Chen - один из программистов Microsoft. Блог интересный, оттуда можно много чего почерпнуть как из истории и архитектуры Windows, так и о том, как надо или не надо писать программы под эту систему.
Была там одна статья, код в которой ввёл меня в ступор (лишнее выкинул):

CODE
BOOL Is64BitWindows() {
BOOL f64 = FALSE;
  return IsWow64Process(GetCurrentProcess(), &f64) && f64;
}

Код определяет, запущен ли процесс 32 на системе 64 (вызывать его в программе которая и так 64-битная смысла нет). Кстати, не забывайте, что IsWow64Process() есть не во всех Windows, так что LoadLibrary(Kernel32.dll) и GetProcAddress(IsWow64Process) вам в помощь.
Так вот, меня поразило "&& f64" (&& - логическое And), ведь f64 до вызова было присвоено FALSE, а значит и вся конструкция "функция AND FALSE" будет равна FALSE. Однако же, потом до меня допёрло, что это верно только в случае когда выражение состоит только из констант (т.е. может быть посчитано компилятором на этапе компиляции), иначе выражения вычисляются слева на право.
Т.к. сама функция IsWow64Process() может почему-то обламаться, то результат складывается во второй параметр (он передаётся по адресу).
Таким образом, выполнение идёт следующим образом: сначала отработает IsWow64Process() и только потом её результат вычисляется совместно с f64 - т.е. на момент "&& f64", переменная "f64" уже изменена функцией.

По той же причине будет работать и следующая конструкция:

CODE
char *s;
<...>
if (s != NULL) {
  if (s[0] == '?') {
    // todo code here
  }
}

при замене её на более короткую:

CODE
char *s;
<...>
if ((s != NULL) && (s[0] == '?')) {
  // todo code here
}

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

Но!

В математике от перестановки результат не меняется, так что конструкции "1 & 0" и "0 & 1" эквивалентны. Однако же в приведённых выше примерах при перестановке условий мы получим либо всегда FALSE в Is64BitWindows(), либо фатальную ошибку чтения памяти по адресу 0 когда s == NULL.

Тут нужно заметить, что в Pascal и Delphi есть магический переключатель $B, который по умолчанию находится в выключенном состоянии, но если его включить, то все проверки будут выполняться полностью, что чревато в примерах выше неприятными последствиями (кроме кода, где проверки разделены на две вложенные). См. $B: Булева оценка в статье по ссылке.
Попробуйте эту программу, а также её, но удалив "?" в коде:

CODE
{$APPTYPE CONSOLE}
var p:pchar;
begin
  p:=nil;
{?$B+}
  writeln((p <> nil) and (p^ = #10));
end.


Но, возвращаясь к упомянутому блогу, ещё одна занимательная статья, про которую хочется сказать:
Non-classical processor behavior: How doing something can be faster than not doing it
Название можно перевести как "когда делать что-то может быть быстрее, чем не делать этого".
Разгадка проста
Когда убираем if() - убираем condition jump в asm коде, а значит конвеер ЦП не будет сбрасывать и опять наполнять кеш инструкций (дорогостоящая по времени операция!).
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Sep 23 2014, 14:23
Сообщение #104


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,353
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 311 раз(а)



Кто работает с PHP наверное знает, что в php.ini нужно обязательно указать часовой пояс, иначе функции работы с датами будут, во-первых, работать неверно, а, во-вторых, вываливать Warning'и.

Устанавливается часовой пояс так (для Москвы):

CODE
[Date]
; Defines the default timezone used by the date functions
; http://php.net/date.timezone
date.timezone = Europe/Moscow

Недавно заметил, что у меня разница в 1 час выводимого времени с тем, что должно быть.
Из-за того что в России отменили переход на зимнее/летнее время (кстати, 26 октября 2014 опять стрелки крутить будем), а в PHP, видимо, это где-то жёстко зашито, то пришлось искать способ явно указывать часовой пояс, без сохранения летнего времени.

Так вот, указывать нужно вот так (для Москвы):

CODE
date.timezone = Etc/GMT-4

У кого-то сейчас может начать трещать шаблон почему вместо "+4" стоит "-4", однако же это такая особенность (выделение моё):
QUOTE
If you disregard the above warning, please also note that the IANA timezone database that provides PHP's timezone support uses POSIX style signs, which results in the Etc/GMT+n and Etc/GMT-n time zones being reversed from common usage.

For example, the time zone 8 hours ahead of GMT that is used in China and Western Australia (among other places) is actually Etc/GMT-8 in this database, not Etc/GMT+8 as you would normally expect.
© PHP Manual: Other time zones
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
Siberian GRemlin
Sep 23 2014, 14:48
Сообщение #105


Advanced Member
***

Группа: CTPAX-X
Сообщений: 533
Регистрация: 4-February 08
Пользователь №: 2
Спасибо сказали: 221 раз(а)



Кто мне подскажет когда и как правильно использовать CoUninitialize? Пишут, что нужно в каждом потоке делать CoInitialize(nil) и CoUninitialize. А что если у меня создаётся поток, в нём проверяется возможно ли подключиться к БД чрез ADO, а в главном потоке нужно либо переходить к работе с БД или завершать работу приложения. Придумал не я, этого требует задание. Дак вот если подгрузка делается в отдельном потоке и никак иначе, то когда делать выгрузку в данном случае?
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Sep 23 2014, 17:35
Сообщение #106


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,353
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 311 раз(а)



Можно одним вызовом со специальным параметром разрешить для всех потоков процесса:
QUOTE
Did you get that? If any thread in the process calls Co­Initialize­[Ex] with the COINIT_MULTI­THREADED flag, then that not only initializes the current thread as a member of the multi-threaded apartment, but it also says, "Any thread which has never called Co­Initialize­[Ex] is also part of the multi-threaded apartment."
© Why does CoCreateInstance work even though my thread never called CoInitialize? The curse of the implicit MTA
И ещё пара полезных ссылок о подводных камнях: для C++ и для Delphi.


Спасибо сказали:
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
Siberian GRemlin
Oct 24 2014, 06:21
Сообщение #107


Advanced Member
***

Группа: CTPAX-X
Сообщений: 533
Регистрация: 4-February 08
Пользователь №: 2
Спасибо сказали: 221 раз(а)



Как я понял, во всех 64-разрядных «Окнах» обращения 32-разрядных программ к реестру перенаправляется. Из
CODE
HKEY_LOCAL_MACHINE\SOFTWARE\
в
CODE
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\
Долго понять не мог почему игра не подхватывает свои параметры, пока не сделал проверку обращений к реестру. «Google» подтвердил.


Спасибо сказали:
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Oct 24 2014, 17:54
Сообщение #108


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,353
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 311 раз(а)



Там не только реестр - там, вообще, очень хитрое колдунство: MSDN: Running 32-bit Applications.
См. ссылки в "In this Section" - там по каждой грабле отдельная статья, объясняющая чем она грозит.
Если ты делаешь установщик и он 32-битный, то проблем не должно быть.
А вот если ты файл реестра просто экспортируешь... я не уверен, но попробуй там, в начале, написать "REGEDIT4" вместо "Windows Registry Editor Version 5.00" (или что оно там на современных системах добавляет) - может RegEdit догадается что файл старый и его нужно в ветку 32 пихать.
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
Axsis
Oct 26 2014, 23:48
Сообщение #109


Advanced Member
***

Группа: CTPAX-X
Сообщений: 121
Регистрация: 6-February 08
Пользователь №: 374
Спасибо сказали: 149 раз(а)



RegEdit'а тоже две версии. Из 32-битных приложений вызывается один и пишет в виртуальные ветки, из 64-битных вызывается другой.
Я однажды долго не мог понять почему .reg файл, импортируемый из тотал командера (32-битного) не показывается в реестре, и в программе (x64) не работают настройки, которые я импортирую.
А потом обнаружил, что regedit, вызванный из тотала и regedit, вызванный через "пуск->выполнить" показывают разный реестр... (в первом случае 32-ух, а во втором - 64-бинтую его версию).
Добавил тот же самый .reg файл в реестр из проводника и всё заработало как и должно было.
PS: а "REGEDIT4" вместо "Windows Registry Editor Version 5.00" писать бесполезно в данном случае...


Спасибо сказали:
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
Siberian GRemlin
Oct 28 2014, 10:02
Сообщение #110


Advanced Member
***

Группа: CTPAX-X
Сообщений: 533
Регистрация: 4-February 08
Пользователь №: 2
Спасибо сказали: 221 раз(а)



Искал как проверить наличие связи с интернетом, нашёл статью. Из рассмотренных в ней вариантов достоверным оказался только последний. Проблема в том, что после такой проверки перестаёт работать «Synapse» — на все запросы приходит ответ с кодом 500. Отключаю проверку связи и всё отлично. Кто-нибудь сталкивался или может знает другие способы проверки связи?
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Oct 28 2014, 12:47
Сообщение #111


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,353
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 311 раз(а)



WSACleanup() убери оттуда.
Как я понял, твой Synapse с сокетами работает, а вызов этой подпрограммы нахрен закрывает библиотеку WinSock.
И, кстати, я там не увидел, чтобы они где-то в коде WSAStartup() делали, чтобы библиотеку закрывать.
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Nov 29 2014, 14:51
Сообщение #112


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,353
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 311 раз(а)



Немного интересной и полезной информации по .BAT файлам.

1) Недавно перечитывая справку по .BAT файлам в Windows, натолкнулся на такую штуку:
CODE
@echo off
if _%1 == _ goto quit
program.exe %1
:quit

Оказывается в Windows (в 98-ой не проверял, но в XP точно есть) можно сделать проще (справка по "goto /?"):
QUOTE
Изменение команды GOTO при включении расширенной обработки команд:
Команда GOTO принимает в качестве метки перехода строку :EOF, которая вызывае передачу управления в конец текущего пакетного файла. Это позволяет легко выйти из пакетного файла без определения каких-либо меток. Команда CALL /? выводит описание расширенных возможностей команды CALL, делающих эту функцию особенно полезной.
Таким образом, скрипт можно сократить:
CODE
@echo off
if _%1 == _ goto :EOF
program.exe %1

Для тех, кто не в курсе - если вместо "goto <конец_файла>" делать exit, то это прекращает выполнение вообще. К примеру, если из одного .BAT файла через call вызвали другой, то при exit произойдёт выход из обоих файлов, а не возврат в верхний.

2) А ещё команду call можно вызывать с метками и параметрами - см. справку по "call /?".

3) Но и это ещё не всё. Перечитывая FAQ к DOS Navigator 1.51 (C:\DN\DOC\DN-FAQ25.TXT) я нашёл там такое:
QUOTE
Q > Зачем в DN.HGL сделано 'comentstring ::' в .BAT-файлах".

A > Потому, что набоp '::' в начале стpоки аналогичен REM, более того, это даже более гpамотно, так как REM все же команда DOS и вся стpока пеpедается командному пpоцессоpу на обpаботку, а ':' это не команда, а метка (что, видимо, и так все знают), а вот если после ':' стоит символ, котоpый не может быть частью названия метки (точнее метка, котоpой нет), то DOS пpосто пpопускает эту стpоку, т.е. получается тот же комментаpий, только экономится запуск целой команды. Для командных файлов с многими REM'ами можно добиться некотоpого ускоpения запуска. А '::' - уже установившийся de-facto стандаpт, кpоме того так пpоще набиpать и так гоpаздо кpасивее.

Не знаю, насколько эта фича документированна, но такая штука:
CODE
@echo off
rem комментарий
:: это тоже комментарий
echo тест
Вполне себе работает.

P.S. Годная, хотя и старая, книжка Стивен Симрин. Библия MS-DOS (версии 3.30), особенно там интересны всякие описания по физическому устройству дисков и файловой системы. Чего я про неё пишу - там описывается, что до MS-DOS 3.30 не было команды call и прямой вызов одного .BAT файла из другого приводил к невозможности возврата, поэтому приходилось выкручиваться.


Спасибо сказали:
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Dec 7 2014, 17:20
Сообщение #113


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,353
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 311 раз(а)



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

То, что основательно выжрало мне время:

1) Все значения в $_POST и $_GET экранируются через addslashes(), независимо от настройки get_magic_quotes_gpc() (которая уже давно протухла и не поддерживается). Сделано это для подтирания соплей криворукожопым дебилам пишущим что-то типа:
CODE
$result = $wpdb->get_results('SELECT * FROM users WHERE name='.$_POST['name']);

забы(и)вающим про(на) mysql_real_escape_string() и подобные функции, чтобы исключить SQL-инъекцию. Подробнее: раз и два. Так что будьте готовы делать stripslashes(), если вам нужна оригинальная строка.

2) Вторая вещь, с которой вы будет как минимум полчаса сношаться не понимая что к чему - это высота <select> при свойстве multiple (множественный выбор). Она не меняется. Чтобы поменялась нужно в файле со стилями указать (auto - чтобы свойство size работало, указывающее сколько строк должно быть видно):
CODE
#wpcontent select[multiple] {
  height: auto;
}

Когда вы допрёте до этого, то наверняка узнаете что разработчики пару лет как в курсе, но им глубоко пофигу на ваши проблемы и потраченное время.

3) Не WP, но рядом - плагин AutoComplete для jQuery (jQuery Autocomplete plugin 1.2.2).
Вызываем так - всё работает:
CODE
jQuery('input[name="city"]').autocomplete('cityfind.php', {
  selectFirst: true, // выбирать первый найденный
  minChars: 4 // как минимум 4 символа должны быть введены
});

Всё отлично и зашибись. Теперь, чтобы не гонять зря запросы на сервер, делаем локальный массив (citylist) и выгружаем все нужные города туда:
CODE
// короткий пример списка
// на деле там 3K городов
// в отдельном файле citylist.js
var citylist = [
"Detroit",
"Los Angeles",
"New York"
];
<...>
jQuery('input[name="city"]').autocomplete(citylist, {
  selectFirst: true,
  minChars: 4
});

И оно нихрена не работает! Мне пришлось дебажить этот плагин прямо по шагам, залезая в каждый метод, чтобы, в итоге таки найти что нужно обязательно добавить свойство:
CODE
matchContains: true,

чтобы эта херня заработала (вставить сюда тонны ненависти)! Потому что:
CODE
function request(term, success, failure) {
  if (!options.matchCase) term = term.toLowerCase();
  // term - введённая строка в нижнем регистре
  var data = cache.load(term);
  if (data) {
    <...> // нам нужно сюда!
  }
}
// смотрим cache.load():
function load(q) {
  // здесь всё нормально
  if (!options.cacheLength || !length) return null;
  // options.url == null (т.к. мы массив передали)
  // но options.matchContains == false (по умолчанию)!
  if (!options.url && options.matchContains) {
    <...> // нам нужно сюда!
  }
  return(null);
}

И гугление в Интернете вам не поможет - я там даже упоминания об этом не нашёл!
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
Siberian GRemlin
Apr 1 2015, 13:11
Сообщение #114


Advanced Member
***

Группа: CTPAX-X
Сообщений: 533
Регистрация: 4-February 08
Пользователь №: 2
Спасибо сказали: 221 раз(а)



У кого-нибудь есть идеи как быстро читать строки из текстового файла размером боле 1 гига (более 90,5 миллионов строк)?

Чтение строками (предполагалось, что там все строки равной длины) заняло 1,5 часа, это при том, что я весь файл загрузил в память. Но оказалось, что в них есть ошибки и длина части строк разнится. Не представляю сколько уйдёт времени на чтение посимвольно.

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

P. S. Отвечу на возможный вопрос. Это выгрузка БД в CSV, нужно сравнить свои значения со всеми значениями в ней.
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Apr 2 2015, 11:31
Сообщение #115


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,353
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 311 раз(а)



Конкретизируй задачу (опиши коротко и понятно на каком-нибудь похожем примере).
Потому что если тебе просто нужно сравнить свои значения с теми что там, то всю БД в память загружать и не нужно - прочитал одну строку, распарсил, нашёл нужные занчения, сравнил, затем прочитал следующую и так далее. При таком подходе тебе нафиг грузить файл в память не упёрлось, что быстрее и проще.
Я бы читал файл блоками:
1) Скажем, известна максимальная длинна строки - пусть будет 500 символов.
2) Читал бы с запасом блок в 512 символов.
3) Парсил бы его и искал конец строки. Скажем, строка бы была длинной в 302 символа.
4) На этом шаге парсил строку и вытаскивал нужные значения.
5) Здесь перемещал бы оставшийся блок в 512-302=210 символов в начало буфера, затем читал бы из файла остаток в 302 байта и переходил бы к пункту 1. Если сделать буфер кольцевой, то даже перемещать ничего не нужно - будет быстрее, но с реализацией замороченнее.
К слову сказать, пункты 2-5 умеет делать буферизованный ввод/вывод Сишных функций fgets() - читает строку из файла. В Delphi можно подключить через external библиотеку msvcrt.dll и оттуда эту функцию тягать (ещё fopen() и fclose() понадобятся).
Если же тебе нужно постоянно что-то там сравнивать, то проще и быстрее будет загрузить твой .CSV обратно в какую-нибудь БД и уже с БД и работать.

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

CODE
program functest;
{$APPTYPE CONSOLE}

function fopen(filename: pchar; filemode: pchar): pointer;  cdecl; external 'msvcrt.dll';
function fgets(s: pchar; n: integer; fptr: pointer): pchar;  cdecl; external 'msvcrt.dll';
function fclose(fptr: pointer): integer; cdecl; external 'msvcrt.dll';

var
  f: pointer;
  s: array[0..1023] of char;
begin
  // open for (r)ead as (t)ext file = rt
  f:=fopen('functest.dpr', 'rt');
  // file open ok
  if (f <> nil) then
  begin
    // first line
    fgets(s, 1024, f);
    writeln(s);
    // second line
    fgets(s, 1024, f);
    writeln(s);
    // close file
    fclose(f);
  end;
end.


Спасибо сказали:
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
Siberian GRemlin
Apr 3 2015, 10:35
Сообщение #116


Advanced Member
***

Группа: CTPAX-X
Сообщений: 533
Регистрация: 4-February 08
Пользователь №: 2
Спасибо сказали: 221 раз(а)



Да, до меня до самого допёрло, что всё сразу в памяти держать не нужно.
Но я немножко по другому сделал. По 10-100 метров (пока не решил сколько лучше брать) гружу, догружаю до конца строки и загоняю в TStringList, и массив сам заполняется, причём достаточно быстро. После сравнения выгружаю и читаю следующий блок.

Как оказалось, длина строки меняется из-за косяка в кодировке: кириллица осталась в юникоде, когда всё остальное — в ANSI. Сообщил об этом, а они сообразить не могут, что я от них хочу.

Можно было сделать это всё через, например, SQL, но заказчик не осилит его использование.
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Apr 3 2015, 15:30
Сообщение #117


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,353
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 311 раз(а)



QUOTE(Siberian GRemlin @ Apr 3 2015, 10:35) *
Можно было сделать это всё через, например, SQL, но заказчик не осилит его использование.
Тогда, как вариант, можно программу сделать, в которую вводишь сервер, порт, логин, пароль и имя базы данных, а она уже сама нужные запросы делает и выводит статистику.
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
Siberian GRemlin
Apr 6 2015, 09:48
Сообщение #118


Advanced Member
***

Группа: CTPAX-X
Сообщений: 533
Регистрация: 4-February 08
Пользователь №: 2
Спасибо сказали: 221 раз(а)



Доступа нет к серверу и не будет. Есть только выгрузка.
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Jul 8 2015, 15:40
Сообщение #119


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,353
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 311 раз(а)



Переписывал на сях пару дней назад bnkextr, а также wav2ogg, причём в последней программе нужно было делить размеры блоков на 255. Если помните, то в OggS размер блока записывается как байт N, а затем N байтов сумма которых даёт полный размер блока.
И тут я вспомнил подсмотренный (уже не помню где) прикольный способ подсчёта.

В общем, если нам нужно разделить блок данных размером datasize байт на пакеты по packsize байт, то, обычно, делают как-то так ("/" = "div"; "%" = "mod" в Pascal/Delphi):

CODE
packets = datasize / packsize;
if (datasize % packsize) {
  packets++;
}

Собственно проверка нам нужна когда остаётся остаток от деления - т.е. какой-то пакет (обычно последний) будет неполный. Занимает всё это безобразие аж 4 строчки и смотрится уродско. А можно записать в одну и красиво:

CODE
packets = (datasize + (packsize - 1)) / packsize;

Если кто не понял что тут делается, то задам вопрос: когда у нас появляется остаток?
Когда datasize целиком не делится на packsize.
Рассмотрим на примере: пусть packsize = 5.
Тогда остаток в datasize может быть 1, 2, 3 или 4.
Если теперь увеличить datasize на packsize - 1 (т.е. на 5 - 1 = 4), то любое из этих чисел даст число большее, либо равное packsize, но (это важно!) всегда будет меньше packsize * 2 - т.е. при делении на packsize мы получим тот самый дополнительный пакет, причём он будет ровно один.
Способ красив, но не лишён недостатков, а именно переполнения (когда сумма делимого вылезает за размеры базового типа), так что использовать его следует в тех случаях, когда гарантировано сложение переполнения дать не может.
У варианта выше с двумя делениями такого недостатка нет, так что имейте это ввиду.
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Aug 24 2015, 22:08
Сообщение #120


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,353
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 311 раз(а)



В последнее время больше пишу на сях, чем на Pascal/Delphi.
Когда-то, помню, тратил кучу времени на перевод примеров или кода с C/C++ (так как большинство всего этого было именно на упомянутых языках) на Pascal/Delphi. Дело в том, что си позволяет сделать много, коротко и просто. Чего Delphi/Pascal не может.
Давайте рассмотрим примеры.
Есть некий буфер в памяти который надо как-то изменить для дальнейшего использования.
Скажем, нужно занулить первый байт.
Pascal:
CODE
Var p: ^Byte; { PByte }
...
p^:=0;

C:
CODE
BYTE *p;
...
*p = 0; /* или p[0] = 0; */

Теперь усложним пример - нам нужно также занулить 4 и 8 байты.
C:
CODE
/* от нуля, так что 4-1 и 8-1 */
p[0] = 0;
p[3] = 0;
p[7] = 0;

А вот с Pascal начинается геморрой:
CODE
p^:=0;
inc(p, 3);
p^:=0;
{ 7-3 = 4 }
inc(p, 4);
p^:=0;
{ вернули назад, иначе при освобождении памяти отхватим ошибку! }
dec(p, 7);

Можно, конечно, попытаться извернуться со своим типом-массивом:
CODE
Type
  TList = Array[0..0] Of Byte;
  PList = ^TList;
...
Var p: ^Byte;
l: plist;
...
l:=Pointer(p);
l[0]:=0;
{ а тут нас ждёт "Error: Constant expression violates subrange bounds",
ибо массив был от 0 до 0, поэтому придётся заводить переменную типа integer,
скажем n, и писать n:=3; l[n]:=0; - тогда будет работать }
l[3]:=0;

Видите, сколько лишнего кода приходится писать? Конечно, можно объявить нашу перменную как Array of Byte, но большинство библиотек и программ работают именно с обычными указателями. Можно ещё все извращения с inc()/dec() обернуть в функцию. Можно ещё много чего. А можно писать на сях, что удобнее, быстрее, короче и проще. Я уж не говорю про #define'ы - эта вещь, вообще, помогает сократить код и выкинуть всё лишнее. А ещё есть "?". Просто посмотрите:
C:
CODE
#define MY_MAX(a,b) (((a) > (b)) ? (a) : (b))
...
m = MY_MAX(x,y);
/* заменить значение 123 на 0, а иначе оставить как есть */
m = (m == 123) ? 0 : m;

Pascal:
CODE
{ здесь для той же операции придётся создавать функцию;
более того - если нужно будет сравнить два значения другого типа (не integer),
то придётся делать ещё одну, отдельную функцию на каждый такой тип данных!
т.к. #define в сях это макрос, то он таких недостатков лишён }
function my_max(a, b: integer);
begin
  if (a > b) then result:=a else result:=b;
end;
...
m:=my_max(x,y);
{ здесь только полностью писать if() }
if (m = 123) then m:=0;

Или, вот, скажем, в сях любое условие считается ложным, только если оно ноль/NULL - иначе истина.
C:
CODE
BYTE *p;
int i;
bool b;
...
p = malloc(10);
/* если p не NULL; Pascal: if (p <> nil) then ... */
if (p) {
  ...
  free(p);
}
/* если b - истина (false = 0); Pascal: if b then ...
режим {$X+} в Pascal должен быть включён, иначе нужно будет
писать полностью if (b = True) then ... */
if (b) {
  ...
}
/* если i что угодно, но не ноль;
можно также записать как if (i != 0) { ... }
Pascal: if (i <> 0) then ... */
if (i) {
  ...
}
/* если i ноль (!i - "не i");
можно также записать как if (i == 0) { ... }
Pascal: if (i = 0) then ... */
if (!i) { ... }

В общем, код сокращается от лишних и ненужных операций, становится проще и понятней для чтения, а главное делать теперь это не в пример быстрей. При написании же кода также тратится куда меньше сил и времени. Pascal/Delphi хорош для изучения языка и начинающих программистов - он приучает к порядку и аккуратности. Но потом многие из особенностей языка начинают откровенно мешать. И вот тогда, в сравнении, приходит осознание насколько же си крут.
Конечно, там нет строк (в смысле, они есть, но ASCIIZ, так что будет много головняка с выделением памяти, склейкой и прочей фигнёй, которая может привести к buffer overflow если не доглядеть; вообще говоря, в C++ есть класс CString (си строчки) и прочие такие костыли), а .DLL файлы, если загружаются не динамически через LoadLibrary(), а статически, то подключаются через адовый геморрой посредством .DEF файлов (в Delphi же это делается одной строчкой...), и ещё много чего, так что будьте готовы к тому, что то что за вас раньше делал компилятор, теперь придётся делать самостоятельно, но язык того стоит.
Так что если у кого-то будут вопросы по языку - задавайте, могу помочь разобраться.
User is offlineProfile CardPM
Go to the top of the page
+Quote Post

9 Страниц V « < 4 5 6 7 8 > » 
Reply to this topicStart new topic
4 чел. читают эту тему (гостей: 4, скрытых пользователей: 0)
Пользователей: 0 -

 



Упрощённая версия Сейчас: 28th March 2024 - 09:25