Каким должен быть язык программирования? Анализ и критика Описание языка Компилятор
Отечественные разработки Cтатьи на компьютерные темы Компьютерный юмор Новости и прочее

Изменение длины объекта в стеке во время исполнения

Вняв совету заняться прототипом, провожу эксперименты. В чём минусы практических попыток создания компилятора? Уходит время, приходится переключать мозги с макроуровня на микроуровень. Приходится покидать мир высоких абстракций и продвинутых концепций и опускаться на грешную землю и пахать её байт за байтом. Вынужден ползать на уровне Си или ассемблера, а не парить в облаках. Но тут уже была тема для такого низкого уровня — двухстековое распределение памяти. И эту тему, как оказалось, можно лучше проработать.

            Уже задавался вопрос:

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

В чём проблема? Стек процессора x86 растёт в сторону уменьшения адресов. Объекты же типа строк и массивов растут в сторону увеличения адресов.
Изменение длины объекта в стеке во время исполнения
            Нулевые элементы массивов на рисунке имеют наименьший адрес, элементы с наибольшим номером имеют наибольший адрес. Но когда помещаем в стек массив с номером 0, то куда можно поместить массив с номером 1? Стек растёт в сторону младших адресов, поэтому все объекты (на рисунке — это массивы 1, 2 и т.д.) размещаются на рисунке «левее». Можно ли длину массива 2 «нарастить»? Сделать её равной не k, а k+1? Нельзя, поскольку элемент k+1 наложится на элемент 0 массива 1. Что нужно, чтобы добавить новые элементы в массив?
  • Узнать, сколько дополнительной памяти для этого потребуется,
  • сдвинуть указатель стека на эту величину,
  • переписать весь массив по новому адресу,
  • добавить в конец новые элементы.
Это необходимый минимум. Могут быть и другие сложности. Ведь на этот массив могут ссылаться другие объекты. Плюс ссылки могут быть внутри самого массива. Вот и думалось: может, бросить эту затею? Ведь когда-то хотел сделать стек программным. Это будет помедленнее, но зато проще.

            По поводу таких особенностей архитектуры x86 хочется сказать «большое спасибо» разработчикам Intel: у них не только порядок байтов обратный (little-endian, малым концом — «остроконечники» по классификации Джонатана Свифта), но ещё и стек растёт в обратном направлении. Если бы всё сделать, как у людей, то помещённый в стек объект мог бы расти. И стек, и объект росли бы в одну и ту же сторону.

            И вот, поругивая Intel, я занимался практической реализаций объектов, помещаемых в стек. Честно говоря, получалось не очень. Как-то всё сложно выходило. Но тут однажды осенила идея… Мы должны развернуть строки и массивы в обратном направлении! Чтобы направление роста стека и массивов было одинаковым:
Изменение длины объекта в стеке во время исполнения
            После такого решения многие моменты встают на свои места. Добавление новых элементов к последнему массиву лишь сдвигает указатель вершины стека. Код, над которым я работал, был переписан. Он сразу стал короче, освободился от многих неестественных функций. Правда, появилась одна неестественность: адресом массива остался нулевой элемент, но это элемент с наибольшим адресом, а не с наименьшим. Да и то я упростил картину. На самом деле массивы имеют в своём составе поле «количество элементов». Именно его адрес является адресом массива. А вот «слева» от него — элемент 0, затем 1 и т. д.
Изменение длины объекта в стеке во время исполнения
            Будет любопытно поработать с такими массивами и строками «шиворот на выворот». Понаблюдаю и поделюсь выводами :)

Опубликовано: 2018.11.09, последняя правка: 2019.01.01    21:36

Оцените

Отзывы

     2018/11/11 16:18, Александр Коновалов aka Маздайщик          # 

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

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

     2018/11/11 23:39, Автор сайта          # 

на практике придётся взаимодействовать с существующими API... придётся писать переходники, которые обращают массивы и строки до и после вызова.

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

     2018/11/15 13:40, kt          # 

