Программирование: введение в профессию. 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

Xто делать с обнаруженными ошибками?

Например, страница 139-140, в описании написано, что цикл должен печатать квадраты от 1 до 25, а в коде от 0 до 25.

Ну а что с ними

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

Только страницы в данном случае 239-240, а не 139-140 :-) И там на 241 странице есть ещё более противная ошибка: написано j++, а должно быть j--.

По поводу

По поводу содержания будущей книги по C++:

1. Сделайте, пожалуйста, себе пометку, что в томе по C++ надо бы объяснить что такое lvalue и rvalue. В вашей книге "Введение в язык C++" этого нет, да и в русскоязычной литературе практически не освещено.

2. Еще в книге по C++ надо бы добавить серьезный раздел о том, как читать и распарсивать C++ код. Объяснить почему * или & прилипает то к типу, то к имени, и есть ли между такими написаниями разница. Рассказать про вывод типа "по спирали". Дать всякие мнемотические правила, которые помогут понимать код программистов, любящих обфусцировать плюсовый код и использовать краевые эффекты.

3. Рассказать про терминологию "на стеке", "в куче". Таких понятий практически нет в русскоязычных книгах по плюсам. Объясните, в какие моменты где создаются/удаляются данные.

4. Обязательно рассказать про умные указатели, объяснить проблемы обычных указателей. Обязательно объяснить теорию владельцах ресурсов, о разделении ответственности за ресурс, подробно, как вы умеете. Рассказать про разные виды умных указателей, в каких случаях какими правильнее пользоваться.

Без этих тем, считаю, книга не будет полной.

Часть этого уже есть

Прочтению сложных типов в только что вышедшем томе посвящён параграф 4.13.3 (стр.401), а правило одного владельца для структур данных рассказано в первом томе, пар. 2.17.4 (стр. 457). Стек и куча тоже многократно упоминаются в уже вышедших томах. Про "прилипание" звёздочки см. комментарий на стр. 250 (во втором томе).

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

Про rvalue и lvalue вы

Про rvalue и lvalue вы ничего не сказали. Будут?

Правило одного владельца у вас написано в части, посвященной Паскалю. На самой последней странице книги (стр. 457), в стиле "а на последок я скажу". А нужно именно в раздеде по C или C++, видимо в теме про умные указатели. Может быть в примечаниях про Rust написать, где принцип одного владельца возведен в абсолют.

И наверно, коль это вошло в стандарт, нужен раздел про лямбды/замыкания. Опять же, на русском языке по этому делу вменяемых книг не найдешь.

Про rvalue и lvalue вы

Про rvalue и lvalue вы ничего не сказали. Будут?

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

Правило одного владельца у вас написано в части, посвященной Паскалю. На самой последней странице книги (стр. 457), в стиле "а на последок я скажу". А нужно именно в раздеде по C или C++, видимо в теме про умные указатели.

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

про Rust написать

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

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

Ни про какие примочки из C++/11 и C++/14 я не стану писать ни при каких условиях. Для всех этих "стандартов" у меня только одна фраза — их авторов нужно расстрелять как особо опасных международных террористов. Ко всем существующим стандартам чистого Си (да, и к ANSI C) это тоже относится, хотя, быть может, в меньшей степени.

Да, и еще.

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

Об этом тоже

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

Опечатка

На странице 229 говорится, что если в программе встречается присваивание x=y, то на выходе сгенерируется что-то вроде

mov eax, [x]
mov [y], eax

Насколько я понимаю, это присваивание y=x; x=y выглядит так:

mov eax, [y]
mov [x], eax

Вы абсолютно правы

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

Спасибо!

const в gcc и clang

Читаю страницу
265
"естественно, компилятор не станет размещать..." строковый литерал "в неизменяемой памяти (кстати, даже в том случае, если массив будет объявлен со словом const)...".
Что-то не так делаю, или не "каждый компилятор"? Похоже, что clang размещает строковый литерал как раз в неизменяемой памяти, хотя при компиляции никаких предупреждений не выдаёт...

Что-то здесь не так

Действительно, обычно компиляторы размещают строковые литералы в неизменяемой памяти, об этом, собственно говоря, как раз и идёт речь на стр. 264-265. Цитируемый вами фрагмент относится к (особому) случаю, когда строковый литерал используется не сам по себе, а в качестве инициализатора для обычного символьного массива. Такой массив, каков бы ни был его инициализатор, размещён будет либо в стеке (если он локальный), либо в сегменте данных.

Если это объяснение не устранило ваши сомнения, давайте сюда ваш пример :-)

Пример

Ага, ну, картинка, видимо, не проходит...
Вот и пример:

#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. Так и кто же из них прав? :)

Как обычно, правы оба

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

В ваших предыдущих объяснениях я упустил тот момент, что массив вы объявляете с const'ом.

А в книге я написал не совсем верно, это я согласен.

О любви

Да, спасибо... Язык Си всё-таки любит нас. Хотя бы в случае gcc!
Ведь если нет явного приведения к неконстантности, он выдаёт предупреждение. А вот если имеется явное приведение

const char str[] = "Hello\n";
char *m = (char *)str;

то он послушно исполняет то, что ему сказали, несмотря на не менее явную подозрительность такого приведения. Но "случайно" никто ничего и не приводит. Язык такой по сути - всё на совести программиста. И просто грех его за это не любить навстречу!

Покрутил - и правда! ;)

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

Я - GCC. Для вас, ребята,
Любой исходник скомпилю!
Я - очень добрый компилятор,
И "сишников" я ВСЕХ люблю!

Коль библию от Кернигана с Ритчи
Освоил ты недрогнувшей рукой,
То ты мне очень даже симпатичен,
И я на всё готов пойти с тобой!

Я разрешу всё-всё, что ты умеешь,
И то, что понимаю я...
Лишь если только ОЧЕНЬ обнаглеешь,
Слегка предупрежу тебя!

Но если ты не внял совету,
Считаешь, что ты круче GCC...

Теперь лишь на себя ты сетуй,
И бейся лбом об стенку со всех сил! ;)

Строго говоря,

Строго говоря, компилятор не добрый, и не злой. Он просто - компилятор.
Он должен из синтаксически верного исходного кода получить объектный. Вот и всё!
А разбирать ваши каракули, залезать вам в мозги... Не царское это дело. Для подобных целей существуют программы класса lint. Если какие-нибудь Убунты, то splint, скорее всего, уже установлен. Вот он, как раз, в красках покажет всю глубину вашего нравственного падения, тут никакие "-Wall" даже рядом не лежали.
А то, понимаешь, "плохой, хороший, злой"...
Компилятор должен работать быстро. И не надо на него вешать несвойственные ему функции.

Ошибка

На странице 218 описывается функция case_up.
Помоему, там ошибка в возврате символа в верхнем регистре.
return c-('a'-'Z')
Должно быть
return c-('a'-'А')
либо (что тоже самое)
return 'A'+(c-'a')

Ага, спасибо

Да, вы совершенно правы, очень досадная опечатка. Спасибо за её выявление. Только это стр. 214, а не 218 :-)

errata file

Андрей Викторович,
хочу предложить вам полезную вещъ:
заносите найденные ошибки/опечатки в файлы errata_volX.pdf (так они не будут теряться в дебрях комментариев на сайте; так сделано во многих серьёзных книгах, которые out of print или что-нибудь ещё в этом духе касательно переиздания)

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

Пожалуй,

Пожалуй, хорошая идея -- постараюсь реализовать. Спасибо.

Учебная

