All streams
Search
Write a publication
Pull to refresh
37
0
Send message

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

Этот синтаксис (так назывемый "K&R синтаксис") объявления функции был официально объявлен obsolescent (т.е. подлежащим удалению из языка) начиная с самого первого стандарта С. Однако ни у кого не поднимается рука его фактически удалить, ибо обратная совместимость с тоннами старого кода до сих пор важна, а переписывать его никто не хочет.


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


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


Это ключевой момент в поведении большинства современных мэйнстримовых компиляторов С: в первую очередь это компиляторы огромной по своему объему устаревшей и довольно низкокачественной базы унаследованного кода, написанной на некоем развеселом С-подобном языке. И только во вторую (если не в пятую) очередь эти компиляторы вспоминают собственно о самом чистом языке С. В частности, в gcc язык С начинается с флагов -std и -pedantic (лучше — -pedantic-errors). А то, что вы получаете в режиме по умолчанию — это не С, а некий студенческий С-подобный суржик.


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


Интересным является утверждение "Каждая глобальная переменная должна быть объявленным ровно один раз без атрибута extern, чтобы выделить память под нее." Это действительно так и стандарт языка требует единственного определения. Однако компиляторы, произрастающие из Юникс (в частности, gcc), всегда игнорировали это правило и позволяли множественные определения переменных, раскиданные по программе. Компиляторы с других платформ как раз выступали за единственность определения, ибо глобальный поиск и удаление лишних определений — неоправданно трудоемкая операция (по меркам тех времен). В конечном итоге победили последние, а не юниксоиды: стандарт С возлагает заботу о единственности определения на автора кода.


А что касается синтаксиса с операндом return в скобках — это было требованием в первых версиях языка С образца начала 70х. Отменилось это требование только в K&R. Скорее всего этот синтаксис было просто тупо унаследован из предшественников C: языков B и/или BCPL.

В С++? Очень большая. Собственно, из-за таких соображений подобные фичи и являются несовместимыми с С+++.

Язык С не представлял собой никакого "шедевра" в 1972, а являлся в то время довольно неудачной поделкой, сделанной впопыхах и на коленке. Главным образом именно поэтому уже первое издание K&R полностью переработало и синтаксис, и семантику языка, отказавшись от большого числа "странных" низкоуровневых свойств языка С образца начала 1970х. С некоторой натяжкой "шедевром" можно назвать K&R C времен первого издания книги. Но настоящий С родился только со вторым изданием K&R, появившимся почти одновременно со стандартом C89/90.

Ради формальной корректности стоит заметить, что ни в С, ни в С++ не существует способа "инициализировать только некоторые элементы" агрегата через посредство {}-инициализатора в объявлении. Инициализированы в вашем примере будут все таки все элементы без исключения. Т.е. элементы, для которых вы не указали инициализатора, будут инициализированы нулями.

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


Кстати, вы перевели термин "statement" как "оператор", что является неприятной особенностью многих переводов книг по С и С++ на русский язык. Возникающая при этом неоднозначность между терминами "statement" и "operator" совершенно неприемлема в серьезной литературе. Также, в начале "А.4. Операторы" вы внезапно перевели один из "statement" как "утверждение", что только добавило путаницы.

"Какой именно стандарт?" Да любой.

Это замечательно. Я тоже принципиально предпочитаю ptr == NULL. Однако при чем здесь "значения одинаковых типов" мне не ясно. Во-первых, NULL запросто может быть тем же самым 0 (как в С, так и в С++). Во-вторых, даже если в С NULL обычно предпочитают объявлять как (void *) 0, это не делает его "значением одинакового типа" в паре с каким-нибудь не-void * указателем.

Вы тролль?
Нет никакого "унижения" в том, что кто-то является начинающим С программистом. Здесь никто не будет спорить с тем, что невладение концепциями "null-point constant" и "null-pointer value" и непонимание разницы между ними — это однозначное и неоспоримое свидетельство того, что индивидуум делает только первые шаги в освоении языка.

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

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