Аппаратные особенности могут раздражать, но программные выкрутасы — прямо бесят. Я намучался из-за единственной глупой команды. Win-64 сейчас требует, чтобы стек программы при исключениях ВСЕГДА был кратен 8, хотя физически процессору на это наплевать. И эта проверка стоит в системном обработчике исключений. Получается идиотская ситуация: произошло исключение, для него написан свой обработчик, Windows его нашла и собирается передать управление и вдруг, о, ужас! Стек не кратен 8! Программа принудительно снимается. При этом в этот момент действует стек обработчика и он кратен 8. Козлы они, а не разработчики! В 32-разрядном режиме этой глупости нет. А так получается вот что: лежит в стеке строка и я хочу отрезать у неё первый символ. Раньше я просто сдвигал указатель стека вверх на байт, а теперь отрезанную строку ещё надо так сдвинуть в стеке, чтобы её начало опять было кратно 8. И приходится двигать и вправо и влево. Из-за одной единственной дурацкой проверки в системной библиотеке Windows.

     2018/11/15 17:46, Автор сайта          # 

В языке Rust как-то обходятся без исключений. Там другой механизм обработки ошибок. И он мне нравится. Но и его можно сделать лучше :)

     2018/11/15 18:43, Comdiv          # 

А насколько это вообще влияет на полезные свойства транслятора? Сколько раз в практической задаче будет встречаться единственный локальный массив, который необходимо растить?

     2018/11/15 20:55, Автор сайта          # 

Транслятор читает входной поток, анализирует и накапливает результаты анализа. Куда он должен сохранить эти результаты, если они промежуточные и ещё нужны при генерации кода? Можно в файл, но можно и в память. В последнем случае можно в «кучу», а можно попробовать в стек. Вот этим и занимаюсь в экспериментах. При этом стараюсь код на Си писать, насколько это возможно, похожим на будущий язык, используя накопленные задумки. В будущем это пригодится для раскрутки компилятора.

Если читать входной поток посимвольно, то вот он — растущий массив. Вы же не можете предугадать, какой длины будет, допустим, очередной идентификатор. На самом деле, динамически меняющие размер объекты — не редкость, вполне обычное явление.

     2018/11/16 18:43, Comdiv          # 

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

На самом деле, динамически меняющие размер объекты — не редкость, вполне обычное явление

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

     2018/11/17 00:05, Автор сайта          # 

Длина идентификатора становится известной после прочтения, соответственно ему нельзя статически выделить памяти ровно столько, сколько он занимает места. Тот же словарь идентификаторов — это контейнер, куда помещаются идентификаторы с заранее неизвестной длиной и в заранее неизвестном количестве.

хотелось бы увидеть некое численное выражение пользы.

Пока рано говорить о каком-то числовом выражении.

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

Если не секрет, что это за проект?

     2018/11/17 03:28, Comdiv          # 

Проект «Восток» — транслятор Оберон-07 https://github.com/Vostok-space/vostok/

     2019/01/01 18:17, Comdiv          # 

Второй стек должен иметь те же ограничения, что и основной, или это предложение для одного стека? Что насчёт более одного растущего массива в одной функции?

     2019/01/01 18:25, Comdiv          # 

kt: Получается идиотская ситуация: произошло исключение, для него написан свой обработчик, Windows его нашла и собирается передать управление и вдруг, о, ужас! Стек не кратен 8! Программа принудительно снимается. При этом в этот момент действует стек обработчика и он кратен 8. Козлы они, а не разработчики!

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

     2019/01/01 20:35, kt          # 

Логики нет, поэтому и козлы. Ещё раз: и процессор и ОС работают совершенно нормально, если RSP не кратен восьми. И только если в этот момент произошло исключение (в моем случае от пошагового режима!), ОС не вызывает зарегистрированный обработчик, хотя стек, при котором ОС его вызывает, уже другой и кратен восьми. Я пробовал вообще убрать эту команду (подменив NTDLL), и тогда мой и отладчик и вся ОС работали совершенно нормально. Но невозможно на всех компьютерах ради этого править NTDLL.DLL.