Учебная программа из раздела обучения ассемблеру (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
Ошибка сегментирования (сделан дамп памяти)

Подозреваю, что

Подозреваю, что вы допустили опечатку в слове FINISH (например, не все буквы на верхнем регистре, ну или любая другая опечатка).

Первый том не

Первый том не видел еще, а вот второй посоветовали, так и почитал. Действительно, очень удачно у Вас получилось как с собственно объяснением материала, так и с выбором темы, по которой столь компактно и доступно написано очень немногое. Фактически, это необходимый промежуточный уровень между программистом-питекантропом, каковыми, безусловно являются студенты младших курсов и программистом-кроманьонцем (старшие студенты). Пропустив этот неандертальский уровень, подойдя вплотную к С++, люди рискуют познакомиться лишь с самым одельфированным и овасяченным его подмножеством, что приведет их неминуемо к Java, а то и к такому субпродукту как .NET. Нужность самой Java сомнений не вызывает - действительно, бывает и объективная сложность ряда задач, и высокая надёжность требуется при низких требованиях к быстродействию. Однако, во многих случаях применение Java (сейчас ведь ее суют куда ни попадя), явно необосновано. И связано такое применение лишь с отсутствием должной квалификации программиста.
Буквально на днях слушая за пельменями в фоновом режиме русскоязычный скринкаст о началах Java, чуть не подавился от удивления - лектор выразил мысль, что нонича машина Java настолько крута, что программы для нее написанные, работают "чу-у-уточку" медленнее, чем аналог на С++, а в ряде случае (наверно, у него писюк с Java-процессором) еще и быстрее. И хотелось бы верить, что он высказал такую ахинею чисто в пылу лекторском, а ведь могло бы быть и иначе - сам написал на Java, потом на С++, сравнил и убедился, что "на Java быстрее"! Но по любому очевидно, этот самый "промежуточный уровень" у него остался непознанным. Ну, или он изобрел на досуге какой-нибудь особо хитрый замедляющий компилятор для языка С++... Или умудрился как-то скомпилить откровенно плюсовой исходник в байт-код Java, тут уже только гадать можно!
Лишнего опять же в Вашей книге нет, что совсем неплохо. Помнится, TASMы и MASMы под DOS радовали меня не сильно. Хотя, и разумеется, что-то дали...
В общем, нужная книжка, и написана хорошо. Остается надеяться, что она попадет в правильные руки, и количество грамотных, подготовленных студентов подрастет. Спасибо.

Вот уж что-то, а

Вот уж что-то, а джава точно не нужна :) В остальном спасибо за отзыв.

Точно. Так оно и

Точно. Так оно и получилось - непреодолимые трудности копипасты :)

Зачем вам копипаста?

Выше на этой странице имеется ссылка на архив примеров, в котором имеется в том числе и обсуждаемая программа :)

Касаемо Джава

Наверно, за Джавой я бы всё-таки что-нибудь, да оставил. Хотя бы мобилки...
Впрочем, здесь, думаю, контекст, всё же, иной. Речь-то, о самых началах, об обучении. Джава - это больше для пенсионеров. И уж учиться на ней нельзя точно! И потом, "Жаба - отстой!" - это не только лозунг, шутка юмора, это и образ жизни, и подход к делу. Выше совершенно справедливо замечено, что без того "пониженного уровня", который обеспечивают оба тома, в программировании делать просто нечего.

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

Очень даже похоже на истину, что начинать обучение всё-таки с Паскаля предпочтительней. Я, помнится, начинал сходу с Си. И средний уровень достигнут был довольно давно. Но путь мой был (с сегодняшней уже точки зрения) весьма кривотернист. Слишком много лишнего текста пришлось отработать. Явно можно было обойтись меньшими затратами и временем. Даже жаль, что уже не студент. Как бы то ни было, хорошая учебная литература не может не радовать.

Картинка, кстати, на обложке второго тома тоже ничего себе. Хорошая. Как бы сказали на Украине (вот нравится мне их язык! Несмотря ни на что! :-)) - "навіває"...

Планета Си. #include хорошая погода.
Здесь Плакающий_Туч с Крылатой_Рыбой
Хоронят Жабу - умерла в конвульсиях байт-кода.
И Жук_с_Граблями закопал... Друзья, спасибо!

Про джаву

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

А в остальном -- спасибо за отзыв, особенно за стих :-) Порадовался.

За Джаву, как

За Джаву, как раз, я особо-то, и не ратовал. Скорее, даже скромно полагал, что Самый Главный Нелюбитель Джавы - это я! :) Вижу, придётся подвинуться... Правда, нелюбовь эта имеет не столь теоретические резоны, тут всё проще - у меня на сегодняшний момент весьма простенький двухъядерник с четырьмя гигами на борту (разумеется, система 32-разрядная). И я бы сказал, хватает с серьёзным запасом. И как-то "модернизировать" комп я не собираюсь. С учётом того, что "помедленнее" как-то "не нравится", очевидно, что Джава мне ни к чему. Ну, а ставить на машину какие-либо системы и программы-студии, которые в принципе предназначены для пожирания жёстких и мягких мегабайтов и многоядерных тактов, мне и в голову никогда не придёт.
Хотелось бы момент ещё отметить, который кажется существенным для общей оценки написанных книг. Скажем так - коммент "из курилки". Там вполне мог бы и остаться. Не думаю, что это правильно...
Один из "незатягивающихся" (на стенке надпись "НЕ КУРИТЬ") нашёл стиль изложения двухтомника схожим со стилем изложения одного из своих преподов (а не студент он уже куда более, чем я!). Специальность у него непрограммерская - психология. Два семестра на основы программирования - за глаза. Пренепременнейше - TurboPascal. Так у них дама, которая должна была читать сей предмет (и даже начала), внезапно заболела декретом, не успев даже поведать о всех тонкостях копирования и вставки текста в этой знаменитой среде (а клятвенно обещала!), и им на весь срок нашли преподавателя "с производства". Он их тут же переориентировал на Си. Что ж, экзамены-то, ему принимать! Так со слов тогдашнего студента, человек этот "с производства", несмотря на то, что мог "как преподать, так и приподдать", был в состоянии объяснить довольно непростые для новичков материи - те же указатели - буквально на пальцах. Процессоры и компиляторы тогда были несколько другие, в частности скорость исполнения передних и задних инкрементов-декрементов отличалась, так он умудрялся объяснять даже трёхэтажные конструкции из указателей, и почему они работают быстрее, чем аналоги с индексной нотацией, языком вполне человеческим. Сейчас, возможно, это уже и не так. Это времена, думаю, скорее, такого компилятора как Watcom (не open!). В общем, оценка деятельности этого временного преподавателя была достаточна высока. И сравнение со ним явно не в минус. Достаточно, возможно, сказать, что этот нынешний психолог, по большей части занимающийся тестированием людей в самых разных целях, знает несколько десятков языков программирования. Но использует он их в целях абсолютно непрограммерских! С его точки зрения, языки программирования являются вполне достаточным средством для выяснения наклонностей человека, его возможностей, способностей, поведенческих стереотипов и способа мышления. Характер же и темперамент легко определяется тем языком, на котором человек пишет предпочтительно (поставленная задача здесь в расчёт не берётся). И вообще, по России, наряду с обязательным изучением русского и родного языка, необходимо включить изучение (с самого раннего детства!) языка Perl (это, кто не знает, самый-самый главный язык!;)), после чего, проведение даже самых простейших из его Perl-тестов (куда там IQ вшивому!), тут же всё покажет об испытуемом - сразу его в дурку отправлять, или же он к чему-то ещё всё-таки пригоден. Надо заметить, что Perl действительно позволяет чрезвычайно разнообразные синтаксические конструкции для выражения одной и той же мысли. И вот они как-то в его непрограммерском совершенно мозгу ассоциируются с какими-то "возможными комплексами", "замещёнными стимулами" и прочими не имеющими никакого отношения к программингу, понятиями. Короче, сплошной "волюнтаризм"!..
Тут подошёл кто-то из административного легиона и указал пальцем на надпись "НЕ КУРИТЬ" (а наказывают там на курение в неположенных местах даже строже, чем за использование откровенно нецензурного слова "Джава" на этом сайте), и поэтому идея обязательного перлообразования всенародной поддержки получить не успела.
Как бы то ни было, очевидно, что если откровенный непрограммист находит для себя понятным и полезным способ изложения тем, явно выходящих за пределы его профессиональной ориентации, то уж людям, изучающим программирование в целях именно профильных, всё это должно быть ясно просто как день. Но, увы, уже который раз попадаются молодые хлопцы, получившие именно профильное образование, но просто не понимающие ЧТО они делают! А хлопцы по всему - не тупые совсем! И такое впечатление складывается, что вина не их вовсе - просто в институте кто-то когда-то не смог сделать то, что должен был - подготовить нормального грамотного специалиста в своей области. А по каким причинам не смог - неважно. Дело не сделано.
А вот здесь картина как раз другая - похоже, наблюдается соответствие. И на сегодня, увы, нечастое...

