1) Бесконечный цикл
Все мы знаем, что в компьютере многое может произойти по принципу «Оно само». Где-то будет скачек напряжения или магнитные наводки. Поэтому легко в нашей программе может произойти инверсия бита. И мы вместо true получим false. А это говорит о том, что цикл вида:
while ( true ) {
doSome();
}
совершенно ненадежен! В любой момент может true поменяться на false и мы выйдем из цикла. Как этого избежать? Очень легко. Мы легко можем повысить надежность бесконечного цикла вот таким «куском» кода.
while(true) {
do {
for(;;) {
doSome();
}
} while(1);
}
Многие могут сказать, что кол-во кода увеличилось. Но на помощь приходит препроцессор.
#define FOREVER_CYCLE_START while(true){do{for(;;){
#define FOREVER_CYCLE_END }}while(1);}
И теперь мы легко можем написать
FOREVER_CYCLE_START
foreverYoung();
FOREVER_CYCLE_END
Великолепно и всё сразу понятно. А новичок не будет думать, что же значит for(;;){ }. и почему условие в for похоже на плачущую рожицу.
2) Установка фигурной скобки
Бесконечные споры. Что же лучше?
if ( COND ) {
doSome();
}
или
if ( COND )
{
doSome();
}
Хватит! Представляю вариант который устроит ВСЕХ!
if ( COND ) {
{
doSome();
}}
Все счастливы и довольны. О холиваре можно забыть… Хотя… стоит ли писать последние две закрывающиеся фигурные скобки на одной строке или стоит разнести на две? Но это уже отдельный вопрос требующий привлечения специалистов и дополнительных обсуждений.
3) Ассемблерные вставки
Никогда не бойтесь вставлять ассемблерный код в свои программы. Такие вставки показывают, что вы знаете не только высокоуровневый С++, но и в ладах с языком машины. Да, иногда, придётся переписывать или подправлять ассемблерный код, что бы перенести программу на другую архитектуру. Но за эту работу вам дополнительно заплатят и в очередной раз ваше начальство убедится, что вы хороший сотрудник. И никто другой не может справится с вашими обязанностями. Есть ещё маленькая хитрость. Вы можете вставлять бесполезные ассемблерные вставки, которые ничего не делают. Например вы можете по вспомнить, разные способы обмена информацией в регистрах:
mov bx, ax
mov ax, cx
mov cx, bx
xchg ax, cx
push ax
mov ax, cx
pop cx
xor ax, cx
xor cx, ax
xor ax, cx
Этот код придаст важности вашей программе. А люди которые будут с ним работать освежат свои знания. С вами может работать неопытный программист, который только что пришел из университета. А мы знаем, что студенты не очень охотно учат ассемблер. Таким образом он сразу же побежит к вам за разъяснением. У него создастся впечатление, что вы гуру, раз можете так легко манипулировать языками программирования. А начальство будет радо видеть, что новые сотрудники бегают к вам за новыми знаниями и вы ими охотно делитесь. Что может повлиять на вашу зарплату. Да и все же знают, что на ассемблере — быстрее!
4) Никогда не используйте const
Программисты как никто другой знает, что мы живём в бурном и быстро развивающемся мире, где все меняется очень быстро. А значит и использовать const смысла нет. Даже когда объект очевидно const, например кол-во спутников земли (у нас всего одна луна), то все равно по какой-то причине луна может расколоться на две. И придётся делать:
const int moons = 1;
moons++;
Что к вашему разочарованию забракует компилятор. Да и к тому же вы можете случайно сделать const, то что вы бы не хотели. Как вам понравится, если кто-то сделать вашу зарплату const? Всё! Прощай карьерный рост и повышение зарплаты.
const salary = 100; // $ в год
Да и к тому же это просто лишняя растрата байт кода. За эти 5 символов можно было бы сделать что-то полезное. Например, foo++.
5) Давайте переменным максимально короткие имена
Ведь запомнить переменную x куда проще чем position_of_object_on_x_axis. Например, этот код выглядит лаконичным и удобно читаемым:
if ( x < y ) {
z++;
} else if ( c > q ) {
c += f;
} else {
r = z + c;
}
Код легко умещается в пределах видимости глаз. И его легко проанализировать просто бросив на него взгляд.
6) CamelCase или snake_case
Еще один вечный холивар. Такой же, как полезна ли венгерская нотация или является пережитком прошлого! Достаточно. Мы программисты должны писать код, а не выяснять кто прав, а кто нет.
Я проанализировал эти стили и разработал универсальный стиль, который подойдет всем без исключения:
vengro_snakeCamel!
Примеры этого стиля:
float float_charPositionX;
int int_moneySpent
char* charPointer_nameOfPerson;
7) Имена переменных
Любой программист знает, что код надо писать так, что бы потом его удобно было читать. Но мы не всегда знаем, кто его будет читать после нас. И хорошо, если этот человек будет знать английский язык. Но далеко не все его знают и мы должны учитывать и эту аудиторию. Объявляйте переменные так, что бы они были понятны и на русском языке.
int int_kolichestvoBukv;
float float_glubinaBasseina;
bool bool_estLiZhiznNaMarse;
Теперь даже ребенок сможет понять смысл ваших переменных. Но все равно. нет предела совершенству. Все же играли в детстве в компьютерные игры? Помните как приходилось общаться с напарниками по команде, а русский язык не поддерживался? Применяйте эти знания и в программировании. Ведь все всё понимали, а значит в этом есть смысл.
int int_koJIu4ecTBo6ykB;
float float_rJly6uHa6acceuHA;
bool bool_ectbJIu7Ku3HbHaMapce;
8) DEFINE'ы
Препроцессор существует уже очень давно и перешел в наследство от языка С. А раз он передался по наследству, значит он представляет из себя некоторую ценность, проверен временем и перенесен на С++, как неотъемлемая часть программирования. Возьмем типичную ситуацию. На 1-ом курсе университета вы изучали pascal. А на 2-ом вам начали преподавать C++. Новые синтаксис может ввести в замешательство. Но не беда. На помощь спешит препроцессор C++.
Вы всегда можете написать:
#define BEGIN {
#define END }
И всё. Процесс перехода на новый язык будет менее болезненным и более понятным.
9) Глобальные переменные
Не стесняйтесь использовать глобальные переменные. Ведь это очень удобно. Во первых не надо заботиться об инициализации. Они должны быть инициализированы нулем. Вы можете легко обращаться из любой части программы к ним, что позволяет избежать лишних параметров в функциях. Повышает гибкость проекта. А если кто-то скажет, что это не потокобезопасно. Так оно и есть. И это остановит вас от использования потоков. Человеческий мозг лучше просчитывает однопоточные программы. И глобальные переменные убережет Вас от использования потоков, что только повысит надежность ваших программ.
10) Исходники
Помните, что все исходные коды программы лучше хранить в одном файле. Это облегчает поиск интересующих вас переменных и функций. Ведь простой поиск по тексту одновременно становится и поиском по всему проекту. Все функции перед глазами, все переменные доступны в паре движений скроллом. Разбиение кода на несколько файлов приводит к тому, что за ним уже надо следить, что бы файлы не потерялись, разруливать обращения к глобальным переменным из каждого модуля компиляции и т. д. Зачем все эти сложности? Когда всё компактно находится рядом куда проще разобраться в исходных кодах, нежели скакать по куче разных файлов.
Эпилог
В ходе моей работы меня почему-то часто критикуют за мой код и стиль кодирования. Хотя не объясняют почему. Часто ругаются и смеются. Поэтому просьба к более опытным программистам — не обижайте своих молодых коллег. Все мы были новичками и прошли через неопытность, сомнения и неуверенность. Будьте добрее. И надеюсь мои советы вам никогда не пригодятся.
Комментарии (65)
amarao
19.11.2015 17:01-39Как сисадмин, хочу сказать, что это не
ваше собачьепрограммистское дело — думать о флипающихся битах. Точнее, есть области, где об этом конкретно думают — это радиационная устойчивость микросхем и кода для них. Но там точно не «делайте двойной цикл».
Для всего остального цивилизованного мира флип бита во-первых ни к чему критическому не приведёт (привет, ECC), во-вторых, кто вам сказал, что бит флипнется на вашем «true», а не на адресе перехода?
Неверно работающее оборудование — это проблема сисадминов. Выглядит она так:
CPU_SrcID#0_Channel#3_DIMM#0 (channel:3 slot:0 page:0x379c86 offset:0xdc0 grain:32 syndrome:0x0 — area:DRAM err_code:0001:0093 socket:0 channel_mask:8 rank:0)
И мы точно знаем, что с этим делать.
А вот вам (программистам) надо подумать, а что вы будете делать, когда все биты в памяти компьютера инвертируются? (hint: распределённость и отказоустойчивость).fshp
20.11.2015 00:09+16то вам сказал, что бит флипнется на вашем «true», а не на адресе перехода
Да вы просто не шарите. while(true) скомпилируется в безусловный переход. Там не будет никакой проверки, будет простой jmp. Goto, если так понятнее. Так что флип бита в этом куске может лишь изменить инструкцию, но не условие (его там попросту нет). А это уже совсем другая проблема.
В конец статьи пропущена картинка с Леонардом Хофстедтером.vsespb
20.11.2015 02:33Наверняка в jmp можно поменять один бит, так чтобы оно превратилось во что-нибудь безобидное и длина инструкции совпала
fshp
20.11.2015 03:03+2Так я и говорю. Изменится инструкция. А от этого уже ничего не спасёт. Хоть в 10 циклов оберните. jmp сменится на hlt (хотя для этого 3 бита флипнуть нужно как минимум, но это для примера). И привет.
maaGames
19.11.2015 17:20+8Если нужно беспокоиться о «перевёрнутом бите», то используется память с ECC.
Но я догадался, что это юморной пост, но не догадался, что он вообще делает на хабре?FSA
20.11.2015 09:53Кстати, отличная идея программе продолжать работе если что-то пошло не так и true вдруг стало false :)
Alexeyslav
20.11.2015 10:35Не очень смешно…
на абапе(SAP R/3) иногда встречается проверка в программах «IF 1 = 0.» а на форумах говорят что в некоторых ситуациях это условие срабатывает!postgree
20.11.2015 12:06А можно описание этих ситуаций. Я хоть и не абапер, но интересно такое узнать. Да и саперов в очередной раз потролить можно будет.
Alexeyslav
20.11.2015 14:40Давно дело было. Это в основном неисправляемые баги в версиях с экзотическими языками где разные символы могут неожиданно быть равны.
Да дело давно было, уже и не помню этого форума. Но суть сводилась примерно к тому что такие условия могли срабатывать на корейских версиях сервера приложений. Видимо когда идёт сравнение двух символов в юникоде сравниваются только первые их байты и получается подобная фигня. Может, этот баг уже исправили, а может и нет. Кто знает где там ещё они зарыты остались.
RolexStrider
20.11.2015 22:01+1Если нужно беспокоиться о «перевёрнутом бите», то используется
Созданный Минобороны США «максимально безопасный» язык Ada получил антипода – язык paranoid, который, как следует из названия, доводит стремление к «безопасному выполнению» до паранойи. Некоторые его конструкции достойны немедленного цитирования (с синхронным переводом):
//типы данных:
х: сомнительное целое;
а: мало_похоже_на массив [x..y а_может_быть z] каких_нибудь символов;
L: безнадежно_поврежденный список слишком_маленьких целых;
//присвоение значения переменной:
x ТОЧНО 3;
x ЧЕСТНОЕ_СЛОВО 3;
x МАМОЙ_КЛЯНУСЬ 3;
maaGames
21.11.2015 06:30Назвали бы язык Рая.)
Перехожу в режим зануды.
Проблемы со слишком_маленькими целыми быть не может, так что там массив слишком маленьких вещественных циселок.)
robert_ayrapetyan
19.11.2015 17:27+18Низкий поклон за проделанный труд! Можно добавить еще один совет — пользуйтесь копипастой, где только возможно, ведь вызов процедуры\функции — это дополнительные инструкции и пустой расход ресурсов процессора.
Mrrl
19.11.2015 20:22+5Не говоря уже о том, что при копипасте можно внести во вставленный кусок небольшие оптимизирующие изменения, связанные со спецификой конкретно этого вызова (например, если один из параметров — степень двойки, то все умножения на него заменить на сдвиги). Главное при этом — не менять комментарии: это лишняя трата времени и умственных усилий, которые можно направить на что-нибудь более полезное. Например, на защиту от спонтанного изменения битов.
fshp
20.11.2015 00:16А вот бывают случаи, когда без копипасты не обойтись. Например, большой switch, в котором несколько веток изменить нужно. Как красиво это сделать я не понимать.
nochkin
20.11.2015 06:39+8Сделать красиво достаточно просто — надо переписать этот участок кода на ассемблере.
macseem_327
20.11.2015 14:37Или заменить на множество вложенных if elseif elseif else, это даст возможность больше кастомизировать условие, да и вообще switch — это же моветон!
leremin
19.11.2015 18:11+8Еще добавлю:
- Проверяйте выбранный элемент в ComboBox через if… else if… else if… со строками. Это облегчает написание кода и сразу видно, что имеется ввиду.
- Всегда создавайте велосипеды для контейнеров из std. Их разработчики — теоретики и ничего не понимают в написании реальных программ. Ваша реализация будет быстрее и лучше
- Пишите методы по три, а лучше по пять тысяч строк. Читателю вашего кода будет гораздо проще в нем разобраться, ведь нет нужды лазить по множеству файлов и классов.
Было бы смешно, не будь этого в проекте, который мне достался в наследство.khim
20.11.2015 01:15Как выглядит контейнер с методами в три тысячи строк я даже боюсь себе представить. Один метод «Do» с 25ю аргументами???
А если серьёзно, то долгое время и вариантов-то не было: STL начал прилично поддерживаться всякими GCC не так давно (по историческим меркам).leremin
20.11.2015 06:43Не, контейнеры отдельно, огромные методы — отдельно. По историческим меркам по Вашей градации это было только что. Когда на того парня наехали зачем ему свои контейнеры — он обиделся, типа куча проделанной работы, а вы не цените.
trigger_af
27.11.2015 14:34В наследство — это ещё ничего, есть возможность улучшать мелкими порциями, надеюсь. У меня вот есть коллега, который привык один работать и продолжает порождать огромные методы и другие вещи, делающие код не поддерживаемым.
Ищу возможность по мирному пока сваливать в другой проект, т.к. сам он в своем коде не плохо разбирается, естественно.
Mrrl
19.11.2015 18:34+23Лучше такое публиковать в пятницу — тогда люди быстрее разберутся, в чём дело.
kamazee
19.11.2015 19:24+19int int_koJIu4ecTBo6ykB; float float_rJly6uHa6acceuHA; bool bool_ectbJIu7Ku3HbHaMapce;
Так Вы же сами пишете, что программист может не знать английского! Предлагаю однозначно более лучший универсальный вариант, который подойдет действительно всем:
int LIeJIoe_koJIu4ecTBo6ykB; float Dpo6Hoe_rJly6uHa6acceuHA; bool JIOru4eckoe_ectbJIu7Ku3HbHaMapce;
Mrrl
19.11.2015 20:15+52Нет, надо сначала написать
typedef int LIeJIoe; typedef float Dpo6Hoe; typedef bool JIOru4eckoe;
а уже потом честно описывать переменные:
LIeJIoe LIeJIoe_koJIu4ecTBo6ykB; Dpo6Hoe Dpo6Hoe_rJly6uHa6acceuHA; JIOru4eckoe JIOru4eckoe_ectbJIu7Ku3HbHaMapce;
xotta6bl4
20.11.2015 16:13Тогда уж лучше писать на джаве.
Целое целое_количествоБукв; Дробное дробное_глубинаБассейна; Логическое логическое_естьЛиЖизньНаМарсе;
novoxudonoser
20.11.2015 00:00+1Статья на хабр это хорошо, но можт стоит указать язык в тегах?
achekalin
20.11.2015 00:38+9Русский, батенька, тут больше за русский ратуют.
Ассемблер, C и Паскаль упоминаются, но все же автор заботится о русскоязычной аудитории, за что ему нижайший (не) поклон!khim
20.11.2015 01:06+3Да ладно вам. Какая же это «забота о русскоязычной аудитории» если все русские буквы куда-то пропали? Что за низкопоклонство перед западом? Возьмите КОИ-7 и будет вам щястя!
ТЫПЕДЕФ ИНТ Целое;
И нет проблем!
ТЫПЕДЕФ ФЛОАТ Дробное;
ТЫПЕДЕФ БООЛ Условие;
Целое ВместимостьВБуквах;
Дробное ГлубинаБассейнаВМетрах;
Условие ЕстьЛиЖизньНаМарсеДаНет;
Правда придётся отказаться от любимного «универсального стиля» в связи с отсуствием подчёркивания, но и так хорошо смотрится.Mrrl
20.11.2015 01:12+2Не пройдёт. При обратном переключении эти надписи, конечно, останутся корректными, но на какое-нибудь
Drobnoe Drobnoe_dlina[osseW[agah^eloweka;
С++ будет ругаться.khim
20.11.2015 07:34Ерунда. Мы что — хуже американцев? Они могут писать романы без буквы «E», а мы не можем обойтись без нескольких букв? Наоборот — это только добавит остроты!
Koshelenok
20.11.2015 08:11+5Чуть было не изошелся на «Ассемблерные вставки» прямо в автобусе пока дочитал! Пять с плюсом за сарказм, если это он.
olekl
20.11.2015 13:06+2По-моему, после «ассемблерных вставок» не догадаться, что тут ирония, странно. Спасибо, статья заставила поулыбаться! Особенно про имена переменных.
vlivyur
20.11.2015 16:06Суперкласс — объектно-ориентированное и процедурное программирования в одном флаконе.
<Зануда>
Венгерская нотация это не про типы переменных, а про характер хранимого значения в ней (зарплата, цена продукта — всё в рублях, decimal, но складывать или перемножать их нельзя, на это и должен указывать префикс).
</Зануда>
TimsTims
Что-то не увидел тэг «сарказм». Он тут есть?
Во-первых не вяжется с пунктом 5) Давайте переменным максимально короткие имена
Во-вторых, как будут твой код читать коллеги, не знающие языка.
В третьих, даже знающим язык — такое читать — глаза ломать. Уж лучше называйте хоть на транскрипции…
Начальству, которое не разбирается особо в коде — вообще пофиг, на чем вы пишете и как пишете
TimsTims
Последняя фраза сбила с толку. Понял, что это всё-же НЕ_СОВЕТЫ, а не «советы, которыми делится автор». Стоило добавить во вторую цитату «готов поделиться несколькими несоветами»…
Количество голосов у так-же запутавшегося zodchiy подтверждают путаницу в трактовках
Подавлю в себе хомяка)
mc_dir
Зато ты был первый!
TimsTims
Зато видно, что с чувством юмора на хабре всё в порядке(почти все поняли шуточный посыл поста, кэп)
ishua
То есть другие советы не смутили?
Draku1a
Тут тэг сарказм — на всю статью.