Подтвердить что ты не робот

Именованные числа как переменные

Я видел это несколько раз недавно в высоком профиле, где константные значения определяются как переменные, названные в честь значения, а затем используются только один раз. Я задавался вопросом, почему это делается?

например. Источник Linux (resize.c)

unsigned five = 5;
unsigned seven = 7;

например. Источник С#.NET(Quaternion.cs)

double zero = 0;
double one = 1;
4b9b3361

Ответ 1

Именование номеров - ужасная практика, в один прекрасный день что-то нужно будет изменить, и вы получите unsigned five = 7.

Если это имеет какое-то значение, придайте ему значащее имя. "Магическое число" five не улучшает магическое число 5, это хуже, потому что оно не может фактически равняться 5.

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

Ответ 2

Хорошо обозначенные переменные

Предоставление собственных имен переменным может значительно прояснить код, например

constant int MAXIMUM_PRESSURE_VALUE=2;

Это дает два основных преимущества:

  • Значение MAXIMUM_PRESSURE_VALUE может использоваться во многих разных местах, если по какой-либо причине изменение этого значения необходимо изменить только в одном месте.

  • В случае использования немедленно показывает, что делает функция, например, следующий код, очевидно, проверяет, опасно ли давление:

    if (pressure>MAXIMUM_PRESSURE_VALUE){
        //without me telling you you can guess there'll be some safety protection in here
    }
    

Плохо названные переменные

Тем не менее, у всех есть аргумент счетчика, и то, что вы показали, выглядит очень хорошо, если принять задуманное до сих пор, что это не имеет смысла. Определение TWO как 2 не добавляет никакого значения

constant int TWO=2;
  • Значение TWO может использоваться во многих разных местах, возможно, для двойных вещей, возможно, для доступа к индексу. Если в будущем вам нужно будет изменить индекс, который вы не можете просто измените на int TWO=3;, потому что это повлияет на все остальные (полностью несвязанные) способы, которыми вы пользовались ДВА, теперь вы вместо этого удвоения и т.д.
  • Где используется, вы не получите больше информации, чем если бы вы использовали "2". Сравните следующие два кода:

    if (pressure>2){
        //2 might be good, I have no idea what happens here
    }
    

    или

    if (pressure>TWO){
        //TWO means 2, 2 might be good, I still have no idea what happens here
    }
    
  • Хуже все еще (как, кажется, здесь) TWO может не равняться 2, если это так, это форма обфускации, где намерение состоит в том, чтобы сделать код менее ясным: очевидно, он достигает этого.

Обычная причина этого - стандарт кодирования, который запрещает магические числа, но не считает TWO магическим числом; это конечно! 99% времени, когда вы хотите использовать значащее имя переменной, но в том, что 1% времени с использованием TWO вместо 2 ничего не получает (извините, я имею в виду ZERO).

этот код вдохновлен Java, но предназначен для языкового агностического

Ответ 3

Краткая версия:

  • Постоянная five, которая просто держит номер пять, довольно бесполезна. Не обходите это без причины (иногда вы должны из-за синтаксиса или правил ввода).
  • Именованные переменные в Quaternion.cs не являются строго необходимыми, но вы можете сделать так, чтобы код был значительно более читабельным с ними, чем без него.
  • Именованные переменные в ext4/resize.c не являются константами. Они называются счетчиками. Их имена немного затушевывают их функции, но этот код на самом деле делает правильно соответствует специализированным стандартам кодирования проекта.

Что происходит с Quaternion.cs?

Это довольно легко.

Сразу после этого:

double zero = 0;
double one = 1;

Код выполняет следующее:

return zero.GetHashCode() ^ one.GetHashCode();

Без локальных переменных, как выглядит альтернатива?

return 0.0.GetHashCode() ^ 1.0.GetHashCode(); // doubles, not ints!

Какой беспорядок! Читаемость определенно на стороне создания локальных жителей здесь. Более того, я думаю, что явное обозначение переменных указывает "Мы подумали об этом осторожно" гораздо более четко, чем просто написать одно запутанное выражение о возврате.

Что происходит с resize.c?

В случае ext4/resize.c эти числа вообще не являются константами. Если вы будете следовать коду, вы увидите, что они являются счетчиками, и их значения фактически изменяются в течение нескольких итераций цикла while.

Обратите внимание, как они инициализируются:

unsigned three = 1;
unsigned five = 5;
unsigned seven = 7;

Три равны одному, да? Что это значит?

Посмотрите, что на самом деле происходит, что update_backups передает эти переменные по ссылке на функцию ext4_list_backups:

/*
 * Iterate through the groups which hold BACKUP superblock/GDT copies in an
 * ext4 filesystem.  The counters should be initialized to 1, 5, and 7 before
 * calling this for the first time.  In a sparse filesystem it will be the
 * sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ...
 * For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ...
 */
static unsigned ext4_list_backups(struct super_block *sb, unsigned *three,
                                  unsigned *five, unsigned *seven)

Это счетчики, которые сохраняются в течение нескольких вызовов. Если вы посмотрите на тело функции, вы увидите, что он жонглирует счетчиками, чтобы найти следующую мощность 3, 5 или 7, создавая последовательность, которую вы видите в комментарий: 1, 3, 5, 7, 9, 25, 27, и c.

Теперь, для самой странной части: переменная three инициализируется 1, потому что 3 0= 1. Мощность 0 - частный случай, хотя, поскольку он единственный раз 3 x= 5 x= 7 x. Попробуйте свои силы при переписывании ext4_list_backups для работы со всеми тремя счетчиками, инициализированными до 1 (3 0 5 0 7 0), и вы Посмотрим, насколько более громоздким становится код. Иногда проще просто сказать вызывающему, чтобы сделать что-то напуганное (инициализировать список до 1, 5, 7) в комментариях.