Ого

Не раскрыть такой коммент я просто не мог :-)

Одного не понял, чем и кому может нравиться Perl. Исторически это первый язык программирования, который когда-то давно в меня не полез :-) При том что я на тот момент уже свыше десятка языков знал, притом очень разных, в их число входили и Лисп, и Пролог, и совсем экзотический Рефал, и всякие C/C++, а Perl мне был нужен здесь и сейчас, я работал в интернет-провайдере, там юзвери развлекались с CGI-скриптами на нём, любимом (1997 год, PHP ещё толком не было), так вот поди ж ты, раз пять книжку О'Рейли про Perl открывал, столько же раз и закрывал, не забравшись дальше десятка страниц. Вот тошнит меня от него, и всё тут. То есть понятно, что имеющийся скриптик я и прочитать могу, и слегка переиначить, но вот чтобы прямо на нём писать — не, не могу.

Думаю, что Perl -

Думаю, что Perl - "потому что Perl"! :)
Ну, или потому, что не так уж много языков позволяют столь разнообразно синтаксически оформлять код - можно хоть перманентной колбасой писать! Попробуйте такое учудить в Python'е - да Гвидо даже и разговаривать с вами не будет, жёсткие отлупы извольте в коде делать, иначе его математическое сознание просто не выдержит такой пытки. Средневековье какое-то... Фортран до такого не доходил. У Ларри всё иначе - не случайно лингвист по образованию. Готов поддерживать во имя сходства с естественными языками чуть не междометия. Два разных человека. Ну, а этот - так и вообще - не программер! Не стоит с ним (даже если спросит!) откровенничать по вопросу какой способ расстановки фигурных скобок вы предпочитаете - тут же последует какой-то диагноз! Не всё так просто... А уж непонимание такой элементарной вещи как "кому может понравиться Perl"... тут уже диагноз может быть и фатальным! :D
Здесь дело не в том, что "Perl"... А в том, что человек-непрограммер менее чем за два семестра успел получить знания по самым основам бытия: "переменная - это указатель", "значение переменной - разыменованный указатель", собственно "сам указатель - адрес (возможно, логический) первого байта стандартного типа или данных (возможно, выровненных) структуры произвольной сложности". То есть, в вопросе о первичности адреса и данных, приоритет за адресом. И уложились эти основы у него фундаментально. А логически объединённые адреса данных и имена функций (адреса их кода!) не что иное, как класс! Чисто полицейская же языковая функция "сокрытия данных" - это просто защита себя любимого от себя же дурака. Для очень многих проектов это абсолютно ни к чему. Дело меняется лишь с усложнением проектов. Но там (это в первую очередь касается кудрявых и румяных молодых ребят от Java, собирающихся вместе радостно спеть и сплясать о не менее кудрявых "перспективах" означенной "Java"), программист уже программистом-то, и не является! Это лишь шестерёнка, пишущая технически необходимую (ПОКА!) часть кода. Программист в больших проектах лишь один. Это тот, кто организует работу этих всех кудрявых шестерёнок. А кудрявые шестерёнки деквалифицируются (если есть от чего!) написанием тучи обязательных геттеров-сеттеров. И такая вот "геттерно-сеттерная" квалификация на сегодня пока ещё нужна. Хотя, какой-нибудь NetBeans, отобедав с аппетитом полутора-двумя гигами памяти, уже умеет их ваять по мановению пары клавиш... Что далее? А ничего! Далее, как и во всех языках, скорее всего, в Java будет появляться всё больше и больше функциональных возможностей. Тенденция, вроде, очевидная. И, как следствие, написание технически нужных на сегодня кусков кода программными средствами. Тем же NetBeans. Вот и все "кудрявые перспективы" современного Java-программиста. Плюс, не надо забывать, что любой язык (за исключением самых простых - ну, Си, конечно же!), имеет свой жизненный цикл. Так что, и кудрявым и пока ещё румяным тоже забывать не следует - "чтобы стоять, я должен держаться корней" никто не отменял...
Отвлёкся малость... Ну, в общем, Perl в данном случае можно рассматривать отчасти и как чудачество. Но лишь отчасти...
Кстати, среди программистов такого тоже навалом.
Канадец Rob Pike, текстовый редактор Acme, про Sam уж вспоминать не будем, дело совсем уж прошлое. Несмотря на весьма положительные и даже восторженные отзывы (в том числе известных весьма персон), никогда не мог понять их смысл! Возможно, каждому своё... Но почему в современном текстовом редакторе нет, чёрт возьми, банальной подсветки синтаксиса?! Может, Пайк позабыл? Ан нет! "Пишите код, ребята, не портите себе глаза!". Вот вам и Пайк. По мне - так "чудачество" - это самый минимум в приговоре!
Голландец Bram Moolenaar, язык программирования Zimbu.
"Дяденька, а почему в вашем языке, в структурных блоках отсутствует открывающая фигурная скобка? Только закрывающая?" - "А на кой? И так же видно, где блок начинается!"... Далее следует просто убийственное математически-строгое обоснование - "Кроме того, сразу отпадает вопрос о \"правильности\" размещения фигурных скобок!". Каково?! Просто Брам-миротворец всех разноскобочных холиваров! :)
И не звали бы их обоих именно так как их и зовут, можно было бы сказать - придурки придурками!
Вот и встречай после этого по одёжке... Так что, по-разному бывает.

М-да

Пожалуй, этот поток сознания я в основном оставлю без комментариев, за одним исключением: таки да, я не использую подсветку синтаксиса. При настройке vim у меня первое действие — в .vimrc добавить syntax off и filetype off. Когда у меня серые буквы на чёрном фоне, я проработать могу часов пятнадцать, не разгибаясь, а вот если буковки становятся разноцветными, да ещё разными по яркости — пиши пропало, уже часа через полтора хочется глаза закрыть и больше не открывать. Вообще не понимаю, как этим можно пользоваться.

Ну и про Perl: всё, что вы тут позиционируете как его достоинства, на самом деле является его недостатками, притом фатальными.

Подсветка синтаксиса

Сурово, ничего не скажешь! :) Хотя и очень субъективно.
Использую Vim с начала века, хоть последние лет 5-6 исключительно в графической ипостаси. Мог бы согласиться, что как встроенные, так и практически все цветовые схемы, встречавшиеся в сети - ужас просто необычайный, но совсем без подсветки я все же не обхожусь. По привычке (как раз начала века) в любой .vimrc вбивается:
autocmd BufEnter *.c colorscheme visualstudio или vcbc, слегка отредактированные.
В память о шестой студии и борланд 5.02.
Только Столярову не говорите! :D

Видимо,

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

Да, конечно, у

Да, конечно, у всех все по разному!
У меня среди знакомых так вообще есть персонаж, который использует исключительно монохромный режим монитора (полстола у него занимает) черт знает каких годов. И лишь там чувствует себя комфортно (с различением цветов у него нормально). Я к таковым не отношусь.
Что же касается редактора Acme, то его просто нельзя рассматривать вне контекста OS Plan9 - он там просто один из основных инструментов. Будучи поставлен на Linux или Windows, он представляет собой нечто совершенно другое, неизвестно для чего нужное. Правда, попадался на Хабре какой-то любитель Acme под Linux, но что-то поверил я ему не сильно. Под Linux есть один редактор. Называется "Vim". Те же, у кого с его освоением возникли проблемы, корячат пальцы на Emacs. У всех же прочих просто рука дрогнула при выборе системы в меню GRUB - заблудились слегка, у них там всяко где-то пониже должно быть написано "Windows".
А под Plan9 (элементарно устанавливается под VBox), штука просто необходимая. Чисто даже из соображений "повышения образованности" поставить можно вполне, да глянуть. Unix'ы разные бывают. Тут такой своеобразный unix, в котором немалую роль играет трехкнопочная мышь. Ну, а авторы, действительно, в представлении не нуждаются! Может, в чем-то и чудаковаты, не без этого...

Актуально?

Я так понимаю, что Ваша книга "Программирование на языке ассемблера NASM для ОС Unix" (2010) уже более не актуальна и всю ее информацию с дополнениями можно найти в этом -втором томе?

Совершенно верно

