Comments 134
очень хорошая постпятничная статья
Спасибо за комплимент. =)
Буду благодарен за совет, в какой блог ее лучше перенести.
Буду благодарен за совет, в какой блог ее лучше перенести.
постпятничная это я с юмором, по другому относится к такому нельзя :) Ведь суть hello, world именно в том чтобы поскорее выплюнуть программу в реальный мир, если строчка появилась, это значит что все работает. Сломал psp, запустил там hello world и душа радуется, можно работать. Поставил питон, написал print «hello, world» — ок, значит питон работает как надо можно приступать. Сравнивать с реальными программами глупо
я бы еще учел неблокируемую запись.
Вам не кажется, что для начала изучения первый вариант намного проще и понятнее последнего?
Может в начале обучения не стоит всё усложнять?
Ведь сперва идёт рассказ о том, что это за оператор и для чего он нужен.
Может в начале обучения не стоит всё усложнять?
Ведь сперва идёт рассказ о том, что это за оператор и для чего он нужен.
Спасибо, Кэп!
Так ведь это и не для начала обучения. Это скорее вроде того, как в институте говорят «а теперь забудьте все, чему вас учили в школе».
Здесь скорее вопрос не в простоте, а в том люди не отдают себе отчет, каким инструментом они пользуются и как легко можно выстрелить себе в ногу, если все время не думать о том, что все может выйти из строя.
Т.е. стандартный Hello World на C в схеме «what I wanted — what I expected — what I got» ломается на втором пункте и это ужасно.
Т.е. стандартный Hello World на C в схеме «what I wanted — what I expected — what I got» ломается на втором пункте и это ужасно.
Вы бы еще обработчик на SIGPIPE поставили.
употребляйте поменьше LSD плз.
Спасибо за совет, но я и так предпочитаю обходиться без подобных веществ.
хм, ну и коноплю тоже курить не следует
Весьма мило.
Эту тему Александреску в шутку поднимал в своей статье Case of D.
Эту тему Александреску в шутку поднимал в своей статье Case of D.
Даже не знал, что иду по стопам классиков. =)
Значит не читали наш перевод: habrahabr.ru/blogs/programming/75451/ ;)
UFO just landed and posted this here
Если Эта статья хабражителям понравится, будут еще статьи на тему «чем учебники отличаются от реальной жизни».
Я нифига не понимаю в C++, но сама идея про отличие учебников от реальной жизни просто отличная — пишите ещё!
Отличная статья!
Такие надо ставить второй главой в учебники в стиле «порадовались? а теперь смотрите что на самом деле.»… Чтобы не расслаблялись.
Такие надо ставить второй главой в учебники в стиле «порадовались? а теперь смотрите что на самом деле.»… Чтобы не расслаблялись.
Думаю, лучше было бы так: две книги, в которых рассматриваются одни и те же примеры. Первая как учебник по языку, а вторая — с точки зрения реальной жизни.
Тогда вторую книгу будут читать единицы, а так хоть «говнокодеров» может меньше станет — они на второй главе все отвалятся =)
Говнокодерство, к сожалению, неистребимо. Зато можно будет что-то понять о человеке по ответу на вопрос «Читали ли Вы эту книгу?»
Два человека:
1) Не делает проверок при написании важных системных библиотек;
2) Усложняет код элементарной программы, которая не является критически важной, из-за недоверия стандартным функциям и возможности возникновения «сферического коня в вакууме».
Внимание вопрос «Кто из них быдлокодер?»
1) Первый;
2) Второй;
3) Оба.
1) Не делает проверок при написании важных системных библиотек;
2) Усложняет код элементарной программы, которая не является критически важной, из-за недоверия стандартным функциям и возможности возникновения «сферического коня в вакууме».
Внимание вопрос «Кто из них быдлокодер?»
1) Первый;
2) Второй;
3) Оба.
Я бы читал только нечетные главы.
На самом деле ваш пример забавен (только во втором фрагменте незачем писать if x return 1 else return 0 — вполне достаточно return x), но он к реальной жизни имеет не больше отношения, чем код из книги.
Конечно, возникают ситуации, когда фраза «а теперь представим, что у вас на выходе многопоточный радиоуправляемый тостер с LED-дисплеем» имеет смысл. Но чаще всё действительно просто.
Пример: если надо реализовать суммирование чисел, я сказал бы, что int sum(int a, int b) { return a + b; } — правильный ответ.
Однако теперь тоже можно начать придираться: а как же быть с переполнением разрядной сетки, а что если ошибка процессора, а что, если многопоточность и прочее и прочее.
У любой задачи есть предусловия, из которых надо исходить. Но обзывать хороший код «книжной выдумкой», а произведение для тостера со свистулькой «реальной жизнью» я бы поостерегся.
Конечно, возникают ситуации, когда фраза «а теперь представим, что у вас на выходе многопоточный радиоуправляемый тостер с LED-дисплеем» имеет смысл. Но чаще всё действительно просто.
Пример: если надо реализовать суммирование чисел, я сказал бы, что int sum(int a, int b) { return a + b; } — правильный ответ.
Однако теперь тоже можно начать придираться: а как же быть с переполнением разрядной сетки, а что если ошибка процессора, а что, если многопоточность и прочее и прочее.
У любой задачи есть предусловия, из которых надо исходить. Но обзывать хороший код «книжной выдумкой», а произведение для тостера со свистулькой «реальной жизнью» я бы поостерегся.
Ну, так можно и к полной параноидальности скатиться…
Давайте еще проверим, есть ли права на запись в консоль?
Тем не менее, автору — спасибо! Неожиданно.
А с тем, что любая программа должна выдавать код возврата, согласен.
Давайте еще проверим, есть ли права на запись в консоль?
Тем не менее, автору — спасибо! Неожиданно.
А с тем, что любая программа должна выдавать код возврата, согласен.
Если считать, что true это не ноль (по стандарту), то можно вместо такого:
if (printf_res < strlen(msg))
{
return 1;
} else {
return 0;
}
Написать:
return (printf_res >= strlen(msg));
Сокращает число строк кода, хотя и несколько ухуджает читаемость
if (printf_res < strlen(msg))
{
return 1;
} else {
return 0;
}
Написать:
return (printf_res >= strlen(msg));
Сокращает число строк кода, хотя и несколько ухуджает читаемость
И вывод может отправляться, например, в какое-нибудь странное устройство, которое просто не умеет записывать больше одного символа за раз.
И все нормальные реализации printf() имеют внутри такой же цикл как и у вас. Разве нет?
UFO just landed and posted this here
Ключевое слово — нормальные реализации. Программа, работа которой сильно зависит от того, чем ее компилировали — не наш метод. =)
К сожалению, нормальная работа любой программы очень сильно зависит от того, чем её компилировали и с какими библиотеками, и какие библиотеки доступны во время выполнения.
Если вы не доверяете нормальной работе printf(), то почему доверяете тому, что макрос STDOUT_FILENO определён корректно? Или почему доверяете тому, что в обёртке стандартной библиотеки для системного вызова write нет ошибок?
Вот я запущу вашу программу с библиотекой в LD_PRELOAD, которая подменяет write() на пустышку, которая всегда возвращает успех. Значит ли это, что необходимо написать собственную обёртку для вызова ядра?
Если вы не доверяете нормальной работе printf(), то почему доверяете тому, что макрос STDOUT_FILENO определён корректно? Или почему доверяете тому, что в обёртке стандартной библиотеки для системного вызова write нет ошибок?
Вот я запущу вашу программу с библиотекой в LD_PRELOAD, которая подменяет write() на пустышку, которая всегда возвращает успех. Значит ли это, что необходимо написать собственную обёртку для вызова ядра?
Еще можно таймауты учесть, можно лимиты проверять, и т.д. Нет предела совершенству.
И вывод может отправляться, например, в какое-нибудь странное устройство, которое просто не умеет записывать больше одного символа за раз.
И все нормальные реализации printf() имеют внутри такой же цикл как и у вас. Разве нет?
#include <stdio.h>
int main()
{
return printf(«Hello world\n») < 0? 1: 0;
}
в документации написано, что ошибка — это отрицательное число. Из чего я делаю вывод, что положительное ошибкой не является.
int main()
{
return printf(«Hello world\n») < 0? 1: 0;
}
в документации написано, что ошибка — это отрицательное число. Из чего я делаю вывод, что положительное ошибкой не является.
эх… как раз скоро придется учиться писать на С в линухе… спасибо за статью!
Там где идет проверка на EINTR еще по хорошему надо воткнуть проверку кода EAGAIN (во всяких олдскульных юниксах может попадаться вариант EWOULDBLOCK).
Но в целом до того как извращаться с неблокируемым write() следовало бы хотя бы дать ссылку RTFM :)
Но в целом до того как извращаться с неблокируемым write() следовало бы хотя бы дать ссылку RTFM :)
Очень правильная статья. Приучаться писать правильный код нужно с младых ногтей, имхо. Переучиваться потом гораздо труднее.
К сожалению, в вузах у нас этому не учат. По крайней мере, нас не учили (далеко не самый последний ВУЗ в России).
К сожалению, в вузах у нас этому не учат. По крайней мере, нас не учили (далеко не самый последний ВУЗ в России).
В дополнение к топику советую всем интересующимся книжку «Linux Системное программирование», автор Роберт Лав. Вот там тоже читаешь — и простейшие вещи, о которых раньше не задумывался, пишутся в двое большее число строчек. Ну и системные вызовы разобраны, чтобы было ясно, что такое write(), а что такое puts().
>const char const * msg = «Hello World!\n»;
с -fPIE будет лишний relocation из-за указателя… так что лучше const char msg[] = «Hello World!\n»;
с -fPIE будет лишний relocation из-за указателя… так что лучше const char msg[] = «Hello World!\n»;
>… и послал всем процессам SIGHUP, который обычно значит «пересмотри конфиги»?
который обычно означает ваш терминал сдох
который обычно означает ваш терминал сдох
Очень многие демоны используют SIGHUP именно как «твои конфиги поменялись, посмотри новые».
демоны по определению не имеют управляющего терминала, и поэтому этот сигнал свободен.
не надо путать причину и следствие.
не надо путать причину и следствие.
Демоны — да, но для простых программ это обозначает «терминал сдох».
Кстати, само название HUP это сокращенное Hang UP — то есть «модем повесил трубку» и идет из времен динозавров, когда тонкие клиенты подключались к мейнфрейму. Когда терминал отваливался, программа на мейнфрейме получала этот сигнал и в норме должна была по нему завершиться. Какой ей смысл висеть на дохлом теримнале?
Уже потом, когда появились демоны, то есть программы не подключеннные к терминалу, им нужен был какой-то сигнал для перечитывания конфигов. Было решено использовать SIGHUP — т.к. для демонов он не использовался.
Кстати, само название HUP это сокращенное Hang UP — то есть «модем повесил трубку» и идет из времен динозавров, когда тонкие клиенты подключались к мейнфрейму. Когда терминал отваливался, программа на мейнфрейме получала этот сигнал и в норме должна была по нему завершиться. Какой ей смысл висеть на дохлом теримнале?
Уже потом, когда появились демоны, то есть программы не подключеннные к терминалу, им нужен был какой-то сигнал для перечитывания конфигов. Было решено использовать SIGHUP — т.к. для демонов он не использовался.
Всё это конечно весело, но по-моему главной задачей программиста как раз и является найти «золотую середину» между… эээм… корректностью программы и надежностью для каждого конкретного случая. Так что и первый вариант с такой точки зрения очень даже ничего ;)
Вот так вот, с казалось бы интересных рассуждений о том, что printf не сможет вывести на некое устройство символы, и рождается подобный код? Еще чуть-чуть и вам уже пора будет выносить часть кода в функции и это только вывод строки…
очень познавательная неординарная статья, побольше бы таких. я думаю она очень полезна большинству разработчиков
const char const * msg = «Hello World!\n»;
const char *const msg =…
Пишите еще и почаще!!!)))
Только одно — какие еще конфиги в программе с единственной константой?))
PS: Спасибо за отличную статью!
PS: Спасибо за отличную статью!
В абсолютном большинстве программ, которые я видел, никто даже не задумывается о проверке статуса стандартных потоков — разве что в программах GNU Coreutils, где всё серьёзно. Меня очень радует, что есть люди, понимающие, что проверка нужна, и стремящиеся рассказать это другим. Спасибо!
Результат printf проверять не надо, поскольку он может быть необъективным из-за буферизации. Зато известно, что любая функция stdio при наличии ошибки устанавливает флаг ошибки у потока, что можно проверить вызовом ferror(). К тому же, некоторые ошибки не проявляются, пока не закрыть поток. Вот пример на чистом C, без POSIX: http://codepad.org/WubHENcE. Правда, только с помощью библиотеки stdio конкретную ошибку определить не получится. Проверить работоспособность можно командой "./a.out > /dev/full".
С более низким API можно и не связываться, поскольку стандартная библиотека C учитывает EINTR и перезапускает системный вызов сама, так что printf() и другие функции не требуют обработки этого случая вручную.
Результат printf проверять не надо, поскольку он может быть необъективным из-за буферизации. Зато известно, что любая функция stdio при наличии ошибки устанавливает флаг ошибки у потока, что можно проверить вызовом ferror(). К тому же, некоторые ошибки не проявляются, пока не закрыть поток. Вот пример на чистом C, без POSIX: http://codepad.org/WubHENcE. Правда, только с помощью библиотеки stdio конкретную ошибку определить не получится. Проверить работоспособность можно командой "./a.out > /dev/full".
С более низким API можно и не связываться, поскольку стандартная библиотека C учитывает EINTR и перезапускает системный вызов сама, так что printf() и другие функции не требуют обработки этого случая вручную.
Еще можно, например, проверять, что malloc вернул не NULL.
Только этого все равно никто не делает (по крайней мере, мне так кажется)
Только этого все равно никто не делает (по крайней мере, мне так кажется)
Потому что все используют xmalloc().
Или пишут на C++ (new не возвращает NULL).
Или не проверяют потому что всё равно выделенный кусок памяти тут же используется и если что — то получим segfault. Всё равно когда память исчерпана кроме как вызвать exit(1) среднестатистическая программа мало что сможет сделать.
Или не проверяют потому что всё равно выделенный кусок памяти тут же используется и если что — то получим segfault. Всё равно когда память исчерпана кроме как вызвать exit(1) среднестатистическая программа мало что сможет сделать.
Перегруженная версия
, кстати, возвращает. Пример:
void* operator new(std::size_t, const std::nothrow_t&) throw();
, кстати, возвращает. Пример:
#include <new> int *p = new (std::nothrow) int; if (p == NULL) cerr << "memory allocation failure\n";
new может вернуть null, если передать дополнительный параметр std::nothrow
Насколько велики при этом сопутствующие потери производительности?
Вот я например, прогаю на чистом C под SoC. И мне каждый лишний вызов обходится неимоверно дорого.
Так что мне значительно проще написать malloc, ничего не проверять, но помнить, что сейчас нельзя поставить меньше 64 метров памяти.
Вот я например, прогаю на чистом C под SoC. И мне каждый лишний вызов обходится неимоверно дорого.
Так что мне значительно проще написать malloc, ничего не проверять, но помнить, что сейчас нельзя поставить меньше 64 метров памяти.
Полностью зависит от того, насколько часто ваш код выделяет динамическую память.
Например, если обходится только статической и автоматической — то нет и потерь :)
Например, если обходится только статической и автоматической — то нет и потерь :)
Чего стоит скорость, если программа может работать неправильно? Сначала следует написать работающий код, а потом его оптимизировать.
Что же касается потерь производительности, то такая версия по сути является дополнительным if'ом, что есть мелочь даже для embedded-систем:
Если компилятор достаточно умён, то дополнительной памяти в стеке этот код занимать не будет.
Что же касается потерь производительности, то такая версия по сути является дополнительным if'ом, что есть мелочь даже для embedded-систем:
#include <stdlib.h> static inline void *xmalloc (size_t size) { void *p = malloc(size); if (p) return p; else { fprintf(stderr, "error: not enough memory\n"); exit(EXIT_FAILURE); } }
Если компилятор достаточно умён, то дополнительной памяти в стеке этот код занимать не будет.
Вообще ВСЕГДА нужно проверять, что возвращаяет malloc, но его тут нет. А использовать exit как в случае про xmalloc, как мне кажется странно. А если нужно завершить соединения, сделать shutdown сокетам, записать в файл, что еще не записал и тд. В более-менее сложной программе нельзя использовать exit.
Верно, xmalloc(), как правило, больше используется в программах, просто обрабатывающих данные, например, cp или gcc.
Ещё как вариант вместо exit() можно писать abort(), это приведёт к сигналу SIGABRT, который можно поймать и обработать.
Ещё как вариант вместо exit() можно писать abort(), это приведёт к сигналу SIGABRT, который можно поймать и обработать.
Речь идет о том, что всегда (!) есть такие вещи, которые стоило бы проверить.
Но никто этого на практике не делает, поскольку это ухудшает читаемость кода (как в примере из топика) или замедляет работу (как в случае с xmalloc — почему именно для меня критично это замедление можно прочесть немногим выше).
Но никто этого на практике не делает, поскольку это ухудшает читаемость кода (как в примере из топика) или замедляет работу (как в случае с xmalloc — почему именно для меня критично это замедление можно прочесть немногим выше).
Я лично всегда проверяю.
Потому что это трудноуловимые грабли: если вдруг malloc по какой то причине все же вернет NULL (а это необязательно нехватка памяти) — мы увидим Segmentation fault.
Если же результат вызова malloc() проверить и в случае NULL выдать вменяемое сообщение об ошибке — сэкономите кучу времени при отладке.
Потому что это трудноуловимые грабли: если вдруг malloc по какой то причине все же вернет NULL (а это необязательно нехватка памяти) — мы увидим Segmentation fault.
Если же результат вызова malloc() проверить и в случае NULL выдать вменяемое сообщение об ошибке — сэкономите кучу времени при отладке.
«Только этого все равно никто не делает (по крайней мере, мне так кажется)»
Дааа… А Вы — делаете?
Дааа… А Вы — делаете?
Подходит под «Hello world» для сетевого программирования.
Когда в сокет пишешь, чтобы обрабатывать сигналы используют write.
Когда в сокет пишешь, чтобы обрабатывать сигналы используют write.
Интересно, но кое-где вы все же перегибаете палку. Например, фраза:
как раз и есть такой перегиб. Мы же пишем «Hello world». Требования к такой программе довольно точно определены — она выводит «Hello world» и от нее не ждут ничего другого…
А в результате мы делаем вывод, что лучше написать свою реализацию printf =) «А также неизвестно на чем будет компилироваться наша программа и с какими библиотеками» — так может поэтому лучше бы переписать стандартную библиотеку, да и компилятор свой наваять? =)
ЗЫ: а вот про обработку ошибок в *nix было реально интересно. Спасибо.
А теперь, что если у нас не «Hello World!\n»? А если там строчка вроде «Use %s to print string»? Нельзя нам здесь пользоваться printf, если мы хотим, чтобы этот код можно было еще где-нибудь использовать. А мы ведь этого хотим, иначе какие же мы программисты.
как раз и есть такой перегиб. Мы же пишем «Hello world». Требования к такой программе довольно точно определены — она выводит «Hello world» и от нее не ждут ничего другого…
А в результате мы делаем вывод, что лучше написать свою реализацию printf =) «А также неизвестно на чем будет компилироваться наша программа и с какими библиотеками» — так может поэтому лучше бы переписать стандартную библиотеку, да и компилятор свой наваять? =)
ЗЫ: а вот про обработку ошибок в *nix было реально интересно. Спасибо.
Напомнило The Evolution of a Programmer :)
Осталось разобраться с EAGAIN и EWOULDBLOCK :)
отличная статься, ждем продолжения)
Нет предела совершенству! Вот тут: www.gnu.org/software/hello/ архив с последней версией Hello World весит 488 кб. =)
> Нельзя нам здесь пользоваться printf, если мы хотим, чтобы этот код можно было еще где-нибудь использовать.
Вполне можно, только писать надо правильно:
printf("%s", «Hello World!\n»);
Вполне можно, только писать надо правильно:
printf("%s", «Hello World!\n»);
> «const char const * msg = "Hello World!\n";»
Автор, очевидно, думает, что он объявил константный указатель на константу? В таком случае ему стоит взять учебник по Си за первый класс и таки обращать внимание на предупреждения компилятора.
Правильно так: «const char* const msg = "Hello World!\n";»
Или так: «char const* const msg = "Hello World!\n";»
Facepalm.
Автор, очевидно, думает, что он объявил константный указатель на константу? В таком случае ему стоит взять учебник по Си за первый класс и таки обращать внимание на предупреждения компилятора.
Правильно так: «const char* const msg = "Hello World!\n";»
Или так: «char const* const msg = "Hello World!\n";»
Facepalm.
не вижу смысла во всем этом.
если Вы решаете задачи посложнее чем Hello World, и если будете так изгаляться, то проект никогда не закончите.
если Вы решаете задачи посложнее чем Hello World, и если будете так изгаляться, то проект никогда не закончите.
Кислятина.
Как вариант можно сделать.
char const msg[] = «Hello World!\n»;
…
char const * end = begin + sizeof(msg)-1;
Таким образом не нужно будет вызывать strlen, и длина строки будет посчитана на этапе компиляции.
char const msg[] = «Hello World!\n»;
…
char const * end = begin + sizeof(msg)-1;
Таким образом не нужно будет вызывать strlen, и длина строки будет посчитана на этапе компиляции.
В реальном мире не будут использоваться ни первая, ни вторая реализация, а будет использоваться библиотека, в которой все секьюрно, надежно, оптимизировано и предусмотрено заранее, а программисту останется лишь проверить статус ошибки.
Объявлять переменные внутри циклов — это не классический Си.
Пользуйтесь стандартом ANSI C (c89) и будет вам счастье.
Пользуйтесь стандартом ANSI C (c89) и будет вам счастье.
c99 позволяет.
Пользуйтесь современными стандартами!
Пользуйтесь современными стандартами!
C99 в полной мере не поддерживается GNU :)
(Кусок man gcc)
> ISO C99. Note that this standard is not yet fully supported; see
> <gcc.gnu.org/gcc-4.2/c99status.html> for more information.
Не пользуйтесь коряво-поддерживаемыми стандартами. Кошерный Си — это C89.
А остальное — non-KISS way :)
(Кусок man gcc)
> ISO C99. Note that this standard is not yet fully supported; see
> <gcc.gnu.org/gcc-4.2/c99status.html> for more information.
Не пользуйтесь коряво-поддерживаемыми стандартами. Кошерный Си — это C89.
А остальное — non-KISS way :)
Разве в ANSI C нельзя объявлять переменные в начале блока? (параграф 3.6.2, «Compound statement, or block»)
В ANSI C (C89) переменные объявляются как и в K&R C, в начале блока _функции_, либо в глобальном пространстве.
Всякие переменные_в_цикле, переменные_в_ифе и прочие — это 99.
Всякие переменные_в_цикле, переменные_в_ифе и прочие — это 99.
Вы ошибаетесь.
flash-gordon.me.uk/ansi.c.txt
— это один из драфтов С89. Пункт 3.1.2.4, третий параграф.
В С99 стало возможным объявлять переменные после операторов внутри блока, в С89 и раньше допускают объявления/инициализации только в начале блока.
flash-gordon.me.uk/ansi.c.txt
— это один из драфтов С89. Пункт 3.1.2.4, третий параграф.
В С99 стало возможным объявлять переменные после операторов внутри блока, в С89 и раньше допускают объявления/инициализации только в начале блока.
В C89 можно объявлять переменные внутри и циклов, и ифов, и вообще везде внутри фигурных скобок:
$ gcc -v 2>&1 |grep 'gcc version'
gcc version 4.3.2 (Debian 4.3.2-1.1)
$ echo "int main() {int a; {int b; } if (0) {int c;} while(0) {int d;} return 0;}" | gcc --std=c89 -x c - -o scope && ./scope && echo Everything is ok || echo Something is wrong
Everything is ok
Printf, вообще говоря, не может написать меньше, чем ему дали на вход. В случае ошибки он вернет отрицательное число, но с такой ошибкой уже ничего не сделаешь. Поэтому правильный Hello World с элементами паранойи выглядит так:
И, кстати, вот тут полная ерунда написана:
Второй const здесь лишний. Это все равно что написать «const const char*». Если Вы хотели сделать постоянным указатель, следовало написать так:
Да-да, проверьте — напишите там ++msg и компилятор все соберет.
А еще лучше — объявить строку как массив:
Статью я бы перенес в блог «Я безмуный» или «Ненормальное программирование».
А если заниматься настоящей паранойей, то нужно доводить дело до конца. Например, не мешало бы еще обрабатывать EAGAIN, а то вдруг у нас стандартный вывод оказался в неблокирующем режиме. И что же делать, если write будет все время возвращать ноль?
#include <stdio.h>
int main() {
return printf("Hello world!") < 0;
}
И, кстати, вот тут полная ерунда написана:
const char const* msg = "something";
Второй const здесь лишний. Это все равно что написать «const const char*». Если Вы хотели сделать постоянным указатель, следовало написать так:
const char* const msg = "something";
Да-да, проверьте — напишите там ++msg и компилятор все соберет.
А еще лучше — объявить строку как массив:
const char msg[] = "something";
Статью я бы перенес в блог «Я безмуный» или «Ненормальное программирование».
А если заниматься настоящей паранойей, то нужно доводить дело до конца. Например, не мешало бы еще обрабатывать EAGAIN, а то вдруг у нас стандартный вывод оказался в неблокирующем режиме. И что же делать, если write будет все время возвращать ноль?
Теперь привязать обработку сигналов и вынести весь этот костыль во внешнюю библиотеку. Получится опять чистенько и красиво.
Для пущей портируемости надо возвращать не 1 и 0, а EXIT_FAILURE и EXIT_SUCCESS из stdlib.h
«быть на C в Linux»
а потом кто-нибудь посмотрит и скажет: ну его нафиг этот линукс.
а потом кто-нибудь посмотрит и скажет: ну его нафиг этот линукс.
Можно сделать еще интереснее — Настоящий «Hello World» на Assembler.
Sign up to leave a comment.
Hello World! как ему следует быть на C в Linux