Страницы

Поиск по вопросам

воскресенье, 30 сентября 2018 г.

Что такое NullReferenceException, и как мне исправить код?

Когда я выполняю некоторый код, выбрасывается исключение NullReferenceException со следующим сообщением:
Object reference not set to an instance of an object.
или
В экземпляре объекта не задана ссылка на объект.
Что это значит, и как мне исправить код?


Ответ

Причина
Вкратце
Вы пытаетесь воспользоваться чем-то, что равно null (или Nothing в VB.NET). Это означает, что либо вы присвоили это значение, либо вы ничего не присваивали.
Как и любое другое значение, null может передаваться от объекта к объекту, от метода к методу. Если нечто равно null в методе "А", вполне может быть, что метод "В" передал это значение в метод "А".
Остальная часть статьи описывает происходящее в деталях и перечисляет распространённые ошибки, которые могут привести к исключению NullReferenceException
Более подробно
Если среда выполнения выбрасывает исключение NullReferenceException, это всегда означает одно: вы пытаетесь воспользоваться ссылкой. И это ссылка не инициализирована (или была инициализирована, но уже не инициализирована).
Это означает, что ссылка равна null, а вы не сможете вызвать методы через ссылку, равную null. В простейшем случае:
string foo = null; foo.ToUpper();
Этот код выбросит исключение NullReferenceException на второй строке, потому что вы не можете вызвать метод ToUpper() у ссылки на string, равной null
Отладка
Как определить источник ошибки? Кроме изучения собственно исключения, которое будет выброшено именно там, где оно произошло, вы можете воспользоваться общими рекомендациями по отладке в Visual Studio: поставьте точки останова в ключевых точках, изучите значения переменных, либо расположив курсор мыши над переменной, либо открыв панели для отладки: Watch, Locals, Autos.
Если вы хотите определить место, где значение ссылки устанавливается или не устанавливается, нажмите правой кнопкой на её имени и выберите "Find All References". Затем вы можете поставить точки останова на каждой найденной строке и запустить приложение в режиме отладки. Каждый раз, когда отладкик остановится на точке останова, вы можете удостовериться, что значение верное.
Следя за ходом выполнения программы, вы придёте к месту, где значение ссылки не должно быть null, и определите, почему не присвоено верное значение.
Примеры
Несколько общих примеров, в которых возникает исключение.
Цепочка
ref1.ref2.ref3.member
Если ref1, ref2 или ref3 равно null, вы получите NullReferenceException. Для решения проблемы и определения, что именно равно null, вы можете переписать выражение более простым способом:
var r1 = ref1; var r2 = r1.ref2; var r3 = r2.ref3; r3.member
Например, в цепочке HttpContext.Current.User.Identity.Name, значение может отсутствовать и у HttpContext.Current, и у User, и у Identity
Неявно
public class Person { public int Age { get; set; } } public class Book { public Person Author { get; set; } } public class Example { public void Foo() { Book b1 = new Book(); int authorAge = b1.Author.Age; // Свойство Author не было инициализировано // нет Person, у которого можно вычислить Age. } }
То же верно для вложенных инициализаторов:
Book b1 = new Book { Author = { Age = 45 } };
Несмотря на использование ключевого слова new, создаётся только экземпляр класса Book, но экземпляр Person не создаётся, поэтому свойство Author остаётся null
Массив
int[] numbers = null; int n = numbers[0]; // numbers = null. Нет массива, чтобы получить элемент по индексу
Элементы массива
Person[] people = new Person[5]; people[0].Age = 20; // people[0] = null. Массив создаётся, но не // инициализируется. Нет Person, у которого можно задать Age.
Массив массивов
long[][] array = new long[1][]; array[0][0] = 3; // = null, потому что инициализировано только первое измерение. // Сначала выполните array[0] = new long[2].
Collection/List/Dictionary
Dictionary agesForNames = null; int age = agesForNames["Bob"]; // agesForNames = null. // Экземпляр словаря не создан.
LINQ
public class Person { public string Name { get; set; } } var people = new List(); people.Add(null); var names = from p in people select p.Name; string firstName = names.First(); // Исключение бросается здесь, хотя создаётся // строкой выше. p = null, потому что // первый добавленный элемент = null.
События
public class Demo { public event EventHandler StateChanged;
protected virtual void OnStateChanged(EventArgs e) { StateChanged(this, e); // Здесь бросится исключение, если на // событие StateChanged никто не подписался } }
Неудачное именование переменных
Если бы в коде ниже у локальных переменных и полей были разные имена, вы бы обнаружили, что поле не было инициализировано:
public class Form1 { private Customer customer;
private void Form1_Load(object sender, EventArgs e) { Customer customer = new Customer(); customer.Name = "John"; }
private void Button_Click(object sender, EventArgs e) { MessageBox.Show(customer.Name); } }
Можно избежать проблемы, если использовать префикс для полей:
private Customer _customer;
Цикл жизни страницы ASP.NET
public partial class Issues_Edit : System.Web.UI.Page { protected TestIssue myIssue;
protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { // Выполняется только на первой загрузке, но не когда нажата кнопка myIssue = new TestIssue(); } }
protected void SaveButton_Click(object sender, EventArgs e) { myIssue.Entry = "NullReferenceException здесь!"; } }
Сессии ASP.NET
// Если сессионная переменная "FirstName" ещё не была задана, // то эта строка бросит NullReferenceException. string firstName = Session["FirstName"].ToString();
Пустые вью-модели ASP.NET MVC
Если вы возвращаете пустую модель (или свойство модели) в контроллере, то вью бросит исключение при попытке доступа к ней:
// Controller public class Restaurant:Controller { public ActionResult Search() { return View(); // Модель не задана. } }
// Razor view @foreach (var restaurantSearch in Model.RestaurantSearch) // Исключение. { }

Способы избежать
Явно проверять на null, пропускать код
Если вы ожидаете, что ссылка в некоторых случаях будет равна null, вы можете явно проверить на это значение перед доступом к членам экземпляра:
void PrintName(Person p) { if (p != null) { Console.WriteLine(p.Name); } }
Явно проверять на null, использовать значение по умолчанию
Методы могут возвращать null, например, если не найден требуемый экземпляр. В этом случае вы можете вернуть значение по умолчанию:
string GetCategory(Book b) { if (b == null) return "Unknown"; return b.Category; }
Явно проверять на null, выбрасывать своё исключение
Вы также можете бросать своё исключение, чтобы позже его поймать:
string GetCategory(string bookTitle) { var book = library.FindBook(bookTitle); // Может вернуть null if (book == null) throw new BookNotFoundException(bookTitle); // Ваше исключение return book.Category; }
Использовать Debug.Assert для проверки на null для обнаружения ошибки до бросания исключения
Если во время разработки вы знаете, что метод может, но вообще-то не должен возвращать null, вы можете воспользоваться Debug.Assert для быстрого обнаружения ошибки:
string GetTitle(int knownBookID) { // Вы знаете, что метод не должен возвращать null var book = library.GetBook(knownBookID);
// Исключение будет выброшено сейчас, а не в конце метода. Debug.Assert(book != null, "Library didn't return a book for known book ID.");
// Остальной код...
return book.Title; // Не выбросит NullReferenceException в режиме отладки. }
Однако эта проверка не будет работать в релизной сборке, и вы снова получите NullReferenceException, если book == null
Использовать GetValueOrDefault() для Nullable типов
DateTime? appointment = null; Console.WriteLine(appointment.GetValueOrDefault(DateTime.Now)); // Отобразит значение по умолчанию, потому что appointment = null.
appointment = new DateTime(2022, 10, 20); Console.WriteLine(appointment.GetValueOrDefault(DateTime.Now)); // Отобразит дату, а не значение по умолчанию.
Использовать оператор ?? (C#) или If() (VB)
Краткая запись для задания значения по умолчанию:
IService CreateService(ILogger log, Int32? frobPowerLevel) { var serviceImpl = new MyService(log ?? NullLog.Instance); serviceImpl.FrobPowerLevel = frobPowerLevel ?? 5; }
Использовать операторы ?. и ?[ (C# 6+, VB.NET 14+):
Это оператор безопасного доступа к членам, также известный как оператор Элвиса за специфическую форму. Если выражение слева от оператора равно null, то правая часть игнорируется, и результатом считается null. Например:
var title = person.Title.ToUpper();
Если свойство Title равно null, то будет брошено исключение, потому что это попытка вызвать метод ToUpper на значении, равном null. В C# 5 и ниже можно добавить проверку:
var title = person.Title == null ? null : person.Title.ToUpper();
Теперь вместо бросания исключения переменной title будет присвоено null. В C# 6 был добавлен более короткий синтаксис:
var title = person.Title?.ToUpper();
Разумеется, если переменная person может быть равна null, то надо проверять и её. Также можно использовать операторы ?. и ?? вместе, чтобы предоставить значение по умолчанию:
// обычная проверка на null int titleLength = 0; if (title != null) titleLength = title.Length;
// совмещаем операторы `?.` и `??` int titleLength = title?.Length ?? 0;
Если любой член в цепочке может быть null, то можно полностью обезопасить себя (хотя, конечно, архитектуру стоит поставить под сомнение):
int firstCustomerOrderCount = customers?[0]?.Orders?.Count() ?? 0;

Книги и учебные ресурсы по JavaScript

Рекомендуемая литература, учебники и документация по JavaScript.
Формат:
Имя Фамилия — «Название книги целиком и полностью», ГГГГ г..
Формат для переведенной литературы:
Имя Фамилия (Name Surname) — «Название книги целиком и полностью», ГГГГ перевода ("Full name of the book", YYYY )
Не создавайте новых ответов — редактируйте этот. Старайтесь сохранять разделение по категориям (когда они будут). Сохраняйте алфавитную (лексикографическую) сортировку по названию книги. Пожалуйста,
не добавляйте сюда видеолекции и интерактивные курсы, не размещайте ссылки на нелегальный контент вроде торрент-трекеров.

Данный перечень входит в поддерживаемый сообществом Сборник учебных ресурсов по программированию


Ответ

JavaScript
Douglas Crockford — «JavaScript: The Good Parts», 2008 (Не рекомендуется новичкам в JS) Marijn Haverbeke — «Eloquent JavaScript: A Modern Introduction to Programming», 2014 Addy Osmani — «Learning JavaScript Design Patterns», 2012 (Не рекомендуется новичкам в программировании) Стоян Стефанов — «JavaScript. Шаблоны», 2011 Дэвид Флэнаган — «JavaScript. Подробное руководство», 2012 (Допустим для новичков в программировании, но ориентирован не на них) Дэвид Херман — «Сила JavaScript. 68 способов эффективного использования JS», 2013 Эрик Фримен, Элизабет Робсон — «Изучаем программирование на JavaScript», 2015 (Подойдет для начинающих) Джон Резиг, Беэр Бибо — «Секреты JavaScript ниндзя», 2015 (Подойдет для более продвинутых) Илья Кантор — «Современный учебник Javascript» (В основном ориентирован на работу online, но есть и pdf/epub версия. Достаточно дружелюбен для новичков) Mozilla Developer Network — раздел о JavaScript (Хороший онлайн справочник по языку, но есть и учебник) Cпецификация (формальное описание синтаксиса, базовых объектов и алгоритмов) языка Javascript - называется ECMAScript. Николас Закас - JavaScript для профессиональных веб-разработчиков (3-е изд.) - 2015. 960 стр. Несмотря на название - для новичков самое-то. Шикарная книга. Николас Закас - ECMAScript 6 для разработчиков - 2017. Книга расскажет о всех изменениях в JS, внесенных ECMAScript 6. (не для новичков).
jQuery
Бер Бибо, Иегуда Кац — «jQuery. Подробное руководство по продвинутому JavaScript», 2011

Как оформить “свободную лицензию” на исходный код ?

Написал программу сортировки (версия сортировки слиянием с "маленькой" дополнительной памятью), исходный текст которой хочу опубликовать в сети. В частности обсудить ее здесь. Желательно, чтобы все могли ее использовать, переделывать и т.д. и никто не смог в дальнейшем запретить остальным делать это. В общем свободный source. Читал (может не очень вдумчиво, слов всюду много) об этом, но так и на понял, какой комментарий надо поместить в начало файла с исходником и нужны ли какие-то еще действия с моей стороны. Прошу помощи. Желательно конкретный "шаблон", а не ссылки на общие указания. UPDATE 1 Всем спасибо за ответы. Как Вы считаете, следующий текст в начале исходника - это то что нужно ? /* Copyright (C) 2012 Vasily Anishchenko This file is part of the Yamsort. (Yet Another Merge Sort Routines)
Yamsort is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
Yamsort is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with Yamsort. If not, see .
(Этот файл — часть Yamsort.
Yamsort - свободная программа: вы можете перераспространять ее и/или изменять ее на условиях Стандартной общественной лицензии GNU в том виде, в каком она была опубликована Фондом свободного программного обеспечения; либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.
Yamsort распространяется в надежде, что она будет полезной, но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Стандартной общественной лицензии GNU.
Вы должны были получить копию Стандартной общественной лицензии GNU вместе с этой программой. Если это не так, см. .)
*/ Может быть еще что-либо нужно (и где ?) добавлять ?


Ответ

Возможно вам пригодится этот большой, может быть даже полный список всевозможных свободных лицензий. И плюс вам за бескорыстие! ---------------------------------- UPDATE 10.03.2012 18:07 -------------------------------------- @Sh4dow, @GLmonster прав насчет GPL - она обязывает раскрывать код проекта, в котором используется компонент, выпущенный под ней, фактически исключая тем самым возможность использования в проприетарном ПО. @avp - GPL больше приспособлена для какого-то идеального утопического мира, имеющим слабое отношение к нашим реалиям. Более демократичны в этом плане, например, BSD и MIT - они допускают использование в коммерческом ПО, способствуя этим увеличению сфер применения, количеству использования и, не исключено, инвестиций. Проприетарность не всегда зло. п.с. это сообщение писалось как комментарий к высказыванию @GLmonster, но у меня что-то не получилось его отправить - он просто пропал, посему пишу как ответ.

И станет 3 бесконечностью

Посмотрим на классический цикл (с пустым телом), который обычно выполняет 3 итерации:
for (int x=0; x<3; ++x);
Задача заключается в том, чтобы сделать его бесконечным.

Соревнование завершено, но ответы публиковать можно
Длительность соревнования 2 недели, начало отсчёта после окончания Код-гольф - Реализация алгоритма выборки комбинаций
Опубликовал это соревнование на codegolf'е.
При оформлении ответа рекомендуется язык делать заголовком (# в начале строки).
Оценка
Побеждает ответ с наибольшим рейтингом. Один участник может публиковать любое число ответов, если их идея или язык различаются.
Голосующих просьба учесть, что система SO откатит 3 и более минуса, поставленные одному участнику (но не 3 плюса). Больше 3 плюсов одному участнику за день ставить тоже не стоит. Лучше вернитесь завтра и доставьте :)
Решения, нарушающие условия помечаются внеконкурсными с описанием, какое требование они нарушают. Они могут быть по-своему интересны, поэтому удалять их не обязательно.
Подробные условия
Можно использовать любой язык в котором поддерживается подобная форма циклов. В чистой программе на этом языке тип данных должен раскрываться в целочисленный или числовой (если в языке поддерживается типизация), а цикл выполняться 3 раза. Использование пользовательских типов недопустимо.
for (int x=0; x<3; ++x); // C, C++, C# for (var x=0; x<3; ++x); // C#, Javascript for (auto x=0; x<3; ++x); // C, C++ for (auto signed x=0; x<3; ++x); // C, C++ for (register int x=0; x<3; ++x); // C, C++ В дополненной программе цикл должен быть бесконечным, а все ограничения кроме сохранения этого фрагмента кода снимаются. Участок с циклом должен быть одинаковым в чистой и дополненных программах. for должен остаться циклом. Более того, он должен сам стать бесконечным. Просто обернуть его во внешний бесконечный цикл нельзя. Тело цикла не должно менять значение переменной. Программа должна работать аналогичным образом с пустым телом цикла.

Кстати, есть 3 мои решения (на C#, C++ и Javascript), использующие совершенно разные подходы. Если никто не предложит такие варианты в течение недели с момента начала отсчёта, я их опубликую. А пока только это:
oCrfu6vbxewj8k8eMw9TDog83T
Первое (на Си++) нашёл @pavel: https://ru.stackoverflow.com/a/587588/178988 Моя версия: http://ideone.com/xgTeCr
Второе (на C#) нашёл @VladD: https://ru.stackoverflow.com/a/587673/178988 Моя версия: http://ideone.com/8bkwoD
Третье (на JS) не написали, публикую: https://ru.stackoverflow.com/a/593174/178988 Фиддл: https://jsfiddle.net/893f6vw8/ Но есть 3 решения с похожей идеей. Подробнее в ответе.

Результаты
Первое место с 24 голосами занимает решение пользователя @soon на Си/Си++:
#define x x,y Второе место с отставанием всего в 1 голос занимает решение пользователя @pavel на Си++. Интересно, что это решение было одним из трёх анонсированных.
#define int bool Третье место с 20 голосами получает решение пользователя @Mike на Перле
в котором константа 3 переопределяется значением бесконечнось

PS: Напоминаю про чат


Ответ

По аналогии с @Qwertiy, но работает еще и в Си
#define x x,y
int main() { for (int x=0; x<3; ++x); return 0; }
Развернется в
int main(void) { for (int x,y=0; x,y<3; ++x,y); return 0; }
y инициализируется нулем и не меняется в теле цикла, поэтому всегда <3

Как писать красивый и читаемый код?

Максимальное сокращение кода. Бывает желания максимально сократить код, но появляются сомнения, не перебор ли это. Ведь можно было бы отдельно объявить переменную и ей уже присвоить выражение, которое без переменной не сразу понятно что делает. Именование переменных. Проблемы с выбором имени для переменной. Если описывать полностью, что она делает, то будет около 16 символов, а если сокращать, то может конфликтовать с пониманием других переменных. Контекст тоже не всегда спасает. Разделение действий. Слишком много всего в одной части кода, что может усложнить понимание. С одной стороны можно было бы вынести в отдельные функции, а с другой стороны нет смысла, так как используются только в той части кода. Уместность комментариев. Свой код всегда понятен, а предположить как его понимают другие, будет не всегда объективно. Так что, не всегда очевидно, нужен комментарий или он будет избыточен. Форматирование. С большой или маленькой буквы начинать переменную, а также в пределах переменной нужно ли большими буквами начинать слова или же разделять их с помощью "_". Делать ли пустую строку между if и другими блоками, а также между переменными и этими блоками или же расставлять по действиям. Делать ли отступы внутри выражений, к примеру if( sometring ) или if(sometring). 6...
Эти пункты только для понимания проблемы, потому что многие забыл, а многие и не замечал, думаю. А также на эти пункты с одной стороны очень легко ответить, а с другой хотелось бы правил или детального разбора. Так вот, есть ли правила гласные/негласные, в которых бы описывалось это и многое другое в этом направлении? Или есть книги посвященные этой теме?


Ответ

Вопрос стиля — на самом деле очень серьёзный вопрос.
Не забывайте, что код пишется вами не для компилятора. Сделать, чтобы было понятно компилятору просто, но ваша цель сложнее: сделать так, чтобы было понятно человеку
Код программы — то же самое литературное произведение. Вы должны донести мысль читателю, причём этим читателем можете оказаться как вы сами через полгода, так и ваш коллега, которому придётся править код пока вы в отпуске.
Хороший код живёт долго, а значит, он будет прочитан, подправлен, понят и объяснён много раз. Понимание чужого кода гораздо сложнее, чем написание нового с нуля, поэтому инвестировать в понятность кода важно (если, конечно, вы желаете своему коду долгой счастливой жизни).
Сокращение кода не нужно. Код должен быть понятным, не больше и не меньше.
Если вам кажется, что где-то нужно расписать для наглядности — так и сделайте, даже пусть придётся вводить дополнительные переменные только чтобы дать имя промежуточному результату. Если, наоборот, вам кажется, что кода слишком много для той простой штуки, которую он делает — вынесите эту штуку в отдельную функцию и придумайте ей правильное название, чтобы пояснить смысл действия. Поддерживайте однородность и общий темп: если кусок кода запускает космическую ракету в полёт, то кусок кода рядом с ним, который читает данные из конфигурационного файла, смотрится нелепо.
Именование переменных и функций. Не жалейте букв! Байты на винчестере подешевели. Вы не должны писать комментарии, чтобы пояснить смысл переменной, иначе читатель должен видеть одно (имя переменной), а в уме держать другое (её смысл). С другой стороны, не надоедайте читателю излишними подробностями. То, что у вас не просто acceptableByteCount, а countOfBytesWhichDidNotPassAtLeastTwoFilters, — скучно. Соблюдайте разумный баланс. Если вам нужна переменная цикла, назовите её i или j. Придерживайтесь общепринятых соглашений, если нужно, придумывайте свои (но разумные!), легко понятные другим.
Именуйте переменные правильно. Название должно отображать смысл. Если вы используете одну и ту же переменную в двух разных смыслах, вы делаете неправильно, разделите её на две переменные с однозначным смыслом. Например, не стоит совмещать длину переданной строки и счётчик оставшихся символов для обработки, хотя начальное значение счётчика и совпадает с длиной строки.
Старайтесь, чтобы текст читался естественно. Например, имя действия должно бы быть глаголом (не vector.Normalization(), а vector.Normalize() или vector.GetNormal()). Имя булевого условия должно быть похоже на условие и скорее всего начинаться с is, has и тому подобного. (Например: hasChanges(), isPrime() и т. п.) Ради бога, используйте английские имена, а не русские транслитом! Поверьте, isZarplataComputed() смотрится ужасно. Исключение — языки с кириллическим синтаксисом (?) или общепринятый стиль команды.
Разделение действий. Да, имеет смысл отделять код в функцию только для того, чтобы правильно назвать этот фрагмент кода. Функции существуют не для повторного использования! Функции нужны для логического разбиения кода на части. Если вы видите, что ваша функция ответственна за разные вещи, и вы не можете придумать ей короткого точного названия, значит, ваша функция делает чересчур много, и её надо разделить. Часто из супердлинной функции в 500 строк получается десяток классов. И это хорошо.
И да, читателю гораздо лучше понять функцию, которая делает одну простую задачу. Если функция делает слишком много, у неё кроме более сложного кода более сложные пред- и постусловия (а значит, её ещё сложнее понять).
Для хорошего разбиения на части проектируйте сверху вниз. Пример: приготовить еду — это что? Решаем, что это значит приготовить французский завтрак. Окей, а что такое приготовить французский завтрак? Это купить круассаны и сварить кофе. А что такое сварить кофе? Это смолоть зёрна в кофемолке, засыпать в турку, добавить воды, поместить на огонь, и т. д. Это естественным образом оформляется в процедуры PrepareMeals, PrepareFrenchBreakfast, BuyCroissants, MakeCoffee. Ничего выдумывать не пришлось.
Уместность комментариев. Старайтесь писать код так, чтобы комментарии не были нужны. Очень часто комментарии избыточны; очень часто они устаревают и перестают отображать действительность. Люди часто меняют логику кода, но забывают пробежаться и по комментариям.
Не забывайте, что выполняется код, а не комментарии. Поэтому ошибочный, вводящий в заблуждение комментарий (например: /* здесь не может получиться NULL */) гораздо хуже его отсутствия. Если у вас есть кусок кода, снабжённый комментарием, объясняющим, что он делает, превратите этот код в функцию, а комментарий — в её имя. Полностью избежать комментариев, вероятно, не удастся, но постарайтесь, чтобы комментарии описывали, почему вы делаете так, как делаете, а что именно вы делаете, должно быть понятно из кода.
Форматирование. Плохое форматирование очень сильно влияет на читаемость кода. Выработайте стиль и придерживайтесь его. Какой именно стиль вы выберете, в общем-то и не важно, главное, чтобы он был логичен и последователен. (Например, если вы ставите пробел после while, наверное стоит ставить пробел и после if.) Старайтесь, тем не менее, не отступать от общепринятых соглашений (например, имена методов в Java принято выбирать в lowerCamelCase), иначе вам трудно будет читать чужой код.
Если вы работаете в команде, не нарушайте общий стиль, даже если вам персонально он не нравится. Если в команде нету общепринятого стиля, предложите его! Расстановка скобок, отступы, пробелы, максимальная длина строк и всё такое важны, чтобы читатель не отвлекался. Непоследовательное форматирование сбивает с толку и отвлекает гораздо больше, чем это кажется — точно так же, как неправильная пунктуация мешает правильно понимать текст литературного произведения.

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

Остаток для негативного аргумента ошибочен?

Во многих языках программирования (C, C++, C#, Java, различные диалекты Паскаля, PHP, Javascript) есть оператор вычисления остатка. Он действует очевидным, единственно верным образом для положительных значений аргумента (17 % 5 == 2), но для отрицательного делимого и положительного делителя он даёт отрицательный результат:
-17 % 5 == -2
Обычное применение оператора %, однако — для вычисления хэшей, индексов в кольцевом буфере, а также вычисление канонического представителя группы чисел, то есть, для представления отношения эквивалентности. Например, номер дня недели естественным образом вычисляется как остаток от деления «глобального» номера дня на 7. Проверка числа на нечётность также определяется остатком при делении на 2.
Однако, оператор % в том виде, как он реализован в упомянутых языках, непригоден без дополнительной обработки: нужна функция наподобие
int mod(int n, int d) { int result = n % d; if (sign(result) * sign(d) < 0) result += d; return result; }
которая обеспечивает положительный результат при положительном делителе.
У такой функции, в отличие от %, есть полезный инвариант:
mod(n + k * d, d) == mod(n, d)
(при условии, что вычисление обеих частей не приводят к переполнению).
Приведу несколько примеров.
1) Проверка на нечётность обычно выглядит так:
//bool odd = n % 2 == 1; // неправильно! bool odd = n % 2 != 0; // довольно искусственно
Но с новым оператором она работает проще:
bool odd = mod(n, 2) == 1; // как и ожидается.
2) Для вычисления bucket'а в хэш-таблице тоже применяется остаток:
int bucketIdx = object.GetHashCode() % buckets.Count; if (bucketIdx < 0) bucketIdx += buckets.Count;
или так (код из .NET 4.0)
int positiveHashCode = object.GetHashCode() & 7FFFFFFF; int bucketIdx = positiveHashCode % buckets.Count;
В то же время
int bucketIdx = mod(object.GetHashCode(), buckets.Count);
сработал бы в любом случае.
3) Приведение угла в градусах к каноническому виду (от 0 до 360):
mod(angle, 360)
В радианах: mod(angle, 2*PI)
То же самое с % выглядит гораздо более тяжеловесно.
Внимание, вопрос: Нужен ли кому-то оператор % в том виде, как он определён в языке? Не лучше было бы, чтобы оператор % возвращал значение как у mod? Я предполагаю, что всякий раз, когда используется оператор %, на самом деле имеется в виду именно mod, и либо входные аргументы всегда положительны, либо используется поправка при отрицательном делимом, либо программа содержит баг.
Есть ли у кого-то примеры (из практики или теоретические), когда нужно именно существующее поведение оператора %?

Двое отвечающих справедливо замечают, что частное q и остаток r при делении n на d должны удовлетворять условию
n == q * d + r
Для того, чтобы это работало, можно переопределить и деление так, чтобы округление выполнялось всегда вниз: q == floor((double)n / (double)d). При этом нужное соотношение будет автоматически выполняться:
4 / 3 == 1 4 % 3 == 1 4 = 1 * 3 + 1 3 / 3 == 1 3 % 3 == 0 3 = 1 * 3 + 0 2 / 3 == 0 2 % 3 == 2 2 = 0 * 3 + 2 1 / 3 == 0 1 % 3 == 1 1 = 0 * 3 + 1 0 / 3 == 0 0 % 3 == 0 0 = 0 * 3 + 0 -1 / 3 == -1 -1 % 3 == 2 -1 = -1 * 3 + 2 -2 / 3 == -1 -2 % 3 == 1 -2 = -1 * 3 + 1
и т. д.


Ответ

Вопрос мне кажется находится скорее в области определения нежели логики. Если окунуться в историю вопроса то делимое и остаток от деления относятся к известной теореме в теории чисел, о том что любое целое число можно разложить на делимое и остаток от деления - теорема доказанная еще во времена Эвклида. Беда только с тем, что во времена Эвклида люди не знали отрицательных чисел. Если одно из чисел - делитель или делимое отрицательно, то задача будет иметь 2 решения - с отрицательным и положительным остатком. Вот тут то и возникает момент договоренности/определения. В русском есть 2-значное толкование, а вот в английском языке остаток обозначается 2-мя терминами: Remainder - остаток от деления (может быть и отрицательным) Residual - буквально осадок (всегда положительный) Так что в Java и проч. языках используется деление по модулю в смысле Remainder, а то что предлагает @VladD - это Residual P.S. Как вычисляется деление по модулю в разных языках можно посмотреть здесь - лишний раз свидетельствует в пользу того, что вопрос не в логике, а в договоренности.

Hello world с пустой функцией main

Столкнулся с такой задачей на собеседовании:
Написать программу, которая печатает в консоли фразу Hello world
И всё бы ничего, если бы после этого стояла точка. Но фраза продолжается:
при условии, что функция main() данной программы выглядит следующим образом:
int main() { return 0; }


Ответ

Ответ очень прост. Их даже несколько. Самый простой, основанный на свойстве глобальных статических объектов. int printHelloWorld() { printf("Hello World"); return 0; } static int testVar = printHelloWorld(); int main () { return 0; } Это, если С стиль. С++ стиль: создаем класс, в конструкторе добавляем вывод, будет вывод до main, в деструкторе - после.