Б'ольшая часть текста книги 2010 года вошла во второй том в виде Части 3; небольшие фрагменты из "Введения" ушли в первый том, во вводную часть (Часть 1). Так или иначе, текст подвергся исправлениям, дополнениям и прочим улучшениям, так что, конечно, правильнее читать новую книгу.

Вопрос по

Вопрос по первому тому (Паскаль, hello.pas) стр. 201:
"Просто компилятор вынужден загнать в файл сразу всё, что нужно для выполнения практически любых операций ввода-вывода, а мы эти возможности пока не используем".
Получилось у меня даже больше 120 Кб. Нельзя ли "загнать" в файл только то, что нужно? А то такие размеры не радуют совсем. То есть уменьшить вес программы именно на Паскале, не забираясь ни в какие ассемблеры?

Никак

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

Ясно, спасибо.

Ясно, спасибо. Просто думал, что поскольку компилятор fpc не самый новый, то и имеется возможность использовать рантайм поменьше, по аналогии с Си, где элементарные программы можно собирать с либами из комплекта VS6 или пользовать TCC (книжка читается под Windows), что делает их размер более приличным.
Точка зрения относительно подсветки синтаксиса нестандартна довольно. Думаю, немногие согласятся. На новогоднюю елку редактор конечно походить не должен, но syntax off - уж очень радикально. Того редактора не видел, но само отсутствие возможности подсветки синтаксиса делают его фактически непригодным для большинства пользователей.

Во-первых, в vim,

Во-первых, в vim, разумеется, есть подсветка синтаксиса — её даже не очень просто отключить (synt off отключает только для текущего буфера, при открытии на редактирование следующего файла снова определяется его тип и включается подсветка), но мне пришлось научиться (найти команду filetype off), поскольку никакие даже зачатки подсветки я на дух не выношу.

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

Никак

Никак лазутчика изловили, Андрей Викторович? И ведь повезло ещё, что сам сознался! :) Иначе...
Действительно, и как таких только в Интернет пускают!

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

Куда ж ты лезешь, бедолага,
Коль даже с Vim'ом не в ладу?
И сделать не успеешь шага,
Считай, накликал уж беду!

И точно - послан уж по маме,
Спасибо хоть, что не в гробу!
Но сразу отлучён от Знаний,
И враз - к позорому столбу!

Это ещё он не лютует,
Это шутейный незачёт!
Убунту хоть поставь какую,
Тогда, быть может, повезёт... ;)

Гыгыгы

Стих понравился :-)

Если серьёзно, ну есть же предисловия, там ясно и недвусмысленно объясняется, почему нужен *nix и почему винда не годится. Но вот нет, поди ж ты. Не, ну ладно человек жаждет остаться под виндой (таких много), но почему тогда именно мои книжки, которые для этого ну никак вообще?

Тоже читаю

Тоже читаю первый том. Указатели, динамические структуры. Языком понятным написано, не вредная совсем у Вас книжка.
Но по ходу чтения пришлось вернуться и к вещам более статическим. Задача предельно простая - имеется файл (с полмиллиона строк) в формате CSV, на каждой строке пара ключ-значение. Из него нужно выдрать все данные и поместить в удобную структуру, постоянно висящую в памяти. Сходу просматривается примитивная сишная структура из двух строк. Ну, можно int и char[], только позже из этих ключей-значений будет формироваться URL, так что, два char[], чтобы потом без кастинга.
Используется массив таких структур. Для получения значения по ключу, его пробегаем в цикле. Вроде, все хорошо работает.
Но "по совету" друзей, а также вдохновленный чтением вашей книжки, решил попробовать затолкать эти строки в такой тип данных, как "ассоциативный массив" (язык D, стандартная библиотека Phobos). Вроде, действительно, как раз, подходит! И никакого пробега в цикле не требуется, значение по ключу получается "напрямую". Так дело в том, что после загона в этот самый "ассоциативный массив" (скорость там сложно определить и сравнить, данных, наверно, мало), памяти стало жраться процентов на 30-40 больше. Один и тот же файл. Всё, что делается - загоняется в сишную структуру (первый случай) и в ассоциативный массив (второй). Далее, просто останавливаемся в цикле for(;;), ожидая ввода ключа для получения значения. Вроде, ничего противозаконного не делал. Откуда такие потери в памяти? Или эти "ассоциативные массивы" в принципе должны жрать больше? Представляется, что их реализация хорошая. Так и неужели дело именно в том, что сама реализация таких типов данных просто подразумевает большее потребление памяти?

Ну а вы чего ожидали

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

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

И ещё, я не уверен, чтО вы подразумевали под "останавливаемся в for(;;)" — вы ведь не имели в виду активное ожидание, правда?

Возможно, делаю

Возможно, делаю чего-то не так. Я не волшебник, только учусь.
Но до собственно поиска дело еще не доходит. Просто читаю файл, каждая строка бьется на две части по csv-разделителю. Обе части забиваю в структуру. Из всех строк получается массив таких структур. Сразу останавливаемся - аналог scanf("%s", any_ID). Ждем ввода. Все. Смотрим память.
Тоже самое при забивании в ассоциативный массив.
Так во втором случае памяти отжирается больше на 30-40%. Вот и не понимаю пока, за счет чего...

scanf("%s", ...) не

scanf("%s", ...) не делайте НИКОГДА, так же как и пресловутый gets. За это с работы увольняют, сам видел. Причины -- жирным шрифтом на стр. 279 (про scanf), потом подробное обсуждение gets на стр. 291-292.

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

Да, понял,

Да, понял, спасибо.
По поводу gets() так уже сколько лет gcc верещит, что "НИЗ-ЗЯ!". scanf(), конечно, из той же оперы... А вот библиотеку с хэшами надо бы какую-нибудь отыскать поприличнее, это да! Все написано до нас - надо только найти. Действительно, вполне могут попасться задачи, где удобнее постоянное время доступа к произвольному элементу, несмотря на потерю памяти. Линейным поиск по-идее быть не должен. Разумеется, "сишную" библиотеку. Сравнение того, что у меня получилось на D и на C, шансов для дальнейшего практического использования D просто не дает. Уж больной тормозной! Прототипы программ, если только... Ну, или чтобы посмотреть "как оно там" и как его реализовать на Си. Или, при случае, есть чего сравнить с "плюсами". Мысли-то и там, и там хорошие есть. Вот только за счет скорости исполнения почему-то!) Такое примерно применение...
А вот что касается моей конкретной задачи (примерно полмиллиона строк в память), то там, пожалуй, линейный поиск вполне уместен. Дело в том, что за несколько лет выяснилось, что обращение к разным данным чрезвычайно неравномерно. От очень частых до чуть более, чем редких. Поэтому и был создан массив структур (около двадцать тысяч из полумиллиона) с линейным поиском, а если запрашиваемые данные были из числа "чуть более, чем редких", шло обращение к файлу. Причем, из таких редких запросов создавался дополнительный короткий кэш-файл, к которому впоследствии и обращались в первую очередь. Именно таким образом и покрывалось около 99 процентов запросов. А использование специфической библиотеки привело бы к потери памяти. Вряд ли нужной... Хотя, конечно, все надо бы считать, "на глаз" тут сложно. Но как говоривал в былые времена один легендарно-неуважаемый программист - "Шестьсот сорок килобайт должно хватать для всех!" ;) Тут он прав.
В общем, книжку читал, что-то новое узнал по любому. По мере чтения вопросы возникают, которых могло бы без нее и не возникнуть.

Не откладывая в

Не откладывая в долгий ящик, покуда есть время, по первому же запросу "hash iibrary for C" выгуглил ссылку https://github.com/troydhanson/uthash. Поверху была, так что судьба ее такая. Завелось практически сходу, как под меня написано. Это даже не либа для хэшей, а просто хедер, в котором аж на 1000 строк чего-то умное намакросячено - еще не смотрел (это для тех, кто говорит, что препроцессор Си "устарел безнадежно"). После подключения этого заголовка имеем обычные, как и во всех скриптовых языках хэши. Памяти, как уже и ожидалось жрётся больше, чем при организации поиске в цикле, но скорость, конечно, повыше. Впрочем, на Си это заметно не сильно (и так быстро!). То есть, можно легко уже и рабочий вариант переписать (там за несколько лет памяти-то на машинах прибавилось). Не думаю, что это самый эффективный вариант реализации хэшей, но он очень простой и начальные представления дает всяко.
Что касается применения для этой же цели языка D, то пришлось отказаться от этой затеи. Исходно подкупило то, что "D - язык для системного программирования", "на нем можно писать как на С" и т.п. Писать-то можно, конечно, как на С, только скорость исполнения получается как на D, а то и как на E! :-) Да и если глянуть его историю - D1, более раннюю библиотеку Tango, то там почему-то даже синтаксически попахивает больше Java, чем С.
В общем, полезная у Вас книжка. Даже уже потому, что заставила разобраться и ответить на какие-то вопросы, которые в ней даже не затрагивались.