Логика такая же идиотская, как и у того «индийского гения», который внутри вызовов WINAPI умудрился запоминать регистры SSE2 (даже, если они не используются), не выравнивая стек на 16, и, тем самым, требуя выравнивания стека на 16 для почти всех вызовов из программы пользователя. Вместо выравнивания в одном месте получаются миллионы выравниваний, рассыпанных по программам, притом, что сам вызов CALL в процессоре меняет стек только на 8 байт, а не на 16 байт, и поэтому такое требование неестественно.

Что касается оптимизации, то не понял, о чем речь. В нашей системе, если возвращается значение-строка, то она пишется в стек и длина помещается в регистр. Например, требуется склеить строку в памяти со строкой в стеке и результат оставить в стеке. Раньше можно было просто увеличить стек на длину приклеиваемой строки и переписать её из памяти в стек, вплотную к имеющейся. Сейчас это тоже будет работать, но только в этот момент исключений быть не должно, иначе крах всей программы и «рапорт» в Микрософт. И все из-за одного идиота с его единственной идиотской проверкой. Теперь результат склейки нужно подвинуть или вправо или влево в стеке. Примерно так:
;---- ОПРЕДЕЛЯЕМ, СКОЛЬКО СВОБОДНОГО МЕСТА СЛЕВА ----

SUB RDI,RSP
JZ M1 ;НЕТ СВОБОДНОГО МЕСТА СЛЕВА

;---- ОПРЕДЕЛЯЕМ, СКОЛЬКО СВОБОДНОГО МЕСТА СПРАВА ----

MOV CH,8
SUB CH,CL
AND CH,7

;---- СУММА СВОБОДНЫХ МЕСТ СПРАВА И СЛЕВА ----

MOV CL,DIL
ADD CH,CL

;---- ЕСЛИ СУММА МЕНЬШЕ 8, ДВИГАЕМ ВЛЕВО ----

CMP CH,8
JAE @

LEA RSI,[RSP+RDI]
MOV RDI,RSP
MOVZX ECX,AL
REP MMOVSB
M1: MOV CL,AL ;ЧАСТО МОЖЕТ ПРИГОДИТЬСЯ 9.5.2012
JMP RBX

;---- ЕСЛИ СУММА НЕ МЕНЬШЕ 8, ДВИГАЕМ ВПРАВО ----

@: MOV CL,DIL
MOV CH,8
SUB CH,CL ;НА СТОЛЬКО СДВИНУТЬ ВПРАВО
MOVZX ESI,CL
MOVZX EDI,CH
MOVZX ECX,AL
MOV EAX,ECX
LEA RSI,[RSP+RSI]
LEA RSI,[RSI+RAX]-1
LEA RDI,[RSI+RDI]
STD
REP MMOVSB
CLD
ADD RSP,8
MOV CL,AL ;ЧАСТО МОЖЕТ ПРИГОДИТЬСЯ 9.5.2012
M10: JMP RBX

     2019/01/01 21:48, Автор сайта          # 

Второй стек должен иметь те же ограничения, что и основной, или это предложение для одного стека?

Стеки, по идее, должны быть равноправны — со всеми вытекающими последствиями.

Что насчёт более одного растущего массива в одной функции?

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

у того «индийского гения»

Он «индус триальный» разработчик. Это пробная версия разработчика-индуса.

     2019/01/02 00:58, Comdiv          # 

пробовал вообще убрать эту команду (подменив NTDLL), и тогда мой и отладчик и вся ОС работали совершенно нормально.

Вы пишите, что проблема проявляется только при исключении. Вы протестировали, что при исключениях не в Вашем отладчике всё работает как надо? Или только, что работает как надо при отсутствии исключений? Для сторонних читателей ведь это всё не очевидно и они видят только ругань.

