Andrey Stolyarov

Андрей Викторович Столяров: сайт автора

ПРОГРАММИРОВАНИЕ: ВВЕДЕНИЕ В ПРОФЕССИЮ

краудфандинговый проект

Программирование: введение в профессию. II: низкоуровневое программирование

image of the cover

Аннотация

Во второй том книги «Программирование: введение в профессию» вошли её третья и четвёртая части.

Третья часть книги посвящена программированию на уровне машинных команд на примере ассемблера NASM. Рассматривается «юзерспейсовская» часть системы команд i386, конвенции системных вызовов Linux/i386 и FreeBSD/i386, изучается макропроцессор, раздельная трансляция и работа компоновщика, приведены сведения об арифметике с плавающей точкой.

Четвёртая часть, посвящённая языку Си, включает, кроме собственно описания этого языка, также краткие сведения о библиотеке ncurses; рассказ о том, как использовать компилятор Си без его стандартной библиотеки; дополнительные сведения об инструментах сборки и отладки программ; наконец, в книге приводится краткое описание систем контроля версий CVS и git.

Публикация в бумажном варианте

Опубликовано издательством МАКС Пресс (Москва) в 2016 году. ISBN 978-5-317-05301-7.

Электронная версия

Электронная версия, идентичная печатному изданию, доступна здесь: http://www.stolyarov.info/books/pdf/progintro_vol2.pdf

Статус бумажной версии

Книга распространялась среди донэйторов проекта и поступала в свободную продажу. К настоящему моменту распродана.

Архив примеров программ

Архив, содержащий примеры программ из первого и второго тома, можно скачать здесь: http://www.stolyarov.info/books/extra/progintro_vol1_2_examples.tgz

Напоминаем, что раскрыть этот архив можно командой

   tar -xzf progintro_vol1_2_examples.tgz

Обновлённую версию файла stud_io.inc можно взять здесь: http://www.stolyarov.info/books/extra/stud_io_inc Не забудьте переименовать файл! Это делается так:

  mv stud_io_inc stud_io.inc

From Алешка (unverified) Wed Aug 25 07:49:00 2021 pencil

c

Здравствуйте Андрей Викторович!!! Очень Вам признателен за такой ТИТАНИЧЕСКИЙ ТРУД!!!

у меня вопрос по С.
на странице 202 есть строка как компилировать и линковать библиотеку.

gcc -Wall -g -lm qe.c -o qe

у меня такая запись не работает, сработала только когда я -lm поставил после qe.c.

Так вот, вопросик, в чем может быть в данном случае проблема? это у меня одного так криво работает?

parent From admin profile Wed Aug 25 09:10:59 2021 pencil

userpic

Обсуждалось же

Обсуждалось же уже, причём на этой же странице: http://www.stolyarov.info/books/programming_intro/vol2#comment-1260

Вкратце: gcc версий до 4.* включительно позволяли библиотеки подключать в любом месте командной строки, начиная с 5.* позволять перестали, и это логично (см. принципы работы редактора связей, описанные в части про ассемблер).

А книга уже вообще-то переиздана, и во втором издании сей момент не только исправлен, но и вставлен комментарий на эту тему.

parent From Алешка (unverified) Thu Aug 26 14:58:00 2021 pencil

Благодарю за

Благодарю за ответ, потерялся я слегка, между изданиями=)
Вопрос закрыт=)

From LearningAble profile Wed Feb 17 08:56:00 2021 pencil

Побитовые операции

Добрый день, Андрей Викторович! Подскажите, пожалуйста, для чего в операциях внесения/удаления элемента в массив (Том 2, стр. 75, 1-е изд.) используются операция "and cl, 11111b", если до этого мы в BL уже имеем нужную позицию (число не превосходящее 32) с помощью:

shl ebx, 3
shr bl, 3

Заранее спасибо!

parent From admin profile Thu Feb 18 08:15:45 2021 pencil

userpic

В тексте

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

parent From Anonymous (unverified) Thu Feb 18 15:01:00 2021 pencil

Все верно, это я

Все верно, это я смешал все в кучу, прошу прощения

From Anonymous (unverified) Tue Feb 16 15:14:00 2021 pencil

Может я ошибаюсь.

Андрей Викторович, добрый день! На странице 286 в функции дважды выполняется присваивание переменной lnum единицы, случаем не опечатка? И почему присваиваем именно единицу, по итогу у нас выдается в стандартный вывод число на единицу больше, чем фактически прочитано строк.

parent From admin profile Tue Feb 16 17:11:54 2021 pencil

userpic

Строки в

Строки в текстовых файлах традиционно нумеруются с единицы — например, при выдаче диагностики всякими компиляторами. Работать вроде должно корректно, там же в условии неравенство нестрогое, а в стандартный вывод там символы выдаются (первые 10 строк), а само это число не выдаётся вроде.

По поводу присваивания дважды — вы абсолютно правы.

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

From Alejandro (unverified) Mon Feb 15 04:21:00 2021 pencil

Здравствуйте,

Здравствуйте, Андрей Викторович! Если ещё есть время, проверьте возможные опечатки
1т.
с.90 - h, j, k, l набраны обычным шрифтом
3т.
с.391 - "контроллера DMA" вместо "контроллер DMA"
4т.
с.202 - "источника (например, прочитать из файла) либо если" запятая после закрывающейся скобки

parent From admin profile Mon Feb 15 09:21:02 2021 pencil

userpic

Круто, все три --

Круто, все три -- в точку. Спасибо!

From Anonymous (unverified) Sun Feb 14 15:37:00 2021 pencil

Подскажите, пожалуйста, что я упустил...

Читаю второй том и до сих пор было ощущение, что понимаю вообще все. Но вот в главе про динамическую память в Си встретил пример

for(i = 0; i < 360; i++)
    k[i] = sin((2*M_PI/360.0) * (double)i);

Смотрю как баран на новые ворота. Что означает (double)i? Или что я пропустил и не усвоил?

parent From admin profile Sun Feb 14 16:01:48 2021 pencil

userpic

Это операция

Это операция преобразования типа выражения, см. стр. 233.

From Anonymous (unverified) Tue Feb 9 09:43:00 2021 pencil

Слишком сложный .bss

На стр. 47 вы даете пример, если написать:
db "This is a string", то размер исполняемого файла увеличится на 16 байт, а если написать resb 16 то ассемблер выделит 16 байт, но размер исполняемого файла не увеличится.
Вопрос следующий, если есть информация в исполняемом файле о резервировании какой-то области памяти, то как инструкция которая за это отвечает может иметь нулевой размер(как она может быть вообще прочитана) ?
Что будет если использовать каким-то образом эти 16 байт(секции .bss), файл будет увеличиваться в размере во время исполнения ?

parent From admin profile Tue Feb 9 14:24:36 2021 pencil

userpic

Ответ на ваш

Ответ на ваш вопрос содержится на стр.44, верхний абзац. На всякий случай: обсуждаемый "размер секции .BSS" -- это 32-битное целое беззнаковое, и совершенно пофигу, чему оно равно, нулю или миллиарду, 32 бита и есть 32 бита. Ну, в 64-битном случае будет 64 бита, но от размера секции BSS в любом случае никак не зависит размер числа, в котором этот размер секции записан.

From Anonymous (unverified) Sun Feb 7 13:10:00 2021 pencil

Банальный (наверное) вопрос

Недавно читал параграф, посвящённый макровызовам (стр 110), в котором приводятся примеры в духе:

%macro	pcall3	4
		push	%4
		push	%3
		push	%2
		call	%1
		add	esp,	12
%endmacro

И предлагается в качестве упражнения описать аналогичные макросы pcall4, pcall5, ... pcall8.

После чего задаётся вопрос: "почему стоит остановиться именно на описании pcall8 ?".

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

В итоге у меня остался только вариант "количество параметров не должно превышать 32 байта", но почему это так, мне не совсем понятно, можно какой-нибудь намёк на истину? =)

parent From admin profile Sun Feb 7 13:49:00 2021 pencil

userpic

Всё намного проще

Эти вот %1, %2 и т.д. — в NASMе предполагают всего одну цифру. То есть максимальный параметр макроса, до которого можно дотянуться без команды rotate — девятый. Первый у нас занят под имя подпрограммы, на параметры остаётся восемь.

From Anonymous (unverified) Thu Feb 4 16:09:00 2021 pencil

Почему два байта, а не четыре?

На стр. 48 предоставлены две команды:

mov eax, ebx
mov eax, 5

На стр. 49 Вы рассказываете, что первая команда будет занимать в памяти два байта, почему два? Мы копируем регистр EBX[а он у нас 32-битный в регистр EAX(так же 32-битный)], но(mov eax, 5) как пишут разные источники под непосредственный операнд выделяется 32 бита, следовательно, 32 бита пересылаются в 32-битный регистр, почему пять байт ?
Не могли бы вы, Андрей Викторович, дать пояснение почему в памяти будут два и пять байт в первом и втором случае.

parent From admin profile Fri Feb 5 09:55:40 2021 pencil

userpic

Да-с...

Для начала — разрядность регистра тут ни при чём от слова совсем, то есть вот вообще, то есть никак. Честно говоря, не могу себе представить, из-за чего в голову могло прийти, что размер кода команды как-то связан с разрядностью операндов (и как именно).

Код команды mov eax, ebx будет 89D8, причём на самом деле здесь код команды — только первый байт (89), а откуда получилось D8 — подробно разобрано в книге, см. первый том, стр. 189 (если кратко, там два старших бита означают, что оба операнда регистровые, а младшие шесть — номера самих регистров).

Код команды mov eax, 5 будет B805000000, здесь, формально говоря, код команды — это первые пять бит из байта B8, в том же байте младшие три бита обозначают регистр eax, а вот это вот 05000000 — тот самый непосредственный операнд из пяти байт.

А вот вопрос "почему коды именно такие" не имеет иного ответа, нежели "так придумали создатели процессора".

From Anonymous (unverified) Wed Feb 3 10:39:00 2021 pencil

Определение DB

На стр. 46 там есть пример:
"welmsg db 'Welcome to Cyberspace!'".
Появляется следующий вопрос, как данная строка может влезть в Define Byte, если выделяется всего 1 байт, а строка состоит из 22 символов(соответственно, 22 * 8 = 176 Б) ?

parent From admin profile Wed Feb 3 11:30:37 2021 pencil

userpic

Можете считать,

Можете считать, что это не "define byte", а "define bytes", если вам так удобнее. И db/dw/dd, и resb/resw/resd предполагают выделение не одного элемента указанного типа, а какого-то их количества (в d? количество определяется инициализатором, в res? указывается явно).

From Александр (unverified) Mon Feb 1 18:45:00 2021 pencil

imul - idiv

Здравствуйте Андрей Викторович!
На стр. 61 есть предложение "Знак остатка, вычисляемого командой imul, всегда совпадает ...". Может быть idiv?

parent From admin profile Tue Feb 2 05:13:27 2021 pencil

userpic

Конечно

Эту поймали уже, но всё равно спасибо за внимательность :-)

From Alejandro (unverified) Sun Jan 31 17:51:00 2021 pencil

Опечатки

Здравствуйте, Андрей Викторович! Прошу проверить возможные опечатки опечатки

1 том
с. 73 - возможно стоит уточнить с какими правами программы откажутся запускаться
с. 232 - ошибка в блок-схеме
с. 256-257 - "3 8 7 5" вместо "3 8 5 7" и "5 7 8 3" вместо "7 5 8 3"
с. 375 - возможно лучше "при удалении первого или любого другого элемента"

2 том
с. 253 - NULL (шрифт)
с. 301 - "для кого либо, кроме владельца"
часть 4, с. ??? - "срочки" вместо "строчки"
с. 336 - от 0 до 16 вместо от 0 до 15
с. 348 - 3-ий вариант MYMACRO должен откомпилироваться

3 том
с. 43 - i-node (шрифт)
с. 116 - а [роль] сигналов в жизни вашей программы
с. 146 - процесс может уже быть
с. 207 - других типов файла
с. 227 - объявление res после операторов
Там же - if(res < 1) { /**/ continue;} if(res == 0) { /* никогда не выполнится */ }
Там же - не используется writefds

4 том
с. 59 - reverse_list_to
с. 60 - res в reverse_list
с. 74 - форматная строка
с. 288 - прЕдуманный
с. 293 - )»)
с. 311 - а те, что спецификации соответствовать
с. 339 - седьмой?
с. 415 - isomorpic
с. 490 - vi или vim?

Введение в Си++ (возможно есть в 4-ом томе)
с. 39 - операторы циклов (for, if, while)

PS: капча настолько жуткая, что аж спать захотелось

parent From admin profile Mon Feb 1 10:33:10 2021 pencil

userpic

re: Опечатки

> с. 73 - возможно стоит уточнить с какими правами программы откажутся запускаться

Честно говоря, не понял, чего вы тут хотите. Есть программы (в основном графические), которые проверяют свой euid, и если он нулевой, отказываются стартовать — заявляют, что их нельзя запускать под root'ом. Именно это в тексте и написано.

> с. 232 - ошибка в блок-схеме

А в чём состоит ошибка?

> с. 256-257

есть такое, уже нашли

> с. 375 - возможно лучше

согласен, фраза корявая. Подумаю, как её переформулировать.

> с. 253 - NULL (шрифт)

уже поправлено

> с. 301 - "для кого либо

с этим вам сюда :-)

> "срочки"

уже вывловлено

> с. 336 - от 0 до 16 вместо от 0 до 15

тоже уже выловлено

> с. 348 - 3-ий вариант MYMACRO должен откомпилироваться

Да неужели?! Сами поймёте, почему не компилируется, или объяснить?

> с. 43 - i-node (шрифт)
> а [роль] сигналов
> с. 146 - процесс может

уже поймали

> с. 207 - других типов файла

пожалуй, тут фраза построена криво, переделаю

> с. 227 - объявление res после операторов
> Там же - if(res < 1) { /**/ continue;} if(res == 0) { /* никогда не выполнится */ }
> Там же - не используется writefds

Вот тут вы абсолютно правы по всем пунктам, большое спасибо! Исправлено.

> с. 59 - reverse_list_to
> с. 60 - res в reverse_list

уже выловлено

> с. 74 - форматная строка

Ого! Что значит замыленный взгляд — я этот фрагмент ко второму изданию правил в хвост и в гриву, но вот этого косяка так и не заметил. Спасибо!

> с. 288 - прЕдуманный

факт

> с. 293 - )»)

Уже поймали

> с. 311 - а те, что спецификации соответствовать

Бррр, а где ошибка-то?

> с. 339 - седьмой?

поправил на "с индексом 7", чтобы исключить разночтение

> с. 415 - isomorpic

факт

> с. 490 - vi или vim?

Конечно, vi. Я подозреваю, что vim'а ещё не было, когда этот интерпретатор писали :-) но дело даже не в этом, vim всё-таки ставить надо, а vi, по идее, должен присутствовать в любой *nix-системе (хотя в наше дебильное время это уже не так).

> операторы циклов (for, if, while)

И ведь тоже факт, да.

В общем, спасибо огромное, целая пачка косяков не попала во второе издание.

> PS: капча настолько жуткая, что аж спать захотелось

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

parent From Alejandro (unverified) Mon Feb 1 11:32:00 2021 pencil

Во второй

Во второй блок-схеме последующие итерации цикла осуществляются при ложном условии (yes и no местами перепутаны).

Ума не приложу, как я мог в "кого-либо" дефис пропустить, но я имел в виду запятую перед "кроме".

На счет MYMACRO: решил перепечатать Ваш пример, и все сразу понял :) Не учел, что он вызывается в операторе if, у которого есть else. Заодно задумался, насколько же Си все-таки кривой язык.

По поводу 4 т. с 311.: перечитал и понял, что это уже я ошибся (подумал, что слово пропущено).

vi: Сейчас попробовал дать команду vi, а в итоге открылся vim O_O

PS: Сайт, на который Вы дали ссылку, не работает с отключенным js.

parent From admin profile Mon Feb 1 12:52:45 2021 pencil

userpic

> Во второй

> Во второй блок-схеме последующие итерации цикла осуществляются при ложном условии

В Паскале именно так и происходит, это же не Си. После слова until записывается условие выхода из цикла (а не продолжения цикла, как в сишном do-while).

> я имел в виду запятую перед "кроме".

А, ну вообще да, пожалуй, тут она нужна. Это на меня в очередной раз подействовали отголоски английской пунктуации :)

> насколько же Си все-таки кривой язык.

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

> vi: Сейчас попробовал дать команду vi, а в итоге открылся vim

Ну кто-то же должен "быть за него" :) Больше того, vim имеет специальный режим совместимости, в котором работать с непривычки несколько тяжко.

> PS: Сайт, на который Вы дали ссылку, не работает с отключенным js.

Чёрт, это косяк, да, буду внимательнее.

From djek0 (unverified) Sun Jan 31 13:34:00 2021 pencil

Опечатка? Стр.

Опечатка? Стр. 159, 1-й том:
"... пока результат оДного не потребуется для другого вычисления."

parent From admin profile Sun Jan 31 14:05:14 2021 pencil

userpic

Нет здесь

Нет здесь никакой опечатки.

Мда. Вот и выросло поколение, не знающее слова "оный".

From Александр (unverified) Sat Jan 30 18:15:00 2021 pencil

Исполнительный адрес

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

parent From admin profile Sun Jan 31 14:08:27 2021 pencil

userpic

Нет, не мог бы.

Нет, не мог бы. В основном это вопрос не ко мне, а к создателям процессора. В документации обычно отражается то, КАК устроены команды, но никто не рассказывает о том, ПОЧЕМУ всё сделано именно так, можно лишь догадываться.

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

From farvater profile Fri Jan 29 12:01:00 2021 pencil

На стр.144В этом

На стр.144
В этом примере мы не обрабатываем ошибки, предполагая, что запись в стандартный поток ввода всегда успешна
Не должен ли быть тут поток вывода?

parent From admin profile Fri Jan 29 21:23:05 2021 pencil

userpic

Абсолютно

Абсолютно верно, и таки да, до сих пор никто не заметил. Спасибо!

Сколько ж там ещё таких "жуков", а?...

From farvater profile Sat Jan 23 17:06:00 2021 pencil

Опечатка

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

parent From admin profile Sat Jan 23 21:15:13 2021 pencil

userpic

Спасибо!

Самое забавное, что нет — до сей поры не заметил её никто. Ещё бы чуть-чуть, и быть ей во втором издании.

parent From farvater profile Sun Jan 24 13:48:00 2021 pencil

Тогда

Тогда проверьте еще стр. 80. Не закрыта открывающая скобка.
...например cli и sti для IF(interrupt flag, но воспользоваться ими в ограниченном режиме нельзя.

parent From admin profile Sun Jan 24 14:50:32 2021 pencil

userpic

Эту уже

Эту уже заметили :-) но всё равно спасибо.

From Anonymous (unverified) Sat Jan 23 08:22:00 2021 pencil

функция open

Андрей Викторович, а можете объяснить, как реализована функция open (вернее, ее вариант с третьим аргументом perms). Судя по заголовку, она не вариадическая? Как же тогда, если в Си нет перегрузки функций?

parent From admin profile Sat Jan 23 21:13:15 2021 pencil

userpic

Сами в

Сами в заголовочник заглянуть никак не можете? Там (в /usr/include/fcntl.h) функция open объявлена именно что как вариадическая, два аргумента заданы, вместо третьего многоточие. Естественно, в Си иначе и не получится.

From Давид (unverified) Thu Jan 7 19:42:00 2021 pencil

Здравствуйте.

Здравствуйте. Небольшое замечание для страницы 439. Сейчас, если создавать 32-битный бинарь на 64-битной архитектуре, требуется передать -m32 в команду gcc, а также передать -m elf_i386 для ld.

У меня сработало в таком виде:
nasm -f elf start.asm // тут как раз флаг -f указывает на создание 32-битного
nasm -f elf calls.asm
gcc -m32 -Wall -c greet3.c // если не указать -m, то будет 64-битный. И тогда линкер будет ругаться на несовместимость типов объектных файлов
ld -m elf_i386 start.o calls.o greet3.o -o greet // здесь тоже для указания именно 32-битного

2 с лишним дня убил на эти ошибки. Кому-то да пригодится. И, если честно, что-то сейчас никакой экономии в размере толком не увидел почему-то, без символов получилось 13Kb. Как получилось меньше килобайта в книге - без понятия)

parent From admin profile Sat Jan 9 06:44:37 2021 pencil

userpic

Про -m32 да, в

Про -m32 да, в исходнике уже добавлено, ко второму изданию всё будет. Про размер ничего сказать не могу, крайне странно — не должно такого быть.

parent From Alejandro (unverified) Tue Feb 2 18:38:00 2021 pencil

Тоже долго

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

nasm -f elf32 start.asm -o start.o
nasm -f elf32 calls.asm -o calls.o
gcc -Wall -Wextra -ansi -pedantic -m32 -c greet.c -o greet.o
ld -m elf_i386 -s --omagic start.o calls.o greet.o -o prog

Итоговый бинарник "похудел" с 12720 байт до 1088. Еще немного сбросит (до 922), если добавить для gcc флаг -O2 (как-то это странно).
При наличии аргумента командной строки программа выполнила 4 системных вызова, без него - 2.

------------------------------

Андрей Викторович, отдельно хотелось бы обратить Ваше внимание на некоторые моменты в 4-ом томе:
с. 174 - использование одного и того же кода для разных ошибок
c. 224 - "make_il" вместо "make_el" (опечатка?)

Также интересует принципы оформления ссылок, так как в книге не видно единообразия (например, int& и &Get()). И еще хочется узнать, планируется ли в новом издании глава про оформление кода на Прологе (она, вроде, не такая большая).

parent From admin profile Tue Feb 2 18:52:21 2021 pencil

userpic

А можно узнать

А можно узнать версии gcc, ld и nasm? Про остальное завтра посмотрю, сейчас некогда, сорри.

parent From Alejandro (unverified) Tue Feb 2 19:15:00 2021 pencil

gcc - 9.3.0nasm -

gcc - 9.3.0
nasm - 2.14.02
ld - 2.34

parent From admin profile Wed Feb 3 10:15:20 2021 pencil

userpic

Ага, вот тут собака и порылась

avst@host:~$ gcc --version
gcc (Debian 4.9.2-10+deb8u1) 4.9.2
[...]

avst@host:~$ nasm -v       
NASM version 2.11.05 compiled on Sep  9 2014
[...]

avst@host:~$ ld --version
GNU ld (GNU Binutils for Debian) 2.25
[...]

avst@host:~$ ls -l greet
-rwxr-xr-x 1 avst avst 1580 Feb  3 13:11 greet
avst@host:~$ uname -a
Linux frock 3.16.0-4-amd64 #1 SMP Debian 3.16.43-2+deb8u5 (2017-09-19) x86_64 GNU/Linux

Вот кто-нибудь ещё после этого верит в существование какого-то там "технического прогресса" в области IT?

parent From Alejandro (unverified) Wed Feb 3 00:15:00 2021 pencil

Чёрт, рано

Чёрт, рано радовался. Такое "решение" хуже самой страшной проблемы. Оказывается, ради экономии памяти этот --omagic заставляет редактор связей объединить секции .data и .text в одну секцию .text, содержимое которой делает доступным и для записи, и для исполнения. readelf это только подтвердил.

Какой ужас.

parent From admin profile Wed Feb 3 10:24:41 2021 pencil

userpic

> с. 174 -

> с. 174 - использование одного и того же кода для разных ошибок

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

> c. 224 - "make_il" вместо "make_el"

Где вы там вообще _el нашли? Везде _il, в том числе в имени print_il. Имелость в виду int list.

> (например, int& и &Get())

Я старался придерживаться простого принципа: когда есть что-то справа, то звёздочка и амперсанд прилипают вправо, когда справа ничего нет — например, надо просто сказать, что кто-то там имеет тип ссылка на int, не показывая этого кого-то — то прилипает влево.

> И еще хочется узнать, планируется ли в новом издании глава про оформление кода на Прологе (она, вроде, не такая большая).

Кстати, да, это мысль.