Из двухтомника

Из двухтомника видела только том второй. Часть, посвященная ассемблеру прошла по диагонали. Что же касается части, освещающей начала Си, то это, пожалуй, одно из наиболее толковых введений в предмет, которые случалось видеть. Особенно порадовало краткое описание библиотеки ncurses. Дело в том, что после самого начального обучения, человек остается один на один со своими проблемами - чисто средствами языка решить какие-то свои даже элементарные проблемы он не может, тут нужен уже навык поиска соответствующих библиотек, коих хоть и написано немало, но без такогого навыка, простейшим путем является поиск языка, в котором возможности этих библиотек являются встроенными. В результате язык Си воспринимается чисто как учебный, "теоретический", несмотря на то, что он как был, как и останется живым, и далеко еще не "полтора десятка лет". Нету ни малейшего намека на то, что ему грядет какая-то замена.
На днях буквально случилось столкнуться с ситуацией, когда обучающийся (основы Lisp), снес рекомендуемый sbcl и заменил его на CLISP только на том основании, что в sbcl нет поддержки readline. То ли в силу лицензионной GNU-тости, то ли просто пакет был так скомпилирован. Установка оболочек readline, таких как rlwrap, rlfe решило проблему тут же. То есть, дело именно в отсутствии элементарных знаний, что ВООБЩЕ существует в природе, в том числе и для Си. А существует немало!
Ясно, что в книгу такого объема вместить удается лишь самое необходимое, возможно, те, кто у Вас обучается, на живых лекциях получают больше. Однако, очень жаль, что порыв на ютубе по слову "Столяров", не получилось отыскать ничего подходящего. Лучшим, что случалось видеть из русскоязычного по началам Си - это "специалистовский" несколькодневный курс Тетерина. Всякие скринкасты и видеоуроки, думаю, дело не очень благодарное, но, все же, было несколько жаль. Это даже не призыв к какой-то "рекламе", хотя в данном случае, это было бы вполне обоснованно, а именно просто сожаление, что вживую преподаваемые (уверена - хорошо) начала Си - да и не только Си, важна методика - остаются достоянием от силы лишь нескольких студенческих групп.

Ну, про язык Си

Ну, про язык Си -- вот уж чего ни разу не видел, так это чтобы его называли "учебным". Как раз для обучения он категорически не годится, при этом он продолжает удерживать первое место по общему объёму работающшего (используемого) кода, на нём написанного.

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

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

Да, "учебным"

Да, "учебным" языком Си назвать действительно сложно. И первым языком он, как и С++, быть не может никак, тут никто не спорит. Но в какой-то же момент он является "учебным" - именно его и изучают. Так вот, дело именно в том, что после его изучения, у многих обучавшихся тут же возникает желание освоить какой-то другой язык, предоставляющий возможности, которые в Си могут быть получены только подключением соответствующих библиотек. Саму же среду, в которой оказывается человек уже после изучения начал Си, достаточно дружелюбной назвать нельзя. Она требует несколько более высокой квалификации. А навыки сходу не приходят. Именно поэтому, мне чрезвычайно импонировало включение в Вашу книгу рассмотрение библиотеки ncurses. То есть, "хочешь - найдешь", было бы это желание "что-то найти". А скорость исполнения языка, его простота делают его пригодным не только для профессиональных целей. То есть, некоторый шаг навстречу пользователю, изучавшему Си, все-таки нужен. Дать ему основы дальнейшего существования вместе с Си. Оно того стоит.
Относительно того, что видеокурсы являются "суррогатом" и "самообманом", согласиться не могу никак. Это один из способов обучения. К "социальным сетям", ютубу, "самому ГУГЛ" отношусь вряд ли лучше Вас. Например, в качестве поисковых систем уже давно используются нигма и дак-дак. Гугл - в запасе. Но пользователи-то здесь причем? Если пользователь посещает ютуб, использует его для хостинга, каких-то других целей, это еще не означает, что он ютубнутый на всю голову! И потом, книги-то Вы издаете! А если порыть в этом издательстве, не найдется ли чего из того, почему к тому же Гуглу отношение у многих не самое лучшее?
Впрочем, здесь, конечно, хозяин барин. Это было лишь сожаление...

А если порыть в

А если порыть в этом издательстве,

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

Том 2-й,

Том 2-й, страницы 258-259
Приводится пример с инициализацией локального массива.
"... данные будут копироваться в область памяти в стековом фрейме каждый раз в начале выполнения такой функции".
А вот что получается у меня:
Как оно получается
Здесь просто объявляется и инициализируется строка в функции main().
Эта же строка явно проглядывается в результирующем файле безо всякого hexa. Так неужели, если далее будет следовать, скажем, фукнция printf("%s", str), принимающая указатель на эту строку, в стек будет копироваться не указатель, а вся строка? Неужели ее нельзя вытащить из уже загруженного в память файла? Область памяти-то, очевидно, read, как минимум?
Или чего-то не так понимаю?

Как оно

Как оно получается

И что, простите? Да, строковый литерал размещён в самом конце сегмента кода, где он, собственно, и должен быть. Плюс к тому в вашей программе нет ничего даже отдалённо напоминающего случай, про который вы говорите: никаких локальных массивов вы в своей функции main не описываете. Возможно, вам стоит обратить внимание на рассуждение, которое приведено в книге на стр. 265--266.

Так неужели, если далее будет следовать, скажем, фукнция printf("%s", str), принимающая указатель на эту строку, в стек будет копироваться не указатель, а вся строка?

Нет, разумеется. Это вообще невозможно, строка -- это массив, а массивы как целое в Си не обрабатываются. У вас какой-то бардак в голове, но я пока не понял его причину.

Не будет

Не будет бардака, не будет и порядка.
Что же я делаю тогда, как не объявляю и не тут же не инициализирую массив символов? И этот массив находится внутри функции main(). Чем же он не локальный, где я неправ?

Ну какой же это массив, в самом деле

Вы объявляете (точнее, не объявляете, а описываете) указатель, а не массив. Компилятор размещает строковый литерал в сегменте кода, как обычно, а ваш указатель str инициализирует адресом начала этого строкового литерала. В том, что всё обстоит именно так, можете убедиться, попробовав присвоить что-нибудь элементу вашего "массива", то есть сделать что-нибудь вроде str[0]='B'. Программа при этом рухнет с грохотом и спецэффектами. А вот если вместо того, что у вас там написано, написать

char str[] = "Hello, world\n";

то тогда это уже будет локальный массив. Впрочем, строковый литерал в конце сегмента кода будет в любом случае, должна же где-то лежать эта строка; обнаружить, что строка копируется в массив в начале функции, можно, либо дизассемблировав исполняемый файл, либо остановив компилятор на стадии генерации ассемблерного кода (флажок -S, потом смотрите файл с суффиксом .s, только учтите, что там синтаксис AT&T)

Да, разница,

Да, разница, конечно, есть... С этим надо посмотреть внимательней, спасибо.
Однако, в случае именно массива, строковый литерал в коде отсутствует, там что-то куда более невнятное. И как сама машина это разбирает, пока неясно! )))
А синтаксис gcc к "человеческому" виду я привожу в конфиге - .intel_syntax.

Ну, тут уж

Ну, тут уж совсем всё просто. Это "невнятное" представляет собой четыре команды mov, по четыре байта каждая. Действительно, это явно быстрее, чем "честно" копировать строку.

Да, спасибо!Уже