Итак, five = 5 хороший стиль кодирования?

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

Однако это один из примеров, когда общие сведения о программировании не отображают полную картину. Взгляните на документ стиля кодирования ядра, особенно в главе по именованию.

ГЛОБАЛЬНЫЕ переменные (которые будут использоваться только в том случае, если они вам действительно нужны) имеют описательные имена, также как и глобальные функции. Если у вас есть функция который подсчитывает количество активных пользователей, вы должны называть это "count_active_users()" или подобное, вы не должны называть его "cntusr()".

...

Локальные имена переменных должны быть короткими и точными. Если у вас есть некоторый случайный счетчик целых чисел, его, вероятно, следует называть "i". Вызов "loop_counter" не является продуктивным, если нет никаких шансов на это будучи неверно понятым. Аналогично, "tmp" может быть примерно любым типом которая используется для хранения временного значения.

Если вы боитесь смешивать имена локальных переменных, у вас есть другой проблема, которая называется синдромом функции-рост-гормон-дисбаланс. См. Главу 6 (Функции).

Частью этого является традиция кодирования в стиле C. Частью этого является целенаправленная социальная инженерия. Много кода ядра - чувствительный материал, и он многократно пересматривался и тестировался. Поскольку Linux - это большой проект с открытым исходным кодом, это не очень больно для вкладов — в большинстве случаев большая проблема заключается в проверке этих вкладов на качество.

Вызов этой переменной five вместо того, чтобы что-то вроде nextPowerOfFive - это способ отговорить вкладчиков от вмешательства в код, который они не понимают. Это попытка заставить вас действительно прочитать код, который вы изменяете подробно, по очереди, прежде чем пытаться внести какие-либо изменения.

Помогли ли разработчики ядра правильное решение? Я не могу сказать. Но это явно целенаправленный ход.

Ответ 4

Моя организация имеет определенные руководящие принципы программирования, одним из которых является использование магических чисел...

например:

if (input == 3) //3 what? Elephants?....3 really is the magic number here...

Это будет изменено на:

#define INPUT_1_VOLTAGE_THRESHOLD 3u 
if (input == INPUT_1_VOLTAGE_THRESHOLD) //Not elephants :(

У нас также есть исходный файл с -200,000 → 200,000 #defined в формате:

#define MINUS_TWO_ZERO_ZERO_ZERO_ZERO_ZERO -200000

который можно использовать вместо магических чисел, например, при ссылке на определенный индекс массива.

Я предполагаю, что это было сделано для "удобочитаемости".

Ответ 5

Числа 0, 1,... являются целыми числами. Здесь "именованные переменные" дают целое число другого типа. Возможно, было бы разумнее указать эти константы (const unsigned five = 5;)

Ответ 6

Я использовал что-то похожее на то, что пару раз записывал значения в файлы:

const int32_t zero = 0 ;

fwrite( &zero, sizeof(zero), 1, myfile );

fwrite принимает указатель const, но если какой-либо функции требуется указатель не const, вы в конечном итоге используете не const-переменную.

P.S.: Это всегда заставляет меня задаваться вопросом, что может быть размером нуля.

Ответ 7

Как вы пришли к заключению, что он используется только один раз? Он является общедоступным, его можно использовать любое количество раз из любой сборки.

public static readonly Quaternion Zero = new Quaternion();
public static readonly Quaternion One = new Quaternion(1.0f, 1.0f, 1.0f, 1.0f);

То же самое относится к классу .Net framework decimal. который также предоставляет публичные константы, подобные этому.

public const decimal One = 1m;
public const decimal Zero = 0m;

Ответ 8

Числам часто присваивается имя, когда эти числа имеют особое значение.

Например, в случае кватернионов тождественный кватернион и кватернион единичной длины имеют особое значение и часто используются в специальном контексте. А именно, кватернион с (0,0,0,1) является тождественным кватернионом, поэтому обычной практикой является их определение вместо использования магических чисел.

Например

// define as static 
static Quaternion Identity = new Quaternion(0,0,0,1);


Quaternion Q1 = Quaternion.Identity;
//or 
if ( Q1.Length == Unit ) // not considering floating point error

Ответ 9

Одно из моих первых заданий программирования было на PDP 11 с использованием Basic. Основной интерпретатор выделял память для каждого необходимого номера, поэтому каждый раз, когда упомянутая программа 0, байт или два будут использоваться для хранения номера 0. Конечно, в те дни память была намного более ограниченной, чем сегодня, и поэтому было важно для сохранения.

Каждая программа в этом рабочем месте начиналась с:

10  U0%=0
20  U1%=1

То есть для тех, кто забыл свой Basic:

Line number 10: create an integer variable called U0 and assign it the number 0
Line number 20: create an integer variable called U1 and assign it the number 1

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

Ааааа, добрые старые времена!

Ответ 10

несколько раз это более читаемо для записи:

double pi=3.14; //Constant or even not constant
...
CircleArea=pi*r*r;

вместо:

CircleArea=3.14*r*r;

и, возможно, вы снова будете использовать pi (вы не уверены, но считаете, что это возможно позже или в других классах, если они являются общедоступными)

а затем, если вы хотите изменить pi=3.14 на pi=3.141596, это проще.

и некоторые другие, такие как e=2.71, Avogadro и т.д.