From Давид (unverified) Sun Jan 3 08:00:00 2021 pencil

Здравствуйте.

Здравствуйте. Возможно, опечатка на странице 284.

> "в зависимости от значения perror"
Разве не "значения errno"?

parent From admin profile Sun Jan 3 09:59:31 2021 pencil

userpic

Уже поправлено,

Уже поправлено, но всё равно спасибо :-)

From Anonymous (unverified) Mon Dec 14 11:28:00 2020 pencil

Возможные опечатки в I томе

Доброго дня, Андрей Викторович!

Прошу проверить возможные опечатки:
с.53
соответстветствующее
c.70
PC-BSD (на википедии "TrueOS (formerly PC-BSD or PCBSD)")
c.128
217A -> 217B
с.197
writeln('Hello, world') -> writeln('Hello, world!')
с.216
quad -> square
с.243
var i: integer; (без перевода строки)
с.264
end -> end;
с.292
3. ... кроме набольшего ...

с.299
for i := 2 to MaxSchool do
с.300
for i := 1 to MaxSchool do

с.340
halt -> halt(1)
с.360
символом "?!" -> символами "?!"
с.412
";" перед end
var p, q: ... (нет перевода строки)
c.413
";" перед end

Возможные неточности в архиве:

";" перед end:

colordemo.pas
ст.42
diamond.pas
ст.19,21,33,35
diamondp.pas
ст.18,20,34
frcancel.pas
ст.17
hanoi2.pas
ст.36,43,110
hanoi3.pas
ст.11,70,72,89
hanoi.pas
ст.26
hello20.pas
ст.9
message_n.pas
ст.9
movingstar.pas
ст.40,55,75
olympcount.pas
ст.28
skip_indented.pas
ст.22
star_slash.pas
ст.9

лишние пробелы после строки:

gennubin.pas, gennumtx.pas
ст.21
hanoi.pas
ст.26
hanoi2.pas
ст.110
hanoi3.pas
ст.89

Большое спасибо за книги!

parent From admin profile Fri Dec 18 11:26:13 2020 pencil

userpic

Спасибо!

> соответстветствующее

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

> TrueOS

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

> 217A -> 217B

Уже известна, следующие три тоже, но всё равно спасибо за внимательность :-)

> var i: integer; (без перевода строки)

В принципе это допустимо, но Вы, пожалуй, правы, единый стиль лучше поддерживать везде.

следующие две известны

> for i := 1 to MaxSchool do

Как говорят, good catch. Спасибо, поправил.

> halt -> halt(1)

Пожалуй, согласен. Поправил.

> с.412

Уже :-)

> ";" перед end:

Часть было уже поправлено, но примерно две трети — новых. Огромное спасибо.

> лишние пробелы после строки:

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

Спасибо за пост, вы мне очень помогли!

parent From Anonymous (unverified) Fri Jan 15 22:45:00 2021 pencil

А известно ли

А известно ли про опечатку на ст. 92?
Права на чтение соответствуют 1, ...
1 в восьмеричной это 001 в двоичной системе и он должен соответстовать правам на исполнение? Хотя дальше, в тексте, 1 используется для обозначения прав на исполнение.
Спасибо.

parent From admin profile Sat Jan 16 09:51:40 2021 pencil

userpic

Эту почему-то

Эту почему-то всегда находят первой :-)

From Давид (unverified) Sun Dec 13 05:39:00 2020 pencil

Здравствуйте,

Здравствуйте, страница 79. Несколько вопросов-замечаний по примеру с дополнением строки "This is a string" подстрокой "long ".

Во-первых, buf2 в объявлении секции .data должен идти раньше buf1, в противном случае когда мы выполним mov edi, buf1+17+5 мы перезапишем buf2. То есть должно быть так:

buf2 db "long "
buf1 db "This is..."

-----

Во-вторых, непонятен момент когда опять же мы сдвигаем buf1 на 22 байта во второй строке. Учитывая, что buf1 будет выше по стеку, чем buf2, теоретически мы можем попасть в область памяти, которая не принадлежит нашему процессу? В таком случае надо объявлять дополнительный буфер поверх buf2? К примеру

buf2 db "long "
buf1 db "This is..."
buf3 db " " ; много символов пробела

Тогда при сдвигании мы попадем в область дополнительного буфера, а это уже точно наша память.

-----

В-третьих, зачем сдвигать на 8 байтов во 2ой строке, когда нам нужно сдвинуть слово "string", а оно длиной в 6 символов.Тогда код со 2ой по 4ую строку преобразуется

mov edi, buf1+20
mov esi, buf1+15
mov ecx, 6

-----

Поправьте, если где-то ошибся. Спасибо.

parent From admin profile Fri Dec 18 11:48:00 2020 pencil

userpic

Эк

> в противном случае когда мы выполним mov edi, buf1+17+5 мы перезапишем buf2

А ничего, что они там (в самом верху той же страницы) описаны каждый по 1024 байта?

> Учитывая, что buf1 будет выше по стеку, чем buf2

По какому ещё стеку, простите? В этом примере никакой стек не используется.

> сдвигать на 8 байтов во 2ой строке

Там сдвигается не "на" восемь байтов, там восемь -- это количество байтов, которые копируются. Но вообще да, восемь тут многовато будет :-) Да и 17, кстати, тоже.

parent From Давид (unverified) Fri Dec 18 14:43:00 2020 pencil

Не обратил

Не обратил внимания что сверху объявляется по 1024 байта. Я как раз и столкнулся с проблемой, когда объявил строки в секции .data, а не в .bss.

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

Первые 2 пункта не актуальны :)

parent From admin profile Fri Dec 18 15:14:45 2020 pencil

userpic

если объявлять

если объявлять строки в .data, то порядок того, как они будут лежать в памяти с точностью до наоборот

Это ещё с какого бодуна? Вас кто-то обманул:

crocodil@frock:~/work/misc$ cat hello.asm
global  _start

section .data

hel             db "Hello, "
wrl             db "world!", 10
len             equ $-hel

section .text

_start:         mov eax, 4
                mov ebx, 1
                mov ecx, hel
                mov edx, len
                int 80h
                mov eax, 1
                mov ebx, 0
                int 80h
crocodil@frock:~/work/misc$ nasm -f elf hello.asm 
crocodil@frock:~/work/misc$ ld -m elf_i386 hello.o -o hello
crocodil@frock:~/work/misc$ ./hello
Hello, world!
crocodil@frock:~/work/misc$

parent From Давид (unverified) Fri Dec 18 15:48:00 2020 pencil

Действительно,

Действительно, значит где-то я ошибся. Буду разбираться. Спасибо за ответ!

From Anonymous (unverified) Sat Nov 28 14:38:00 2020 pencil

Опечатка?

На с. 45:

... в четвёртое - число 5 и т.д.

Это, вроде бы, опечатка, там должно быть число 3? В старой версии книги тоже самое.

parent From admin profile Sat Nov 28 18:01:47 2020 pencil

userpic

Есть такая, уже

Есть такая, уже нашли. Спасибо за внимательность :)

From Иван (unverified) Tue Nov 24 02:31:00 2020 pencil

Фрагментация динамической памяти

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

parent From admin profile Tue Nov 24 07:49:26 2020 pencil

userpic

В книге этого

В книге этого нет. Насколько я понимаю, при использовании современных (ну как современных... лет за 25 последних) версий malloc/free правило стека неактуально, поскольку от хранения свободных блоков в виде списка давно уже отказались, сейчас там что-то вроде бинарного дерева, в котором сиблинги автоматически сливаются.

parent From Anonymous (unverified) Wed Nov 25 02:45:00 2020 pencil

А что

А что подразумевается под правилом стека?

parent From admin profile Wed Nov 25 19:04:53 2020 pencil

userpic

Освобождать

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

parent From Иван (unverified) Wed Nov 25 21:57:00 2020 pencil

Проблема всё таки актуальна в наши дни.

Я так понял, это проблема существует и поныне. И если не придерживаться некоторых правил, то можно быстро дыр в памяти наделать. Вот тут описываются рекомендации https://cpp4arduino.com/2018/11/06/what-is-heap-fragmentation.html .
В любом случае, Андрей Викторович, на протяжении чтения всего второго тома, этот вопрос мне не давал покоя. Может его как-то стоит осветить в книге?

parent From admin profile Wed Nov 25 22:53:00 2020 pencil

userpic

Вы таки издеваетесь? :)

Сослаться на сайт, где люди на полном серьёзе применяют Си++ (!) на микроконтроллерах (!) и на этом основании говорить, что проблема актуальна — это, я бы сказал, эпично.

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

UPD: а в статье не просто Си++, там C++17. Точно буйные.

parent From Иван (unverified) Wed Nov 25 23:50:00 2020 pencil

Дак ведь нам,

Дак ведь нам, студентам, читающим ваши книги, ещё неведомы эти хитросплетения: "плюсы-не плюсы". Важно, что вопрос по хранению и управлению блоками динамической памяти, возникает в голове сразу после того, как вы вводите этот материал. К тому же, вы учите не самому Си, а пониманию, как там-всё это за кадром работает. Вы хотя бы успокойте где-нибудь, мол управление через умную кучу и всё инкапсулировано, и нечего лезть, куда тебя не просят; всё будет работать, выделяй-освобождай, Вася, как душе угодно.
А ссылку я вам скинул именно эту, потому, что там всё оформлено красиво и по полочкам. Подобные темы и на хабре и на rsdn обсуждаются.
http://rsdn.org/forum/cpp/908825.flat

parent From admin profile Thu Nov 26 11:49:00 2020 pencil

userpic

Во-первых,

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

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

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

From Anonymous (unverified) Tue Nov 17 15:14:00 2020 pencil

Здравствуйте,

Здравствуйте, Андрей Викторович.
Во втором томе, на странице 402:
Отметим, что двух идентификаторов в описании переменной быть не может, поэтому параметр функции, который в описани указателя на эту функцию должен быть безымянным....
а полностью описание указателя на функцию replace_f(или другую с тем же профилем) окажется таким:
int(*(*replace_f_ptr(int(*)(int,int)))(int,int);

Как читателю, этот момент с безымянным указателем мне показался непонятным. Почему непременно должно быть такое описание, а не int(*(*replace_f_ptr(int(*fun)(int,int)))(int,int); ? Я так понял, вы имели ввиду, что бессмысленно указывать второй идентификатор, а не то, что это приведёт к ошибке компиляции. Но если я всё-таки всё правильно понял, то идентификатор с точки зрения лучшей читаемости кода можно было оставить.
https://stackoverflow.com/questions/64874499/complex-type-declaration-with-an-anonymous-profile-as-a-parameter

parent From admin profile Tue Nov 17 16:14:00 2020 pencil

userpic

Ну да, есть такое

Когда-то давно — это я точно помню, мне тогда ещё двадцати не было, а в том возрасте события запоминаются ярко и подробно — я столкнулся с компиляторами, запрещавшими больше одного идентификатора в любом описании/объявлении, за исключением заголовков функций (в смысле настоящих заголовков функций, не являющихся частями описания переменных или типов). Сейчас проверил — компилятор жрёт больше одного идентификатора даже в режиме -ansi -pedantic; но тот компилятор, про который я рассказываю, мне попался году этак в 1993, и он, скорее всего, относился к эпохе до пришествия стандартизаторов.

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

int (*replace_f(int (*func)(int a, int b)))(int c, int d)
{
    return func;
}

int (*(*replace_f_ptr)(int (*func)(int a, int b)))(int c, int d) = replace_f;

Эта штука всё ещё компилируется.

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

А, ну и ещё, раз уж вы ссылку на stackoverflow тут кинули (руки бы оторвал разработчикам этого сайта, но это нынче едва ли не всех сайтов касается) — там вам уже показали пример, когда такие идентификаторы "имеют смысл, потому что задают размерность массива в очередном параметре". Ну так вот, там не уточнили, что, во-первых, это C99; во-вторых, это VLA; в-третьих, в приличных проектах за использование VLA отрывают голову, даже если другие возможности из C99 не запрещены.

parent From Иван (unverified) Tue Nov 17 19:15:00 2020 pencil

Тут сбивало с

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

From Mg (unverified) Sun Nov 15 19:41:00 2020 pencil

Первый том

Первый том очень понятный(за исключением параграфов 1.5.5-1.5.7) и помог мне сделать хоть какой-то прогресс. Сделал игру "змейку", использовав ваш код про очередь и про "звездочку" и немножко их изменив. С другими книгами по программированию я топтался на месте так и не закончив. Спасибо большое вам за книги! Какие книги посоветуете на счет параграфов 1.5.5-1.5.7?

parent From admin profile Mon Nov 16 11:08:19 2020 pencil

userpic

А что, параграф

А что, параграф 1.5.4 (который про бесконечности) осилили? Если да, то всё в порядке, просто забейте на 1.5.5--1.5.7, вернётесь к ним позже, когда освоитесь с программированием и будете примерно представлять, о чём идёт речь. Если же нет -- ну, это хуже, но программировать можно и без этого.

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

From (unverified) Tue Nov 10 21:21:00 2020 pencil

В введении в

В введении в программирование на странице 30 допущена неточность: Мнемоника jl есть сокращение от "jump if Less", а не Lower.

parent From admin profile Tue Nov 10 21:33:00 2020 pencil

userpic

Вы делаете

Вы делаете весьма распространённую ошибку. Нет, на самом деле именно Lower, как противоположность Greater. Противоположные мнемоники используют в названии букву g (jg, jge, jng, jnge). Если бы jl было от "less", противоположным словом было бы "more", а мнемоники были бы jm, jme, jnm, jnme. Кстати, "jump if no more" мне чем-то нравится по звучанию :-)

UPD: Вынужден взять свои слова назад, был неправ. Посмотрел разнообразные справочники, везде именно less, lower вообще нигде не встречается. Видимо, это некая языковая тонкость, которая от меня ускользнула. Приношу свои извинения за попытку дезинформации.

parent From Anonymous (unverified) Thu Nov 12 10:46:00 2020 pencil

Обращаю ваше

Обращаю ваше внимание на то, что на странице 65 той же книги вы пишите: "... в процессорое i386 предусмотрена команда jl (от слов jump if less than ...)"

parent From admin profile Thu Nov 12 11:52:49 2020 pencil

userpic

А вот это уже

А вот это уже опечатка, да. Спасибо.

From Anonymous (unverified) Mon Nov 2 18:27:00 2020 pencil

Желательно ввести дополнительный комментарий

День добрый.
В книге встречаются примеры вида
double dbl_sum(const double *a, int size)
{
return size > 0 ? *a + dbl_sum(a+1, size-1): 0;
}
(стр. 394, вторая книга)
понятно, что пример учебный, дабы "вбить в голову" как работает рекурсия лишний раз. И возможно даже компилятор его оптимизирует в итерацию. Но все же не плохо бы уточнить каждый раз по возможности комментарием вида "не смотря на изящность решения, при большом размере массива лучше так не делать, пример учебный, чтоб закрепить понимание рекурсии". А то ведь студенты читают.

parent From admin profile Fri Nov 13 22:02:24 2020 pencil

userpic

Эту рекурсию

Эту рекурсию нельзя преобразовать в итерацию, она же не хвостовая. По поводу комментария подумаю.

From Schrodinger7Cat (unverified) Sun Nov 1 15:55:00 2020 pencil

Опечатка стр 326 том 2

Опечатка в фрагменте кода внизу страницы.
Написано:

if(first){
first = first->next;
while(first){
free(first->last);
first = first->next;
...

должно быть:
f(first){
first = first->next;
while(first){
free(first->prev); /* here was error */
first = first->next;
...
P.S. все же не удобно пользоваться PDF без поиска и возможности копирования.

parent From admin profile Sun Nov 1 17:42:40 2020 pencil

userpic

Эту уже нашли,

Эту уже нашли, там ещё абзацем выше вместо first должно быть last. Спасибо за внимательность :-)

From Anonymous (unverified) Mon Oct 26 09:40:00 2020 pencil

Ошибка ?

Во втором томе на стр. 182 сказано:
В результате сравнения при неравенстве операндов C3->0, C0->1 (если st0 > st1), или ->0 (если st0 < st1). ... флаг C0 оказывается аналогичен флагу CF (для беззнаковых чисел).

Но ведь это не так. При сравнении беззнаковых чисел, CF устанавливается в 1, если уменьшаемое меньше вычитаемого.
В табл. 3.3 описывается jb (a < b) (CF=1).

Например программа ниже, на моем компьютере, выводит CF=0.

global _start
section .data
x               dt 111.0
y               dt 33.0
section .text
_start:    fld tword [y]
           fld tword [x]           ; st0=x, st1=y
           fcom                    ; x > y
           fstsw ax                ; C0=1 ?
           sahf                    ; C0->CF

           jc .cf
           print 'CF=0'
           jmp .end
.cf:       print 'CF=1'
.end:      syscall 1, 0

При этом все становится на свои места, если сказать:
В результате сравнения при неравенстве операндов C3->0, C0->0 (если st0 > st1), или ->1 (если st0 < st1).

parent From admin profile Mon Oct 26 21:29:59 2020 pencil

userpic

Там перепутаны

Там перепутаны "больше" и "меньше", это уже обнаружили.

А что у вас за print и syscall? Вроде никаких файлов с макросами не подключается?

parent From Anonymous (unverified) Tue Oct 27 08:28:00 2020 pencil

print и syscall - мои макросы

В программе действительно не хватает "%include ...". Это ошибка, впрочем, не меняющая сути.

From Евгений Овчинников (unverified) Sun Oct 25 05:44:00 2020 pencil

На стр.157

На стр.157 второго тома есть код:
err2len equ $-err1msg

Вероятно должно быть:
err2len equ $-err2msg

parent From admin profile Sun Oct 25 12:17:35 2020 pencil

userpic

Ффффффак... в смысле, факт

Что характерно, в архиве примеров в файле copy.asm эта фигня тоже есть, а в файле asmcopy/message.asm, где имеется копия того же самого фрагмента, огрех таки исправлен. Вот мне, наверное, хорошо было, когда я это всё писал.

Большое спасибо за выявление этой ошибки, это очень своевременно — теперь во втором издании будет на одну ошибку меньше.

From Narek (unverified) Sat Oct 17 23:42:00 2020 pencil

Опечатка

Здравствуйте. На странице 122 есть фрагмент кода:
mov ecx, array
mov esi, arr_len

Вы перепутали ecx и esi местами.
Должно быть:
mov ecx, arr_len
mov esi, array

Большое спасибо за великолепные книги.

parent From admin profile Sun Oct 18 09:32:00 2020 pencil

userpic

Да нет, почему

Там же как раз пример "что будет, если". Чтобы всё сломалось, их как раз и надо было перепутать.

From Anonymous (unverified) Mon Sep 21 06:31:00 2020 pencil

Опечатка к первому тому

Добрый день!
Стр 376. "pp:= @first" должно быть pp:= first

parent From admin profile Tue Sep 22 13:10:00 2020 pencil

userpic

Да неужели? :-)

Перечитайте параграф с самого начала, вы в нём, по-видимому, ничего не поняли. NB: если здесь убрать операцию взятия адреса, получится несоответствие типов, поскольку first имеет тип itemptr (указатель на item), тогда как pp имеет тип ^itemptr, т.е. указатель на указатель на item.

From Anonymous (unverified) Tue Sep 15 16:43:00 2020 pencil

Том 2 Стр. 371 Опечатка

"Что касается дополнительного отступа для для всего тела..."

parent From admin profile Wed Sep 16 15:24:50 2020 pencil

userpic

Уже известна,

Уже известна, но всё равно спасибо :-)

From Anonymous (unverified) Mon Sep 14 16:10:00 2020 pencil

Том 1 Стр. 54 Опечатка

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

parent From admin profile Wed Sep 16 15:23:55 2020 pencil

userpic

Да нет, почему.

Да нет, почему. Ну то есть я согласен, получилось коряво, но с "единицами", по-моему, будет ещё корявее. Надо подумать, как это переформулировать.

From Lazy Lone Lion (unverified) Sun Sep 6 17:01:00 2020 pencil

Ошибка в первом томе.

К сожалению я не в курсе, исправлял ли кто-то её или нет.
На 261 странице первого (!) тома сказано:
""... 7Е3 -- это то же самое что 700.0 ..."

Что не верно. Ибо 7Е3 = 7000.0 а не 700.0

parent From admin profile Sun Sep 6 18:32:42 2020 pencil

userpic

Спасибо!

Раньше эту ошибку никто не заметил, и вы тут совершенно правы. Большое спасибо.

From Radegast (unverified) Wed Sep 2 21:00:00 2020 pencil

На странице 399, Том 2 нашлась очепятка )

Андрей Викторович, здравствуйте!
На странице 399 Тома 2 в описании функции обнаружил опечатку:
...
int_bin_tree_traverse(r->left);
(*callback)(r->val, userdata);
int_bin_tree_traverse(r->right);
...

Не хватает параметров в рекурсивных вызовах. Вот такая штука у меня заработала:
...
int_bin_tree_traverse(r->left, callback, userdata);
....
int_bin_tree_traverse(r->right, callback, userdata);

Спасибо Вам огромное за Ваши труды!!! Успехов во всех начинаниях!!!

parent From admin profile Thu Sep 3 09:39:58 2020 pencil

userpic

Есть такая, уже

Есть такая, уже нашли. Вы совершенно правы, спасибо за внимательность.

From COOL_NICK (unverified) Sat Aug 22 23:50:00 2020 pencil

Недопонимание

На 154 странице 2-ого тома фрагмент кода:

%macro	kernel 1-*

%ifdef OS_FREEBSD

  %rep %0
    %rotate -1
	  	push dword %1
  %endrep
		mov eax, [esp]
		int 80h
		jnc %%ok
		mov ecx, eax
		mov eax, -1
		jmp short %%q
  %%ok:
		xor ecx, ecx
  %%q:
		add esp, (%0-1)*4

%elifdef OS_LINUX
...

Не могу понять, почему к esp прибавляется (%0-1)*4 вместо %0*4, ведь команда push dword %1 вызывалась %0 раз.

parent From admin profile Sun Aug 23 10:19:44 2020 pencil

userpic

Это просто ошибка

Её даже недавно выловили, хотя перед этим она просуществовала больше десяти лет. Должно быть действительно (%0 * 4).

From (unverified) Fri Aug 14 19:59:00 2020 pencil

Добрый день.

Добрый день. Второй том, с. 442:
реультат макропроцессирования выдаётся на стандартный вывод (в абзаце про ключ -E)
Касательно -S: можно добавить -masm=intel и получить ассемблерный выхлоп с синтаксисом Intel.

parent From admin profile Fri Aug 21 05:47:56 2020 pencil

userpic

thanks

Спасибо, опечатку вы заметили первым. Про asm=intel тоже стоит упомянуть, во втором издании добавлю.

From putilin (unverified) Fri Jul 10 15:09:00 2020 pencil

Опечатки в I томе

Комментарии к I тому закрыты, поэтому пишу сюда.
Прошу проверить возможные опечатки:

с. 243, 258, 284, 300, 328, 332, 366, 391, 395
";" в строках перед end.

c. 253
Вызов процедуры должен быть
PrintChars(ch; count - 1)

с. 278
Вместо ''''' требуется '''

с. 331
Массив AllColors не меняется, можно задать как const.

При вызове процедуры MakeLine происходит лишнее неявное преобразование типа
fgcolor - ожидается integer, подаётся word.
Можно исправить описание процедуры:
procedure MakeLine(line: integer; fgcolor: word)

Лишняя ";" после вызова MakeLine.

*в книге не описана возможность занесения в массив элементов типа word
значений типа string: (Black, Blue, Green, Cyan...) как в перечислимый тип.

c. 371
Если решается 1я задача со стр. 359, надо использовать read вместо readln - без
перевода строки.

Вызов функции SOLEmpty(s) д.б. SOLIsEmpty(s).

c. 373
В функциях QOLGet и QOLIsEmpty queue^.first и queue^.last заменить на
queue.first и queue.last.

c. 383
Параметры функций и процедур должны разделяться ";", а не ",".

c. 398
if MatchIdx(idxs+i, idxp+1) then
заменить на
if MatchIdx(str, pat, idxs+i, idxp+1) then

с. 399
На с. 398 в тексте предполагается, что Match использует параметры-значения, но
функция описана с параметрами-переменными - испарвить на
function Match(str, pat: string): boolean;

if ParamCount < 3 then
заменить на
if ParamCount < 2 then
Ожидается 2 параметра.

c. 437 (2 места)
[ x"$c $d" != x"$res" ] заменить на
[ "$c $d" != "$res" ]

Большое спасибо за книги!

parent From admin profile Sat Jul 11 12:44:48 2020 pencil

userpic

Круто

с. 243, 258, 284, 300, 328, 332, 366, 391, 395

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

(они, конечно, и так работают — и тем труднее такие штуки вылавливать замыленным взглядом, особенно для человека, привыкшего к Си, а я именно такой)

c. 253

Уже известно, но всё равно спасибо за внимательность :-)