Во-вторых, не надо забывать, что GCC и Clang не являются компиляторами языка С. GCC и Clang — это в первую очередь средства для сборки разношерстной legacy codebase GNU-шных проектов, аккумулированной в течение долгих декад криворукого гамнокодинга. Она написана на некоем развеселом разношерстном "суржике" лишь внешне отдаленно напоминающем С. От GCC и Clang требуется умение в умолчательной конфигурации собирать эту codebase.


Поддержка же именно языка С является для GCC и Clang второстепенным побочным проектом, реализуемым по остаточному принципу. Язык С для этих компиляторов начинается с флагов -std=... и -pedantic (лучше — -pedantic-errors). Без этого даже говорить не о чем.

Вот именно http://c-faq.com/null вам и нужно внимательно почитать. Там все правильно и тщательно разъяснено. Ни слова о том, что "для #define NULL использовалось ненулевое значение" там нет.


Речь там идет о том, что на некоторых платформах null-указатель является физически ненулевым. Это правда. Однако к #define NULL это не имеет никакого отношения. #define NULL в С на таких платформах все равно будет 0 или (void *) 0.


if (ptr == 0) законно и переносимо. Это гарантируется стандартом языка С.

Вы несете чушь.


Выражение ptr == 0 не является и никогда не являлось сравнением с "нулевым адресом". Согласно требования языков С и С++ выражение ptr == 0 должно быть распознано компилятором именно как сравнение с null-указателем. Компилятор обязан транслировать его в сравнение ptr в с правильным представлением null-указателя на данной платформе. По этой причине никакой разницы между ptr == 0 и ptr == NULL нет и никогда не было. Но всех платформах NULL может быть определен как просто 0, даже если на какой-то из этих платформ null-указатель представляется физически как ненулевое значение.


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

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

"Объявления переменных"??? O_o
Запятая в объявлениях переменных не имеет никакого отношения к оператору "запятая".

Если на некоторой платформе нулевой адрес является валидным и используемым, то на такой платформе null-указатель просто не будет представляться нулевым адресом. Null-указатели ни в С, ни в С++ никак не привязаны к нулевому адресу. Пожтому все ваши рассуждения попадают мимо кассы.

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


Но, во-вторых, конечно, это лишь формально-педантичная ремарка. Никакого самостоятельного смысла константные rvalue-ссылки не несут.

Про самую короткую программу


Ответ неправильный. Правило "implicit int" было полностью удалено из языка С в стандарте С99. Но даже в самом первом — "классическом" — стандарте С (ANSI/ISO C89/90) грамматически запрещались объявления с пустым decl-specifier-seq. То есть, пользуясь правилом "implicit int" вы могли написать объявление extern var; или const var;, но ни в коем случае не просто var;. Поэтому ни о каком main; в языке С не может быть и речи.
Более того, легальность использования идентификатора main для "посторонних" целей зависит от того, работаем ли мы в hosted evironment или в freestanding environment.


Про fork


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


Про move и лямбду


Ответ странный: "компилятор не умеет с ней работать"? Шито? Компилятор все прекрасно умеет: он работает совершенно верно, в полном соответствии с правилами overload resolution.


Про x и bar


Ответ странный. "Если вы хотите приведение типа, надо писать вот так int bar((int(x)));". Нет, достаточно написать так: int bar = int(x); или, лучше, так int bar{int(x)};


Про inline


Вопрос отформатирован криво. В строке компиляции фигурируют main.cpp и foo.cpp. Что это за файлы? Второго варианта функции не видно, пока не открыт "Ответ". Поэтому совершенно не ясно, про что нам рассказывает автор.


Ответ правильный, но странный: распинаться про какой-то местечковый линкер нам не надо — это никому не интересно. Достаточно заметить, что в С++ требуется, чтобы inline функция с внешним связыванием была определена одинаково во всех единицах трансляции, где она используется. Нарушение этого правила приводит к неопределенному поведению.


Про конструкторы


Наконец-то красивый вопрос!

Я не вижу никакой опасности в такой коммутативности. А запрещать чисто ради запрещения в С не принято.

Что такое "коммутативна для байт"??? О_о

Information

Rating
Does not participate
Registered
Activity