Стеки, по идее, должны быть равноправны

Почему? Ради скорости?

А за счёт чего объект имеет возможность расти? За счёт того, что он на вершине стека

Это понятно. Как поведёт себя язык? Не позволит создавать ещё один растущий массив или начнёт усложнять логику для альтернативного размещения 2-го массива? Что будет, если захочется массив обернуть высокоуровневой структурой наподобие StringBuilder. Всё ещё пытаюсь понять, насколько это всё это позволит получить выгоду в среднестатистической задаче. Вопрос практический.

     2019/01/02 01:48, Автор сайта          # 

«По идее» — это потому что у меня нет компилятора моего языка, в котором делал бы, как считаю нужным. А по факту хоть и работаю с 2 стеками, но в Си. Поэтому оглядываюсь: «А не слишком ли я наглею, вмешиваясь в процесс использования памяти»? Поэтому равноправие стеков — это ещё и «как бы чего не вышло». Да и в будущем, наверное, можно продолжить эту линию. Хотя... ещё одна идея возникла, потом озвучу в новой статье.

Всё ещё пытаюсь понять, насколько это всё это позволит получить выгоду в среднестатистической задаче.

// некая функция — это её тело
некий массив = создать массив (параметры)
Когда получает управление «некая функция», то она в своём стеке имеет адрес возврата и какие-то собственные локальные объекты. Когда вызывается функция «создать массив», то у неё собственный стек, в котором дугой адрес возврата и свои локальные объекты. Но отрабатывая, она записывает «некий массив» не в свой стек, а в стек вызвавшей функции, т. е. стек «некой функции». Потом решили удлинить «некий массив» внутри «некой функции»:
// продолжение тела некой функции
добавить элементы (некий массив, параметры)
Функция «добавить элементы» использует тот же стек, что и функция «создать массив». Но новые элементы массива создаются в стеке «некой функции». Чудес тут нет, но во всех языках подобное проделывается только в «куче».

Идея с двумя стеками возникла как ответ на вопрос: «Как вернуть из функции объект заранее неизвестной длины, но не в «куче», а там же, где и остальные локальные объекты». Вопрос о том, чтобы из функции возвращать не один объект, а несколько, не стоял. Хотя есть такие идеи, но они не на слуху. Если желаете, я найду статью на Хабре про это и дам ссылку. Была примерно такая идея:
a, b, c  = f (x, y, z)
Хотя больших сложностей с распределением памяти тут не предвидится (два стека тут тоже бы выручили), но тут дополнительная синтаксическая сложность. Оправдана ли она? Уж лучше тогда вернуть из функции какой-то контейнер (но один контейнер!), а внутри — эти самые a, b, c.

     2019/01/02 02:24, Comdiv          # 

О возврате нескольких значений я и не думал, когда задавал вопрос. Я вёл речь о >=2 локальных растущих буферах. Что сделает язык — запретит такое объявление? Что если работать с растущим массивом локально, но внутри более высокоуровневой структуры?

     2019/01/02 13:07, kt          # 

Comdiv: об этом как раз и написано в заметке об исключениях в Win64. Встроенный отладчик — это лишь один из потребителей исключений (в основном INT 2/INT 3). Если задача является отладочным процессом, например, для WinDbg, такой проблемы не существует. Однако исключения в программе необходимо обрабатывать и без участия «внешнего» отладчика. Получается, что неведомый мне разработчик запретил использовать стек как мне удобно. Т.е. в стеке вообще не предусмотрено хранение объектов с длиной не кратной 8. До Win 7 такой проблемы не было. Создатели процессора также таких запретов не вводили. Раньше только при обращении к WinAPI нужно было выравнивать стек на 4, иначе программы просто сходили с ума без предупреждений. Сейчас же не только WinAPI требует выравнивания (пусть, не страшно), но и просто стек без выравнивания не позволяет перехватывать любые исключения. Вероятно, автор этого безобразного ограничения просто никогда не видел код, сгенерированный не для С++.

     2019/01/02 14:47, Автор сайта          # 