с. 278

Вот ни фига. Там идея в том, что перед символом выдаются два обратных апострофа, а после символа -- два прямых. А чтобы их выдать, нужно на каждый внутри строки нарисовать два подряд, что там и сделано -- первый открывает строковый литерал, следующие четыре дают два символа апострофа. При всём при этом обратные апострофы -- это простые символы, их "удваивать" не нужно.

с. 331 Массив AllColors не меняется, можно задать как const.

Я сознательно отказался от рассмотрения типизированных констант (см. замечание на стр. 260), а нетипизированных таких вроде бы не бывает.

лишнее неявное преобразование типа

Пожалуй, соглашусь, хотя ни лишнего машинного кода, ни потери информации здесь не происходит.

*в книге не описана возможность занесения в массив элементов типа word значений типа string: (Black, Blue, Green, Cyan...) как в перечислимый тип.

Я не вполне понимаю, при чём тут перечислимый тип и тем более тип String. Инициализация массивов описана на стр.300-301.

с. 371

тоже уже известный косяк

SOLIsEmpty(s)

Да, факт. Спасибо.

с. 373

И про это тоже уже знаю

с. 383

Да, факт, и это вы первый заметили.

c. 398

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

с. 399

и с этим та же ситуация

c. 437 (2 места)

Я привык к тому, что аргументы команды test (она же квадратные скобки) не могут быть пустыми. В современных версиях bash и sh этого ограничения уже нет, как я только что выяснил. Видимо, к следующму изданию всё-таки поправлю.

From Александр (unverified) Sun Jun 28 21:47:00 2020 pencil

Возможно опечатка

На странице 284 6-я строчка сверху: "... выбираемое в зависимости от значения perror". Думаю должно быть не perror, а errno.

parent From admin profile Mon Jun 29 09:37:00 2020 pencil

userpic

Да, факт.

Спасибо.

From Anonymous (unverified) Sat Jun 27 13:27:00 2020 pencil

функция string_length

Здравствуйте, на странице 262 последний вариант функции string_length возвращает на 1 больше(считает нулевой символ).

parent From admin profile Sat Jun 27 16:49:38 2020 pencil

userpic

Good catch

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

From Trollkarl (unverified) Fri Jun 26 19:42:00 2020 pencil

Добрый день. В

Добрый день. В первом томе на стр. 143: "и совершенно неважо, какова будет итоговая формулировка".

parent From admin profile Fri Jun 26 21:39:49 2020 pencil

userpic

Спасибо, эту вы

Спасибо, эту вы заметили первым.

From Anonymous (unverified) Tue Jun 16 19:56:00 2020 pencil

Добрый день, во

Добрый день, во втором томе на стр 216 ошибка в функции case_up, вместо return c-('a'-'Z'); по-видимому, должно быть
return c-(('a'-'Z')+('Z'-'A'));
Спасибо.

parent From admin profile Wed Jun 17 12:06:32 2020 pencil

userpic

Есть такое, но

Есть такое, но уже известно.

From Anonymous (unverified) Sun May 31 18:38:00 2020 pencil

Добрый день,

Добрый день, возможно уже исправлено: в первом томе на стр 340 oбъявляется переменная 'mult', далее используется 'mul'. Большое спасибо!

parent From admin profile Mon Jun 1 22:46:14 2020 pencil

userpic

Эту тоже уже

Эту тоже уже нашли, но спасибо за внимательность :)

From Anonymous (unverified) Wed May 27 14:06:00 2020 pencil

Возможно уже

Возможно уже исправляли:
стр. 292, вторая строка сверху: "знакомую нам тех пор", видимо должно быть: "знакомую нам с тех пор".

Большое спасибо за труд.

parent From admin profile Wed May 27 17:26:33 2020 pencil

userpic

Да, эта у меня

Да, эта у меня уже помечена, но всё равно спасибо :-)

From Anonymous (unverified) Sun May 10 21:45:00 2020 pencil

Битовые поля

Добрый день, Андрей Викторович. Позвольте небольшой комментарий (кажется, здесь об этом еще никто не писал) касательно битовых полей. До появления "стандартов" спецификация Си позволяла указывать ширину только для [un]signed int (в C99 к ним примазался еще bool, который на самом деле int). Указание ширины для других типов - нестандартная возможность GCC (sic!), ставшая со временем чем-то самим собой разумеющимся. Но, например, структура с unsigned char на с. 336 не понравится компилятору, запущенному с -ansi -pedantic; у Кернигана и Ритчи этот момент также оговаривается. Возможно, если дойдет дело до переиздания, стоит об этом упомянуть.

parent From admin profile Mon May 11 09:45:32 2020 pencil

userpic

Да, косяк с моей

Да, косяк с моей стороны. Обязательно поправлю при переиздании.

From putilin (unverified) Wed May 6 19:34:00 2020 pencil

Опечатки

Прошу проверить возможные опечатки:

с. 73
"статус объекта с номером 510 - в 29-м бите 15-го элемента массива".
Должен быть 30й бит 15-го элемента массива. 510/32 = 15+30/32.

с. 75
При очистке массива
lp: mov [esi+4*ecx], eax
массив очистится с адреса [esi+4] до [esi + 4*15], а требуется с [esi] до [esi + 4*15].
Т.е. программа должна выглядеть:
xor eax, eax
> mov ecx, 16
mov esi, set512
>lp: mov [esi+4*ecx-4], eax
loop lp
Как в примере на с. 69.
В последнем примере на с. 76 - то же самое.

с. 108
"...четыре (байта) расходуются на представление числа 1000"
Д.б. 10000.

с. 154
В строке
add esp, (%0-1)*4
[esp] будет указывать на последний параметр макроса, он так и останется на вершине стэка. Предполагалось убрать все параметры.
Должно быть:
add esp, %0*4

с. 157
В программе copy.asm не обрабатываются ошибки по результату выполнения макросов kernel для вызовов read и write.
На с. 145 вы предупреждаете:
"При чтении, как и при использовании других системных вызовов, может произойти ошибка ... это обнаруживается по "отрицательному" значению регистра eax после возврата из вызова".

с. 159
.again:
kernel 3, [fdsrc], buffer, bufsize
cmp eax, 0
jle .end_of_file
...

Производится проверка eax <= 0 и для ситуации конца файла (eax = 0) и для ошибки (eax < 0). При выполнении условия следует переход для обработки ситуации конца файла, но нет обработки ситуации ошибки (когда eax < 0), которую надо обрабатывать отдельно - аналогично вызовам open на с. 158.
Если в программе не предполагается обработка ошибок для read и write, можно было ограничиться проверкой eax = 0:
cmp eax, 0
je .end_of_file

с. 183
В коде строка fxcn д.б. fxch.

с. 210
В вызове функции
d = discr(p, q, r)
ф-я должна называться discrim.

с. 241
for(i = 0, j = arr_len - 1; i < j; i++, j++)
должно быть
for(i = 0, j = arr_len - 1; i < j; i++, j--)
иначе - бесконечный цикл.

с. 326
В примере
if(last)
...
last = first->prev
должно быть
last = last->prev

В примере
if(first)
...
free(first->last)
должно быть
free(first->prev)

с. 336
"...на который отведено 4 бита, т.е. он может принимать значения от 0 до 16".
Должно быть "от 0 до 15".

с. 428
Предложение:
т.к. часть
pn = setpair(fgcolor, all_colors[i]);
att = COLOR_PAIR(pn);
не меняется во вложенном цикле, можно вынести её во внешний.

Большое спасибо за книги!

parent From admin profile Thu May 7 13:30:09 2020 pencil

userpic

Спасибо!

> с. 73

спасибо, эту вы заметили первым

> с. 75

про эту уже знаю :-)

> с. 108

эту тоже вы первым заметили

> с. 154

И в самом деле, поместили в стек %0 dword'ов, а вычищаем %0-1. То есть вы совершенно правы, хотя я это не сразу понял. Здесь не было учтено "волшебное" двойное слово, характерное для системных вызовов FreeBSD. И тоже этого до вас никто не заметил, хотя этому примеру уже около десяти лет — он присутствовал ещё в самом первом издании "книжки по ассемблеру, вышедшем в 2010 году.

> с. 157

Единственный вызов kernel на этой странице -- вывод сообщения о неправильном количестве аргументов в поток диагностики. Ошибки на stdout'е и stderr'е практически никогда не проверяются -- я не припомню случая, чтобы хоть раз такое видел.

> с. 159

См. текст строчкой выше (непосредственно перед примером кода). И никто тут никому ничего не должен, чтение до конца файла обычно именно так и выполняют — а с учётом того, что здесь у нас язык ассемблера, загромождать примеры раздельной обработкой ошибки и конца файла вообще ни к чему. А вот "ограничиваться проверкой на ноль", как вы предлагаете — нельзя категорически: если ошибка таки случится, программа зациклится (зависнет).

> с. 183
> с. 210
> с. 241
> с. 326
> с. 336

Уже известны, но всё равно спасибо за внимательность :)

> с. 428

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

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

From Anonymous (unverified) Wed Mar 25 22:32:00 2020 pencil

Опечатка?

В третьем томе на странице 181 подсеть в локальной сети Бориса, похоже, обозначена с ошибкой. Видится, вместо адреса подсети 192.168.64.0 должен быть адрес 192.168.45.64.
Если всё же это не опечатка, не могли бы Вы поподробнее объяснить, откуда этот адрес взялся

parent From admin profile Thu Mar 26 08:16:41 2020 pencil

userpic

Абсолютно

Абсолютно верно, на рисунке ошибка. Спасибо, до вас на неё никто внимания не обратил.

From Anonymous (unverified) Sat Mar 14 05:15:00 2020 pencil

Возможная опечатка

Полагаю, в третьем томе на странице 113 в предложении "Вызов sigaction появился позже, имеет..." пропущено слово "который"

parent From admin profile Sat Mar 14 14:46:25 2020 pencil

userpic

Да нет, тут всё

Да нет, тут всё так и задумано, структура предложения корректна.

Там, кстати, строчкой выше есть опечатка — не согласован род (написано "была" вместо "было"), но про неё я уже знаю :-)

From Anonymous (unverified) Tue Dec 17 07:49:00 2019 pencil

Опечатка на стр. 120

Здравствуйте. В сноске 37 на странице 120 опечатка в числах
из ряда Фибоначчи.

parent From admin profile Tue Dec 17 10:19:04 2019 pencil

userpic

Да, факт

Спасибо :)

From Anonymous (unverified) Tue Aug 13 04:26:00 2019 pencil

не могу запустить 32 битный эльф на 64 бит

Здравствуйте
Пробую собрать первую программу на Nasm

$ nasm -f elf32 1.asm
$ ld -m elf_i386 1.o -o 1
$ ./1
-bash: ./1: cannot execute binary file: Exec format error
$ ldd 1
not a dynamic executable

В хекс редакторе видно, что это эльф, 32 битный, т.е. все ок, но почему не запускается?

Ставил
sudo apt-get install libc6-dev-i386
и еще много чего, что находил в инете - толку 0. Убунта 64 бита.

parent From admin profile Tue Aug 13 21:08:00 2019 pencil

userpic

странно

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

Причин, по которым может не работать — увы, не знаю. Теоретически может флаг noexec висеть на рабочем разделе, но вряд ли, вроде бы убунта в таком не замечена.

А какой дистр убунты и что говорит команда "uname -a"? И такой вопрос: на других языках (на Паскале или Си) программы (пёс с ними, что 64-битные) из той же директории запускаются? Просить вас сделать 32-битную программу на Си не буду, там слишком много чего надо учесть.

parent From Anonymous (unverified) Sat Aug 17 08:41:00 2019 pencil

извините

Вы будете ругаться, но - я это тестил в WSL убунте. Оказалось, что там нет поддержки 32 бит, хоть как не старайся. Поставил нормальную и все заработало. Еще раз извините.
p.s. капча очень сложная, с плохим зрением по десять раз ввожу - не понимаю, что.

From Sergei (unverified) Sun Aug 4 16:08:00 2019 pencil

Пример на ncurses (movingstar)

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

timeout(delay_duration);
// Some code
while((key = getch()) != key_escape) {
    switch(key) {
    // some code
    case KEY_UP:
        set_direction(&s, 0, -1);
        break;
    // some code
    case ERR:
        move_star(&s, col-1, row-1);
        break;
    // some code
    }
}

Ясно, что если нажать, например, на стрелку "вверх" и не отпускать, то getch будет всё время возвращать KEY_UP и никогда не вернёт ERR, то есть move_star не будет вызван. Можно было бы вынести move_star из тела switch и делать его на каждой итерации цикла, но тогда возникнет другая проблема: если зажать клавишу, то getch начнёт отдавать управление слишком быстро (не дожидаясь окончания timeout-а), и звёздочка начнёт ускоряться при зажатии стрелки.
По-видимому, правильным решением здесь будет отказаться от использования timeout и организовывать задержку с помощью функции sleep или подобной. При этом считывание клавиш имеет смысл производить чаще, чем собственно двигать точку.

timeout (0);
int move_timeout = 200;
int read_timeout = 50;
int reads_per_move = move_timeout / read_timeout;
for (;;)
{
    for (int i = 0; i < reads_per_move; i++)
    {
        int key = getch ();
        if (key == key_escape)
            return;

        process_key (key); // changing direction is done here

        usleep (read_timeout * 1000);
    }
    move_star (&s, col-1, row-1);
    // some code
}

parent From admin profile Sun Aug 4 17:55:23 2019 pencil

userpic

Можно и так, да.

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

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

parent From Sergei (unverified) Sun Aug 4 19:47:00 2019 pencil

Можно и так, да.

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


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

Так вышло, что на чистом Си мне писать почти не доводилось, а в Си++ строчные комментарии в порядке вещей. Строчные комментарии не допускаются в чистом Си, в случае если включить флаги компилятора --std=c89 --pedantic. Во многих проектах такие флаги включены, и обычно там явно прописано, что использование строчных комментариев не допускается. Но честно говоря, в реальности я не вижу ни одной проблемы, которая могла бы возникнуть от их использования, за исключением, возможно, каких-либо проблем с переносимостью в случае использования экзотического компилятора. В конце концов, строчные комментарии пришли не из стандарта, а из Си++, и они ничем не хуже ключевых слов const и inline. Но в любом случае в каждом проекте требования к коду свои.

Ну, и в любом случае, в примере кода на сайте (в отличие от реальной программы) даже русские буквы могут быть в комментарии, не говоря уже о формате комментария :)

parent From admin profile Tue Aug 13 21:13:00 2019 pencil

userpic

так что

так что ситуация в каком-то смысле вполне реальная.

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

в Си++ строчные комментарии в порядке вещей

Совершенно верно. Как верно и то, что Си++ — принципиально другой язык с принципиально другими традициями и для принципиально других целей.

в реальности я не вижу ни одной проблемы

Вы читать умеете? Я не сказал, что это может создать проблемы, я сказал, что это моветон. Если угодно, демонстрация отсутствия вкуса. Или, если прагматичнее — то, что может навести на мысль об отсутствии у автора программы чёткого понимания, что Си и Си++ — это совершенно разные языки.

From yuri (unverified) Sat Jun 15 09:29:00 2019 pencil

Занятые порты

Если процесс взаимодействует с чем-то по сети, то он должен избрать порт для своего сокета. Понятно, что порт нужно выбирать из тех, которые на данный момент свободны. Возникает вопрос - как внутри программы узнать, какие порты в системе уже заняты на данный момент?
В терминале это узнать можно, если я не ошибаюсь, через что-то вроде:
netstat -at | tail -n +3 | grep -v 'LISTEN' | sed 's/\ \{1,\}/\ /g' |
cut -d' ' -f 4 | sed 's/cpu_name://' | sort -u

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

parent From admin profile Sat Jun 15 17:27:52 2019 pencil

userpic

Ой 8-()

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

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

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

Если же всё-таки такая задача встала, то уж, конечно, внешние программы запускать не надо. Самое простое, что приходит в голову — запустить цикл bind'ов с возрастанием номера порта по принципу «пока свободный не найдём».

Если интересно, откуда netstat свою информацию черпает — добро пожаловать в файловую систему /proc :-) Только не надо это руками делать, совершенно не переносимое решение.

parent From yuri (unverified) Sat Jun 15 18:09:00 2019 pencil

Не подумал

Я почему-то даже не подумал, что можно не использовать bind, если не важно, какой порт. Тогда, действительно, всё значительно упрощается.

Что касается цикла bind'ов: это было попробовано, но почему-то получалось, что при работающем сервере и при одном запущенном на данном компьютере клиентском процессе второй успешно "биндится" с тем же портом, но после этого не проходит connect. Так что в цикл я поместил и bind, и connect, после чего оказалось, что bind к сокету с адресом применять нельзя (EINVAL) (чего и следовало ожидать). В общем, без "костылей" я обойти этим способом проблему не смог.

parent From admin profile Sat Jun 15 19:29:22 2019 pencil

userpic

Я почему-то

Я почему-то даже не подумал, что можно не использовать bind

Том 3, стр. 211, второй абзац снизу :-) (сразу перед прототипом вызова connect)

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

Рискну предположить, что вы на этот сокет повесили SO_REUSEADDR, как в том же третьем томе на стр. 215 предлагается. Так вот это там предлагается для слушающих портов делать, чтоб не залипали, а отнюдь не для всех подряд.

parent From yuri (unverified) Sun Jun 16 05:46:00 2019 pencil

Спасибо!Что-то

Спасибо!
Что-то совсем невнимательно читал эту главу.

From Anonymous (unverified) Sat Jun 8 02:22:00 2019 pencil

Самостоятельного разгадывания ребуса не случилось.

(стр. 326) Не могли бы Вы уточнить зачем взятие адреса, а потом dereference за скобкой. Нельзя ли так:
first ? first->prev : last = tmp;

parent From admin profile Mon Jun 10 10:48:46 2019 pencil

userpic

Нет, нельзя.

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

parent From Sergei (unverified) Sun Jun 23 18:50:00 2019 pencil

Стало

Стало интересно, проверил. В чистом Си действительно нельзя, а в Си++ можно. Там результат тернарного оператора считается lvalue, возможно, из-за появления в языке ссылок:


#include <stdio.h>

int main ()
{
    int a, b;

    a = 3;
    b = 5;

    ((a > b) ? a  : b) = 11;

    printf ("a = %d, b = %d\n", a, b);

    return 0;
}

Компилятор gcc этот код действительно не скомпилирует с ошибкой:

lvalue.c: In function ‘main’:
lvalue.c:10:24: error: lvalue required as left operand of assignment
   10 |     ((a > b) ? a  : b) = 11;
      |                        ^

А если переименовать файл в .cpp и вызвать g++ вместо gcc, то происходит чудо:

$ gcc lvalue.c -o lvalue
$ ./lvalue 
a = 3, b = 11

parent From admin profile Mon Jun 24 13:44:30 2019 pencil

userpic

Интересно

Я этого не знал, спасибо.

В принципе это логично, и да, логично именно из-за появления ссылок — если считать имя переменной выражением типа ссылка. Вопрос лишь в том, какая из спецификаций C++ это позволяет (вполне возможно, что это GNU extension).

parent From Sergei (unverified) Mon Jun 24 17:59:00 2019 pencil

Насколько я

Насколько я понимаю, это именно по стандарту так, причём начиная с C++98. И все реально существующие компиляторы (gcc, clang, msvc) это поддерживают.

Естественно, выражение (cond ? a : b) будет lvalue только в случае, когда и a, и b -- lvalue одного и того же типа, что, в принципе, логично. Возможно, есть ещё какие-то случаи, там логика довольно мутная, особенно в C++11/14/17, причём даже между ними есть различия. Подробно это описано, например, здесь.

From Sergei (unverified) Wed Apr 10 16:34:00 2019 pencil

Односвязный список и рекурсия

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

parent From admin profile Sat Apr 13 22:00:22 2019 pencil

userpic

Что такое

Что такое "весьма большой"? В подавляющем большинстве случаев, во всяком случае, из моей личной практики, списки не превышают сотню элементов; крайне редко встречаются задачи, где списки имеют длину порядка тысяч или десятков тысяч элементов. Глубина в 10000 рекурсивных вызовов — это пока ещё далеко от опасного предела (размер фрейма при рекурсивном удалении списка будет 16 байт на 32-битной архитектуре, 32 байта на 64-битной, а под стек в большинстве случаев выделяется 8Mb, так что там ещё больше порядка в запасе).

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

Это всё, впрочем, не значит, что надо непременно удалять списки рекурсивными процедурами. Открою секрет — я сам их почти всегда циклом удаляю. Но причина тут уж точно не в переполнении стека.

parent From Sergei (unverified) Wed Apr 17 15:37:00 2019 pencil

Не убедительно

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

Кроме того, если даже переполнения стека не произойдёт, при отладке программа всё равно может упасть из-за порчи памяти раньше, и стек вызовов глубиной 10000, мягко говоря, не сделает отладку приятнее :)

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

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

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

parent From admin profile Wed Apr 17 20:15:58 2019 pencil

userpic

При

При использовании gets переполняется не стек, а та переменная, куда читают. Если она при этом в стеке, то можно затереть адрес возврата и всё такое прочее.

Переполнение стека из-за слишком глубокой рекурсии не создаёт никакой уязвимости кроме разве что denial of service, ибо программа при этом тупо падает. Заметим, она и по переполнению кучи может упасть, различие чисто количественное.

Остальное комментировать не вижу никакого смысла.

From yuri (unverified) Thu Apr 4 21:31:00 2019 pencil

Cложные описания и модификатор const

Как применить правила, описанные в разделе 4.13.3, к описаниям со страницы 259?

const int *p;
p -- это указатель на int константный;

int * const q;
q -- это константный указатель на int.

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

И ещё вопрос из той же оперы: как описать "указатель на константную область памяти, в которой находятся указатели на константные области памяти, в которых хранятся int'ы"?

parent From admin profile Fri Apr 5 13:22:05 2019 pencil

userpic

const int *p; p -- это

const int *p;
p -- это указатель на int константный;

здесь "константный" относится не к указателю, а к int. То есть это указатель на целочисленную константу.

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

"указатель на константную область памяти, в которой находятся указатели на константные области памяти, в которых хранятся int'ы"?

const int * const *p;

parent From Anonymous (unverified) Sat Apr 6 13:53:00 2019 pencil

здесь

здесь "константный" относится не к указателю, а к int. То есть это указатель на целочисленную константу.

Да, это я понимаю. Получается, что "константный инт" соответствует "const int", а константному указателю - "* const".

Если, например, взять ваш ответ на мой второй вопрос:

const int * const *p;

>> ...*p
p - это указатель на...
>> ...*const...
неизменяемый указатель на
>> const int...
неизменяемый int.

То есть в случае с указателями const стоит справа от "указателя", а в случае со стандартными типами - слева.
Вот это несоответствие и смущало.

parent From admin profile Sun Apr 7 23:22:48 2019 pencil

userpic

То есть в

То есть в случае с указателями const стоит справа от "указателя", а в случае со стандартными типами - слева.
Вот это несоответствие и смущало.

Видимо, всё-таки вы не всё поняли. Например:

const int *p;
int const *p;

это строго одно и то же, то есть эти два описания вообще ничем не различаются. Принципиально не то, как расположены const и int, а то, как расположены const и звёздочка.