Да, спасибо!
Уже под gdb всяко-разно покрутил, там без всяких вставок интеловский синтаксис легко делается одной строкой в ~/.gdbinit - set disassembly-flavor intel.
Надо сказать, что "вроде бы хорошо знакомые вещи" при ближайшем рассмотрении оказываются не такими уж и знакомыми. Во всяком случае, как бы смотришь с "другой стороны".
Написано хорошо у вас, на понимание ориентировано... То есть, не зря книжку взял.

Ну, что можно

Ну, что можно сходу сказать, так это то, что если люди из-за одной строки лезут в hex с отладчиком, то книга методически написана верно, и уже не зря. Даже из-за одной строки залезть в отладчик стоит, посмотреть - как там чего. Да и изначальная ориентация на *nix верна абсолютно - сама система как бы предназначена для обучения - три ведра с горкой уже предустановленных утилит, при некотором желании не оставляющих шансов на то, что какие-то непонятки в полученном бинарнике могут остаться. Синтаксис интел (ATT только для тех, специализируется на ассемблерах) всеми ими поддерживается с незапамятных времен. Хочешь - их пользуй, хочешь "покрасивше" - ставь какой-нибудь EDB (тот же Olly) - и дизассемблер, и hex тебе тут, и регистры, и стек, и чего хочешь - короче, было бы желание учиться...

Да, все верно

Да, все верно написано относительно стека и массива.
Вот наиболее наглядно из того, что смотрел
В первом случае (char str[]= "very long initialization";) строка, действительно, полностью копируется в стек, собака!
Во втором (char *str = "very long string";) - в стеке только указатель.
Получается, что str[] = "very long initialization" запись неверная в принципе.
Я так, правда, никогда и не делал, но теперь уж точно не буду! :-)

А у вас ус

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

Действительно, отклеился! :(

Ох, уж мне этот клей от Майкрософт...
По вышеподсказанному EDB отлично видно, ЧТО находится в стеке при str[] и *str.
Хотя, если бы кто раньше мне сказал, что строка в принципе может копироваться в стек, я бы в него непременно плюнул, и сейчас мне было бы стыдно. Полагалось, что для изменяемых строк существует выделение памяти в куче, и только. Оказывается, дело обстоит несколько иначе - если в строке не целый роман, то можно и в стеке - побыстрее должно быть, однако!

При чём тут

При чём тут "побыстрее"? Локальные переменные, будь то хоть маленький char, хоть огромный пятимерный массив, заводятся в стеке. Их, собственно говоря, больше негде заводить, ведь в языке Си (самом по себе) нет никакой кучи и никогда не было. Функции malloc и free — библиотечные, компилятор о них не знает, а если и знает, то должен делать вид, что не знает.

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

Да, спасибо!

Да, спасибо! Бардака, вроде, поменьше стало...
Правда, поначалу смутили несколько "огромные пятимерные массивы" в стеке, поскольку ulimit -s у меня показывает 8192К возможного стека... Ведь, ежели вдруг еще внезапно приспичит рекурнуться (а мало ли?!) с эдакими массивами, тут же поимеем SIGSEGV по морде!
Но оказывается, даже и на такой космический случай имеется волшебная структура rlimit из файла sys/resource и волшебная же функция setrlimit(), с помощью которых можем стек и увеличить. Во всяком случае, повторный вызов функций getrlimit(RLIMIT_STACK, &stack) и system("ulimit -s"); показывают уже запрошенные размеры стека. Плюс, говорят, имеется для той же цели еще какой-то ключик у gcc. То есть, не все так плохо, на самом деле.

Насчёт огромных пятимерных

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

Прочитал.

Прочитал. Хорошее введение в низкий уровень. Понятное, доступное. Напомнило куда более раннюю книжку Sivarama P. Dandamudi "Guide to Assembly Language in Linux". Мастхэв. Необязательно даже на книжной полке, в мозгах - однозначно.

Это о другом. В

Это о другом. В книге Дандамунди ассемблер -- самоцель, у меня он никогда самоцелью не был.

Вдогонку.

Уже написал, что введение в низкий уровень хорошее. Однако, вижу тут уже люди во всякие графические приблуды полезли! Плохого в этом ничего нет, инструментарий всякий знать нужно, и дело даже не в том, что edb - это не "unix-way", а в том, что в самой книге отсутствуют даже примеры работы с элементарными утилитами, соответствующими уровню обучающихся, нет никаких рекомендаций по начальным простейшим инструментами. GDB - это довольно сложно, довольно круто и не всегда нужно. В то же время существует куча консольных утилит, хекс-редакторов, отладчиков, в том числе (по-моему, очень удобных) vi-like. Всякие ald, hc, bvi, radare - только сходу вспомнилось! Кучи их. Кстати, очень жаль, что не попадалось 100%-ного аналога dos'овского debug - чудная была игрушка для 16 разрядов. А пужать людей синтаксисом ATT, родного лишь для больших машин, совершенно ни к чему. Уж если NASM, так и все человек должен видеть в нормальном синтаксисе. Вот в этом, по-моему, недостаток книги.

Э?

Знаете, вот вообще ничего не понял из вашего текста. Как сейчас говорят, не распарсил.

Читаю Вашу

Читаю Вашу книжку, стандартные функции ввода-вывода. В параллель открыто еще несколько. Пробую читать файл fopen/fread/fclose и open/read/close. Думал, будет одно и то же. Однако, нет:
fopen and open
Получается, что не "одно и то же"! Разница в результирующем бинарнике в целый килобайт.
Файл один и тот же, всяко более 256 байт, компилятор один, ключи тоже, ldd выдает одинаковый вывод. Куда дальше рыть? Откуда такая разница? Чем-то плохи эти open/read/write? Еще и сделать-то ничего не успел, а уже килобайт отдай дяде! Куда годится...

Довольно неожиданный эффект

По идее, должно быть с точностью до наоборот, ведь open/read/write -- это системные вызовы, которые и первая программа использует тоже, только неявно, а вот fopen/fread/fwrite -- библиотечные функции, которых вторая программа не использует.

Подозреваю, что при статической сборке вторая программа даст существенно мЕньший бинарник, чем первая. А вот почему так происходит при динамической сборке -- я объяснить не могу.

По поводу

По поводу "существенно меньшего бинарника" при статической сборке получилось не сильно.
Действительно, у gcc и pcc размер бинарника несколько меньше как при статической сборке, так и при динамической. (TCC же просто отказался собирать статически. И перекокос в его случае, возможно, просто связан с особенностями самого компилятора и его личной "стандартной библиотеки Си" - libtcc.a). Однако, разница совсем невелика. В исходники, правда, пока не залезал, но что-то появилось устойчивое ощущение, что open()/read()/write() -это "не совсем" системные вызовы, а просто одноименные (может, чуть более низкоуровневые, чем в случае fopen()/fread()/fwrite()) функции-оболочки, сделанные для "какой-нибудь с чем-нибудь совместимости".

В общем, думаю,

В общем, думаю, вопрос ушел.
open/read/write - это все-таки системные вызовы. К счастью, в моем файле 256 байт - это целая куча строк. Так под strace видно, что вывод каждой строки fwrite'ом делается отдельным вызовом write с вытекающими. И даже будучи просто запущенной под time, вторая программа работает быстрее.
С панталыку сбил какой-то неутык в компиляторе TCC. Размеры получаемого файла там существенно меньше, но есть и свои "особенности", как выясняется...

Если программы

Если программы те же, что были на иллюстрации несколькими комментами выше, то я бы ещё на вашем месте обратил внимание на то, что сказано по поводу анализа возвращаемого значения read на стр.302 (первый абзац сверху). К fread это тоже относится.

Да, в рабочих-то

Да, в рабочих-то условиях проверять, конечно, надо!
Единственное, что осталось непонятным, так это ПОЧЕМУ функция fwrite() использует системный вызов write на каждую строку? Странная довольно реализация... Вроде, высокоуровневая функция, буферизация какая-то, системные вызовы как-то экономиться должны! И где здесь "экономия"?
Если не врет strace
Разумеется, в случае варианта с write, вызов единственный - сразу выводит то, что просят.

fwrite не

fwrite не буферизует, буферизация действует только на потоковый ввод-вывод, а блочный буферизовать слишком сложно, чтобы это было в библиотеке. Потому никто их и не использует, все для бинарно-блочных файлов используют низкий уровень. fread/fwrite просто бессмысленны.

Вопрос по буферизации

А имеется ли вообще смысл вместо длительной записи в цикле в файл результатов какой-либо обработки, записывать предварительно в специально выделенный для этой цели буфер, а потом одним ударом забабахать весь буфер в файл перед его закрытием? Имеется ли какая-то при этом выгода, или то на то и выходит?

Depends

Как обычно, зависит от конкретики. Если сравнивать скорость записи в файл по одному байту и запись блока, то разница в скорости будет примерно как размер этого блока :-) Имеется в виду, естественно, запись без локальной буферизации, то есть, например, 4096 обращений к write по одному байту или одно обращение к write на запись блока в 4096 байт.

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

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

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