Я вёл речь о >=2 локальных растущих буферах. Что сделает язык — запретит такое объявление?

первый массив = создать массив (первый набор параметров)
добавить элементы (первый массив, параметры) // всё законно, перед добавлением вершина
// стека указывает на младший байт массива
второй массив = создать массив (второй набор параметров)
добавить элементы (первый массив, параметры) // ошибка: проверка перед добавлением
// выявила, что вершина стека уже отодвинута
Здесь проверка — во время исполнения. Надо бы подумать, как её сделать во время компиляции. Хорошую загадку подкинули.

Что если работать с растущим массивом локально, но внутри более высокоуровневой структуры?

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

     2019/01/20 13:42, utkin          # 

Прочитал статью и ничего не понял. Так в статье-то и нет рецепта. Есть три массива: массив0, массив1 и массив2. Автор там разводит теорию о порядке размещения байт. Но ни одним словом не обмолвился, как на стеке менять длину объектов. Я не увидел, как можно в два раза увеличить массив1 на стеке. Вот хоть тресни. Или по условию задания требуется рост всех трех массивов. Как это сделать на стеке? Затем представим, что у нас перестает быть необходимым массив1. Как этого добьется автор?

Собственно сама статья не соответствует названию, так как не раскрывает принципов изменения длины объекта на стеке.

     2019/01/20 17:14, Автор сайта          # 

Эта тема — продолжение цикла статей про возврат функциями объектов переменной длины и то, как двухстековая модель памяти в этом помогает. В приведённых примерах массив 0 и массив 1 — «статисты», приведены лишь для наглядности, чтобы наглядно показать расположение объектов в памяти. Наверное, Вы заметили, что они находятся не на вершине стека, поэтому увеличение их длины невозможно.

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

К массивам 0 и 1 эта функция отношения не имеет и менять их размер не уполномочена. Если бы стояла задача менять размер всех массивом, тогда это задача не для стека, а для «кучи». Но в «куче» — медленно. Поэтому если порядок создания и уничтожения объектов совпадает с дисциплиной FIFO, то лучше использовать стек: достаточен один, если все объекты фиксированного размера (известного во время компиляции), и необходимо два, если есть объекты переменного размера.

Языки типа PHP или Haskell хранят объекты переменного размера в «куче», даже если порядок — FIFO. А ведь могли бы ускорить свою работу.

     2019/01/20 17:37, utkin          # 

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

     2019/01/20 18:08, Автор сайта          # 

Логика тут простая. Вам нужно создать объект, длина которого известна только во время исполнения? И дисциплина их использования — FIFO? Тогда два стека Вам подойдут. FIFO не подходит? Надо удлинять не только последний объект? Тогда используй «кучу».

     2019/01/20 18:22, utkin          # 

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

     2019/01/21 02:43, ВежливыйЛис          # 

Вам нужно создать объект, длина которого известна только во время исполнения?