From Vlad (unverified) Tue Apr 2 08:58:00 2019 pencil

Печать регистра

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

parent From admin profile Tue Apr 2 18:07:00 2019 pencil

userpic

Гыгыгыгыгы

Знаете, как в том анекдоте было: приходит ветеринар к терапевту, ну терапевт его спрашивает, мол, на что жалуетесь? Ветеринар разочарованным голосом: "не, ну так-то каждый дурак может".

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

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

Что до печати числа — это упражнение, не шибко сложное, но и не совсем тривиальное; я, когда кого-то учу ассемблеру, эту задачу всегда рассматриваю как обязательную. И вам настоятельно советую решить её самостоятельно, она того стоит. Лучше всего разбить её на подзадачи: написать отдельно подпрограмму, которая размещает десятичное представление заданного числа в указанной области памяти, отдельно подпрограмму, которая подсчитывает длину строки (в предположении, что в конце строки располагается "нулевой символ") и дальше тупо обратиться к системному вызову write.

parent From Vlad (unverified) Wed Apr 3 21:14:00 2019 pencil

Понял. Спасибо!

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

parent From admin profile Fri Apr 5 13:16:21 2019 pencil

userpic

Планируется-планируется

Задачник — это следующий этап проекта после четвёртого тома. К сожалению, назвать конкретные сроки его выхода тяжело.

From K_Anonymous (unverified) Tue Mar 26 15:40:00 2019 pencil

Кодировка PDF-файла

Здравствуйте. Подскажите, пожалуйста, в какой кодировке находится текст данной книги? У меня ubuntu-based дистрибутив, текст копируется битыми символами. Я помню, что в одной из Ваших книг по NASM я использовал онлайн-перекодировщик, но все настройки потерялись и мне не хотелось бы снова тратить время на их поиск или на распознавание её типа. Копирование для электронного конспекта. Спасибо.

parent From admin profile Sat Mar 30 23:37:36 2019 pencil

userpic

Ни в какой

Текстовый слой в PDF-файлах, представленных на этом сайте, преднамеренно сломан. Рекомендую воспринимать эти файлы как аналог бумажной книги, только без бумаги. В бумажных книгах тоже нет ни поиска, ни возможности копирования в конспект.

From Alexander (unverified) Mon Mar 18 20:47:00 2019 pencil

Взаимодействия процессов

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

parent From admin profile Tue Mar 19 21:41:11 2019 pencil

userpic

Первое, что

Первое, что приходит в голову: открыть некий FIFO на запись (причём при открытии использовать O_NONBLOCK, чтоб не "повиснуть"), потом по сигналу (например, SIGUSR1) выплёвывать в этот FIFO значения счётчиков. Тот, кому эти счётчики нужны, открывает тот же FIFO на чтение, когда нужны счётчики - шлёт первому процессу SIGUSR1 и идёт читать, что пришло.

Естественно, возможен целый ряд других решений. И да, я продолжаю настаивать, что решение с разделяемой памятью — самое неудачное из всех возможных.

From Ivan P (unverified) Sat Mar 2 01:06:00 2019 pencil

Запуск на Mac OS (OS X)

Долго пытался запустить первый пример hello5.asm на macOS 10.14.3, пришел к такому решению.
файл stud_io.inc можно не менять

в тексте программы заменить _start на start
получится такой тест программы

%include "stud_io.inc"
global start

section .text
start: mov eax, 0
again:  PRINT   "Hello"
        PUTCHAR 10
        inc     eax
        cmp     eax, 5
        jl      again
        FINISH

запускаем командами

> nasm -f macho hello5.asm                        
> t2 ld -macosx_version_min 10.7.0 -o hello5 hello5.o
> t2 ./hello5 

parent From admin profile Sat Mar 2 16:10:26 2019 pencil

userpic

Можно и так

Формат исполняемых файлов там не такой, как под настоящей BSD, так что всё логично. Но вообще у линкера обычно есть параметр, задающий имя метки для точки входа. У GNU ld это флажок -e; если у вашего линкера тоже есть такой параметр, то можно сказать что-то вроде -e _start и не менять текст программы.

parent From Ivan P (unverified) Sat Mar 9 20:35:00 2019 pencil

Запуск на Mac OS (OS X)

Все-таки файл stud_io.inc нужно чуть поменять, как для FreeBSD, чтобы запускалась программа и работала, иначе только компилируется и собирается.
Я это сделал, когда писал первое сообщение, но забыл указать!

;%define STUD_IO_LINUX
%define STUD_IO_FREEBSD

parent From admin profile Sun Mar 10 17:47:00 2019 pencil

userpic

Очевидно

Это понятно, она же не Linux совершенно :-) В примерах, в которых используются в явном виде системные вызовы, следует применять конвенцию FreeBSD.

From Alexander (unverified) Mon Jan 21 19:55:00 2019 pencil

Linux Namespaces

Андрей Викторович добрый вечер, хотел услышать вашего профессионального мнения по поводу namespaces в Linux, при сборке одного приложения в скрипте использовалась функция unshare. Но в моем дистрибутиве debian по умолчанию отключен в ядре kernel.unprivileged_userns_clone. Я тестировал на виртуальном образе, эта функция через обычного пользователя, запускает процесс с uid root, не совсем понятно пока какому принципу это происходит, без какой либо авторизации. Хотелось бы понять для чего это реализовано и стоит ли использовать в работе? Не просто так она же выключена.

parent From admin profile Mon Jan 21 22:08:00 2019 pencil

userpic

Вообще это (в

Вообще это (в смысле namespaces) сделано для контейнеров типа OpenVZ, эта штука также известна под названием VPS (точнее, это один из способов создания VPS).

«Использовать в работе» для чего-то отличного от контейнеров я бы эту штуку не стал. Иначе говоря, если в ваши планы не входит создание нового OpenVZ, то про namespaces следует забыть нафиг. Про то, как эта unshare работает, ничего сказать не могу, не изучал вопрос.

P.S. и вообще, https://en.wikipedia.org/wiki/Linux_namespaces :-)

From Alexander (unverified) Tue Dec 11 19:51:00 2018 pencil

1. Добрый вечер

1. Добрый вечер Андрей Викторович, не могли бы пояснить, как правильно реализовать Makefile для файлов паскаль, имею такую структуру директории:

Pascal
├── Makefile
├── prog_1
└── src
    ├── prog_1.pas
    └── prog_2.pas

Я написал такой файл:

NAMES = prog_1
DIR = src
SRCMODULES = $(NAMES:%=$(DIR)/%.pas)
OBJMODULES = $(NAMES:%=%.o)
PASCAL = fpc
PASCALFLAGS = -g -o./
OBJSIZE = size

all: $(SRCMODULES)
	$(PASCAL) $^ $(PASCALFLAGS)
	$(OBJSIZE) $(OBJMODULES)
	@rm $(OBJMODULES) 

Как сделать так, чтобы при добавлении новых имен в переменной NAMES = prog_1 prog_2, получался цикл в заголовке цели для каждого имени, а не список всех имен, но при этом не создавать для каждого нового имени - цель? Или так нельзя реализовать?

Еще проведите ликбез, в MacOS и Windows есть калькулятор программиста, там есть логические операции и представление числа в разных системах счисления, как это реализовать в терминале, математические операции я выполняю, передавая строку в калькулятор

bc <<< "a+b"

А вот логические операции он не понимает, и как можно выводить в поток вывода десятичное число сразу в 16-ой и 2-ой системе? Если просто командой нельзя реализовать так, и для этого нужно писать скрипт то, что использовать для представления?

parent From admin profile Thu Dec 13 12:27:00 2018 pencil

userpic

а оно надо?

Первый вопрос, который приходит в голову: эти ваши файлы .pas представляют собой отдельные программы или составные части одной программы? Если это модули одной программы, make вам не нужен, fpc имеет встроенные возможности для этого: запускаете его для компиляции основной программы, он подцепляет файлы модулей. Если очень хочется модули в отдельную директорию (хотя мне не вполне понятно, зачем) — используйте флаг -Fu.

Далее, чтобы "получался цикл" — делать не надо. Надо в зависимостях цели "all" указать не исходные файлы, а имена получающихся модулей, и без команд; для сборки модулей использовать обобщённую цель (шаблон). См. т.2, стр. 453.

Калькуляторов с логикой не встречал. Что касается вывода чисел в 16-ричной системе, попробуйте printf '%x':

crocodil@hvost:~$ printf '%x\n' 25
19
crocodil@hvost:~$ printf '%x\n' 30
1e
crocodil@hvost:~$ printf '%X\n' 30
1E
crocodil@hvost:~$ 

parent From Alexander (unverified) Fri Dec 14 18:33:00 2018 pencil

Благодарю, всё

Благодарю, всё заработало! Это отдельные программы, про модули теперь буду знать.

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

# compute expression
alias calc='function _calc(){ echo "$(($1))"; };_calc'
# decimal_to_binary
alias d2b='function _decbin(){ D2B=({0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1}); echo ${D2B[$1]}; };_decbin'
# decimal_to_octal
alias d2o='function _decoct(){ D2O=({0..7}{0..7}{0..7}{0..7}); echo ${D2O[$1]}; };_decoct'
# decimal_to_hexadecimal
alias d2x="printf '%X\n' $1"
# binary_to_hexadecimal
alias b2h='function _binhex(){ printf '%X\n' $((2#$1));}; _binhex'
# binary_to_decimal
alias b2d='function _bindec(){ echo $((2#$1))";}; _bindec'
# hexadecimal_to_decimal
alias x2d='function _hexdec(){ echo "$((16#$1))";}; _hexdec'
# hexadecimal_to_binary
alias x2b='function _hexbin(){ D2B=({0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1}); echo ${D2B[$((16#$1))]};}; _hexbin'

Объясните пожалуйста два момента, которые мне не понятны:

1. Если пишу тестовый псевдоним

alias myecho="echo $1"

- он работает и принимает значение аргумента.

alias calc="echo $(($1))"

- не работает, пришлось делать через функцию, почему не передает значение в выражение?

2. По какому принципу это работает?

D2B=({0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1}); echo ${D2B[number]}

parent From admin profile Wed Dec 26 22:12:00 2018 pencil

userpic

По алиасам

По алиасам — если не ошибаюсь, слово function можно опустить, ничего не изменится.

По первому вопросу: это только кажется, что myecho что-то там принимает. На самом деле происходит вот что: слово myecho преобразуется в "echo $1", где $1 заменяется пустой строкой, как и любая неопределённая переменная. Параметр никуда не передаётся, он остаётся где был, но в данном случае это именно то, чего хотели. Попробуйте

alias myecho="echo XXX $1 YYY"

— поймёте, что я имел в виду.

По второму вопросу — понятия не имею :-) Я вообще не знаю такого синтаксиса, но я и не претендую на то, что знаю весь Bash.

parent From Alexander (unverified) Mon Jan 21 19:25:00 2019 pencil

А я думал, что

А я думал, что псевдоним это как define в Си, очень доходчиво объяснили, да не ошибаетесь, функцию опустил, удобно меньше писанины. Со вторым вопросом разобрался, оказалось всё очень просто, почитал man bash, этот прием называется "Brace Expansion" может кому пригодиться.

bash$ echo a{d,c,b}e
ade ace abe 

Это выражение формирует двойчный массив из 256 элементов, и подставляемое десятичное число является индексом массива, в ячейки которого находится его двойчное представление.

From Alexander (unverified) Mon Nov 5 12:44:00 2018 pencil

Раздельная трансляция

Добрый день Андрей Викторович, возникло не допонимание видимости объектов, имеется программа состоящая из файлов {main.c; module1.c; module1.h; module2.c; module2.h} в файле module1.h прописано перечисление
enum bool {off,on};
Где еще что нужно правильно прописать, чтобы он виделся в module2.c ? Т.к. когда я про него забыл и прописал еще раз в module2.h компилятор ругнулся, что два одинаковых объявления.
Еще заметил для переменных, если делаю объявление в файле module1.c обязательно нужно как в книге прописать это же объявление в заголовочном файле с extern, а если объявление делаю только в заголовочном файле, без extern, main.c видит эти переменные.
Хорошо усвоил, что не глобальное то static...)))

parent From admin profile Tue Nov 6 19:19:00 2018 pencil

userpic

Гм...

Во-первых и в-главных: так делать не надо. Не нужен такой enum, он только запутает дело и будет мешаться. Логическая ложь -- ноль, всё остальное -- истина, точка, всё, все свободны. Во всяком случае, если мы пишем на чистом Си.

Во-вторых: описание типа -- любого, хоть enum, хоть struct, хоть union, хоть произвольный typedef -- совершенно верно, выносится в заголовочник. А чтобы то, что есть в заголовочнике, было где-то "там" видно, "оттуда" этот заголовочник нужно подключить с помощью #include. А чтобы компилятор не ругался -- прочитайте ещё раз параграф 4.11.4 и сделайте во всех своих заголовочниках защиту от повторного включения (подчёркиваю: НЕ НАДО ДУМАТЬ, НУЖНА ОНА ИЛИ НЕТ! она делается -- по определению -- во всех заголовочных файлах на автопилоте).

Что касается "объявления в заголовочном файле без extern", то руки оторвать тому, кто делал этот линкер. Действительно, это одна из тех ошибок, которые "современный" линкер зачем-то прощает. Очень хочется того, кто это придумал, убивать медленно и мучительно, ибо в этом поведении нет никакой логики, просто вот есть ошибка (грубая! выдающая непонимание происходящего!), которую решили простить.

parent From Alexander (unverified) Wed Nov 7 17:30:00 2018 pencil

Уточнение по поводу сказанного

По первому пункту, часто мелкает слово "deprecate" по поводу магических чисел. Значит в чистом Си "0" и "1" к этому не относиться, это нормально использовать их в коде ?

По второму пункту спасибо всё заработало, увидив один проект на просторах, который внёс сомнения. Проект был реализован на плюсах, он состоял из 5 файлов сpp, и 21-ого файла header, в заголовочниках по мимо того, что вы перечислили было class, template, inline. В файлах .cpp прописан только #include "Application.h". Сам Application.h состоит только из включения используемых стандартных библиотек и всех заголовочных файлов проекта. При чем в каждом заголовочнике прописано вместо защиты от повторного включения в начале файла #pragma once. Можете прояснить отличается принцип раздельной компиляции для Си от плюсов, можно это использовать или опять кто-то напридумывал?

parent From admin profile Wed Nov 7 18:45:52 2018 pencil

userpic

Страньше и страньше

В чистом Си — в использовании 0 для обозначения лжи, 1 для обозначения истины не может быть ничего ненормального.

В Си++ есть встроенный тип bool и константы true и false, то есть там подобные enum'ы городить будет вообще ошибкой.

Про всевозможные #pragma забудьте совсем, как страшный сон.

Принципы раздельной компиляции для Си и Си++ не различаются вообще.

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

parent From Alexander (unverified) Thu Nov 8 08:14:00 2018 pencil

Нет вы ошибаетесь...

Как начал читать ваш первый том, по вашему же совету снес Windows и поставил Linux и выбрал для себя рабочую среду разработки tmux+vim, очень удобно. По поводу Visual Studio, да вы правы есть похожи сходства c их "Precompiled Headers" и загаловочным файлом "stdafx.h".

Благодарю за пояснения, значит, такие методы не есть хорошо, свой enum убрал, больше не использую, а по поводу bool в Си++, посмотрел в Си тоже сейчас появился в стандарте 99 stdbool.h, с теми же true и false. Можно ли использовать его?

parent From admin profile Fri Nov 9 16:08:57 2018 pencil

userpic

Из C99 не надо

Из C99 не надо использовать ничего. От слова "совсем".

parent From Alexander (unverified) Sat Nov 10 09:55:00 2018 pencil

Кто хоть такое

Кто хоть такое по напридумывал, что потом пользоваться нельзя...) Прочитав вашу с соавторами статью «Чистая компиляция как парадигма программирования», сразу хочется предложить воплотить в реальность выводы статьи " ... Если учесть как негативный, так и положительный опыт языка C++, можно создать новый язык ... ".

parent From admin profile Sat Nov 10 17:35:15 2018 pencil

userpic

Кто-кто, известно кто

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

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

From Roman (unverified) Wed Oct 17 10:00:00 2018 pencil

Опечатки

На странице 80 пример в верху страницы. Вроде бы scas сравнивает с содержимым по адресу EDI, а там заносится в ESI. Прошу прощения если повтор.

На странице 79 строка "This is a string" содержит 16 символов (нулевой не рассматривается как я понял). Последний 'g' содержится по адресу buf1+15. И перемещаем мы 5 элементов, а не 8 как пишется в ECX. Не таким ли должен быть правильный код?:
std
mov edi, buf1+15+5
mov esi, buf1+15
mov ecx, 5
...

parent From admin profile Wed Oct 17 21:42:20 2018 pencil

userpic

Первое -- да,

Первое -- да, повтор, но всё равно спасибо.

Во втором случае мы перемещаем восемь символов -- длина слова "string" и ещё один (как раз таки нулевой). Мы их перемещаем на пять позиций, чтобы было куда вставить слово "long" и пробел.

From Anonymous (unverified) Sun Oct 7 18:25:00 2018 pencil

Здравствуйте, у меня вопрос.

Здравствуйте, Андрей Викторович, объясните, пожалуйста, фрагмент на стр. 90: командой "push ebp" мы сохраняем старое значение ebp (т.е. занесли его значение в esp), а командой "mov ebp, esp" "снимаем" последнее значение esp (т.е. старое значение ebp). Вопрос: не понимаю, зачем нужна вторая команда, ведь уже после первой ("push ebp") в ebp и в esp одинаковые значения?
Спасибо.

parent From admin profile Sun Oct 14 18:17:03 2018 pencil

userpic

тяжело :)

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

Если бы надо было значение ebp занести в esp, можно было бы написать mov esp, ebp. Команда push делает совершенно не это.

From Alexander (unverified) Tue Sep 25 14:12:00 2018 pencil

Формирование пакетов сообщения

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

parent From admin profile Thu Sep 27 19:39:11 2018 pencil

userpic

Вам в любом

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

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

parent From Alexander (unverified) Sat Nov 17 21:52:00 2018 pencil

Дополнительные пояснения

На микроконтроллере сделал уст-во и теперь хочу связать его с ПК по UART.
Т.к. на МК мало памяти, то управление с ПК ASCII сообщениями будет осуществляться через собственный мини протокол. Этот протокол будет следующего вида
{start byte, command byte, data 2-byte, crc byte, end byte }.
Со стороны МК есть аппаратный буфер передачи и приема размером один байт. Первоначально я сначала разделил пакеты сообщения на постоянные и переменные. К постоянным отнес те команды которые не требуют передачи дополнительной информации. И передавал их через готовый массив:

enum {buffer_size = 6};

const uint8_t* const control_command[][buffer_size]={{0x00,0x00,0x00,0x00,0x00,0x00},{},{}};

void uart_send_message(uint8_t* msg[])
{
      uint8_t i;
      for ( i=0; i < buffer_size; ++i)
           uart_transmit(*msg[i]);         
}

После вашей подсказки со структурами код изменил убрав двумерный массив и добавив структуру с массивом:

typedef struct {
       buffer[buffer_size];
} const control_command;

control_command turn_on = {0x00,0x00,0x00,0x00,0x00,0x00};

void uart_send_message(control_command* msg)
{
      uint8_t i;
      for ( i=0; i < buffer_size; ++i)
           uart_transmit(msg->buffer[i]);         
}

C переменными сообщениями я решил задачу сразу создав структуру пакета:

typedef struct {
       uint8_t stx;
       uint8_t command_type;
       uint8_t data_h;
       uint8_t data_l;
       uint8_t crc;
       uint8_t etx;
} send_packet;

Сначала формировал в функции пакет с данными

void make_packet(send_packet* msg, uint8_t* data);

А потом собранный пакет преобразовывал в массив и его передавал:

length_packet = sizeof (send_packet);
uint8_t* raw = malloc(length_packet);
memcpy( raw, &data_packet, length_packet);

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

Со стороны ПК хочется сделать приложение которое из обычных ASCII команд будет формировать готовые пакеты данных для передачи на МК. Программу можно разделить на две части: первая считывает строку и формирует передаваемое сообщение, а вторая часть передача и прием сообщении через преобразователь USB-UART, который отображается в системе как ttyUSB0. Вторая часть мне мутно представляется... Я понял из ваших книг, что в Unix все есть файл, и отталкиваясь от этого надо работать с файлом ttyUSB0, получается что надо использовать функции read() и write(), но не понимаю как правильно передавать и получать данные побайтно и будет ли мешать буферизация, могли бы объяснить алгоритм работы.

На перспективу хочу сделать Lan, закал микросхему у которой есть аппаратный стек протоколов TCP/IP, если я правильно понимаю, то эту же программу немного модернизировав, добавив сокет, можно заворачивать эти же пакеты в TCP и получить уже управление по сети?

parent From admin profile Sun Nov 18 08:46:00 2018 pencil

userpic

Для начала

Для начала всё это не имеет никакого (то есть вообще никакого) отношения к термину, который вы применили в исходном сообщении — здесь нет никаких дейтаграмм. И, коль скоро речь зашла о терминологии, здесь нет никаких ASCII-сообщений, ваш протокол откровенно бинарный. И ещё: никаких преобразователей "USB-UART" не бывает в природе, бывает преобразователь USB-COM, это совершенно не одно и то же - например, UART может обслуживать RS485, а не COM; в любом случае после UART нужен ещё (аппаратный) драйвер того порта, с которым вы будете работать.

Далее, коль скоро речь идёт о микроконтроллере, какая к дьяволу там динамическая память? Сами же говорите, мало памяти, и сами при этом там развозите кучу. И, кстати, я не понял, какое отношение имеют эти три строчки к "передаче пакета" -- взяли уже готовый пакет, выделили память под его копию, скопировали туда... передача-то где? И зачем готовый пакет куда-то копировать? Он же уже готовый.

Что касается работы с /dev/ttyUSB0, прочитайте в третьем томе главу про терминал, дисциплину линии и всё прочее. NB: если Unix'у не сказать, что за com-портом что-то отличное от терминала, он будет уверен, что там терминал. Чтобы общаться с вашей железкой, для начала надо вывести порт из канонического режима. Остальное в книжке написано.

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

parent From Alexander (unverified) Sun Nov 25 19:19:00 2018 pencil

Извините за

Извините за неясность изложения своих мыслей. Постараюсь правильно структурировать их последовательность. После прочтения в третьем томе раздела Сети и Протоколы, я представил, что если нет строго исполнения модели OSI, можно сделать предположение, что написав программный протокол, который будет включать в себя пакеты передаваемые по сети и они будут проверяться при получении, их можно будет передавать и в дейтаграммах и в других физических протоколах, главное чтобы на обоих концах были обработаны эти пакеты. Решил смоделировать и отладить на UDP этот протокол. Написал клиент сервер. Да вы правы протокол бинарный. Конечная цель была перенести его на связать МК с ПК. Задумка его следующая:
При запуске клиента в аргумент передается команда, которая обрабатывается в пакет:
1) Постоянная команда посылает только конкретное действие
./udp_cli light on
udp_srv отвечает сообщением: Turn on the light
2) Переменная команда еще передает параметр
./upd_cli set brightness 50
udp_srv отвечает сообщением: Brightness set to 50

Я приводил примеры по написанному на ПК клиенте т.е. создав сокет, обрабатываем аргументы командной строки и на основании их отправляем сразу или формируем пакет.

unsigned char* parse_command (argv, data_packet);

в функции идет проверка команды, и формируется структура через функцию

void make_packet(cmd_type,data,data_paket);

при формировании два варианта события

1)Если команда постоянная, то просто копируются из массива готовый набор байтов.
2)Если команда переменная, то задается сама команда и через объединение считывается значение передаваемой переменной.

После вашего совета динамику отправил к дъяволу, т.к. только начал переносить код на МК, я сомневался как передавать структуру в массив, потом попробовал напрямую, т.к. структура это тот же массив, единственное сомневаюсь, прямое преобразование может как то плохо влиять к примеру

