Я видел множество определений похожих на такие:
#define bit_0 1 #define bit_1 2 #define bit_2 4 и так далее...
И вроде бы очевидно, вот вам слова, каждое из которых обозначает число, в котором выставлен один бит. Всё хорошо, только число, в котором установлен один единственный бит записывается в си стандартным образом (как в прочем и во многих других языках это делает аналогично):
1 << bit
И с первого взгляда, особенно если это взглад человека только начавшего изучать язык программирования, запись bit_14
смотрится куда понятнее, чем (1 <
< 14)
, да ещё и короче в записи на целых 3 символа (или на 1, если убрать пробелы, но я их оставил, чтобы подразнить читателя на едкое замечание).
Аргумент работает только потому, что определения приближают вид программы к разговорному языку, однако его несостоятельность в том, что опладение языком состоит не в том, чтобы быстро с него переводить на родной (или другой хорошо знакомый), а научиться думать в нём. Перестать анализировать каждый символ, и научиться воспринимать целиком конструкции языка. И тогда запись в стандартных конструкциях языка будет восприниматься как эталон, а подобные переопределения — как диалектизмы.
Есть простая и очевидная запись для установки, сброса и инвертирования одного бита в переменной:
var |= 1 << bit_to_set; var &= ~(1 << bit_ro_clear); var ^= 1 << bit_to_invert;
Я знаю, что многие любят определять вместо них макросы:
#define bit_set(var, bit) ((var) |= 1<<(bit))
Или даже:
#define bit_0_set(x) ((var) |= 1)
Это по сути тоже диалектизм, который к тому же захламляет программу новыми определениями и ставит её в зависимость от дополнительных заголовков.
Очевидной идиомой в си для цикла со счётчиком является всем известный
for(init; limit; advance)
Эта запись во многом эквивалентна записи
init; while(limit) { ... advance; }
Единственная разница с точки зрения машины в том, что применение continue
внутри цикла while
пропускает часть advance
. а внутри for
— нет. Но речь здесь не про машинный фактор, а про человеческий. Использование for
служит сигналом того, цикл — со счётчиком, а while
— любой другой. И этого стоит придерживаться, пока нет серьёзных (очень серьёзных) аргументов против.
Также не следует прятать изменение счётчика внутрь тела, как бы сладко не смотрелись пре- и постмодификации.
Ответ на этот вопрос требует скорее квалификации психолога или социолога, нежели программиста. Тем не менее можно выделить некоторые характерные моменты.
Весьма характерной причиной для многих (даже весьма опытных) программистов является стремление приблизить текст программы к языку общения, как правило к английскому, реже к родному. Это выражается в том, что программист делает определения многих идиом под именами на естественном языке (примеры приведены как раз в «Одном единственном бите...»). Фактически это превращает язык программирования в некий суржик, где все непонятные или трудно запоминаемые выражения заменены таковыми из другого языка. Проблема такого суржикостроения заключается в том, что для его понимания требуется уже знание не одного языка, а целых двух
Реже встречаются попытки перемешать один язык программирования с другим. Комический пример языкового суржика не раз гулял по Сети. Вот его отрывок:
#define begin { #define end }
Здесь человек просто «перевёл си в паскаль». Это конечно может на каком-то этапе помочь освоить новый язык, проведя аналогии с уже известным. Однако, как только этап ознакомления окончится это будет тормозить дальнейшее освоение, так как наработанная привычка мыслить по аналогии рано или поздно наткнётся на конструкции, не имеющие аналогий.
Во многом подобные процессы поддерживаются программисты с малым стажем либо теми, кто просто не желает углубляться в тонкости того или иного языка программирования. Такие люди мыслят как иностранцы, всячески избегая сложных конструкций и, более того, подталкивая к этому других («неужели нельзя писать понятно?»).
Произвольная начальная часть идиом опущена в целях упорядочивания по алфавиту. В тексте определения ссылка на переменную часть принимается по умолчанию при отсутствии прямого указания, либо даётся словом определённый. Так в определении идиомы * sizeof(type)
в выражении массив из определённого числа элементов подразумевается, что размер стоит перед знаком звёздочки (n * sizeof
…), а в идиоме |= mask
в словах инверсия битов переменной подразумевается, что переменная стоит перед знаком операции: (var |=
…).
Если определение идиомы представляет собой действие (установка, сброс, завершение, присвоение и т. п.), это означает, что действие совершается в некоторый момент времени — компиляции, либо исполнения (являясь побочным эффектом). Если же определение представляет некую предметную сущность (величину, переменную, тип, токен, выражение), то данная сущность — предмет подстановки (может быть заменено на другой фрагмент программы с сопоставимым результатом), результат вычисления (используемый в качестве аргумента функции, операции, параметра определения).
&= ~mask
&= ~(1<
<n)
* sizeof(type)
ptrdiff_t
), между элементами с некоторой разницей индексов.
/ sizeof(type)
^= mask
^= 1<
<n
|= mask
|= 1<
<n
~0
1 <
< n
assert(!"Message")
assert(cond || !"Message")
const argument
const
.
for(init; limit; advance)
for(;;)
if(cond) return errorcode;
if(cond) {…; return errorcode;}
cond
с выдачей кода ошибки (может быть 0 / NULL, в случае возврата указателя).
return (errno = errorcode), failresult
sizeof(array)/sizeof(*array)
array''..(если массив определён с этим количеством). Следует отметить, что аргумент должен быть именно массивом, не сведённым к указателю (то есть проверить аргумент функции не удастся).