Воистину depends!

Собственно, я не собирался ничего "измерять", так уж, на скорую руку получилось...
Предельно глупая функция копирования файла. Просто читаем построчно первый, пишем во второй в цикле. Все обычно. Работает, естественно, правильно. А далее - собираем всё в предварительно замаллоченный буфер и перед закрытием файла, в него сбрасываем. Ну, или работаем его в стеке. Как, собственно, и хотелось. В надежде получить какой-то эффект. И я его получил! ))) Так мне даже рядом не показалось, что здесь что-то можно было сделать "не так"!
А вот и результат!
То есть, глядя уже на второй исходник, можно было бы предположить, что произойдёт. Но этого я не увидел, просто запустил под time. И далее уже просто поднимал упавшую на пол челюсть! :) Разница в скорости исполнения составила более 500! Можно, оказывается, и так писать на Си. Тем более, поставленная задача была решена верно. Скорее всего, на каком-нибудь Low-Speed-C-Contest я занял бы место не последнее.
В общем, я считаю, что написал я "функцию копирования файла" очень даже не зря.
Мораль сей басни такова - это вам не шахматы, тут думать нужно...

Это не из той сказки сценка

При чём тут буферизация, при чём тут ввод-вывод, вы же всё время, какое можно, угробили сначала strcat'ом, а потом fprintf'ом. На одних поисках и копированиях.

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

Да, конечно!

Правда, при замене fprinf() на fputs() ничего заметного я не получил.
Ну, а смещение начала буфера для записи в цикле, разумеется, сделало результаты уже сравнимыми.
Попытка нашинковать копируемый файл на ровные дольки тоже результат дала.
Так вот оно получилось
Можно было, конечно, еще в один цикл загнать, изменяя размер количества читаемых/записываемых байт и измеряя время, но я по-простецки полтора десятка раз запустил программку под time.
Безо всяких gnuplot'ов картина мира довольно наглядна. Как бы то ни было, выяснено, что временами можно что-то поймать, изменяя размер буфера.

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? А то к концу книжки же сниться будет! :-)

К концу книжки

К концу книжки этот warning начнёт сливаться с окружающим пейзажем и перестанет бросаться в глаза. Обратите внимание, что я про это паразитное предупреждение упоминаю, см. т.1, стр. 202.

В общем, проблема известная, существует уже лет десять, разработчикам на неё, судя по всему, наплевать. Лично мне она тоже уже давно не мешает. А grep -v у вас не отработало из-за того, что это всё выдаётся на stderr, а grep на свой stdin получает stdout.

Попробуйте вот так:

fpc 2>&1 | grep -v "forget -T"

Да, спасибо!

Совсем другое дело! В .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

Как всегда - все просто. Когда знаешь, конечно! )))
То, что я там понаписал ранее, не лезло ни в какие рамы. Даже не по длине - просто невозврат нуля одной из связанных && команд (а такое возможно) нуль в итоге уже не вернет. А компилятор ничего другого возвращать и не должен! Ну, а здесь все четко... Спасибо.

Читаю второй

Читаю второй том. Вроде, понятным языком написано. Смотрю комменты, вроде, тоже понятно излагается. Вот и задумал вопрос задать незамысловатый, который временами встает, но точного ответа по нему не знаю.
Многократно в течение жизни приходилось читать файлы, парсить их всяко-разно. Преимущественно использовались 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() (но она мне почему-то никогда не нравилась). Как бы оно, чтобы "эффективней"?

Если интересно

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

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

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

Да, книги

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

Для начала

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

Что касается выравнивания стека как такового, то вообще-то его и не надо разглядывать, никого оно не волнует, кроме компиляторщиков (и то сильно не всех). Но если очень хочется, то стоит обратить внимание на то, как тот же gcc оформляет вызовы функций, то есть посмотреть, какой ассемблерный код генерируется, и попробовать это проделать при различном совокупном размере локальных переменных (плюс совокупный размер параметров самой "длинной" из вызываемых функций, под длинной я в данном случае подразумеваю количество параметров). Вот тут, например, народ это обсуждал: http://stackoverflow.com/questions/1061818/stack-allocation-padding-and-...

Прочитал оба

Прочитал оба тома. Вижу, комментарии положительные преобладают явно. Не буду оригинальным — действительно, хорошее введение в программирование на русском языке. Если по чесноку, не ожидалось. Как-то давно сложилась точка зрения, что на русском языке ничего подобного появиться не может, уж тем более в условиях, которые сложились с образованием в нынешней России. Те же основы программирования мной, да и многими моими знакомыми осваивались по (тоже весьма немногочисленным — проблема образования не только проблема России) англоязычным источникам. В первую очередь вспоминаются лекции по C/C++, читаемые в ряде учебных заведений США — используется там достаточно давно для учебных целей интерпретатор Ch. В Европе для целей обучения используется местный интерпретатор ROOT, предназначенный, что интересно, вовсе не для профессиональных программистов, а именно для пользователей. Очень вменяемые люди читали! Многие по сей день просто используют ch-shell даже для административных целей. Синтаксис bash — это, наверно, самая непереносимая на платоформу мозгов, штука. Ну, а язык Си поймет даже туземец из тростниковых зарослей каких-нибудь островов Тристан-да-Кунья. Кстати, та же беда, что и в bash, касается и синтаксиса скриптового языка vim. Сам по себе, редактор просто волшебный, но как только случается придти в голову мысли изменить в нем что-то чуть-чуть нетривиально, тут же с завистью начинаешь поглядывать на emacs. Вот нету в жизни совершенства! Попадались также знатные англоязычные лекции по Ocaml и Scheme. Во всех случаях ощущалась ориентация на понимание обучающегося, что в других (всех остальных!) случаях, не ощущалось совершенно! И это же есть в ваших книгах.
В общем, спасибо за книги, творческих успехов в Новом году (не за горами, а когда еще зайти случиться?). Кстати, и крисмас у вас, смотрю, подходящий! ;) Удачи.

"...с завистью