unsigned char msg[]= (unsigned char*)data_packet;

Где то у пиндосов прочитал, что это деприкайтед, и желательно использовать сериализацию... с ней еще не разбирался.

Потом пакет отправляется серверу и там происходит проверка пакета через функцию

void check_packet(message);

После проверке целостности пакета идет разбор самого пакета

void parse_packet(message);

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

При написании думал, что знаю указатели но что то запутался, не могли бы пояснить в чем различие, создали тип структуры control_command; после создаю массив структур типа control_command, в чем разница

const control_command array_command[];

от

const control_command* array_command[];

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

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

typedef struct {
char* message[20];
}cmd;

cmd command;
char msg[20];

Через стандартную библиотеку работает

snprintf(msg,20,"%s",*command.message);

сообщение отсылается клиенту, а если пытаюсь назначением

msg=*command.message;

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

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

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

Позвольте вас поправить, "USB-UART" существует в природе, это преобразование кодированного сигнала NRZ в кодированный сигнал NRZI. Доказательство тому микросхема CP2102. UART был разработан в AT&T Bell Laboratories для подключения PDP-1 к телетайпу, задолго до появления RS-232. USB-COM является преобразованием кодированного сигнала NRZI в биполярное кодирование сигнала. На ПК использовались микросхемы 8250 UART, которые как вы правильно сказали аппаратным драйвером переводили кодированный сигнал NRZ в биполярный кодированный сигнал RS-232, который более устойчив к электрическим помехам.

Подскажите, а куда можно прислать вам свои код, чтобы вы посмотрели?

parent From admin profile Mon Nov 26 14:03:26 2018 pencil

userpic

Что-то тут всё совсем плохо

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

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

Присылать мне свой код не надо -- мне совершенно не интересно его смотреть.

parent From Alexander (unverified) Tue Nov 27 14:01:00 2018 pencil

Андрей

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

static void copy_message(unsigned char* buffer,int len_buffer, int number)
{
	int i;
	for (i=0;i<len_buffer;i++)
		buffer[i]=0;
	for (i=0;array_cmd[number].message[i];i++){
		buffer[i]=array_cmd[number].message[i];
	}
}

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

    enum {dgram_size = 508}
    int sockfd;
    short int recv_byte;
    unsigned char msg[dgram_size];
    struct sockaddr_in saddr,caddr;

    recv_packet(sockfd, msg, dgram_size, 0,(struct sockaddr *)&caddr, sizeof(caddr));

Сама функция

static int recv_packet (int fd, void* ptr, int num_byte,int flags,
		  struct sockaddr* addr, socklen_t* lenptr )
{
    short int recv_byte;
    if (0 > (recv_byte = recvfrom(fd, ptr, num_byte, flags, addr, lenptr))) {
	 fprintf(stderr, "Failed to receive packet...\n");
    	 fflush(stderr); 
    	 exit(EXIT_FAILURE);
    }
    return recv_byte;	
}

Выдает предупреждение

src/udp_srv02.c:59:35: warning: incompatible integer to pointer conversion passing 'unsigned long' to parameter of type 'socklen_t *'
      (aka 'unsigned int *') [-Wint-conversion]
                                                        (struct sockaddr *)&caddr, sizeof(caddr));
                                                                                   ^~~~~~~~~~~~~
src/udp_srv02.c:15:39: note: passing argument to parameter 'lenptr' here
                  struct sockaddr* addr, socklen_t* lenptr )

Я понял, что передаются разные типы, и что функция принимает адрес, а sizeof выдает значение, но при этом всё работает, как правильно передать размер?

parent From admin profile Thu Nov 29 00:31:21 2018 pencil

userpic

Увы, ни черта вы меня не поняли

Тут у вас, конечно, всё элементарно, а только я вам принципиально не скажу, "как надо". Вы на мой сайт пришли за рекомендацией? Ну так вот вам моя рекомендация: идите изучайте Паскаль, программируйте на нём, и пока не будете (на Паскале!!!) уверенно обращаться со списками и прочими динамическими структурами данных, пока не будете, опять же, УВЕРЕННО пользоваться var-параметрами и чётко понимать, когда они нужны, а когда нет, пока не достигнете полной уверенности во всём, что касается переменных, адресов, передачи параметров и т.п., пока не напишете две-три программы размером в три-четыре тысячи строк каждая, даже не думайте о возвращении к Си.

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

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

From Anonymous (unverified) Mon Sep 24 19:09:00 2018 pencil

Здравствуйте,

Здравствуйте, не понимаю принцип подсчета адреса ячейки из темы "3.2.5. Косвенная адресация; исполнительный адрес", а именно, на стр. 53 адрес вида [matrix+eax+4*ebx] должен указать точный адрес нужного элемента массива. Но если 1-е число 1-й строки находится по адресу matrix+4, 2-е число 1-й строки - matrix+8, 15-е число 1-й строки matrix+60, 1-е число 2-й строки matrix+64 и т.д., то адрес указан неверно. Ощущение такое, что я ошибаюсь в представлении о подсчете адресов. Объясните, пожалуйста.
Спасибо.

parent From admin profile Thu Sep 27 19:37:19 2018 pencil

userpic

Странная проблема

Я думаю, вам станет изрядно проще, если вы будете нумеровать элементы массива с нуля, а не с единицы. Самый первый элемент самой первой строки (то есть имеющий индексы 0,0) находится по адресу matrix (без всяких добавлений), следующий элемент той же строки (т.е. с индексами 0,1) находится по адресу matrix+4; по адресу matrix+60 у нас будет начальный элемент следующей строки (то есть элемент с индексами 1,0), ну и так далее.

From Alexander (unverified) Thu Sep 6 07:43:00 2018 pencil

Буфер ввода-вывода

Столкнулся с некоторым недопонимаем, работы с буфером ввода-вывода, при использовании scanf(), в буфере остаются еще какие-то данные, при использование fflush(stdin) они не сбрасываются и если следом за этими командами использовать getchar(), то он их считывает. Возникают вопросы, исходя из этого, получается что в системе используется несколько буферов, один буфер библиотеки stdio, а другой буфер ядра, правильно я понимаю? И как тогда правильно очищать буфер ввода?

parent From admin profile Fri Sep 7 23:12:00 2018 pencil

userpic

Странная формулировка

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

Теперь по существу. Цитирую документацию:

For input streams, fflush() discards any buffered data that has been fetched from the underlying file, but has not been consumed by the application.


Такое может потребоваться, например, если вы решили по-тихому поменять поток, открытый на дескрипторе того же stdin'а (например, сделать dup2(fd, 1);)— чтобы в буфере не оставалось ошмётков от старого потока. Использовать же fflush(stdin) после scanf, чтобы потом читать из того же потока — по меньшей мере странно: в общем случае вы не можете (никак!) предугадать, до какого места библиотечные функции успели дочитать данные из потока. Если нужно отбросить все данные, например, до конца строки — getchar вам в руки.

parent From Alexander (unverified) Fri Sep 21 09:42:00 2018 pencil

Взаимодействие с виртульной ФС

Добрый день Андрей Викторович, спасибо большое разобрался, написал функцию:

static void clean_garbage(void)
{
    int garbage;
    while((garbage = getchar())!='\n' && garbage != EOF)
        continue;
}

Теперь возник другой вопрос, на ноутбуке есть функция яркости дисплея, установлена оболочка LXDE, оконный менеджер openbox, в котором прописал скрипт, который привязывает Fn клавиши регулировки яркости к моей программе, теперь суть самой программы:
1. Она считывает информацию из файлов
/sys/class/backlight/acpi_video0/max_brightness
/sys/class/backlight/acpi_video0/brightness
2. Из первого файла она считывает максимальное возможное значение, а из второго текущее значение и изменяет его при нажатии на клавиши регулировки.
3. У меня всё это корректно работает только когда я задаю через root права 666 на файл brightness.

Я считаю это не совсем правильным, и я не понимаю, как моей программе нужно получить доступ для редактирования файла brightness. Программа должна состоять в какой-то группе или при выполнении программы нужно создавать процесс с подменной uid ?

parent From admin profile Sun Sep 23 08:22:33 2018 pencil

userpic

Ага

continue в теле цикла не нужен, тело можно оставить пустым (оставить одну только точку с запятой или пустой блок {})

По поводу прав — программа состоять в группе не может, в группе может состоять пользователь. Скорее всего, на этих файлах уже какая-то группа есть, специально предназначенная для этой цели — вот добавьте своего пользователя в эту группу (редактированием /etc/groups), а права этим файлам поставьте 664 или 660. Правда, они при перезагрузке, скорее всего, будут слетать, для этого тоже есть штатное решение, но оно зависит от дистра и используемых в нём демонов, и к тому же я его всё равно не помню.

Если файлы в группе root, то придётся создать самостоятельно группу под эту задачу, а к файлам применять ещё chgrp или chown.

parent From Alexander (unverified) Tue Sep 25 13:55:00 2018 pencil

Заработало!

Всё получилось, спасибо большое, разобрался, система Debian, сделал, как вы сказали правило backlight.rules добавил SUBSYSTEM=="backlight",RUN+="/bin/chgrp video /sys/class/backlight/%k/brightness"
SUBSYSTEM=="backlight",RUN+="/bin/chmod g+w /sys/class/backlight/%k/brightness"

Наверно еще лучше было написать модуль ядра, минуя эти лишние телодвижения, получается при запуске задавать начальное значение и считывая состояние батареи, код fn клавиш, сразу менять значение переменной. Жалко, что в 3 томе у вас примера написания нету.

parent From admin profile Thu Sep 27 19:40:55 2018 pencil

userpic

Модуль ядра для

Модуль ядра для таких целей напоминает расстрел стаи воробьёв стратегической ракетой.

From yuri (unverified) Thu Aug 16 08:35:00 2018 pencil

Опечатка?

Здравствуйте!
Не могу понять, есть ли ошибка в томе 2 на странице 75?
Во-первых, ранее в книге говорилось, что при использовании loop в ECX задается число итераций, а у нас их тут должно быть, если я не ошибаюсь, 16 (обратиться к каждому элементу 0..15 и занести туда 0).
Во-вторых, если представить что для элементов 2..15 цикл уже был выполнен, то сейчас в ECX - 1; затем мы заносим в элемент с индексом 1 ноль, команда loop уменьшает ECX на 1 и, т.к. там уже 0, больше никуда не прыгает.
По всему получается, что должно быть 16.

p.s. Большое спасибо за ваши книги!

parent From admin profile Fri Aug 17 07:14:30 2018 pencil

userpic

абсолютно верно

Там действительно должно быть 16, причём эту опечатку до вас никто не заметил. Спасибо!

parent From yuri (unverified) Fri Aug 17 16:57:00 2018 pencil

И кроме того

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

parent From admin profile Fri Aug 17 17:42:40 2018 pencil

userpic

Верно

Да, вы правы. Этот фрагмент в книжке — результат некорректной замены dec/cmp/jge на loop. То есть 15-то там было правильно поставлено, но после замены на loop всё разом стало плохо :)

From Евгений (unverified) Tue Jul 31 06:58:00 2018 pencil

Размер машинной команды

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

parent From admin profile Tue Aug 14 16:41:40 2018 pencil

userpic

тут есть несколько уровней понимания

Самый простой ответ — что размер команды можно определить по первому байту. Интересно, кстати, что ответ вообще-то не вполне верный, если учесть существование префиксов вроде REP или модификаторов битности, но можно упереться рогом и заявить, что, мол, модификаторы сначала все по одному считываются, а потом из первого байта становится ясно, сколько всего ещё читать.

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

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

From Anonymous (unverified) Mon Jul 23 18:28:00 2018 pencil

"В первые два

"В первые два "слова" будет занесено число 1, в третье слово 2, в четвертое - число 5 и т.д." Последовательность 1, 1, 2, 3, 5, 8, 13, 21, значит четвертое слово 3 (стр 47)

parent From admin profile Tue Jul 24 18:46:59 2018 pencil

userpic

Уже знаю

Уже знаю про эту опечатку, но всё равно спасибо за внимательность :)

From Anonymous (unverified) Thu Jul 12 12:30:00 2018 pencil

Ошибка в имени регистра

В примере к строковой команде scasb используется регистр esi, хотя надо edi.
В описании команды всё ок.

parent From admin profile Thu Jul 12 22:27:19 2018 pencil

userpic

точно

это уже нашли, но всё равно спасибо :-)

From Anonymous (unverified) Mon May 21 21:40:00 2018 pencil

Вызов функции

На странице 231 написано, что в C операция вызова функции является арифметической операцией.

Что под этим имеется в виду?

parent From admin profile Mon May 21 22:19:47 2018 pencil

userpic

Имеется в виду

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

From Anonymous (unverified) Thu Mar 8 11:44:00 2018 pencil

На стр. 326

На стр. 326 написано last = first->prev, хотя по идее должно быть написано last = last->prev.

Следующая опечатка находится на той же странице:
free(first->last), но должно быть free(first->prev)

И пользуясь случаем хочу спросить, какую литературу посоветуете по ассемблеру после изучения вашей книги?

parent From admin profile Thu Mar 8 12:50:24 2018 pencil

userpic

Спасибо!

Обе эти ошибки ещё никто не заметил :) Так что большое спасибо.

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

parent From Anonymous (unverified) Thu Mar 8 16:26:00 2018 pencil

Понимание "как

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

parent From admin profile Sat Mar 10 19:33:59 2018 pencil

userpic

Есть, конечно

по-английски on bare metal.

Только ассемблер-то тут при чём? Пишите на Си :-)

From Anonymous (unverified) Thu Feb 15 04:08:00 2018 pencil

Опечатка

Не знаю писали уже или нет но мне кажется опечатка в коде на странице 288 (второй том, 4.6.3. Работа с текстовыми файлами). Там написано *count++ , инкрементируется указатель, возможно автор хотел написать (*count)++ , или может дело в версий компилятора, но у меня только так заработал :)
В целом книги автора мне очень нравятся, нравится стиль изложения, спасибо большое за ваши труды

parent From admin profile Wed Feb 21 23:25:07 2018 pencil

userpic

Большое спасибо

Крайне неприятная опечатка, и самое интересное, что вы её заметили первым.

From Аркадий (unverified) Tue Oct 17 10:25:00 2017 pencil

опечатка discr вместо discrim

2-й том §4.2. стр.210
вместо d = discr(p, q, r); должно быть d = discrim(p, q, r);

parent From admin profile Fri Oct 20 16:17:29 2017 pencil

userpic

Спасибо

есть такое, причём вы первый, кто заметил. В архиве примеров (файл qe.c) этой ошибки нет.

From Anonymous (unverified) Fri Sep 22 13:54:00 2017 pencil

По поводу ошибок

По поводу ошибок 2-го тома:
380 страница, ссылка на параграф о максимальной длине строк из первой книги неправильная. Написано §2.15.14, а должно быть §2.15.4.

parent From admin profile Sat Sep 23 07:26:55 2017 pencil

userpic

Есть такое

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

From Anonymous (unverified) Tue Aug 29 10:47:00 2017 pencil

По поводу

По поводу ошибок 1-го тома:
→ 374-ая страница,
→ 3-ий по счёту пример на этой странице,
→ надо добавить в условие первого цикла проверку first <> nil,
иначе так можно разыменовать nil... :-)

parent From admin profile Thu Aug 31 23:13:03 2017 pencil

userpic

Спасибо!

Да, факт. В своём экземпляре я это пометил, если когда дойдёт дело до переиздания — исправлю. Спасибо!

From Anonymous (unverified) Fri Apr 7 09:09:00 2017 pencil

Столкнулся с

Столкнулся с программой, которая упорно не желает отдавать на конвейер свой стандартный вывод. Попытка получить этот самый вывод с именованного канала тоже эффекта не имела. Между тем, работая "сама по себе", программа, разумеется, текст на консоль выдает. Исходника нет, только бинарник. Команда strings показывает обычные printf'ы. И тем не менее!
Пришлось вернуться к учебникам. В том числе, к вашему. Явно какие-то пробелы со стандартными потоками или настройками терминала (перепробованы все возможные терминалы системы). И уже первый же эксперимент со стандартными потоками привёл к непредсказуемому результату:
Тыц!
То есть, вызывая syscall 4 с параметром 0 (i = 0, STDIN_OUT), почему-то вижу строку в консоли. А, вроде, не должен... Дальше - всё нормально, i = 1, i = 2 - оно и должно выводиться, больше - нет. Но почему выводится при i = 0?

parent From admin profile Sun Apr 9 19:15:00 2017 pencil

userpic

Ну Семёёёёёёёён семёёёёёёныч

догадаться никак?

Это же (очевидно) один и тот же поток, ваш лидер сеанса открыл терминал на чтение и запись и потом dup'ом (или dup2) скопировал в трёх экземплярах -- это самая очевидная и простая реализация старта сеанса на терминале. Закладываться на это, разумеется, не надо: лидер сеанса совершенно не обязан действовать именно так, и далеко не все эмуляторы терминалов юзают такой мерзкий хак, но -- им этого и не запрещает никто.

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

А вообще таких козлов, которые делают вот такие вот бинарники, надо даже не отстреливать, а убивать долго и мучительно.

upd Кстати, кажется, можно и проще: посмотрите на программу screen, она, кажется, имеет встроенный логгинг, при этом запущенные под ней программы про него ничего узнать не могут.

P.S. Глянул ещё раз на ваш исходничек... самое страшное там -- пляски с STDOUT_FILENO. Сам факт их наличия демострирует проблемы настолько капитальные, что я бы вам советовал с них начать, а не с системного вызова.

From Anonymous (unverified) Sun Oct 16 22:33:00 2016 pencil

Не компилируется пример

т2 стр. 211
"...Нужная библиотека называется "m", а подключается она указанием флажка -lm"
Далее пример:
$ gcc -Wall -g -lm qe.c -o qe
НЕ КОМПИЛИТСЯ И ВЫДАЕТ ПРЕЖНЮЮ ОШИБКУ, ПОТОМУ ЧТО:
$ gcc -Wall -g qe.c -o qe -lm

Юзаю Linux ubuntu + gcc

parent From admin profile Tue Oct 18 17:51:26 2016 pencil

userpic

В принципе это

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

crocodil@frock:~/work/misc$ gcc -Wall -g -lm qe.c -o qe
crocodil@frock:~/work/misc$ gcc --version
gcc (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3

Т.е. эта логика, наконец, допёрла до создателей gcc только к пятой версии, а на всех версиях до четвёртой включительно работало в том числе и так, как написано в книге.

From Михайлович (unverified) Sun Oct 9 07:45:00 2016 pencil

Забавно! :)

К примерно таким же последствиям, что и у коллеги, оставившего #93 пост, привело чтение двухтомника и у меня. Тоже пришла мысль проверить квалификацию персонала. Но поскольку пришла она совершенно независимо, то база для контрольных вопросов по языку Си была взята из книги Хезфилда и Кирби. И очень было интересно наблюдать, как человек, вроде бы, уже имеющий достаточный опыт, совершенно искренне не понимает, почему код вида:

#include <stdio.h>

void increment(char *p) {++p;}

int main()
{
	char *s = "Hello, world!\n";
	increment(s);
	printf("%s\n", s);
	return 0;
}

выводит-таки "Hello, world"! Налицо явный пробел - при обучении не было акцентировано внимание на тот факт, что в Си "ВСЕ И ВСЕГДА" передается только по значению. А скорее, сыграло роль бессистемное чтение различного рода макулатуры по С++. Вот вещь элементарная, вроде, но в какой-то момент это непонимание может сыграть очень дурную шутку. Человек уже не учится, человек уже работает!
Кстати, в процессе вопрос возник о реализации сиплюсплюс-подобной ссылки. В простейших случаях, это, разумеется, несложно - раздаем направо и налево указателям звезды с амперсандами, и всего делов! Получатся эдакие указатели "брежневского вида". Сложнее несколько будет выстроить typedef'ы, призванные упростить такую запись. Но как бы это сделать "попроще" и "покрасивее"? И как это было сделано в компиляторах С++? "Первокомпайлер С++" Страуструпа, так и вообще, если память не изменяет, переводил плюсовой код на Си... Можно ли там отыскать нечто полезное, позволяющее использовать сиплюсплюс-подобные ссылки именно на Си?

parent From admin profile Mon Oct 10 21:47:00 2016 pencil

userpic

В чистом Си

В чистом Си ссылок нет, только указатели (спасибо Капитану Очевидность). Ссылки -- это синтаксический сахар, введённый именно что языком Си++, и если в языке их нет -- то, значит, нет.

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

P.S. вы человеку этому ещё задайте вопрос, почему если s[2]='r' сделать, программа в тыкву превратится. И почему этого не произойдёт, если вместо *s в описании написать s[]. И какие возможности при этом потеряются. И почему. А потом отберите у этой обезьяны гранату в виде Си и дайте ему что-нибудь не такое опасное, PHP какой-нибудь.

parent From Михайлович (unverified) Thu Oct 27 13:54:00 2016 pencil

Да, конечно! :)

А что касается "обезьяны", то у нас на фирме занимаются исключительно разработкой, где Си уготовано далеко не первое место. Просто требованием при приёме на работу является специализированное высшее образование. Думаю, что согласитесь, что язык Си обязательно должен входить в арсенал любого программиста, чем бы он не занимался. И он, разумеется, входит во все вузовские программы. Просто хотелось бы иметь ДЕЙСТВИТЕЛЬНО грамотных специалистов. А потом, чем чёрт не шутит, может, и другие направления деятельности появятся.
Так что, все "обезьяны" у нас пишут практически исключительно на PHP :)
Просто повторюсь, хотелось бы иметь ДЕЙСТВИТЕЛЬНО грамотных специалистов. С ДЕЙСТВИТЕЛЬНО высшим образованием. А какое высшее образование может быть без уверенного знания архитектуры и, соответственно, Си? Дело именно в этом.

parent From Anonymous (unverified) Sat Nov 12 00:15:00 2016 pencil

Вынести

Вынести понятие "леводопустимого выражения" в систему типов -- это дорогого стоит.

А что мне может дать именно леводопустимость ссылки? Как её можно использовать?
Ссылки как формальные параметры - тут, вроде, всё понятно. Можно вернуть ссылку из функции. Её чему-то присвоить. Но собственно-то ссылку зачем мне менять? В каких случаях мне это может понадобиться?

parent From admin profile Sat Nov 12 09:26:09 2016 pencil

userpic

Видимо, у вас

Видимо, у вас таки бардак в голове. Саму ссылку изменить семантически невозможно (за исключением гнилого трюка со ссылочным полем структуры/класса). И, разумеется, не нужно. От неё ведь даже адрес взять невозможно, поскольку над ссылкой вообще нет "своих" операций. Моя фраза про леводопустимость, разумеется, не имела и не могла иметь никакого отношения к изменению самой ссылки как таковой.

From Ivo (unverified) Wed Oct 5 12:42:00 2016 pencil

Хорошие у Вас

Хорошие у Вас книги, Андрей Викторович.
Тут уже отметили, что имеется чёткая ориентация на ПОНИМАНИЕ того, что люди делают. Это заметно как по книгам, так и по комментариям и ответам на сайте. Недолго думая, среди программирующего персонала провели лёгенький (типа, "неформальный") опрос. Вопросы взяли из древнейшей "Хрестоматии" Богатырёва. Так подавляющее большинство (а это уже не студенты) не смогло ответить на элементарные вопросы. Пишут люди, правда, не под Unix, но ведь не секрет, что MSDOS, откуда пришли современные поделия от Майкрософт, представляла собой именно изрядно кастрированный Unix! Да и ветку NT ваяли именно спецы от Unix. А откуда бы им ещё взяться, "спецам-то"? Самостоятельно же свой Xenix (или как он там назывался) Майкрософт когда-то так и не вытянула. В общем, (типа, "неформально") было предложено провинившимся пройти указанную книжку всем "затруднившимся". К примеру, под (тоже информация с этого сайта) интерпретатором Ch. Достаточно вполне. Ну, а попавшимся же вторично на откровенном незнании элементарных вещей, кроме уже вполне "типа, формального" увольнения ничего не светит.
Так что, в хвост и в гриву студентов, Андрей Викторович, в хвост и в гриву! Во имя добра. Да и их самих тоже!

