Здесь важен второй фактор, а именно -- постоянство занятия.
Если вы от раза к разу не успеваете забыть предмет занятий, то есть, меняете вентиль достаточно часто с этой точки зрения, то вы -- не только программист, но и сантехник.
А сейчас? На промежутке в 25 лет вполне имеет смысл учитывать явление фантазма.
Ваши интерпретации примечаний никого не интересуют.
Зато они интересуют тех, кто хочет понять, как всё происходит на самом деле. Мы же в публичном поле общаемся.
Гарантий того что написано в описании возвращаемого значения примечания не дают.
Да, это проблема большинства документаций: остаются двусмысленности и не отвеченные вопросы.
Тем более если по вашему коду не установлен неблокирующий вывод то это не значит что ваш код корректен, тем более что у вас есть и функция установки неблокирующего флага.
Вообще-то, код -- не мой, если вы ещё до сих пор не заметили.
Функция есть, но не используется, а в реальности используется блокирующий ввод/вывод, и обсуждается именно он.
Однако вы не обрабатываете эту ситуацию и для случая неблокирующего вызова у вас обработка не сделана.
Для случая неблокирующего вызова обработка, очевидно, не сделана, потому что данный случай в текущем варианте кода не используется.
Ваши оценки тоже никого не интересуют.
Это, потому что вы не можете их принять.
Цель review заключается не в том, чтобы reviewer своими резкими высказываниями с намеренными недосказанностями пытался создавать ложное впечатление одновременно и собственного мнимого "величия", и мнимой ничтожности review'ируемого, а в том, чтобы объяснить проблемные места и задать направление, что следует изучить, дабы review'ируемый мог эффективно подтянуть свой уровень именно в том, что требуется для исправления проблемных мест, и в следующий раз выдавать уже код, близкий к нормальному.
Задание не выполнено.
Вы так упорно продолжаете это повторять, хотя все уже неоднократно это видели. Или вы в этом всё ещё не уверены?
Однако, это -- общепринятая практика, поэтому специально об этом и не говорят.
На самом деле, сказано:
При старте сервер открывает TCP сокет с IP адресом и портом из стартовых параметров(1 и 2 параметры), берет его на прослушивание и ожидает подключений от клиента.
Написано "подключений", во множественном числе. При этом, из описания алгоритма работы клиента ясно, что клиент открывает только одно TCP/IP-соединение.
Значит, после того, как клиент закроет соединение и завершится, сервером будет обработано только одно подключение от клиента, поэтому, раз в условии написано "подключений", сервер должен продолжить, чтобы принять ещё хотя бы одно подключение.
Также, по поводу клиента явно сказано, что он завершает работу, а по поводу сервера -- нет.
Разве там сказано, что может быть записано меньше, чем затребовано?
Функция возвращает число записанных в сокет байтов ( в нормальном случае должно быть равно значению параметра len ) или -1 в случае ошибки.
"Ненормальный" случай исчерпывающе и недвусмысленно не описан. Можно предположить, что под "ненормальным" случаем понимается возврат -1. Правда, не сказано, что "ненормальный" случай этим и исчерпывается. Явно про что-то, отличное от len и -1 не сказано.
И это -- какая-то "вторичная" документация, она может быть искажённой и поэтому --ошибочной. Смотреть следует первичную.
К тому же, оказывается, что есть различия в поведении для UNIX'ов и для Windows, поэтому ссылаться на UNIX-документацию здесь не следует.
If no error occurs, send returns the total number of bytes sent, which can be less than the number requested to be sent in the len parameter. Otherwise, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling WSAGetLastError.
Это -- не вся правда. Там есть ещё кое-что:
If no buffer space is available within the transport system to hold the data to be transmitted, send will block unless the socket has been placed in nonblocking mode. On nonblocking stream oriented sockets, the number of bytes written can be between 1 and the requested length, depending on buffer availability on both the client and server computers.
Видите, в секции для return value не стали уточнять, что which can be less than the number requested to be sent in the len parameter относится к сокету, который has been placed in nonblocking mode.
В приведённом же коде сокеты не переводятся в nonblocking mode. Поэтому там не требуется проверять, весь ли затребованный блок был отправлен.
Вам на практике удавалось создать/наблюдать ситуацию, когда для блокирующих сокетов под Windows реально возникает эффект возврата из send меньшего количества, чем затребовано?
И вообще, с таким кодом как у вас, лучше ни с кем не спорить. Просто впитывайте то что вам говорят, пока ещё без подколок и издевательств.
Почему человек должен вам верить на слово, что ему именно так и следует поступать? Авторитетов не существует. Или это акт -- "устрашения", учитывая упоминание подколок и издевательств?
Но проблема с одновременностью, конечно, имеется; вот и гадай теперь, как быть…
Очень просто: скажем, каждые 3 месяца докупается один диск и меняется на один из тех, что был установлен в RAID'е с самого начала, и так -- пока не будет докуплено N - 1 дисков.
В результате, в RAID'е будут диски с "разбегом" в 3 месяца, а в запасе тоже будут диски, с частично отработанным ресурсом.
В дальнейшем, при выходе из строя дисков в RAID'е, логично будет заменять их на диски из запаса, выбирая такие из них, чтобы "разбег" по выработке ресурса дисков в RAID'е оставался относительно равномерным.
Риск того, что новые диски в самом начале массово начнут выходить из строя, невелик. Да и никакие ухищрения при таком развитии событий не спасут.
Но чем дальше от момента создания RAID'а, тем диверсифицированнее при таком подходе становится защита от флеш-моба дискового "падежа".
За уменьшение риска в одном месте приходится платить увеличением рисков в другом, а именно, -- тем, что запасные диски могут оказаться и вовсе невостребованными.
Вы считаете const частью типа (и это правильно, потому что const можно убрать под typedef).
Буквально на каждом шагу в стандарте можно прочитать:
A pointer or reference to a cv-qualified type...
"cv-qualified type".
То, что const является частью типа, ярче всего проявляется в определении множества переменных. Так не получится определить:
int a, const b{5};
вместо:
int a;
int const b{5};
Или -- наоборот, отменить const, который указан для типа, для некоторых из переменных, определяемых через запятую.
То есть, запись
int * const p;
Я логически группирую как int * (const p);и читаю как «константный объект p, являющийся указателем на int».
Тогда, если следовать этой логике, необходимо писать:
int const i;
логически группировать как int (const i); и читать как "константный объект i, являющийся int'ом".
Наверное, ваш подход ближе к тому, как компилятор видит код.
Да, поскольку квалификаторы являются частью типа по стандарту, а компиляторы следуют стандарту.
Но лично я быстрее соображаю, когда имею дело с «константным объектом», чем с «объектом константного типа», потому что при попытке что-то сделать с p, во втором случае нужно пройти на уровень глубже и увидеть, что тип не позволяет его менять, а в первом случае я знаю, что сам объект не изменяемый.
Думаю, это вопрос перестройки мышления.
Я выбрал вариант, соответствующий стандарту, потому что, несмотря на начальные усилия, которые, возможно, придётся потратить, далее не возникает никаких нестыковок и прочих неожиданностей, и можно работать с производными типами любой сложности.
Примерно так и раскручивается: p — указатель (не константный) на указатель (константный). То есть я группирую так: int * (const *) p; и получаю, что p указатель на const-объект.
Но правая * относится к "p — указатель (не константный)". Не константный, а вы её пометили как (const *).
Правильная разметка — int (* const) * p;. p — указатель (не константный, правая *) на указатель (константный, левая *) на (не константный) int.
Внимание движется справа налево, от p к int.
Если мы запишем сначала так:
int *const *p;
Затем заведём алиас на тип:
typedef int *T;
И перепишем первое, используя второе, то автоматически получим:
T const *p;
Как можно заметить, const опять справа от того, на что он действует.
А эти модификаторы никак не связаны с описанным выше механизмом, поэтому их-то, как раз, логичнее всего "запихнуть" левее типа, чтобы не "мешались", то есть, не смешивались с механизмом задания производного типа.
Если "в лоб" по вашей логике, то получается, что можно менять значение int и значение p, но нельзя менять значение второй слева *.
Как устроены декларации?
Слева пишется тип, справа -- выражение. Тип результата выражения, написанного справа, есть тип, написанный слева.
Пример:
int *p;
Каков тип выражения *p? Это написано слева: int.
Какой должен быть тип у p, чтобы выражение *p имело тип int? Очевидно, что -- указатель на int.
Для того, чтобы не гадать, а методично прочитать тип, необходимо идти в обратную сторону: если p разыменовывается, то это -- указатель на что-то там.
При этом cv-квалификация указывается между операцией и действием:
int *const p;
То есть, операция * применяется к p, как бы, сквозь const. Поскольку операция разыменования -- префиксная, то внимание движется справа налево: p, потом const, потом *.
Получается: p есть const'антный указатель на ... int.
Если же написано:
int const *const p;
То читается при движении внимания справа налево так: p есть const'антный указатель на const'антный int.
Это, что касается проговаривания. Не видно, чтобы мозг ломался.
Компилятору безразлично, «const T», или «T const», стандарт разрешает оба написания.
Верно.
Правильного, логически обоснованного ответа не существует
В том смысле, что "обязательно надо так, а не иначе" -- да, не существует.
и весь спор — чистая субъективщина.
Не совсем, есть объективные соображения за то, чтобы писать справа.
Во всех случаях, кроме рассматриваемого, const действует на то, что слева. Мозг "обучается" и привыкает к этому.
Если теперь в рассматриваемом случае поступать наоборот, растёт вероятность допустить ошибку, потому что мозгу легче действовать на автомате.
Ну, и читать легче, когда всё единообразно.
Остаются только аргументы типа «так принято» и отсылки к авторитетам.
Отсылка к авторитетам не может быть аргументом.
Попробуйте привести пример. «Производный тип» у вас — это какой? Созданный наследованием?
Существуют базовые типы, производные типы, и типы, определяемые пользователем. В нашем случае типы, определяемые пользователем, ведут себя как базовые, поэтому их условно можно рассматривать как тоже базовые.
Производные типы образуются с помощью "указателей на", "массивов из", "функций, возвращающих" и, если берём C++, "указателей на member такого-то типа" и "ссылок на".
"Производность" типа можно рекурсивно "увеличивать" с некоторыми ограничениями. В частности, указатель может быть на любой производный тип, кроме ссылки, функция не может возвращать массив и функцию, массив не может состоять из функций и из ссылок, указатель на member не может быть на ссылку, ссылка тоже не может быть на ссылку.
Массивы, функции и ссылки не могут иметь собственной cv-квалификации, то есть, в частности, не могут быть const. Таковыми могут быть только указатели и указатели на member'ы.
Поскольку оба вида указателя обозначаются символом * слева от сущности, и cv-квалификация задаётся между сущностью и *, то получается, что const всегда действует на то, что слева.
Например:
// ук-ль на ук-ль на int
int **p;
// ук-ль на ук-ль на const int
int const **p;
// ук-ль на const ук-ль на int
int *const *p;
// const ук-ль на ук-ль на int
int **const p;
// ук-ль на const ук-ль на const int
int const *const *p;
// const ук-ль на ук-ль на const int
int const **const p;
// const ук-ль на const ук-ль на int
int *const *const p;
// const ук-ль на const ук-ль на const int
int const *const *const p;
В итоге, он создал язык D как эволюцию C++ и в нём разрешил обсуждаемую здесь неоднозначность, явно запретив «int const» в пользу «const int». Он то, наверное, получше нас язык чувствует?
Авторитетов не существует.
Александреску -- человек? Значит, он тоже может ошибаться, вне зависимости от опыта? И, значит, может иметь иррациональные предпочтения?
Пользуясь тем, что для многих он -- авторитет, и эти самые многие безоговорочно примут его точку зрения, потому что -- это же Александреску так сказал, он может "протолкнуть" не лучшие решения, но -- в пользу своих предпочтений (которые, кстати, со временем имеют свойство меняться). Человек, потому что, здесь всё понятно.
Именно поэтому могут быть только сухие аргументы. Никаких ссылок на авторитеты быть не может.
Кстати, вы заметили, что я в этом ответе ни на какой "авторитет" и не подумал даже пытаться ссылаться?
Язык C разрешает делать объявления новых типов внутри оператора приведения типа, внутри оператора sizeof, в объявлениях функций (типы возвращаемого значения и типы параметров)
Нет.
У суперинтеллекта есть интересы?
Даже по поводу понимания есть сомнения.
Здесь важен второй фактор, а именно -- постоянство занятия.
Если вы от раза к разу не успеваете забыть предмет занятий, то есть, меняете вентиль достаточно часто с этой точки зрения, то вы -- не только программист, но и сантехник.
Нет.
Программист -- это составитель управляющих программ для ЭВМ.
Зато вы -- архитектор, разработчик ТЗ, разработчик протоколов, алгоритмист, отлаживатель/тестировщик (избавлятель программ от "лажи").
Это -- компьютерщики.
А сейчас?
На промежутке в 25 лет вполне имеет смысл учитывать явление фантазма.
Зато они интересуют тех, кто хочет понять, как всё происходит на самом деле.
Мы же в публичном поле общаемся.
Да, это проблема большинства документаций: остаются двусмысленности и не отвеченные вопросы.
Вообще-то, код -- не мой, если вы ещё до сих пор не заметили.
Функция есть, но не используется, а в реальности используется блокирующий ввод/вывод, и обсуждается именно он.
Для случая неблокирующего вызова обработка, очевидно, не сделана, потому что данный случай в текущем варианте кода не используется.
Это, потому что вы не можете их принять.
Цель review заключается не в том, чтобы reviewer своими резкими высказываниями с намеренными недосказанностями пытался создавать ложное впечатление одновременно и собственного мнимого "величия", и мнимой ничтожности review'ируемого, а в том, чтобы объяснить проблемные места и задать направление, что следует изучить, дабы review'ируемый мог эффективно подтянуть свой уровень именно в том, что требуется для исправления проблемных мест, и в следующий раз выдавать уже код, близкий к нормальному.
Вы так упорно продолжаете это повторять, хотя все уже неоднократно это видели. Или вы в этом всё ещё не уверены?
А, то есть, у вас и сейчас такое же понимание?
Тогда понятно, почему вы выкладываете в качестве "примера, как надо" такой код.
Для чего здесь приведение к
(char *)
?Или это демонстрация вашего понимания
const
25-летней давности?Однако, это -- общепринятая практика, поэтому специально об этом и не говорят.
На самом деле, сказано:
Написано "подключений", во множественном числе.
При этом, из описания алгоритма работы клиента ясно, что клиент открывает только одно TCP/IP-соединение.
Значит, после того, как клиент закроет соединение и завершится, сервером будет обработано только одно подключение от клиента, поэтому, раз в условии написано "подключений", сервер должен продолжить, чтобы принять ещё хотя бы одно подключение.
Также, по поводу клиента явно сказано, что он завершает работу, а по поводу сервера -- нет.
Разве это имеет хоть какое-то значение?
Авторитетов не существует.
Существуют только аргументы.
Вот это -- уже совсем другое дело.
Разве там сказано, что может быть записано меньше, чем затребовано?
"Ненормальный" случай исчерпывающе и недвусмысленно не описан.
Можно предположить, что под "ненормальным" случаем понимается возврат
-1
.Правда, не сказано, что "ненормальный" случай этим и исчерпывается.
Явно про что-то, отличное от
len
и-1
не сказано.И это -- какая-то "вторичная" документация, она может быть искажённой и поэтому --ошибочной. Смотреть следует первичную.
К тому же, оказывается, что есть различия в поведении для UNIX'ов и для Windows, поэтому ссылаться на UNIX-документацию здесь не следует.
Хорошо.
Это -- не вся правда.
Там есть ещё кое-что:
Видите, в секции для return value не стали уточнять, что which can be less than the number requested to be sent in the len parameter относится к сокету, который has been placed in nonblocking mode.
В приведённом же коде сокеты не переводятся в nonblocking mode.
Поэтому там не требуется проверять, весь ли затребованный блок был отправлен.
Вам на практике удавалось создать/наблюдать ситуацию, когда для блокирующих сокетов под Windows реально возникает эффект возврата из
send
меньшего количества, чем затребовано?Почему человек должен вам верить на слово, что ему именно так и следует поступать?
Авторитетов не существует.
Или это акт -- "устрашения", учитывая упоминание подколок и издевательств?
Вы точно не готовы review'ить чужой код.
Вы имеете ввиду, что файл, отправляемый на сервер с таким именем на сервере в том каталоге уже может быть, и нужно этот случай обработать?
Или что сервер не должен завершаться после приёма одного файла?
Не проще ли ясно выражать свои мысли, что именно вы имеете ввиду?
Нет, проблема есть -- клиент тогда должен разобрать этот параметр.
Но в задании ничего такого нет.
А как вы в четвертом параметре для клиента передадите несколько имён файлов?
Очень просто: скажем, каждые 3 месяца докупается один диск и меняется на один из тех, что был установлен в RAID'е с самого начала, и так -- пока не будет докуплено N - 1 дисков.
В результате, в RAID'е будут диски с "разбегом" в 3 месяца, а в запасе тоже будут диски, с частично отработанным ресурсом.
В дальнейшем, при выходе из строя дисков в RAID'е, логично будет заменять их на диски из запаса, выбирая такие из них, чтобы "разбег" по выработке ресурса дисков в RAID'е оставался относительно равномерным.
Риск того, что новые диски в самом начале массово начнут выходить из строя, невелик.
Да и никакие ухищрения при таком развитии событий не спасут.
Но чем дальше от момента создания RAID'а, тем диверсифицированнее при таком подходе становится защита от флеш-моба дискового "падежа".
За уменьшение риска в одном месте приходится платить увеличением рисков в другом, а именно, -- тем, что запасные диски могут оказаться и вовсе невостребованными.
Буквально на каждом шагу в стандарте можно прочитать:
"cv-qualified type".
То, что
const
является частью типа, ярче всего проявляется в определении множества переменных. Так не получится определить:вместо:
Или -- наоборот, отменить
const
, который указан для типа, для некоторых из переменных, определяемых через запятую.Тогда, если следовать этой логике, необходимо писать:
логически группировать как
int (const i);
и читать как "константный объектi
, являющийсяint
'ом".Да, поскольку квалификаторы являются частью типа по стандарту, а компиляторы следуют стандарту.
Думаю, это вопрос перестройки мышления.
Я выбрал вариант, соответствующий стандарту, потому что, несмотря на начальные усилия, которые, возможно, придётся потратить, далее не возникает никаких нестыковок и прочих неожиданностей, и можно работать с производными типами любой сложности.
Но правая
*
относится к "p
— указатель (не константный)".Не константный, а вы её пометили как
(const *)
.Правильная разметка —
int (* const) * p;
.p
— указатель (не константный, правая*
) на указатель (константный, левая*
) на (не константный)int
.Внимание движется справа налево, от
p
кint
.Если мы запишем сначала так:
Затем заведём алиас на тип:
И перепишем первое, используя второе, то автоматически получим:
Как можно заметить,
const
опять справа от того, на что он действует.А эти модификаторы никак не связаны с описанным выше механизмом, поэтому их-то, как раз, логичнее всего "запихнуть" левее типа, чтобы не "мешались", то есть, не смешивались с механизмом задания производного типа.
А если взять пример менее тривиальный?
Если "в лоб" по вашей логике, то получается, что можно менять значение
int
и значениеp
, но нельзя менять значение второй слева*
.Как устроены декларации?
Слева пишется тип, справа -- выражение.
Тип результата выражения, написанного справа, есть тип, написанный слева.
Пример:
Каков тип выражения
*p
?Это написано слева:
int
.Какой должен быть тип у
p
, чтобы выражение*p
имело типint
?Очевидно, что -- указатель на
int
.Для того, чтобы не гадать, а методично прочитать тип, необходимо идти в обратную сторону: если
p
разыменовывается, то это -- указатель на что-то там.При этом cv-квалификация указывается между операцией и действием:
То есть, операция
*
применяется кp
, как бы, сквозьconst
.Поскольку операция разыменования -- префиксная, то внимание движется справа налево:
p
, потомconst
, потом*
.Получается:
p
естьconst
'антный указатель на ...int
.Если же написано:
То читается при движении внимания справа налево так:
p
естьconst
'антный указатель наconst
'антныйint
.Это, что касается проговаривания.
Не видно, чтобы мозг ломался.
Верно.
В том смысле, что "обязательно надо так, а не иначе" -- да, не существует.
Не совсем, есть объективные соображения за то, чтобы писать справа.
Во всех случаях, кроме рассматриваемого,
const
действует на то, что слева. Мозг "обучается" и привыкает к этому.Если теперь в рассматриваемом случае поступать наоборот, растёт вероятность допустить ошибку, потому что мозгу легче действовать на автомате.
Ну, и читать легче, когда всё единообразно.
Отсылка к авторитетам не может быть аргументом.
Существуют базовые типы, производные типы, и типы, определяемые пользователем. В нашем случае типы, определяемые пользователем, ведут себя как базовые, поэтому их условно можно рассматривать как тоже базовые.
Производные типы образуются с помощью "указателей на", "массивов из", "функций, возвращающих" и, если берём C++, "указателей на member такого-то типа" и "ссылок на".
"Производность" типа можно рекурсивно "увеличивать" с некоторыми ограничениями. В частности, указатель может быть на любой производный тип, кроме ссылки, функция не может возвращать массив и функцию, массив не может состоять из функций и из ссылок, указатель на member не может быть на ссылку, ссылка тоже не может быть на ссылку.
Массивы, функции и ссылки не могут иметь собственной cv-квалификации, то есть, в частности, не могут быть
const
.Таковыми могут быть только указатели и указатели на member'ы.
Поскольку оба вида указателя обозначаются символом
*
слева от сущности, и cv-квалификация задаётся между сущностью и*
, то получается, чтоconst
всегда действует на то, что слева.Например:
Та же самая попытка сослаться на чей-то авторитет.
Мало ли, кто что предпочитает.
Авторитетов не существует.
Александреску -- человек?
Значит, он тоже может ошибаться, вне зависимости от опыта?
И, значит, может иметь иррациональные предпочтения?
Пользуясь тем, что для многих он -- авторитет, и эти самые многие безоговорочно примут его точку зрения, потому что -- это же Александреску так сказал, он может "протолкнуть" не лучшие решения, но -- в пользу своих предпочтений (которые, кстати, со временем имеют свойство меняться).
Человек, потому что, здесь всё понятно.
Именно поэтому могут быть только сухие аргументы.
Никаких ссылок на авторитеты быть не может.
Кстати, вы заметили, что я в этом ответе ни на какой "авторитет" и не подумал даже пытаться ссылаться?
Ещё и внутри составного литерала.