тогда используйте функцию alloca (http://man7.org/linux/man-pages/man3/alloca.3.html) и не мучайте коллег.

     2019/01/21 08:45, utkin          # 

Вам нужно создать объект, длина которого известна только во время исполнения?

Я вообще не хочу об этом ничего знать. Потому что в каком-нибудь алгоритме вычисления массы начинки пирожка это не нужно. Это нужно системе, а программисту НА ФИГ не упало никуда. Нет НИ ОДНОЙ ПРАКТИЧЕСКОЙ ЗАДАЧИ, где это требуется. Если это должно работать, то так, чтобы я об этом не имел ни малейшего представления, просто потому, чтобы я не мог это испортить своими умными мыслями по поводу того, как это хорошо было бы использовать. Но прозрачного механизма у Вас нет.

     2019/01/21 11:41, Автор сайта          # 

используйте функцию alloca

Недостатки alloca уже разбирались в других статьях, повторяться не буду.

Я вообще не хочу об этом ничего знать.

Да никто не впихивает Вам в мозги какие-то знания. Достаточно было просто пройти мимо этой страницы.

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

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

     2019/01/21 13:03, utkin          # 

Я имел ввиду, что если это будет механизм не встроенный в систему и влияющий на синтаксис, то он не нужен.

Добавить свой отзыв

Написать автору можно на электронную почту mail(аt)compiler.su

Авторизация

Регистрация

Выслать пароль

Карта сайта


Каким должен быть язык программирования?

Анализ и критика

Устарел ли текст как форма представления программы

Русский язык и программирование

Многоязыковое программирование

Синтаксис языков программирования

Синтаксический сахар

Некоторые «вкусности» Алгол-68

«Двухмерный» синтаксис Python

Почему языки с синтаксисом Си популярнее языков с синтаксисом Паскаля?

Должна ли программа быть удобочитаемой?

Стиль языка программирования

Тексто-графическое представление программы

●  Разделители

●  Строки программы

●  Слева направо или справа налево?

Комментарии

●  Длинные комментарии

●  Короткие комментарии

●  Комментарии автоматической генерации документации

●  Нерабочий код

●  Помеченные комментарии

Нужны ли беззнаковые целые?

Шестнадцатиричные и двоичные константы

Условные операторы

Переключатель

Циклы

●  Продолжение цикла и выход из него

Некошерный «goto»

Изменение приоритетов операций

Операции присвоения и проверки на равенство. Возможно ли одинаковое обозначение?

Так ли нужны операции «&&», «||» и «^^»?

Постфиксные инкремент и декремент

Почему в PHP для конкатенации строк используется «.»?

Указатели и ссылки в C++

●  Обработка ошибок

Использование памяти

Почему динамическое распределение памяти — это плохо

Как обеспечить возврат функциями объектов переменной длины?

●  Типы переменного размера (dynamically sized types, DST) в языке Rust

●  Массивы переменной длины в C/C++

●  Размещение объектов в стеке, традиционный подход

●  Размещение объектов переменной длины с использованием множества стеков

●  Размещение объектов переменной длины с использованием двух стеков

●  Реализация двухстековой модели размещения данных

●  Двухстековая модель: тесты на скорость

●  Изменение длины объекта в стеке во время исполнения

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

Можно ли забыть о «куче», если объекты переменной длины хранить в стеке

Безопасность и размещение объектов переменной длины в стеке

Массивы, структуры, типы, классы переменной длины

О хранении данных в стеке, вместо заключения

Реализация параметрического полиморфизма

Описание языка

Компилятор

Отечественные разработки

Cтатьи на компьютерные темы

Компьютерный юмор

Новости и прочее

Последние комментарии

2019/06/25 11:22 ••• VIT1972
Идеальный транслятор

2019/06/05 23:21 ••• kt
К вопросу о совершенствовании языка программирования

2019/06/04 14:52 ••• Неслучайный читатель
Деньги = работа / знание

2019/05/29 00:42 ••• rst256
Программирование без программистов — это медицина без врачей

2019/05/14 16:10 ••• utkin
Обработка ошибок

2019/05/09 18:05 ••• евгений
Русский язык и программирование

2019/04/28 14:08 ••• Автор сайта
Статьи Дмитрия Караваева

2019/04/22 16:19 ••• Автор сайта
Почему языки с синтаксисом Си популярнее языков с синтаксисом Паскаля?

2019/04/03 22:24 ••• Антон
Все голосования

2019/04/02 12:28 ••• Автор сайта
Шестнадцатиричные и двоичные константы

2019/04/02 12:25 ••• Автор сайта
Выбор кодировки для компилятора

2019/03/24 14:55 ••• Автор сайта
Реализация двухстековой модели размещения данных