From Anonymous (unverified) Sun Oct 2 08:46:00 2016 pencil

Все ли функции нужны?

Здравствуйте, Андрей Викторович!
Читаю ваши книги. Не столько в целях ознакомления с основами программирования, сколько из соображений упорядочивания, систематизации и усугубления. Что-то изменяется, что-то просветляется, появляются какие-то вопросы... То есть, процесс затыкания дыр в имеющейся "личной парадигме программирования", так скажем. Одновременно просматривается стандартная библиотека. Нарвался на никогда не использовавшуюся мною функцию strdup(). Пытаюсь понять, зачем и в каких случаях она могла бы мне понадобиться - кто же писал ее зачем-то! Так вот, пока понять не могу, что она мне может дать вместо обычно применяемой strcpy(), кроме дополнительного выделения памяти и обязанности не забыть ее же освободить? Бывают ли такие случаи?

parent From admin profile Sun Oct 2 17:42:00 2016 pencil

userpic

Нужны, конечно,

Нужны, конечно, далеко не все функции, некоторые даже не просто не нужны, а запрещены -- тот же gets пресловутый. Я бы сказал, что довольно бессмысленна функция sscanf, хотя со мной кто-то, возможно, и не согласится. Совершенно бесполезными мне кажутся fread/fwrite; или вот есть ещё системный вызов creat, я так и не понял, накой чёрт он вообще есть.

А вот strdup я как раз довольно активно использую. Больше того, при работе на C++, чтобы не освобождать delete'ом выделенное malloc'ом, я часто вынужден писать собственную её версию (обычно я называю её strdup_n), которая от оригинала отличается использованием new вместо malloc'а. Разумеется, это примерно то же самое, как вызвать strlen, потом malloc, потом strcpy; ну так и что с того? Вообще любую из функций string.h можно сделать за один, максимум за два цикла, то есть без string.h можно вообще обойтись, но тогда программа будет несколько хуже читаться.

parent From Mikhail Baynov (unverified) Wed Nov 22 12:51:00 2017 pencil

Как вы думаете,

Как вы думаете, имеет ли смысл издать книжку - мануал по стандартным библиотекам си, освещающую эти аспекты? Было бы интересно знать, от чего в коде стоит избавляться. Лучше наверное сразу на английском, озаглавить например "C language best practices". И содержание - каждая библиотека, и по каждой функции расписано где и для чего применять, напротив нерекомендованных - крест и рекомендации как и на что заменить.

parent From admin profile Mon Nov 27 21:17:07 2017 pencil

userpic

?

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

From spb_NIcK_ (unverified) Sun Oct 2 08:19:00 2016 pencil

Прочитал оба

Прочитал оба тома. Вижу, комментарии положительные преобладают явно. Не буду оригинальным — действительно, хорошее введение в программирование на русском языке. Если по чесноку, не ожидалось. Как-то давно сложилась точка зрения, что на русском языке ничего подобного появиться не может, уж тем более в условиях, которые сложились с образованием в нынешней России. Те же основы программирования мной, да и многими моими знакомыми осваивались по (тоже весьма немногочисленным — проблема образования не только проблема России) англоязычным источникам. В первую очередь вспоминаются лекции по C/C++, читаемые в ряде учебных заведений США — используется там достаточно давно для учебных целей интерпретатор Ch. В Европе для целей обучения используется местный интерпретатор ROOT, предназначенный, что интересно, вовсе не для профессиональных программистов, а именно для пользователей. Очень вменяемые люди читали! Многие по сей день просто используют ch-shell даже для административных целей. Синтаксис bash — это, наверно, самая непереносимая на платоформу мозгов, штука. Ну, а язык Си поймет даже туземец из тростниковых зарослей каких-нибудь островов Тристан-да-Кунья. Кстати, та же беда, что и в bash, касается и синтаксиса скриптового языка vim. Сам по себе, редактор просто волшебный, но как только случается придти в голову мысли изменить в нем что-то чуть-чуть нетривиально, тут же с завистью начинаешь поглядывать на emacs. Вот нету в жизни совершенства! Попадались также знатные англоязычные лекции по Ocaml и Scheme. Во всех случаях ощущалась ориентация на понимание обучающегося, что в других (всех остальных!) случаях, не ощущалось совершенно! И это же есть в ваших книгах.
В общем, спасибо за книги, творческих успехов в Новом году (не за горами, а когда еще зайти случиться?). Кстати, и крисмас у вас, смотрю, подходящий! ;) Удачи.

parent From Anonymous (unverified) Fri Aug 11 11:11:00 2017 pencil

"...с завистью

"...с завистью поглядывать на emacs..."
Не надо "поглядывать с завистью". Использовать надо emacs. Чего там не хватает нормальным людям, так это переключения режимов. Ставим плагин evil, и все - имеем все режимы Vim, всю его клавиатуру. Ну, с небольшими исключениями (которые можно легко при желании поправить) - напр. нельзя ":set num", нельзя перейти в Normal с помощью Ctrl-c, только ECS или Ctrl-[, не работает ":di", только его аналог ":reg", и тому подобные мелочи.
Короче, ставим evil, работаем так же как в Vim, а все функции пишем на elisp. Плохо, что ли? Для меня такой гибрид оказался находкой просто сказочной. Горя не знаю. Vim - редактор замечательный, но VimScript - это даже хуже, чем bash.
Emacs - редактор не менее замечательный, но уродовать там собственные пальцы его исходными биндингами совершенно ни к чему.
Короче, берем отовсюду все, что есть лучшего, и живем счастливо. Есть в жизни совершенство, но приходится иногда поискать.
Ну, и "ctrl:swapcaps", конечно, еще никакому редактору не мешало.

From Anonymous (unverified) Sun Oct 2 05:26:00 2016 pencil

Да, книги

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

parent From admin profile Sun Oct 2 17:36:34 2016 pencil

userpic

Для начала

Для начала замечу, что конвенции Си и Паскаля различаются уж точно не тем, как они выравнивают стек. Что касается выравнивания стека, то вроде бы в моей книжке про это нет ни слова, если не считать очевидных замечаний о том, что ESP при работе с 32-битным кодом следует всегда сохранять кратным четырём.

Что касается выравнивания стека как такового, то вообще-то его и не надо разглядывать, никого оно не волнует, кроме компиляторщиков (и то сильно не всех). Но если очень хочется, то стоит обратить внимание на то, как тот же gcc оформляет вызовы функций, то есть посмотреть, какой ассемблерный код генерируется, и попробовать это проделать при различном совокупном размере локальных переменных (плюс совокупный размер параметров самой "длинной" из вызываемых функций, под длинной я в данном случае подразумеваю количество параметров). Вот тут, например, народ это обсуждал: http://stackoverflow.com/questions/1061818/stack-allocation-padding-and-alignment

From Alex (unverified) Fri Sep 30 13:37:00 2016 pencil

Читаю второй

Читаю второй том. Вроде, понятным языком написано. Смотрю комменты, вроде, тоже понятно излагается. Вот и задумал вопрос задать незамысловатый, который временами встает, но точного ответа по нему не знаю.
Многократно в течение жизни приходилось читать файлы, парсить их всяко-разно. Преимущественно использовались regex-библиотеки. Вроде, всё хорошо. Разве что, не совсем понятно, как оно работает. Вот, скажем, для начала какой-то regex-шаблон "компилируется" какой-то функцией. Затем имеем возможность использовать целую кучу других функций, использующих уже этот "скомпилированный шаблон". Имеются также более специализированные csv-библиотеки. Тоже, вроде бы, упрощающие жизнь. Правда, чего они на самом деле делают и как - тоже ясного немного...
Так и вопрос - настолько же они эффективны, сколь и непонятны, или как? Ведь зачастую распарсить можно и просто обойдясь каким-нибудь вспомогательным указателем. Во всяком случае, чётко представляешь себе, что делаешь!
Вот к примеру (фантазировать особо не стал) раздербан строк файла /etc/passwd по сепаратору ":" (ну, и вывод для теста с другим сепаратором "|"). Возможно, даже вспомогательный указатель тут лишний, это надо внимательней смотреть. Но суть ясна - двигаем ручками указатель, ставим нули, кусаем строку по частям:

#include <stdio.h>
#include <string.h>

#define ENOUGH	64
#define MORE_THEN_ENOUGH ENOUGH*10
#define DELIM ':'

void parse(char *buf, char *value_01, char *value_02, char *value_03, char *value_04,
			 char *value_05, char *value_06, char *value_07)
{
	char *p = strchr(buf, DELIM);
	*p = '\0';
	strcpy(value_01, buf);
	buf = ++p;

	p = strchr(buf, DELIM); *p = '\0';
	if(strlen(buf))
		strcpy(value_02, buf);
	else
		strcpy(value_02, "");
	buf = ++p;

	p = strchr(buf, DELIM); *p = '\0';
	if(strlen(buf))
		strcpy(value_03, buf);
	else
		strcpy(value_03, "");
	buf = ++p;

	p = strchr(p, DELIM); *p = '\0';
	if(strlen(buf))
		strcpy(value_04, buf);
	else
		strcpy(value_04, "");
	buf = ++p;

	p = strchr(buf, DELIM); *p = '\0';
	if(strlen(buf))
		strcpy(value_05, buf);
	else
		strcpy(value_05, "");
	buf = ++p;

	p = strchr(buf, DELIM); *p = '\0';
	if(strlen(buf))
		strcpy(value_06, buf);
	else
		strcpy(value_06, "");
	buf = ++p;

	*(strchr(buf, '\n')) = '\0';
	if(strlen(p))
		strcpy(value_07, p);
	else
		strcpy(value_07, "");

}

int main()
{
	char value_01[ENOUGH]; char value_02[ENOUGH]; char value_03[ENOUGH];
	char value_04[ENOUGH]; char value_05[ENOUGH]; char value_06[ENOUGH];
	char value_07[ENOUGH];

	FILE *file;
	char buf[MORE_THEN_ENOUGH];
	
	if(!(file = fopen("/etc/passwd", "r")))
	{
		printf("Your system 'WINDOWS'? Pass a medical examination!!!\n");
		return 0xDDD;
	}
	
	while(fgets(buf, MORE_THEN_ENOUGH, file))
	{
		parse(buf, value_01, value_02, value_03, value_04, value_05, value_06, value_07);
		printf("%s|%s|%s|%s|%s|%s|%s\n", value_01, value_02, value_03, value_04,
				value_05, value_06, value_07);
	}
	fclose(file);

	return 0;
}

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

parent From admin profile Fri Sep 30 19:26:00 2016 pencil

userpic

Если интересно

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

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

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

From Anonymous (unverified) Tue Sep 27 13:26:00 2016 pencil

did you forget -T?

Взялся было за самообразование в области FreePascal'я, да наткнулся то, что fpc по всякому поводу и без оного пишет warning: "/usr/bin/ld.bfd: warning: link.res contains output sections; did you forget -T?". Не то, чтобы оно очень сильно беспокоило, но вообще-то мое процессорное время не для того, чтобы выписывать на мониторе всякую ерунду. Обратился к разработчикам с классическим русским вопросом - "Что делать?". Как выяснилось, это не разработчики, это отморозки какие-то - сказали, что-то вроде "это ничаво, и так хорошо, не беспокойтесь, а была бы другая версия, то там был бы специальный параметр..." Короче, я так понял, что этот "did you forget -T?" просто непременный атрибут именно моей версии этого компилятора.
Попробовал, конечно, убрать - fpc hello.pas 2>/dev/null, но при таком раскладе давятся все warning'и (ошибки, слава богу, идут в stdout). А вот вариация по типу fpc hello.pas|grep -v "did you forget" почему-то не отработала.
Недолго думая, написал вот такую колбасу:

fpc hello.pas 1>tmp1 2>tmp2 && less tmp1|grep -v "^$" && less tmp2|grep -v "/usr/bin/ld.bfd: warning: link.res contains output sections; did you forget -T?"

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

parent From admin profile Tue Sep 27 18:35:18 2016 pencil

userpic

К концу книжки

К концу книжки этот warning начнёт сливаться с окружающим пейзажем и перестанет бросаться в глаза. Обратите внимание, что я про это паразитное предупреждение упоминаю, см. т.1, стр. 202.

В общем, проблема известная, существует уже лет десять, разработчикам на неё, судя по всему, наплевать. Лично мне она тоже уже давно не мешает. А grep -v у вас не отработало из-за того, что это всё выдаётся на stderr, а grep на свой stdin получает stdout.

Попробуйте вот так:

fpc 2>&1 | grep -v "forget -T"

parent From Anonymous (unverified) Tue Sep 27 19:47:00 2016 pencil

Да, спасибо!

Совсем другое дело! В .vimrc дописал ко всем своим собакам:

let @f=':!fpc % 2>&1 | grep -v "did you forget -T" | grep -v "^$"' " Free Pascal

И по @f имею вполне аккуратный вывод:

:!fpc hello.pas 2>&1 | grep -v "link.res contains output sections; did you forget -T" | grep -v "^$"
Free Pascal Compiler version 2.6.4+dfsg-4 [2014/10/14] for i386
Copyright (c) 1993-2014 by Florian Klaempfl and others
Target OS: Linux for i386
Compiling hello.pas
Linking hello
5 lines compiled, 0.0 sec

Как всегда - все просто. Когда знаешь, конечно! )))
То, что я там понаписал ранее, не лезло ни в какие рамы. Даже не по длине - просто невозврат нуля одной из связанных && команд (а такое возможно) нуль в итоге уже не вернет. А компилятор ничего другого возвращать и не должен! Ну, а здесь все четко... Спасибо.

From Alex71 (unverified) Sat Sep 24 06:32:00 2016 pencil

Читаю Вашу

Читаю Вашу книжку, стандартные функции ввода-вывода. В параллель открыто еще несколько. Пробую читать файл fopen/fread/fclose и open/read/close. Думал, будет одно и то же. Однако, нет:
fopen and open
Получается, что не "одно и то же"! Разница в результирующем бинарнике в целый килобайт.
Файл один и тот же, всяко более 256 байт, компилятор один, ключи тоже, ldd выдает одинаковый вывод. Куда дальше рыть? Откуда такая разница? Чем-то плохи эти open/read/write? Еще и сделать-то ничего не успел, а уже килобайт отдай дяде! Куда годится...

parent From admin profile Sat Sep 24 13:16:19 2016 pencil

userpic

Довольно неожиданный эффект

По идее, должно быть с точностью до наоборот, ведь open/read/write -- это системные вызовы, которые и первая программа использует тоже, только неявно, а вот fopen/fread/fwrite -- библиотечные функции, которых вторая программа не использует.

Подозреваю, что при статической сборке вторая программа даст существенно мЕньший бинарник, чем первая. А вот почему так происходит при динамической сборке -- я объяснить не могу.

parent From Anonymous (unverified) Sun Sep 25 23:39:00 2016 pencil

По поводу

По поводу "существенно меньшего бинарника" при статической сборке получилось не сильно.
Действительно, у gcc и pcc размер бинарника несколько меньше как при статической сборке, так и при динамической. (TCC же просто отказался собирать статически. И перекокос в его случае, возможно, просто связан с особенностями самого компилятора и его личной "стандартной библиотеки Си" - libtcc.a). Однако, разница совсем невелика. В исходники, правда, пока не залезал, но что-то появилось устойчивое ощущение, что open()/read()/write() -это "не совсем" системные вызовы, а просто одноименные (может, чуть более низкоуровневые, чем в случае fopen()/fread()/fwrite()) функции-оболочки, сделанные для "какой-нибудь с чем-нибудь совместимости".

parent From Anonymous (unverified) Mon Sep 26 07:37:00 2016 pencil

В общем, думаю,

В общем, думаю, вопрос ушел.
open/read/write - это все-таки системные вызовы. К счастью, в моем файле 256 байт - это целая куча строк. Так под strace видно, что вывод каждой строки fwrite'ом делается отдельным вызовом write с вытекающими. И даже будучи просто запущенной под time, вторая программа работает быстрее.
С панталыку сбил какой-то неутык в компиляторе TCC. Размеры получаемого файла там существенно меньше, но есть и свои "особенности", как выясняется...

parent From admin profile Mon Sep 26 11:25:30 2016 pencil

userpic

Если программы

Если программы те же, что были на иллюстрации несколькими комментами выше, то я бы ещё на вашем месте обратил внимание на то, что сказано по поводу анализа возвращаемого значения read на стр.302 (первый абзац сверху). К fread это тоже относится.

parent From Anonymous (unverified) Mon Sep 26 23:21:00 2016 pencil

Да, в рабочих-то

Да, в рабочих-то условиях проверять, конечно, надо!
Единственное, что осталось непонятным, так это ПОЧЕМУ функция fwrite() использует системный вызов write на каждую строку? Странная довольно реализация... Вроде, высокоуровневая функция, буферизация какая-то, системные вызовы как-то экономиться должны! И где здесь "экономия"?
Если не врет strace
Разумеется, в случае варианта с write, вызов единственный - сразу выводит то, что просят.

parent From admin profile Tue Sep 27 09:45:22 2016 pencil

userpic

fwrite не

fwrite не буферизует, буферизация действует только на потоковый ввод-вывод, а блочный буферизовать слишком сложно, чтобы это было в библиотеке. Потому никто их и не использует, все для бинарно-блочных файлов используют низкий уровень. fread/fwrite просто бессмысленны.

parent From Anonymous (unverified) Sun Oct 2 11:49:00 2016 pencil

Вопрос по буферизации

А имеется ли вообще смысл вместо длительной записи в цикле в файл результатов какой-либо обработки, записывать предварительно в специально выделенный для этой цели буфер, а потом одним ударом забабахать весь буфер в файл перед его закрытием? Имеется ли какая-то при этом выгода, или то на то и выходит?

parent From admin profile Mon Oct 3 06:53:00 2016 pencil

userpic

Depends

Как обычно, зависит от конкретики. Если сравнивать скорость записи в файл по одному байту и запись блока, то разница в скорости будет примерно как размер этого блока :-) Имеется в виду, естественно, запись без локальной буферизации, то есть, например, 4096 обращений к write по одному байту или одно обращение к write на запись блока в 4096 байт.

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

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

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

parent From Anonymous (unverified) Mon Oct 3 15:43:00 2016 pencil

Воистину depends!

Собственно, я не собирался ничего "измерять", так уж, на скорую руку получилось...
Предельно глупая функция копирования файла. Просто читаем построчно первый, пишем во второй в цикле. Все обычно. Работает, естественно, правильно. А далее - собираем всё в предварительно замаллоченный буфер и перед закрытием файла, в него сбрасываем. Ну, или работаем его в стеке. Как, собственно, и хотелось. В надежде получить какой-то эффект. И я его получил! ))) Так мне даже рядом не показалось, что здесь что-то можно было сделать "не так"!
А вот и результат!
То есть, глядя уже на второй исходник, можно было бы предположить, что произойдёт. Но этого я не увидел, просто запустил под time. И далее уже просто поднимал упавшую на пол челюсть! :) Разница в скорости исполнения составила более 500! Можно, оказывается, и так писать на Си. Тем более, поставленная задача была решена верно. Скорее всего, на каком-нибудь Low-Speed-C-Contest я занял бы место не последнее.
В общем, я считаю, что написал я "функцию копирования файла" очень даже не зря.
Мораль сей басни такова - это вам не шахматы, тут думать нужно...

parent From admin profile Mon Oct 3 18:19:29 2016 pencil

userpic

Это не из той сказки сценка

При чём тут буферизация, при чём тут ввод-вывод, вы же всё время, какое можно, угробили сначала strcat'ом, а потом fprintf'ом. На одних поисках и копированиях.

Возьмите fputs вместо fprintf -- думаю, уже за счёт этого получится раза в два быстрее. А потом в цикле, где читаете и копируете, заведите указатель на текущее место в строке и после каждого копирования сдвигайте его на strlen(buf), чтобы не надо было каждый раз весь буфер просматривать в поисках его конца. После этого, подозреваю, скорости если не сравняются, то станут похожими.

parent From Anonymous (unverified) Tue Oct 4 15:05:00 2016 pencil

Да, конечно!

Правда, при замене fprinf() на fputs() ничего заметного я не получил.
Ну, а смещение начала буфера для записи в цикле, разумеется, сделало результаты уже сравнимыми.
Попытка нашинковать копируемый файл на ровные дольки тоже результат дала.
Так вот оно получилось
Можно было, конечно, еще в один цикл загнать, изменяя размер количества читаемых/записываемых байт и измеряя время, но я по-простецки полтора десятка раз запустил программку под time.
Безо всяких gnuplot'ов картина мира довольно наглядна. Как бы то ни было, выяснено, что временами можно что-то поймать, изменяя размер буфера.

From Anonymous (unverified) Thu Sep 22 07:37:00 2016 pencil

Вдогонку.

Уже написал, что введение в низкий уровень хорошее. Однако, вижу тут уже люди во всякие графические приблуды полезли! Плохого в этом ничего нет, инструментарий всякий знать нужно, и дело даже не в том, что edb - это не "unix-way", а в том, что в самой книге отсутствуют даже примеры работы с элементарными утилитами, соответствующими уровню обучающихся, нет никаких рекомендаций по начальным простейшим инструментами. GDB - это довольно сложно, довольно круто и не всегда нужно. В то же время существует куча консольных утилит, хекс-редакторов, отладчиков, в том числе (по-моему, очень удобных) vi-like. Всякие ald, hc, bvi, radare - только сходу вспомнилось! Кучи их. Кстати, очень жаль, что не попадалось 100%-ного аналога dos'овского debug - чудная была игрушка для 16 разрядов. А пужать людей синтаксисом ATT, родного лишь для больших машин, совершенно ни к чему. Уж если NASM, так и все человек должен видеть в нормальном синтаксисе. Вот в этом, по-моему, недостаток книги.

parent From admin profile Thu Sep 22 20:59:05 2016 pencil

userpic

Э?

Знаете, вот вообще ничего не понял из вашего текста. Как сейчас говорят, не распарсил.

From Anonymous (unverified) Thu Sep 22 06:00:00 2016 pencil

Прочитал.

Прочитал. Хорошее введение в низкий уровень. Понятное, доступное. Напомнило куда более раннюю книжку Sivarama P. Dandamudi "Guide to Assembly Language in Linux". Мастхэв. Необязательно даже на книжной полке, в мозгах - однозначно.

parent From admin profile Thu Sep 22 20:58:20 2016 pencil

userpic

Это о другом. В

Это о другом. В книге Дандамунди ассемблер -- самоцель, у меня он никогда самоцелью не был.

From xbin (unverified) Sun Sep 18 13:27:00 2016 pencil

Том 2-й,

Том 2-й, страницы 258-259
Приводится пример с инициализацией локального массива.
"... данные будут копироваться в область памяти в стековом фрейме каждый раз в начале выполнения такой функции".
А вот что получается у меня:
Как оно получается
Здесь просто объявляется и инициализируется строка в функции main().
Эта же строка явно проглядывается в результирующем файле безо всякого hexa. Так неужели, если далее будет следовать, скажем, фукнция printf("%s", str), принимающая указатель на эту строку, в стек будет копироваться не указатель, а вся строка? Неужели ее нельзя вытащить из уже загруженного в память файла? Область памяти-то, очевидно, read, как минимум?
Или чего-то не так понимаю?

parent From admin profile Sun Sep 18 14:04:00 2016 pencil

userpic

Как оно

Как оно получается

И что, простите? Да, строковый литерал размещён в самом конце сегмента кода, где он, собственно, и должен быть. Плюс к тому в вашей программе нет ничего даже отдалённо напоминающего случай, про который вы говорите: никаких локальных массивов вы в своей функции main не описываете. Возможно, вам стоит обратить внимание на рассуждение, которое приведено в книге на стр. 265--266.

Так неужели, если далее будет следовать, скажем, фукнция printf("%s", str), принимающая указатель на эту строку, в стек будет копироваться не указатель, а вся строка?

Нет, разумеется. Это вообще невозможно, строка -- это массив, а массивы как целое в Си не обрабатываются. У вас какой-то бардак в голове, но я пока не понял его причину.