"...с завистью поглядывать на emacs..."
Не надо "поглядывать с завистью". Использовать надо emacs. Чего там не хватает нормальным людям, так это переключения режимов. Ставим плагин evil, и все - имеем все режимы Vim, всю его клавиатуру. Ну, с небольшими исключениями (которые можно легко при желании поправить) - напр. нельзя ":set num", нельзя перейти в Normal с помощью Ctrl-c, только ECS или Ctrl-[, не работает ":di", только его аналог ":reg", и тому подобные мелочи.
Короче, ставим evil, работаем так же как в Vim, а все функции пишем на elisp. Плохо, что ли? Для меня такой гибрид оказался находкой просто сказочной. Горя не знаю. Vim - редактор замечательный, но VimScript - это даже хуже, чем bash.
Emacs - редактор не менее замечательный, но уродовать там собственные пальцы его исходными биндингами совершенно ни к чему.
Короче, берем отовсюду все, что есть лучшего, и живем счастливо. Есть в жизни совершенство, но приходится иногда поискать.
Ну, и "ctrl:swapcaps", конечно, еще никакому редактору не мешало.

Все ли функции нужны?

Здравствуйте, Андрей Викторович!
Читаю ваши книги. Не столько в целях ознакомления с основами программирования, сколько из соображений упорядочивания, систематизации и усугубления. Что-то изменяется, что-то просветляется, появляются какие-то вопросы... То есть, процесс затыкания дыр в имеющейся "личной парадигме программирования", так скажем. Одновременно просматривается стандартная библиотека. Нарвался на никогда не использовавшуюся мною функцию strdup(). Пытаюсь понять, зачем и в каких случаях она могла бы мне понадобиться - кто же писал ее зачем-то! Так вот, пока понять не могу, что она мне может дать вместо обычно применяемой strcpy(), кроме дополнительного выделения памяти и обязанности не забыть ее же освободить? Бывают ли такие случаи?

Нужны, конечно,

Нужны, конечно, далеко не все функции, некоторые даже не просто не нужны, а запрещены -- тот же gets пресловутый. Я бы сказал, что довольно бессмысленна функция sscanf, хотя со мной кто-то, возможно, и не согласится. Совершенно бесполезными мне кажутся fread/fwrite; или вот есть ещё системный вызов creat, я так и не понял, накой чёрт он вообще есть.

А вот strdup я как раз довольно активно использую. Больше того, при работе на C++, чтобы не освобождать delete'ом выделенное malloc'ом, я часто вынужден писать собственную её версию (обычно я называю её strdup_n), которая от оригинала отличается использованием new вместо malloc'а. Разумеется, это примерно то же самое, как вызвать strlen, потом malloc, потом strcpy; ну так и что с того? Вообще любую из функций string.h можно сделать за один, максимум за два цикла, то есть без string.h можно вообще обойтись, но тогда программа будет несколько хуже читаться.

Хорошие у Вас

Хорошие у Вас книги, Андрей Викторович.
Тут уже отметили, что имеется чёткая ориентация на ПОНИМАНИЕ того, что люди делают. Это заметно как по книгам, так и по комментариям и ответам на сайте. Недолго думая, среди программирующего персонала провели лёгенький (типа, "неформальный") опрос. Вопросы взяли из древнейшей "Хрестоматии" Богатырёва. Так подавляющее большинство (а это уже не студенты) не смогло ответить на элементарные вопросы. Пишут люди, правда, не под Unix, но ведь не секрет, что MSDOS, откуда пришли современные поделия от Майкрософт, представляла собой именно изрядно кастрированный Unix! Да и ветку NT ваяли именно спецы от Unix. А откуда бы им ещё взяться, "спецам-то"? Самостоятельно же свой Xenix (или как он там назывался) Майкрософт когда-то так и не вытянула. В общем, (типа, "неформально") было предложено провинившимся пройти указанную книжку всем "затруднившимся". К примеру, под (тоже информация с этого сайта) интерпретатором Ch. Достаточно вполне. Ну, а попавшимся же вторично на откровенном незнании элементарных вещей, кроме уже вполне "типа, формального" увольнения ничего не светит.
Так что, в хвост и в гриву студентов, Андрей Викторович, в хвост и в гриву! Во имя добра. Да и их самих тоже!

Забавно! :)

К примерно таким же последствиям, что и у коллеги, оставившего #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'ы, призванные упростить такую запись. Но как бы это сделать "попроще" и "покрасивее"? И как это было сделано в компиляторах С++? "Первокомпайлер С++" Страуструпа, так и вообще, если память не изменяет, переводил плюсовой код на Си... Можно ли там отыскать нечто полезное, позволяющее использовать сиплюсплюс-подобные ссылки именно на Си?

В чистом Си

В чистом Си ссылок нет, только указатели (спасибо Капитану Очевидность). Ссылки -- это синтаксический сахар, введённый именно что языком Си++, и если в языке их нет -- то, значит, нет.

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

P.S. вы человеку этому ещё задайте вопрос, почему если s[2]='r' сделать, программа в тыкву превратится. И почему этого не произойдёт, если вместо *s в описании написать s[]. И какие возможности при этом потеряются. И почему. А потом отберите у этой обезьяны гранату в виде Си и дайте ему что-нибудь не такое опасное, PHP какой-нибудь.

Да, конечно! :)

А что касается "обезьяны", то у нас на фирме занимаются исключительно разработкой, где Си уготовано далеко не первое место. Просто требованием при приёме на работу является специализированное высшее образование. Думаю, что согласитесь, что язык Си обязательно должен входить в арсенал любого программиста, чем бы он не занимался. И он, разумеется, входит во все вузовские программы. Просто хотелось бы иметь ДЕЙСТВИТЕЛЬНО грамотных специалистов. А потом, чем чёрт не шутит, может, и другие направления деятельности появятся.
Так что, все "обезьяны" у нас пишут практически исключительно на PHP :)
Просто повторюсь, хотелось бы иметь ДЕЙСТВИТЕЛЬНО грамотных специалистов. С ДЕЙСТВИТЕЛЬНО высшим образованием. А какое высшее образование может быть без уверенного знания архитектуры и, соответственно, Си? Дело именно в этом.

Вынести

Вынести понятие "леводопустимого выражения" в систему типов -- это дорогого стоит.

А что мне может дать именно леводопустимость ссылки? Как её можно использовать?
Ссылки как формальные параметры - тут, вроде, всё понятно. Можно вернуть ссылку из функции. Её чему-то присвоить. Но собственно-то ссылку зачем мне менять? В каких случаях мне это может понадобиться?

Видимо, у вас

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

Не компилируется пример

т2 стр. 211
"...Нужная библиотека называется "m", а подключается она указанием флажка -lm"
Далее пример:
$ gcc -Wall -g -lm qe.c -o qe
НЕ КОМПИЛИТСЯ И ВЫДАЕТ ПРЕЖНЮЮ ОШИБКУ, ПОТОМУ ЧТО:
$ gcc -Wall -g qe.c -o qe -lm

Юзаю Linux ubuntu + gcc

В принципе это

В принципе это логично, поскольку в опциях линкера (по причинам, описанным, например, в параграфе 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 только к пятой версии, а на всех версиях до четвёртой включительно работало в том числе и так, как написано в книге.

Столкнулся с

Столкнулся с программой, которая упорно не желает отдавать на конвейер свой стандартный вывод. Попытка получить этот самый вывод с именованного канала тоже эффекта не имела. Между тем, работая "сама по себе", программа, разумеется, текст на консоль выдает. Исходника нет, только бинарник. Команда strings показывает обычные printf'ы. И тем не менее!
Пришлось вернуться к учебникам. В том числе, к вашему. Явно какие-то пробелы со стандартными потоками или настройками терминала (перепробованы все возможные терминалы системы). И уже первый же эксперимент со стандартными потоками привёл к непредсказуемому результату:
Тыц!
То есть, вызывая syscall 4 с параметром 0 (i = 0, STDIN_OUT), почему-то вижу строку в консоли. А, вроде, не должен... Дальше - всё нормально, i = 1, i = 2 - оно и должно выводиться, больше - нет. Но почему выводится при i = 0?

Ну Семёёёёёёёён семёёёёёёныч

догадаться никак?

Это же (очевидно) один и тот же поток, ваш лидер сеанса открыл терминал на чтение и запись и потом dup'ом (или dup2) скопировал в трёх экземплярах -- это самая очевидная и простая реализация старта сеанса на терминале. Закладываться на это, разумеется, не надо: лидер сеанса совершенно не обязан действовать именно так, и далеко не все эмуляторы терминалов юзают такой мерзкий хак, но -- им этого и не запрещает никто.

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

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

upd Кстати, кажется, можно и проще: посмотрите на программу screen, она, кажется, имеет встроенный логгинг, при этом запущенные под ней программы про него ничего узнать не могут.

P.S. Глянул ещё раз на ваш исходничек... самое страшное там -- пляски с STDOUT_FILENO. Сам факт их наличия демострирует проблемы настолько капитальные, что я бы вам советовал с них начать, а не с системного вызова.

По поводу

По поводу ошибок 1-го тома:
→ 374-ая страница,
→ 3-ий по счёту пример на этой странице,
→ надо добавить в условие первого цикла проверку first <> nil,
иначе так можно разыменовать nil... :-)

Спасибо!

Да, факт. В своём экземпляре я это пометил, если когда дойдёт дело до переиздания — исправлю. Спасибо!

По поводу ошибок

По поводу ошибок 2-го тома:
380 страница, ссылка на параграф о максимальной длине строк из первой книги неправильная. Написано §2.15.14, а должно быть §2.15.4.

Есть такое

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

Отправить комментарий

Содержание этого поля является приватным и не предназначено к показу.
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Доступны HTML теги: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <pre>
  • Строки и параграфы переносятся автоматически.

Подробнее о форматировании

CAPTCHA
Проверка на бота
Image CAPTCHA
Copy the characters (respecting upper/lower case) from the image.