parent From Anonymous (unverified) Sun Sep 18 14:26:00 2016 pencil

Не будет

Не будет бардака, не будет и порядка.
Что же я делаю тогда, как не объявляю и не тут же не инициализирую массив символов? И этот массив находится внутри функции main(). Чем же он не локальный, где я неправ?

parent From admin profile Sun Sep 18 15:09:00 2016 pencil

userpic

Ну какой же это массив, в самом деле

Вы объявляете (точнее, не объявляете, а описываете) указатель, а не массив. Компилятор размещает строковый литерал в сегменте кода, как обычно, а ваш указатель str инициализирует адресом начала этого строкового литерала. В том, что всё обстоит именно так, можете убедиться, попробовав присвоить что-нибудь элементу вашего "массива", то есть сделать что-нибудь вроде str[0]='B'. Программа при этом рухнет с грохотом и спецэффектами. А вот если вместо того, что у вас там написано, написать

char str[] = "Hello, world\n";

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

parent From Anonymous (unverified) Sun Sep 18 15:22:00 2016 pencil

Да, разница,

Да, разница, конечно, есть... С этим надо посмотреть внимательней, спасибо.
Однако, в случае именно массива, строковый литерал в коде отсутствует, там что-то куда более невнятное. И как сама машина это разбирает, пока неясно! )))
А синтаксис gcc к "человеческому" виду я привожу в конфиге - .intel_syntax.

parent From admin profile Sun Sep 18 16:22:46 2016 pencil

userpic

Ну, тут уж

Ну, тут уж совсем всё просто. Это "невнятное" представляет собой четыре команды mov, по четыре байта каждая. Действительно, это явно быстрее, чем "честно" копировать строку.

parent From Anonymous (unverified) Sun Sep 18 16:32:00 2016 pencil

Да, спасибо!Уже

Да, спасибо!
Уже под gdb всяко-разно покрутил, там без всяких вставок интеловский синтаксис легко делается одной строкой в ~/.gdbinit - set disassembly-flavor intel.
Надо сказать, что "вроде бы хорошо знакомые вещи" при ближайшем рассмотрении оказываются не такими уж и знакомыми. Во всяком случае, как бы смотришь с "другой стороны".
Написано хорошо у вас, на понимание ориентировано... То есть, не зря книжку взял.

parent From Михаил (unverified) Mon Sep 19 06:36:00 2016 pencil

Ну, что можно

Ну, что можно сходу сказать, так это то, что если люди из-за одной строки лезут в hex с отладчиком, то книга методически написана верно, и уже не зря. Даже из-за одной строки залезть в отладчик стоит, посмотреть - как там чего. Да и изначальная ориентация на *nix верна абсолютно - сама система как бы предназначена для обучения - три ведра с горкой уже предустановленных утилит, при некотором желании не оставляющих шансов на то, что какие-то непонятки в полученном бинарнике могут остаться. Синтаксис интел (ATT только для тех, специализируется на ассемблерах) всеми ими поддерживается с незапамятных времен. Хочешь - их пользуй, хочешь "покрасивше" - ставь какой-нибудь EDB (тот же Olly) - и дизассемблер, и hex тебе тут, и регистры, и стек, и чего хочешь - короче, было бы желание учиться...

parent From Anonymous (unverified) Mon Sep 19 08:45:00 2016 pencil

Да, все верно

Да, все верно написано относительно стека и массива.
Вот наиболее наглядно из того, что смотрел
В первом случае (char str[]= "very long initialization";) строка, действительно, полностью копируется в стек, собака!
Во втором (char *str = "very long string";) - в стеке только указатель.
Получается, что str[] = "very long initialization" запись неверная в принципе.
Я так, правда, никогда и не делал, но теперь уж точно не буду! :-)

parent From admin profile Mon Sep 19 13:19:00 2016 pencil

userpic

А у вас ус

А у вас ус отклеился ссылка битая :-) Запись я бы не стал называть "неверной в принципе", бывают случаи, когда именно такой вариант играбелен --- например, в случае, когда строка представляет собой некое сообщение, в котором в зависимости от параметров функции нужно сначала что-то "подкрутить". Кроме того, если переменная глобальная или локальная-статическая, то никакого копирования не будет, а строка окажется в сегменте данных, при том что синтаксис ровно тот же. В общем, пользоваться инициализацией массивов нужно, просто надо при этом понимать, что происходит. Это, впрочем, при работе на Си обязательно всегда.

parent From Anonymous (unverified) Mon Sep 19 16:13:00 2016 pencil

Действительно, отклеился! :(

Ох, уж мне этот клей от Майкрософт...
По вышеподсказанному EDB отлично видно, ЧТО находится в стеке при str[] и *str.
Хотя, если бы кто раньше мне сказал, что строка в принципе может копироваться в стек, я бы в него непременно плюнул, и сейчас мне было бы стыдно. Полагалось, что для изменяемых строк существует выделение памяти в куче, и только. Оказывается, дело обстоит несколько иначе - если в строке не целый роман, то можно и в стеке - побыстрее должно быть, однако!

parent From admin profile Mon Sep 19 21:15:14 2016 pencil

userpic

При чём тут

При чём тут "побыстрее"? Локальные переменные, будь то хоть маленький char, хоть огромный пятимерный массив, заводятся в стеке. Их, собственно говоря, больше негде заводить, ведь в языке Си (самом по себе) нет никакой кучи и никогда не было. Функции malloc и free — библиотечные, компилятор о них не знает, а если и знает, то должен делать вид, что не знает.

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

parent From Anonymous (unverified) Mon Sep 19 23:56:00 2016 pencil

Да, спасибо!

Да, спасибо! Бардака, вроде, поменьше стало...
Правда, поначалу смутили несколько "огромные пятимерные массивы" в стеке, поскольку ulimit -s у меня показывает 8192К возможного стека... Ведь, ежели вдруг еще внезапно приспичит рекурнуться (а мало ли?!) с эдакими массивами, тут же поимеем SIGSEGV по морде!
Но оказывается, даже и на такой космический случай имеется волшебная структура rlimit из файла sys/resource и волшебная же функция setrlimit(), с помощью которых можем стек и увеличить. Во всяком случае, повторный вызов функций getrlimit(RLIMIT_STACK, &stack) и system("ulimit -s"); показывают уже запрошенные размеры стека. Плюс, говорят, имеется для той же цели еще какой-то ключик у gcc. То есть, не все так плохо, на самом деле.

parent From admin profile Mon Sep 26 11:14:25 2016 pencil

userpic

Насчёт огромных пятимерных

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

From Анна (unverified) Sun Sep 18 09:16:00 2016 pencil

Да, "учебным"

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

parent From admin profile Sun Sep 18 14:00:24 2016 pencil

userpic

А если порыть в

А если порыть в этом издательстве,

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

From Анна (unverified) Sat Sep 17 06:29:00 2016 pencil

Из двухтомника

Из двухтомника видела только том второй. Часть, посвященная ассемблеру прошла по диагонали. Что же касается части, освещающей начала Си, то это, пожалуй, одно из наиболее толковых введений в предмет, которые случалось видеть. Особенно порадовало краткое описание библиотеки ncurses. Дело в том, что после самого начального обучения, человек остается один на один со своими проблемами - чисто средствами языка решить какие-то свои даже элементарные проблемы он не может, тут нужен уже навык поиска соответствующих библиотек, коих хоть и написано немало, но без такогого навыка, простейшим путем является поиск языка, в котором возможности этих библиотек являются встроенными. В результате язык Си воспринимается чисто как учебный, "теоретический", несмотря на то, что он как был, как и останется живым, и далеко еще не "полтора десятка лет". Нету ни малейшего намека на то, что ему грядет какая-то замена.
На днях буквально случилось столкнуться с ситуацией, когда обучающийся (основы Lisp), снес рекомендуемый sbcl и заменил его на CLISP только на том основании, что в sbcl нет поддержки readline. То ли в силу лицензионной GNU-тости, то ли просто пакет был так скомпилирован. Установка оболочек readline, таких как rlwrap, rlfe решило проблему тут же. То есть, дело именно в отсутствии элементарных знаний, что ВООБЩЕ существует в природе, в том числе и для Си. А существует немало!
Ясно, что в книгу такого объема вместить удается лишь самое необходимое, возможно, те, кто у Вас обучается, на живых лекциях получают больше. Однако, очень жаль, что порыв на ютубе по слову "Столяров", не получилось отыскать ничего подходящего. Лучшим, что случалось видеть из русскоязычного по началам Си - это "специалистовский" несколькодневный курс Тетерина. Всякие скринкасты и видеоуроки, думаю, дело не очень благодарное, но, все же, было несколько жаль. Это даже не призыв к какой-то "рекламе", хотя в данном случае, это было бы вполне обоснованно, а именно просто сожаление, что вживую преподаваемые (уверена - хорошо) начала Си - да и не только Си, важна методика - остаются достоянием от силы лишь нескольких студенческих групп.

parent From admin profile Sat Sep 17 10:45:30 2016 pencil

userpic

Ну, про язык Си

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

К сожалению, должен сказать, что ни на ютьюбе, ни на любых других сервисах компании Google от меня вы не найдёте ничего и никогда, ни сейчас, ни в будущем. И в так называемых "социальных сетях" тоже -- во всяком случае, пока они не станут реально сетями, распределёнными и не принадлежащими никому в виде целого (а этого в обозримом будущем не произойдёт).

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

From Anonymous (unverified) Sat Sep 10 10:21:00 2016 pencil

Тоже читаю

Тоже читаю первый том. Указатели, динамические структуры. Языком понятным написано, не вредная совсем у Вас книжка.
Но по ходу чтения пришлось вернуться и к вещам более статическим. Задача предельно простая - имеется файл (с полмиллиона строк) в формате CSV, на каждой строке пара ключ-значение. Из него нужно выдрать все данные и поместить в удобную структуру, постоянно висящую в памяти. Сходу просматривается примитивная сишная структура из двух строк. Ну, можно int и char[], только позже из этих ключей-значений будет формироваться URL, так что, два char[], чтобы потом без кастинга.
Используется массив таких структур. Для получения значения по ключу, его пробегаем в цикле. Вроде, все хорошо работает.
Но "по совету" друзей, а также вдохновленный чтением вашей книжки, решил попробовать затолкать эти строки в такой тип данных, как "ассоциативный массив" (язык D, стандартная библиотека Phobos). Вроде, действительно, как раз, подходит! И никакого пробега в цикле не требуется, значение по ключу получается "напрямую". Так дело в том, что после загона в этот самый "ассоциативный массив" (скорость там сложно определить и сравнить, данных, наверно, мало), памяти стало жраться процентов на 30-40 больше. Один и тот же файл. Всё, что делается - загоняется в сишную структуру (первый случай) и в ассоциативный массив (второй). Далее, просто останавливаемся в цикле for(;;), ожидая ввода ключа для получения значения. Вроде, ничего противозаконного не делал. Откуда такие потери в памяти? Или эти "ассоциативные массивы" в принципе должны жрать больше? Представляется, что их реализация хорошая. Так и неужели дело именно в том, что сама реализация таких типов данных просто подразумевает большее потребление памяти?

parent From admin profile Mon Sep 12 18:30:00 2016 pencil

userpic

Ну а вы чего ожидали

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

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

И ещё, я не уверен, чтО вы подразумевали под "останавливаемся в for(;;)" — вы ведь не имели в виду активное ожидание, правда?

parent From Anonymous (unverified) Tue Sep 13 01:46:00 2016 pencil

Возможно, делаю

Возможно, делаю чего-то не так. Я не волшебник, только учусь.
Но до собственно поиска дело еще не доходит. Просто читаю файл, каждая строка бьется на две части по csv-разделителю. Обе части забиваю в структуру. Из всех строк получается массив таких структур. Сразу останавливаемся - аналог scanf("%s", any_ID). Ждем ввода. Все. Смотрим память.
Тоже самое при забивании в ассоциативный массив.
Так во втором случае памяти отжирается больше на 30-40%. Вот и не понимаю пока, за счет чего...

parent From admin profile Tue Sep 13 20:47:19 2016 pencil

userpic

scanf("%s", ...) не

scanf("%s", ...) не делайте НИКОГДА, так же как и пресловутый gets. За это с работы увольняют, сам видел. Причины -- жирным шрифтом на стр. 279 (про scanf), потом подробное обсуждение gets на стр. 291-292.

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

parent From Anonymous (unverified) Wed Sep 14 07:09:00 2016 pencil

Да, понял,

Да, понял, спасибо.
По поводу gets() так уже сколько лет gcc верещит, что "НИЗ-ЗЯ!". scanf(), конечно, из той же оперы... А вот библиотеку с хэшами надо бы какую-нибудь отыскать поприличнее, это да! Все написано до нас - надо только найти. Действительно, вполне могут попасться задачи, где удобнее постоянное время доступа к произвольному элементу, несмотря на потерю памяти. Линейным поиск по-идее быть не должен. Разумеется, "сишную" библиотеку. Сравнение того, что у меня получилось на D и на C, шансов для дальнейшего практического использования D просто не дает. Уж больной тормозной! Прототипы программ, если только... Ну, или чтобы посмотреть "как оно там" и как его реализовать на Си. Или, при случае, есть чего сравнить с "плюсами". Мысли-то и там, и там хорошие есть. Вот только за счет скорости исполнения почему-то!) Такое примерно применение...
А вот что касается моей конкретной задачи (примерно полмиллиона строк в память), то там, пожалуй, линейный поиск вполне уместен. Дело в том, что за несколько лет выяснилось, что обращение к разным данным чрезвычайно неравномерно. От очень частых до чуть более, чем редких. Поэтому и был создан массив структур (около двадцать тысяч из полумиллиона) с линейным поиском, а если запрашиваемые данные были из числа "чуть более, чем редких", шло обращение к файлу. Причем, из таких редких запросов создавался дополнительный короткий кэш-файл, к которому впоследствии и обращались в первую очередь. Именно таким образом и покрывалось около 99 процентов запросов. А использование специфической библиотеки привело бы к потери памяти. Вряд ли нужной... Хотя, конечно, все надо бы считать, "на глаз" тут сложно. Но как говоривал в былые времена один легендарно-неуважаемый программист - "Шестьсот сорок килобайт должно хватать для всех!" ;) Тут он прав.
В общем, книжку читал, что-то новое узнал по любому. По мере чтения вопросы возникают, которых могло бы без нее и не возникнуть.

parent From Anonymous (unverified) Thu Sep 15 05:11:00 2016 pencil

Не откладывая в

Не откладывая в долгий ящик, покуда есть время, по первому же запросу "hash iibrary for C" выгуглил ссылку https://github.com/troydhanson/uthash. Поверху была, так что судьба ее такая. Завелось практически сходу, как под меня написано. Это даже не либа для хэшей, а просто хедер, в котором аж на 1000 строк чего-то умное намакросячено - еще не смотрел (это для тех, кто говорит, что препроцессор Си "устарел безнадежно"). После подключения этого заголовка имеем обычные, как и во всех скриптовых языках хэши. Памяти, как уже и ожидалось жрётся больше, чем при организации поиске в цикле, но скорость, конечно, повыше. Впрочем, на Си это заметно не сильно (и так быстро!). То есть, можно легко уже и рабочий вариант переписать (там за несколько лет памяти-то на машинах прибавилось). Не думаю, что это самый эффективный вариант реализации хэшей, но он очень простой и начальные представления дает всяко.
Что касается применения для этой же цели языка D, то пришлось отказаться от этой затеи. Исходно подкупило то, что "D - язык для системного программирования", "на нем можно писать как на С" и т.п. Писать-то можно, конечно, как на С, только скорость исполнения получается как на D, а то и как на E! :-) Да и если глянуть его историю - D1, более раннюю библиотеку Tango, то там почему-то даже синтаксически попахивает больше Java, чем С.
В общем, полезная у Вас книжка. Даже уже потому, что заставила разобраться и ответить на какие-то вопросы, которые в ней даже не затрагивались.

From Xmmy (unverified) Mon Sep 5 11:23:00 2016 pencil

Вопрос по

Вопрос по первому тому (Паскаль, hello.pas) стр. 201:
"Просто компилятор вынужден загнать в файл сразу всё, что нужно для выполнения практически любых операций ввода-вывода, а мы эти возможности пока не используем".
Получилось у меня даже больше 120 Кб. Нельзя ли "загнать" в файл только то, что нужно? А то такие размеры не радуют совсем. То есть уменьшить вес программы именно на Паскале, не забираясь ни в какие ассемблеры?

parent From admin profile Mon Sep 5 11:29:09 2016 pencil

userpic

Никак

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

parent From Xmmy (unverified) Mon Sep 5 12:41:00 2016 pencil

Ясно, спасибо.

Ясно, спасибо. Просто думал, что поскольку компилятор fpc не самый новый, то и имеется возможность использовать рантайм поменьше, по аналогии с Си, где элементарные программы можно собирать с либами из комплекта VS6 или пользовать TCC (книжка читается под Windows), что делает их размер более приличным.
Точка зрения относительно подсветки синтаксиса нестандартна довольно. Думаю, немногие согласятся. На новогоднюю елку редактор конечно походить не должен, но syntax off - уж очень радикально. Того редактора не видел, но само отсутствие возможности подсветки синтаксиса делают его фактически непригодным для большинства пользователей.

parent From admin profile Mon Sep 5 13:34:36 2016 pencil

userpic

Во-первых, в vim,

Во-первых, в vim, разумеется, есть подсветка синтаксиса — её даже не очень просто отключить (synt off отключает только для текущего буфера, при открытии на редактирование следующего файла снова определяется его тип и включается подсветка), но мне пришлось научиться (найти команду filetype off), поскольку никакие даже зачатки подсветки я на дух не выношу.

Во-вторых, если вы пытаетесь учиться под Windows, я вам, как автор обсуждаемой книги, настоятельно рекомендую на эту книгу больше времени не тратить. Найдите себе другой учебник. Моя книга заведомо бесполезна для людей, не готовых к расставанию с виндой.

parent From Anonymous (unverified) Mon Sep 5 15:52:00 2016 pencil

Никак

Никак лазутчика изловили, Андрей Викторович? И ведь повезло ещё, что сам сознался! :) Иначе...
Действительно, и как таких только в Интернет пускают!

Задумал как-то виндузятник
Наук постичь хотя б основы.
Тут надо было б аккуратней,
А он попал на Столярова...

Куда ж ты лезешь, бедолага,
Коль даже с Vim'ом не в ладу?
И сделать не успеешь шага,
Считай, накликал уж беду!

И точно - послан уж по маме,
Спасибо хоть, что не в гробу!
Но сразу отлучён от Знаний,
И враз - к позорому столбу!

Это ещё он не лютует,
Это шутейный незачёт!
Убунту хоть поставь какую,
Тогда, быть может, повезёт... ;)

parent From admin profile Mon Sep 5 20:01:39 2016 pencil

userpic

Гыгыгы

Стих понравился :-)

Если серьёзно, ну есть же предисловия, там ясно и недвусмысленно объясняется, почему нужен *nix и почему винда не годится. Но вот нет, поди ж ты. Не, ну ладно человек жаждет остаться под виндой (таких много), но почему тогда именно мои книжки, которые для этого ну никак вообще?

From Le0nX (unverified) Mon Sep 5 05:00:00 2016 pencil

Актуально?

Я так понимаю, что Ваша книга "Программирование на языке ассемблера NASM для ОС Unix" (2010) уже более не актуальна и всю ее информацию с дополнениями можно найти в этом -втором томе?

parent From admin profile Mon Sep 5 11:21:41 2016 pencil

userpic

Совершенно верно

Б'ольшая часть текста книги 2010 года вошла во второй том в виде Части 3; небольшие фрагменты из "Введения" ушли в первый том, во вводную часть (Часть 1). Так или иначе, текст подвергся исправлениям, дополнениям и прочим улучшениям, так что, конечно, правильнее читать новую книгу.

From Anonymous (unverified) Thu Sep 1 22:34:00 2016 pencil

Касаемо Джава

Наверно, за Джавой я бы всё-таки что-нибудь, да оставил. Хотя бы мобилки...
Впрочем, здесь, думаю, контекст, всё же, иной. Речь-то, о самых началах, об обучении. Джава - это больше для пенсионеров. И уж учиться на ней нельзя точно! И потом, "Жаба - отстой!" - это не только лозунг, шутка юмора, это и образ жизни, и подход к делу. Выше совершенно справедливо замечено, что без того "пониженного уровня", который обеспечивают оба тома, в программировании делать просто нечего.

Видел как первый том, так и второй. Не могу сказать, что встретил много незнакомых слов, но читалось с удовольствием. Увлекательно написано! :-) А кое-что из "начал Паскаля" даже было просто незнакомо. Кроме того, в процессе чтения наблюдалось некоторое устаканивающее в мозгах действие. Кстати, жаль, что это произошло не раньше ...

Очень даже похоже на истину, что начинать обучение всё-таки с Паскаля предпочтительней. Я, помнится, начинал сходу с Си. И средний уровень достигнут был довольно давно. Но путь мой был (с сегодняшней уже точки зрения) весьма кривотернист. Слишком много лишнего текста пришлось отработать. Явно можно было обойтись меньшими затратами и временем. Даже жаль, что уже не студент. Как бы то ни было, хорошая учебная литература не может не радовать.

Картинка, кстати, на обложке второго тома тоже ничего себе. Хорошая. Как бы сказали на Украине (вот нравится мне их язык! Несмотря ни на что! :-)) - "навіває"...

Планета Си. #include хорошая погода.
Здесь Плакающий_Туч с Крылатой_Рыбой
Хоронят Жабу - умерла в конвульсиях байт-кода.
И Жук_с_Граблями закопал... Друзья, спасибо!

parent From admin profile Fri Sep 2 16:48:41 2016 pencil

userpic

Про джаву

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

А в остальном -- спасибо за отзыв, особенно за стих :-) Порадовался.

parent From Anonymous (unverified) Sat Sep 3 08:49:00 2016 pencil

За Джаву, как

За Джаву, как раз, я особо-то, и не ратовал. Скорее, даже скромно полагал, что Самый Главный Нелюбитель Джавы - это я! :) Вижу, придётся подвинуться... Правда, нелюбовь эта имеет не столь теоретические резоны, тут всё проще - у меня на сегодняшний момент весьма простенький двухъядерник с четырьмя гигами на борту (разумеется, система 32-разрядная). И я бы сказал, хватает с серьёзным запасом. И как-то "модернизировать" комп я не собираюсь. С учётом того, что "помедленнее" как-то "не нравится", очевидно, что Джава мне ни к чему. Ну, а ставить на машину какие-либо системы и программы-студии, которые в принципе предназначены для пожирания жёстких и мягких мегабайтов и многоядерных тактов, мне и в голову никогда не придёт.
Хотелось бы момент ещё отметить, который кажется существенным для общей оценки написанных книг. Скажем так - коммент "из курилки". Там вполне мог бы и остаться. Не думаю, что это правильно...
Один из "незатягивающихся" (на стенке надпись "НЕ КУРИТЬ") нашёл стиль изложения двухтомника схожим со стилем изложения одного из своих преподов (а не студент он уже куда более, чем я!). Специальность у него непрограммерская - психология. Два семестра на основы программирования - за глаза. Пренепременнейше - TurboPascal. Так у них дама, которая должна была читать сей предмет (и даже начала), внезапно заболела декретом, не успев даже поведать о всех тонкостях копирования и вставки текста в этой знаменитой среде (а клятвенно обещала!), и им на весь срок нашли преподавателя "с производства". Он их тут же переориентировал на Си. Что ж, экзамены-то, ему принимать! Так со слов тогдашнего студента, человек этот "с производства", несмотря на то, что мог "как преподать, так и приподдать", был в состоянии объяснить довольно непростые для новичков материи - те же указатели - буквально на пальцах. Процессоры и компиляторы тогда были несколько другие, в частности скорость исполнения передних и задних инкрементов-декрементов отличалась, так он умудрялся объяснять даже трёхэтажные конструкции из указателей, и почему они работают быстрее, чем аналоги с индексной нотацией, языком вполне человеческим. Сейчас, возможно, это уже и не так. Это времена, думаю, скорее, такого компилятора как Watcom (не open!). В общем, оценка деятельности этого временного преподавателя была достаточна высока. И сравнение со ним явно не в минус. Достаточно, возможно, сказать, что этот нынешний психолог, по большей части занимающийся тестированием людей в самых разных целях, знает несколько десятков языков программирования. Но использует он их в целях абсолютно непрограммерских! С его точки зрения, языки программирования являются вполне достаточным средством для выяснения наклонностей человека, его возможностей, способностей, поведенческих стереотипов и способа мышления. Характер же и темперамент легко определяется тем языком, на котором человек пишет предпочтительно (поставленная задача здесь в расчёт не берётся). И вообще, по России, наряду с обязательным изучением русского и родного языка, необходимо включить изучение (с самого раннего детства!) языка Perl (это, кто не знает, самый-самый главный язык!;)), после чего, проведение даже самых простейших из его Perl-тестов (куда там IQ вшивому!), тут же всё покажет об испытуемом - сразу его в дурку отправлять, или же он к чему-то ещё всё-таки пригоден. Надо заметить, что Perl действительно позволяет чрезвычайно разнообразные синтаксические конструкции для выражения одной и той же мысли. И вот они как-то в его непрограммерском совершенно мозгу ассоциируются с какими-то "возможными комплексами", "замещёнными стимулами" и прочими не имеющими никакого отношения к программингу, понятиями. Короче, сплошной "волюнтаризм"!..
Тут подошёл кто-то из административного легиона и указал пальцем на надпись "НЕ КУРИТЬ" (а наказывают там на курение в неположенных местах даже строже, чем за использование откровенно нецензурного слова "Джава" на этом сайте), и поэтому идея обязательного перлообразования всенародной поддержки получить не успела.
Как бы то ни было, очевидно, что если откровенный непрограммист находит для себя понятным и полезным способ изложения тем, явно выходящих за пределы его профессиональной ориентации, то уж людям, изучающим программирование в целях именно профильных, всё это должно быть ясно просто как день. Но, увы, уже который раз попадаются молодые хлопцы, получившие именно профильное образование, но просто не понимающие ЧТО они делают! А хлопцы по всему - не тупые совсем! И такое впечатление складывается, что вина не их вовсе - просто в институте кто-то когда-то не смог сделать то, что должен был - подготовить нормального грамотного специалиста в своей области. А по каким причинам не смог - неважно. Дело не сделано.
А вот здесь картина как раз другая - похоже, наблюдается соответствие. И на сегодня, увы, нечастое...

parent From admin profile Sat Sep 3 22:00:32 2016 pencil

userpic

Ого

Не раскрыть такой коммент я просто не мог :-)

Одного не понял, чем и кому может нравиться Perl. Исторически это первый язык программирования, который когда-то давно в меня не полез :-) При том что я на тот момент уже свыше десятка языков знал, притом очень разных, в их число входили и Лисп, и Пролог, и совсем экзотический Рефал, и всякие C/C++, а Perl мне был нужен здесь и сейчас, я работал в интернет-провайдере, там юзвери развлекались с CGI-скриптами на нём, любимом (1997 год, PHP ещё толком не было), так вот поди ж ты, раз пять книжку О'Рейли про Perl открывал, столько же раз и закрывал, не забравшись дальше десятка страниц. Вот тошнит меня от него, и всё тут. То есть понятно, что имеющийся скриптик я и прочитать могу, и слегка переиначить, но вот чтобы прямо на нём писать — не, не могу.

parent From Anonymous (unverified) Sun Sep 4 14:39:00 2016 pencil

Думаю, что Perl -

Думаю, что Perl - "потому что Perl"! :)
Ну, или потому, что не так уж много языков позволяют столь разнообразно синтаксически оформлять код - можно хоть перманентной колбасой писать! Попробуйте такое учудить в Python'е - да Гвидо даже и разговаривать с вами не будет, жёсткие отлупы извольте в коде делать, иначе его математическое сознание просто не выдержит такой пытки. Средневековье какое-то... Фортран до такого не доходил. У Ларри всё иначе - не случайно лингвист по образованию. Готов поддерживать во имя сходства с естественными языками чуть не междометия. Два разных человека. Ну, а этот - так и вообще - не программер! Не стоит с ним (даже если спросит!) откровенничать по вопросу какой способ расстановки фигурных скобок вы предпочитаете - тут же последует какой-то диагноз! Не всё так просто... А уж непонимание такой элементарной вещи как "кому может понравиться Perl"... тут уже диагноз может быть и фатальным! :D
Здесь дело не в том, что "Perl"... А в том, что человек-непрограммер менее чем за два семестра успел получить знания по самым основам бытия: "переменная - это указатель", "значение переменной - разыменованный указатель", собственно "сам указатель - адрес (возможно, логический) первого байта стандартного типа или данных (возможно, выровненных) структуры произвольной сложности". То есть, в вопросе о первичности адреса и данных, приоритет за адресом. И уложились эти основы у него фундаментально. А логически объединённые адреса данных и имена функций (адреса их кода!) не что иное, как класс! Чисто полицейская же языковая функция "сокрытия данных" - это просто защита себя любимого от себя же дурака. Для очень многих проектов это абсолютно ни к чему. Дело меняется лишь с усложнением проектов. Но там (это в первую очередь касается кудрявых и румяных молодых ребят от Java, собирающихся вместе радостно спеть и сплясать о не менее кудрявых "перспективах" означенной "Java"), программист уже программистом-то, и не является! Это лишь шестерёнка, пишущая технически необходимую (ПОКА!) часть кода. Программист в больших проектах лишь один. Это тот, кто организует работу этих всех кудрявых шестерёнок. А кудрявые шестерёнки деквалифицируются (если есть от чего!) написанием тучи обязательных геттеров-сеттеров. И такая вот "геттерно-сеттерная" квалификация на сегодня пока ещё нужна. Хотя, какой-нибудь NetBeans, отобедав с аппетитом полутора-двумя гигами памяти, уже умеет их ваять по мановению пары клавиш... Что далее? А ничего! Далее, как и во всех языках, скорее всего, в Java будет появляться всё больше и больше функциональных возможностей. Тенденция, вроде, очевидная. И, как следствие, написание технически нужных на сегодня кусков кода программными средствами. Тем же NetBeans. Вот и все "кудрявые перспективы" современного Java-программиста. Плюс, не надо забывать, что любой язык (за исключением самых простых - ну, Си, конечно же!), имеет свой жизненный цикл. Так что, и кудрявым и пока ещё румяным тоже забывать не следует - "чтобы стоять, я должен держаться корней" никто не отменял...
Отвлёкся малость... Ну, в общем, Perl в данном случае можно рассматривать отчасти и как чудачество. Но лишь отчасти...
Кстати, среди программистов такого тоже навалом.
Канадец Rob Pike, текстовый редактор Acme, про Sam уж вспоминать не будем, дело совсем уж прошлое. Несмотря на весьма положительные и даже восторженные отзывы (в том числе известных весьма персон), никогда не мог понять их смысл! Возможно, каждому своё... Но почему в современном текстовом редакторе нет, чёрт возьми, банальной подсветки синтаксиса?! Может, Пайк позабыл? Ан нет! "Пишите код, ребята, не портите себе глаза!". Вот вам и Пайк. По мне - так "чудачество" - это самый минимум в приговоре!
Голландец Bram Moolenaar, язык программирования Zimbu.
"Дяденька, а почему в вашем языке, в структурных блоках отсутствует открывающая фигурная скобка? Только закрывающая?" - "А на кой? И так же видно, где блок начинается!"... Далее следует просто убийственное математически-строгое обоснование - "Кроме того, сразу отпадает вопрос о \"правильности\" размещения фигурных скобок!". Каково?! Просто Брам-миротворец всех разноскобочных холиваров! :)
И не звали бы их обоих именно так как их и зовут, можно было бы сказать - придурки придурками!
Вот и встречай после этого по одёжке... Так что, по-разному бывает.

parent From admin profile Mon Sep 5 11:26:43 2016 pencil

userpic

М-да

Пожалуй, этот поток сознания я в основном оставлю без комментариев, за одним исключением: таки да, я не использую подсветку синтаксиса. При настройке vim у меня первое действие — в .vimrc добавить syntax off и filetype off. Когда у меня серые буквы на чёрном фоне, я проработать могу часов пятнадцать, не разгибаясь, а вот если буковки становятся разноцветными, да ещё разными по яркости — пиши пропало, уже часа через полтора хочется глаза закрыть и больше не открывать. Вообще не понимаю, как этим можно пользоваться.

Ну и про Perl: всё, что вы тут позиционируете как его достоинства, на самом деле является его недостатками, притом фатальными.

parent From Anonymous (unverified) Sun Sep 18 11:28:00 2016 pencil

Подсветка синтаксиса

Сурово, ничего не скажешь! :) Хотя и очень субъективно.
Использую Vim с начала века, хоть последние лет 5-6 исключительно в графической ипостаси. Мог бы согласиться, что как встроенные, так и практически все цветовые схемы, встречавшиеся в сети - ужас просто необычайный, но совсем без подсветки я все же не обхожусь. По привычке (как раз начала века) в любой .vimrc вбивается:
autocmd BufEnter *.c colorscheme visualstudio или vcbc, слегка отредактированные.
В память о шестой студии и борланд 5.02.
Только Столярову не говорите! :D

parent From admin profile Sun Sep 18 14:08:18 2016 pencil

userpic

Видимо,

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

parent From Anonymous (unverified) Sun Sep 25 11:12:00 2016 pencil

Да, конечно, у

Да, конечно, у всех все по разному!
У меня среди знакомых так вообще есть персонаж, который использует исключительно монохромный режим монитора (полстола у него занимает) черт знает каких годов. И лишь там чувствует себя комфортно (с различением цветов у него нормально). Я к таковым не отношусь.
Что же касается редактора Acme, то его просто нельзя рассматривать вне контекста OS Plan9 - он там просто один из основных инструментов. Будучи поставлен на Linux или Windows, он представляет собой нечто совершенно другое, неизвестно для чего нужное. Правда, попадался на Хабре какой-то любитель Acme под Linux, но что-то поверил я ему не сильно. Под Linux есть один редактор. Называется "Vim". Те же, у кого с его освоением возникли проблемы, корячат пальцы на Emacs. У всех же прочих просто рука дрогнула при выборе системы в меню GRUB - заблудились слегка, у них там всяко где-то пониже должно быть написано "Windows".
А под Plan9 (элементарно устанавливается под VBox), штука просто необходимая. Чисто даже из соображений "повышения образованности" поставить можно вполне, да глянуть. Unix'ы разные бывают. Тут такой своеобразный unix, в котором немалую роль играет трехкнопочная мышь. Ну, а авторы, действительно, в представлении не нуждаются! Может, в чем-то и чудаковаты, не без этого...

From Anonymous (unverified) Fri Aug 26 10:48:00 2016 pencil

Точно. Так оно и

Точно. Так оно и получилось - непреодолимые трудности копипасты :)

parent From admin profile Sat Aug 27 09:11:01 2016 pencil

userpic

Зачем вам копипаста?

Выше на этой странице имеется ссылка на архив примеров, в котором имеется в том числе и обсуждаемая программа :)

From Anonymous (unverified) Fri Aug 26 00:55:00 2016 pencil

Первый том не

Первый том не видел еще, а вот второй посоветовали, так и почитал. Действительно, очень удачно у Вас получилось как с собственно объяснением материала, так и с выбором темы, по которой столь компактно и доступно написано очень немногое. Фактически, это необходимый промежуточный уровень между программистом-питекантропом, каковыми, безусловно являются студенты младших курсов и программистом-кроманьонцем (старшие студенты). Пропустив этот неандертальский уровень, подойдя вплотную к С++, люди рискуют познакомиться лишь с самым одельфированным и овасяченным его подмножеством, что приведет их неминуемо к Java, а то и к такому субпродукту как .NET. Нужность самой Java сомнений не вызывает - действительно, бывает и объективная сложность ряда задач, и высокая надёжность требуется при низких требованиях к быстродействию. Однако, во многих случаях применение Java (сейчас ведь ее суют куда ни попадя), явно необосновано. И связано такое применение лишь с отсутствием должной квалификации программиста.
Буквально на днях слушая за пельменями в фоновом режиме русскоязычный скринкаст о началах Java, чуть не подавился от удивления - лектор выразил мысль, что нонича машина Java настолько крута, что программы для нее написанные, работают "чу-у-уточку" медленнее, чем аналог на С++, а в ряде случае (наверно, у него писюк с Java-процессором) еще и быстрее. И хотелось бы верить, что он высказал такую ахинею чисто в пылу лекторском, а ведь могло бы быть и иначе - сам написал на Java, потом на С++, сравнил и убедился, что "на Java быстрее"! Но по любому очевидно, этот самый "промежуточный уровень" у него остался непознанным. Ну, или он изобрел на досуге какой-нибудь особо хитрый замедляющий компилятор для языка С++... Или умудрился как-то скомпилить откровенно плюсовой исходник в байт-код Java, тут уже только гадать можно!
Лишнего опять же в Вашей книге нет, что совсем неплохо. Помнится, TASMы и MASMы под DOS радовали меня не сильно. Хотя, и разумеется, что-то дали...
В общем, нужная книжка, и написана хорошо. Остается надеяться, что она попадет в правильные руки, и количество грамотных, подготовленных студентов подрастет. Спасибо.

parent From admin profile Sat Aug 27 09:12:58 2016 pencil

userpic

Вот уж что-то, а

Вот уж что-то, а джава точно не нужна :) В остальном спасибо за отзыв.

From Anonymous (unverified) Thu Aug 25 11:35:00 2016 pencil

Учебная

Учебная программа из раздела обучения ассемблеру (ubuntu 16.04.1) выдаёт такое:

user1@cmp:~/asm-test$ nasm -f elf asm-test.asm
asm-test.a:11: warning: label alone on a line without a colon might be in error
user1@cmp:~/asm-test$ ld -m elf_i386 asm-test.o -o asm-test
user1@cmp:~/asm-test$ ./asm-test
Hello
Hello
Hello
Hello
Hello
Ошибка сегментирования (сделан дамп памяти)

parent From admin profile Thu Aug 25 17:21:53 2016 pencil

userpic

Подозреваю, что

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

From Александр (unverified) Mon Aug 22 17:33:00 2016 pencil

errata file

Андрей Викторович,
хочу предложить вам полезную вещъ:
заносите найденные ошибки/опечатки в файлы errata_volX.pdf (так они не будут теряться в дебрях комментариев на сайте; так сделано во многих серьёзных книгах, которые out of print или что-нибудь ещё в этом духе касательно переиздания)

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

parent From admin profile Mon Aug 22 18:43:36 2016 pencil

userpic

Пожалуй,

Пожалуй, хорошая идея -- постараюсь реализовать. Спасибо.

From Евгений (unverified) Mon Aug 8 13:30:00 2016 pencil

Ошибка

На странице 218 описывается функция case_up.
Помоему, там ошибка в возврате символа в верхнем регистре.
return c-('a'-'Z')
Должно быть
return c-('a'-'А')
либо (что тоже самое)
return 'A'+(c-'a')

parent From admin profile Sun Aug 14 15:04:00 2016 pencil

userpic

Ага, спасибо

Да, вы совершенно правы, очень досадная опечатка. Спасибо за её выявление. Только это стр. 214, а не 218 :-)

From Ольга (unverified) Sat Jul 23 17:22:00 2016 pencil

const в gcc и clang

Читаю страницу
265
"естественно, компилятор не станет размещать..." строковый литерал "в неизменяемой памяти (кстати, даже в том случае, если массив будет объявлен со словом const)...".
Что-то не так делаю, или не "каждый компилятор"? Похоже, что clang размещает строковый литерал как раз в неизменяемой памяти, хотя при компиляции никаких предупреждений не выдаёт...

parent From admin profile Sun Aug 14 15:11:59 2016 pencil

userpic

Что-то здесь не так

Действительно, обычно компиляторы размещают строковые литералы в неизменяемой памяти, об этом, собственно говоря, как раз и идёт речь на стр. 264-265. Цитируемый вами фрагмент относится к (особому) случаю, когда строковый литерал используется не сам по себе, а в качестве инициализатора для обычного символьного массива. Такой массив, каков бы ни был его инициализатор, размещён будет либо в стеке (если он локальный), либо в сегменте данных.

Если это объяснение не устранило ваши сомнения, давайте сюда ваш пример :-)

parent From Ольга (unverified) Thu Aug 18 01:21:00 2016 pencil

Пример

Ага, ну, картинка, видимо, не проходит...
Вот и пример:

#include <stdio.h>
int main()
{
    const char str[] = "Hello\n";
    char *m = (char *)str;
    *m = 'M';
    puts(str);
    return 0;
}

И gcc и clang, оба компилируют без вопросов энд предупреждений, однако, при исполнении, в первом случае имеем вывод "Mello", во втором же получаем Segmentation fault. Так и кто же из них прав? :)

parent From admin profile Thu Aug 18 06:12:00 2016 pencil

userpic

Как обычно, правы оба

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

В ваших предыдущих объяснениях я упустил тот момент, что массив вы объявляете с const'ом.

А в книге я написал не совсем верно, это я согласен.

parent From Ольга (unverified) Thu Aug 18 14:12:00 2016 pencil

О любви

Да, спасибо... Язык Си всё-таки любит нас. Хотя бы в случае gcc!
Ведь если нет явного приведения к неконстантности, он выдаёт предупреждение. А вот если имеется явное приведение

const char str[] = "Hello\n";
char *m = (char *)str;

то он послушно исполняет то, что ему сказали, несмотря на не менее явную подозрительность такого приведения. Но "случайно" никто ничего и не приводит. Язык такой по сути - всё на совести программиста. И просто грех его за это не любить навстречу!

parent From Anonymous (unverified) Tue Dec 13 23:10:00 2016 pencil

Покрутил - и правда! ;)

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

Я - GCC. Для вас, ребята,
Любой исходник скомпилю!
Я - очень добрый компилятор,
И "сишников" я ВСЕХ люблю!

Коль библию от Кернигана с Ритчи
Освоил ты недрогнувшей рукой,
То ты мне очень даже симпатичен,
И я на всё готов пойти с тобой!

Я разрешу всё-всё, что ты умеешь,
И то, что понимаю я...
Лишь если только ОЧЕНЬ обнаглеешь,
Слегка предупрежу тебя!

Но если ты не внял совету,
Считаешь, что ты круче GCC...

Теперь лишь на себя ты сетуй,
И бейся лбом об стенку со всех сил! ;)

parent From Anonymous (unverified) Tue Apr 18 06:55:00 2017 pencil

Строго говоря,

Строго говоря, компилятор не добрый, и не злой. Он просто - компилятор.
Он должен из синтаксически верного исходного кода получить объектный. Вот и всё!
А разбирать ваши каракули, залезать вам в мозги... Не царское это дело. Для подобных целей существуют программы класса lint. Если какие-нибудь Убунты, то splint, скорее всего, уже установлен. Вот он, как раз, в красках покажет всю глубину вашего нравственного падения, тут никакие "-Wall" даже рядом не лежали.
А то, понимаешь, "плохой, хороший, злой"...
Компилятор должен работать быстро. И не надо на него вешать несвойственные ему функции.

From Sergei (unverified) Sat Jul 16 21:48:00 2016 pencil

Опечатка

На странице 229 говорится, что если в программе встречается присваивание x=y, то на выходе сгенерируется что-то вроде

mov eax, [x]
mov [y], eax

Насколько я понимаю, это присваивание y=x; x=y выглядит так:

mov eax, [y]
mov [x], eax

parent From admin profile Thu Jul 21 08:10:09 2016 pencil

userpic

Вы абсолютно правы

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

Спасибо!

From Anonymous (unverified) Sun Jul 10 07:49:00 2016 pencil

По поводу

По поводу содержания будущей книги по C++:

1. Сделайте, пожалуйста, себе пометку, что в томе по C++ надо бы объяснить что такое lvalue и rvalue. В вашей книге "Введение в язык C++" этого нет, да и в русскоязычной литературе практически не освещено.

2. Еще в книге по C++ надо бы добавить серьезный раздел о том, как читать и распарсивать C++ код. Объяснить почему * или & прилипает то к типу, то к имени, и есть ли между такими написаниями разница. Рассказать про вывод типа "по спирали". Дать всякие мнемотические правила, которые помогут понимать код программистов, любящих обфусцировать плюсовый код и использовать краевые эффекты.

3. Рассказать про терминологию "на стеке", "в куче". Таких понятий практически нет в русскоязычных книгах по плюсам. Объясните, в какие моменты где создаются/удаляются данные.

4. Обязательно рассказать про умные указатели, объяснить проблемы обычных указателей. Обязательно объяснить теорию владельцах ресурсов, о разделении ответственности за ресурс, подробно, как вы умеете. Рассказать про разные виды умных указателей, в каких случаях какими правильнее пользоваться.

Без этих тем, считаю, книга не будет полной.

parent From admin profile Sun Jul 10 20:10:34 2016 pencil

userpic

Часть этого уже есть

Прочтению сложных типов в только что вышедшем томе посвящён параграф 4.13.3 (стр.401), а правило одного владельца для структур данных рассказано в первом томе, пар. 2.17.4 (стр. 457). Стек и куча тоже многократно упоминаются в уже вышедших томах. Про "прилипание" звёздочки см. комментарий на стр. 250 (во втором томе).

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

parent From Xintrea (unverified) Tue Jul 12 17:35:00 2016 pencil

Про rvalue и lvalue вы

Про rvalue и lvalue вы ничего не сказали. Будут?

Правило одного владельца у вас написано в части, посвященной Паскалю. На самой последней странице книги (стр. 457), в стиле "а на последок я скажу". А нужно именно в раздеде по C или C++, видимо в теме про умные указатели. Может быть в примечаниях про Rust написать, где принцип одного владельца возведен в абсолют.

И наверно, коль это вошло в стандарт, нужен раздел про лямбды/замыкания. Опять же, на русском языке по этому делу вменяемых книг не найдешь.

parent From admin profile Tue Jul 12 20:38:14 2016 pencil

userpic

Про rvalue и lvalue вы

Про rvalue и lvalue вы ничего не сказали. Будут?

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

Правило одного владельца у вас написано в части, посвященной Паскалю. На самой последней странице книги (стр. 457), в стиле "а на последок я скажу". А нужно именно в раздеде по C или C++, видимо в теме про умные указатели.

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

про Rust написать

Я обычно не пишу о том, чего не знаю. Вероятность того, что я буду знать Rust ко времени работы над четвёртым томом — ну, в общем, не слишком высока.

коль это вошло в стандарт, нужен раздел про лямбды/замыкания.

Ни про какие примочки из C++/11 и C++/14 я не стану писать ни при каких условиях. Для всех этих "стандартов" у меня только одна фраза — их авторов нужно расстрелять как особо опасных международных террористов. Ко всем существующим стандартам чистого Си (да, и к ANSI C) это тоже относится, хотя, быть может, в меньшей степени.

parent From Xintrea (unverified) Tue Jul 12 20:16:00 2016 pencil

Да, и еще.

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

parent From admin profile Tue Jul 12 20:40:32 2016 pencil

userpic

Об этом тоже

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

From Anonymous (unverified) Thu Jul 7 11:04:00 2016 pencil

Xто делать с обнаруженными ошибками?

Например, страница 139-140, в описании написано, что цикл должен печатать квадраты от 1 до 25, а в коде от 0 до 25.

parent From admin profile Thu Jul 7 12:28:22 2016 pencil

userpic

Ну а что с ними

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

Только страницы в данном случае 239-240, а не 139-140 :-) И там на 241 странице есть ещё более противная ошибка: написано j++, а должно быть j--.


pencil

пояснение


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

Если вы искали сайт замечательного писателя-фантаста Андрея Михайловича Столярова, то вам, к сожалению, не сюда.

Андрей Михайлович Столяров в библиотеке Мошкова

Авторские права © Андрей Викт. Столяров, 2009 — 2024