Готовые программы написанные на c. Пример простой программы на языке си. Все комментарии следует писать на английском

Теги: Первая программа на си. Азы си. Си Borland. Си Code Gear. Си Embarcadero. Си MS Visual Studio. Си MS Express.

Пишем первую программу на си

Д ля начала, необходимо установить программное обеспечение. В принципе не важно, каким ПО вы будете пользоваться, также как не важна и операционная система. Но в течение всего курса я буду приводить примеры на MS Visula Studio 2012 Express Edition. Visual Studio 2012 Express Edition бесплатный и его за глаза хватит для изучения всего курса. Кроме того, как показала практика, он гораздо строже относится к коду и даёт более полноценное описание ошибок и предупреждений. При изучении языка можно использовать Borland (он же CodeGEAR, он же Embarcadero и т.д.), Dev Cpp, MinGW, или gcc, или что вы ещё захотите.

Пример для MS Visual Studio

1. Открываем IDE, заходим Файл | Создать проект...

2. Выбираем консольное приложение и даём ему имя. В данном случае first_program

4. Ставим галочку "Пустой проект".

5. После чего получаем пустую структуру проекта. Добавим новый элемент: правый клик мыши по папке
"Файлы исходного кода" | Добавить | Создать элемент...

Добавляем новый cpp файл, но сохраняем его с расширением.c

Я назвал файл main.c Всё, готово, можно писать программу. шаги для других платформ.

Borland

У меня установлен только Code Gear C++Builder 2007, но в остальных (и предыдущих) релизах всё делается также.

1. Создадим новый проект File | New | Other...

2. Добавляем консольное приложение

3. Выбираем язык си

4. Получаем готовый проект. Его необходимо сохранить с тем именем, которое захотите. До тех пор сам проект и все файлы будут иметь имена по умолчанию. Вы можете удалить то, что Borland по умолчанию прописал в тексте программы.

Пример для cc/gcc для терминала

О ткройте ваш любимый текстовый редактор и скопируйте туда код программы.

#include int main(int argc, char* argv) { printf("Hello, World!"); scanf("1"); return 0; }

Если вы сохранили программу в файле с именем hello.c, то наберите в терминале команду

Cc hello.c -o hello

Gcc hello -o hello

При этом, очевидно, вы должны находиться в папке с программой. gcc создаст исполняемый файл с именем hello. Запустите его, и он выведет Hello, World!

Иногда могут возникнуть проблемы с правами доступа. Проверьте, что у вас исполняемый файл, иначе дайте себе привелегии на запуск.

Chmod 760 hello

Если у вас несколько файлов, то необходимо будет перечислить имена всех си файлов по порядку. Например, если у вас есть ещё два файла simple.h и simple.c, то нужно прописать

Cc hello.c simple.c -o hello

Код программы

Принято в первой программе выводить Hello, World! на экран.

#include #include int main(int argc, char* argv) { printf("Hello, World!"); _getch(); return 0; }

Запустите программу (Run | Run или F9 для борланда, Построение | Построить решение или F5 для MS) Программа выведет Hello, World! и будет ждать, когда вы нажмёте на любую клавишу.

Рассмотрим код подробнее. Первые две строки

#include #include

директивы компилятору на подключение стандартных библиотек stdio (Standard Input Output - стандартная библиотека ввода вывода) и conio (Console Input Output - стандартная библиотека консоли вывода вывода). Расширение.h указывает, что это заголовочные файлы (header files). Компилятор копирует код библиотек conio и stdio, и даёт возможность использовать функции, описанные в этих библиотеках.

Int main(int argc, char* argv)

Это функция main. Она отличается от остальных функций, которые вы можете определить тем, что является точкой входа - с неё начинается выполнение программы.

Функция main имеет два параметра - число параметров argc и массив переданных параметров argv. Эти аргументы необязательные, поэтому можно их не писать. Об их использовании поговорим позже.

#include #include int main() { printf("Hello, World!"); _getch(); return 0; } Функция main должна возвращать целое число. Если это 0, то функция отработала без ошибок. В современном стандарте си можно не возвращать 0, и описать функцию как void main. #include #include void main() { printf("Hello, World!"); _getch(); } Наша программа теперь выглядит совсем просто. Строка printf("Hello, World!"); выводит строку Hello, World! на экран монитора. _getch() ожидает нажатия на клавишу.

Давайте сделаем что-нибудь посложнее, чтобы научиться добавлять новые файлы в программу. Сейчас для вас важно научиться добавлять новые файлы, если часть кода останется непонятной, это не беда.
1. Создайте новый заголовочный файл в папке "Заголовочные файлы", назовите его simple.h
2. Создайте новый файл simple.c в папке "Файлы исходного кода".
3. Добавьте в simple.h

#ifndef _SIMPLE_H_ #define _SIMPLE_H_ #include #include void doSomething(); #endif

Здесь мы объявили новую функцию doSomething. У неё отсутствует тело, оно будет описано в файле simple.c. Здесь же мы подключаем и библиотеки stdio и conio
Добавьте в simple .c

#include "simple.h" void doSomething() { printf("It works!"); _getch(); }

Мы включаем в файл simple.c заголовочный файл. Он пишется в двойных кавычках, потому что это не файл из стандартной библиотеки. Файлы стандартной библиотеки обычно располагаются в папке include самой IDE. Если поместить туда наши файлы, то их тоже можно будет объявлять в угловых скобках. В двойных кавычках можно также прописывать абсолютные пути к файлам. Так как мы уже включили библиотеки conio и stdio в.h файле, то они "видны" и в.c файле.
Далее, в main.c

От переводчика. Искал в интернете простой и легко применимый гайдлайн по написанию программ на C++. Мне понравился один из вариантов, и я решил его перевести и опубликовать. Если хабрапользователи хорошо встретят этот топик, могу перевести и другие связанные документы, а также гайдлайны по написанию кода от других компаний.

1 Введение

Настоящий документ содержит рекомендации по написанию программ на языке C++.

Но для появления ещё одного списка рекомендаций, помимо указанных источников, есть несколько причин. Основная причина - их излишняя обобщённость, поскольку зачастую требуется задать частные правила (в особенности правила именования). Данный документ содержит комментарии, что делает его более удобным в использовании при проведении ревизий кода, чем другие уже существующие документы. К тому же, рекомендации по программированию обычно вперемешку содержат описания проблем стиля и технических проблем, что не совсем удобно. Этот документ не содержит каких-либо технических рекомендаций по C++, делая упор на вопросах стиля.

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

1.1 Формат документа
Рекомендации сгруппированы по темам и пронумерованы, чтобы на них можно было ссылаться во время ревизий кода. Рекомендации разделены по степени важности: обязательные, настоятельно рекомендуемые и общие.1. Допускаются любые нарушения рекомендаций, если это улучшает читаемость.

2. Правила могут быть нарушены, если против них есть персональные возражения.

Это попытка создать набор общих рекомендаций, не навязывая всем единый стиль. Опытные программисты обычно всё равно подгоняют стиль под себя. Подобный список рекомендаций, имеющийся под рукой (или хотя бы требование ознакомиться с ним), обычно заставляет людей задумываться о стиле программирования и оценке их собственных практик в этой области.

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

3 Соглашения об именовании

3.1 Общие соглашения об именовании
3. Имена, представляющие типы, должны быть обязательно написаны в смешанном регистре, начиная с верхнего.

Line, SavingsAccount

4. Имена переменных должны быть записаны в смешанном регистре, начиная с нижнего.

Line, savingsAccount
Общая практика в сообществе разработчиков C++. Позволяет легко отличать переменные от типов, предотвращает потенциальные коллизии имён, например: Line line;

5. Именованные константы (включая значения перечислений) должны быть записаны в верхнем регистре с нижним подчёркиванием в качестве разделителя.

MAX_ITERATIONS, COLOR_RED, PI
Общая практика в сообществе разработчиков C++. Использование таких констант должно быть сведено к минимуму. В большинстве случаев реализация значения в виде метода - лучшее решение:

Int getMaxIterations() // НЕЛЬЗЯ: MAX_ITERATIONS = 25 { return 25; }
Эта форма более читаемая и гарантирует единый интерфейс к значениям, хранящимся в классе.

6. Названия методов и функций должны быть глаголами, быть записанными в смешанном регистре и начинаться с нижнего.

GetName(), computeTotalWidth()
Совпадает с правилом для переменных, но отличие между ними состоит в их специфических формах.

7. Названия пространств имён следует записывать в нижнем регистре.

Model::analyzer, io::iomanager, common::math::geometry
Общая практика в сообществе разработчиков C++.

8. Следует называть имена типов в шаблонах одной заглавной буквой.

Template ... template ...
Общая практика в сообществе разработчиков C++. Позволяет выделить имена шаблонов среди других используемых имён.

9. Аббревиатуры и сокращения в именах должны записываться в нижнем регистре.
exportHtmlSource(); // НЕЛЬЗЯ: exportHTMLSource(); openDvdPlayer(); // НЕЛЬЗЯ: openDVDPlayer();
Использование верхнего регистра может привести к конфликту имён, описанному выше. Иначе переменные бы имели имена dVD, hTML и т. д., что не является удобочитаемым. Другая проблема уже описана выше; когда имя связано с другим, читаемость снижается; слово, следующее за аббревиатурой, не выделяется так, как следовало бы.

10. Глобальные переменные всегда следует использовать с оператором разрешения области видимости (::).

::mainWindow.open(), ::applicationContext.getName()
Следует избегать использования глобальных переменных. Предпочтительнее использование синглтонов.

11. Членам класса с модификатором private следует присваивать суффикс-подчёркивание.

Class SomeClass { private: int length_; }
Не считая имени и типа, область видимости - наиболее важное свойство переменной. Явное указание модификатора доступа в виде подчёркивания избавляет от путаницы между членами класса и локальными переменными. Это важно, поскольку переменные класса имеют большее значение, нежели переменные в методах, и к ним следует относиться более осторожно.

Дополнительным эффектом от суффикса-подчёркивания является разрешение проблемы именования в методах, устанавливающих значения, а также в конструкторах:

Void setDepth (int depth) { depth_ = depth; }
Проблема заключается в том, что существует два варианта подчёркивания - в виде суффикса и в виде префикса. Оба варианта широко используются, но рекомендуется именно первый вариант, потому что он обеспечивает лучшую читаемость. Следует отметить, что определение модификатора доступа у переменных - иногда спорный вопрос. Хотя кажется, что рекомендуемая практика набирает сторонников и становится всё более распространённой в среде профессионалов.

12. Настраиваемым переменным следует давать то же имя, что и у их типа.

Void setTopic(Topic* topic) // НЕЛЬЗЯ: void setTopic(Topic* value) // НЕЛЬЗЯ: void setTopic(Topic* aTopic) // НЕЛЬЗЯ: void setTopic(Topic* t) void connect(Database* database) // НЕЛЬЗЯ: void connect(Database* db) // НЕЛЬЗЯ: void connect (Database* oracleDB)
Сокращайте сложность путём уменьшения числа используемых терминов и имён. Также упрощает распознавание типа просто по имени переменной.

Не являющиеся настраиваемыми переменные могут быть названы по их назначению и типу:

Point startingPoint, centerPoint; Name loginName;

13. Все имена следует записывать по-английски.

14. Переменные, имеющие большую область видимости, следует называть длинными именами, имеющие небольшую область видимости - короткими.

Имена временных переменных, использующихся для хранения временных значений или индексов, лучше всего делать короткими. Программист, читающий такие переменные, должен иметь возможность предположить, что их значения не используются за пределами нескольких строк кода. Обычно это переменные i , j , k , l , m , n (для целых), а также c и d (для символов).

15. Имена объектов не указываются явно, следует избегать указания названий объектов в именах методов.

(Пункт № 16 отсутствует.- Примечание переводчика.)

3.2 Особые правила именования
17. Слова get/set должны быть использованы везде, где осуществляется прямой доступ к атрибуту.

Employee.getName(); employee.setName(name); matrix.getElement(2, 4); matrix.setElement(2, 4, value);
Общая практика в сообществе разработчиков C++. В Java это соглашение стало более-менее стандартным.

18. Слово compute может быть использовано в методах, вычисляющих что-либо.

ValueSet->computeAverage(); matrix->computeInverse()
Дайте читающему сразу понять, что это времязатратная операция.

19. Слово find может быть использовано в методах, осуществляющих какой-либо поиск.

Vertex.findNearestVertex(); matrix.findMinElement();
Дайте читающему сразу понять, что это простой метод поиска, не требующий больших вычислений.

20. Слово initialize может быть использовано там, где объект или сущность инициализируется.

Printer.initializeFontSet();
Следует отдавать предпочтение американскому варианту initializ e, нежели британскому initialis e. Следует избегать сокращения init.

21. Переменным, представляющим GUI, следует давать суффикс, соответствующий имени типа компонента.

MainWindow, propertiesDialog, widthScale, loginText, leftScrollbar, mainForm, fileMenu, minLabel, exitButton, yesToggle и т. д.
Улучшает читаемость, поскольку имя даёт пользователю прямую подсказку о типе переменной и, следовательно, ресурсах объектов.

22. Множественное число следует использовать для представления наборов (коллекций) объектов.

VectorPoints; int values;
Улучшает читаемость, поскольку имя даёт пользователю прямую подсказку о типе переменной и операциях, которые могут быть применены к этим элементам.

23. Префикс n следует использовать для представления числа объектов.

NPoints, nLines
Обозначение взято из математики, где оно является установившимся соглашением для обозначения числа объектов.

24. Суффикс No следует использовать для обозначения номера сущности.

TableNo, employeeNo
Обозначение взято из математики, где оно является установившимся соглашением для обозначения номера сущности.

Другой неплохой альтернативой является префикс i : iTable, iEmployee. Он ясно даёт понять, что перед нами именованный итератор.

25. Переменным-итераторам следует давать имена i, j, k и т. д.

For (int i = 0; i < nTables); i++) { : } for (vector::iterator i = list.begin(); i != list.end(); i++) { Element element = *i; ... }
Обозначение взято из математики, где оно является установившимся соглашением для обозначения итераторов.

Переменные с именами j , k и т. д. рекомендуется использовать только во вложенных циклах.

26. Префикс is следует использовать только для булевых (логических) переменных и методов.

IsSet, isVisible, isFinished, isFound, isOpen
Общая практика в сообществе разработчиков C++, иногда используемая и в Java.

Использование этого префикса избавляет от таких имён, как status или flag . isStatus или isFlag просто не подходят, и программист вынужден выбирать более осмысленные имена.

В некоторых ситуациях префикс is лучше заменить на другой: has , can или should :

Bool hasLicense(); bool canEvaluate(); bool shouldSort();

27. Симметричные имена должны использоваться для соответствующих операций.

Get/set, add/remove, create/destroy, start/stop, insert/delete, increment/decrement, old/new, begin/end, first/last, up/down, min/max, next/previous, old/new, open/close, show/hide, suspend/resume, и т. д.
Уменьшайте сложность за счёт симметрии.

28. Следует избегать сокращений в именах.

ComputeAverage(); // НЕЛЬЗЯ: compAvg();
Рассмотрим два вида слов. Первые - обычные слова, перечисляемые в словарях, которые нельзя сокращать. Никогда не сокращайте:

Cmd вместо command cp вместо copy pt вместо point comp вместо compute init вместо initialize и т. д.

Второй вид - слова, специфичные для какой-либо области, которые известны по своему сокращению/аббревиатуре. Их следует записывать сокращённо. Никогда не пишите:

HypertextMarkupLanguage вместо html CentralProcessingUnit вместо cpu PriceEarningRatio вместо pe и т. д.

29. Следует избегать дополнительного именования указателей.

Line* line; // НЕ РЕКОМЕНДУЕТСЯ: Line* pLine; // НЕ РЕКОМЕНДУЕТСЯ: Line* linePtr;
Множество переменных в C/C++ являются указателями. Только в том случае, когда тип объекта в языке C++ особенно важен, имя должно отражать его.

30. Нельзя давать булевым (логическим) переменным имена, содержащие отрицание.

Bool isError; // НЕЛЬЗЯ: isNoError bool isFound; // НЕЛЬЗЯ: isNotFound
Проблема возникает, когда такое имя используется в конъюнкции с оператором логического отрицания, что влечёт двойное отрицание. Результат не обязательно будет отрицанием !isNotFound .

31. Константы в перечислениях могут иметь префикс - общее имя типа.

Enum Color { COLOR_RED, COLOR_GREEN, COLOR_BLUE };
Это даёт дополнительную информацию о том, где находится объявление, какие константы описаны в одном перечислении, а также какую концепцию являют собой константы.

Другим подходом является обращение к константам по их общему типу: Color::RED, Airline::AIR_FRANCE и т. д.

Обратите внимание, что имя перечисления обычно записано в единственном числе, например: enum Color {...}. Имя во множественном числе хорошо выглядит при объявлении, но не очень хорошо подходит для практического использования.

32. Классам исключений следует присваивать суффикс Exception.

Class AccessException { : }
Классы исключений в действительности не являются частью архитектуры программ, и такое именование отделяет их от других классов.

33. Функциям (методам, возвращающим какие-либо значения) следует давать имена в зависимости от того, что они возвращают, а процедурам - в зависимости от того, что они выполняют (методы void).

Улучшайте читаемость. Такое именование даёт понять, что метод делает, а что нет, а также избавляет код от потенциальных побочных эффектов.

4 Файлы

4.1 Файлы исходных кодов
34. Заголовочным файлам C++ следует давать расширение .h (предпочтительно) либо .hpp. Файлы исходных кодов могут иметь расширения .c++ (рекомендуется), .C, .cc либо .cpp.

MyClass.c++, MyClass.h
Это расширения, одобряемые стандартом C++.

35. Класс следует объявлять в заголовочном файле и определять (реализовывать) в файле исходного кода, имена файлов совпадают с именем класса.

MyClass.h, MyClass.c++

Облегчает поиск связанных с классом файлов. Очевидное исключение - шаблонные классы, которые должны быть объявлены и определены в заголовочном файле.

36. Все определения должны находиться в файлах исходного кода.

Class MyClass { public: int getValue () {return value_;} // НЕЛЬЗЯ! ... private: int value_; }
Заголовочные файлы объявляют интерфейс, файлы исходного кода его реализовывают. Если программисту необходимо найти реализацию, он должен быть уверен, что найдёт её именно в файле исходного кода.

37. Содержимое файлов не должно превышать 80 колонок.

80 колонок - широко распространённое разрешение для редакторов, эмуляторов терминалов, принтеров и отладчиков; файлы передаются между различными людьми, поэтому нужно придерживаться этих ограничений. Уместная разбивка строк улучшает читаемость при совместной работе над исходным кодом.

38. Нельзя использовать специальные символы (например, TAB) и разрывы страниц.

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

39. Незавершённость разбитых строк должна быть очевидна.

TotalSum = a + b + c + d + e; function (param1, param2, param3); setText ("Long line split" "into two parts."); for (int tableNo = 0; tableNo < nTables; tableNo += tableStep) { ... }
Разбивка строк появляется, когда ограничение на 80 колонок, описанное выше, нарушается. Сложно дать жёсткие правила по разбивке, но примеры выше показывают общие принципы.

В общем случае:

  • разрыв после запятой;
  • разрыв после оператора;
  • выравнивание новой строки с началом выражения на предыдущей строке.
4.2 Включения файлов
40. Заголовочные файлы должны содержать защиту от вложенного включения.

#ifndef COM_COMPANY_MODULE_CLASSNAME_H #define COM_COMPANY_MODULE_CLASSNAME_H: #endif // COM_COMPANY_MODULE_CLASSNAME_H
Конструкция позволяет избегать ошибок компиляции. Это соглашение позволяет увидеть положение файла в структуре проекта и предотвращает конфликты имён.

41. Директивы включения следует сортировать (по месту в иерархии системы, ниже уровень - выше позиция) и группировать. Оставляйте пустую строку между группами.

#include #include #include #include #include "com/company/ui/PropertiesDialog.h" #include "com/company/ui/MainWindow.h"
Пути включения не должны быть абсолютными. Вместо этого следует использовать директивы компилятора.

42. Директивы включения должны располагаться только в начале файла.

Общая практика. Избегайте нежелательных побочных эффектов, которые может вызвать «скрытое» включение где-то в середине файла исходного кода.

5 Выражения

5.1 Типы
43. Локальные типы, используемые в одном файле, должны быть объявлены только в нём.

Улучшает сокрытие информации.

44. Разделы класса public, protected и private должны быть отсортированы. Все разделы должны быть явно указаны.

Сперва должен идти раздел public , что избавит желающих ознакомиться с классом от чтения разделов protected/private .

45. Приведение типов должно быть явным. Никогда не полагайтесь на неявное приведение типов.

FloatValue = static_cast(intValue); // НЕЛЬЗЯ: floatValue = intValue;
Этим программист показывает, что ему известно о различии типов, что смешение сделано намеренно.

5.2 Переменные
46. Следует инициализировать переменные в месте их объявления.

Это даёт гарантию, что переменные пригодны для использования в любой момент времени. Но иногда нет возможности осуществить это:

Int x, y, z; getCenter(&x, &y, &z);
В этих случаях лучше оставить переменные неинициализированными, чем присваивать им какие-либо значения.

47. Переменные никогда не должны иметь двойной смысл.

Улучшайте читаемость, убеждаясь, что все представленные концепции не предполагают разночтений. Сокращайте возможность ошибки из-за побочных эффектов.

48. Следует избегать использования глобальных переменных.

Не существует причины использовать глобальные переменные в C++ (на самом деле существует.- Примечание переводчика). То же касается глобальных функций и (статических) переменных, область видимости которых - весь файл.

49. Не следует объявлять переменные класса как public.

Эти переменные нарушают принципы сокрытия информации и инкапсуляции. Вместо этого используйте переменные с модификатором private и соответствующие функции доступа. Исключение - класс без поведения, практически структура данных (эквивалент структур языка C). В этом случае нет смысла скрывать эти переменные.

Обратите внимание, что структуры в языке C++ оставлены только для совместимости с C; их использование ухудшает читаемость кода. Вместо структур используйте классы.

(Пункт № 50 отсутствует.- Примечание переводчика.)

Float* x; // НЕ РЕКОМЕНДУЕТСЯ: float *x; int& y; // НЕ РЕКОМЕНДУЕТСЯ: int &y;
То, что переменная - указатель или ссылка, относится скорее к её типу, а не к имени. Программисты на C часто используют другой подход, но в C++ лучше придерживаться этой рекомендации.

(Пункт № 52 отсутствует.- Примечание переводчика.)

53. Следует избегать неявного сравнения булевых (логических) переменных и указателей с нулём.

If (nLines != 0) // НЕ РЕКОМЕНДУЕТСЯ: if (nLines) if (value != 0.0) // НЕ РЕКОМЕНДУЕТСЯ: if (value)
Стандарт C++ не гарантирует, что значения переменных int и float, равные нулю, будут представлены как бинарный 0. Также при явном сравнении видно сравниваемый тип.

Логично было бы предположить, что также и указатели не следует неявно сравнивать с нулём, например, if (line == 0) вместо if (line). Последнее является очень распространённой практикой в C/C++, поэтому также может быть использовано.

54. Переменные следует объявлять в как можно меньшей области видимости.

Это упрощает контроль над действием переменной и сторонними эффектами.

5.3 Циклы
55. Нельзя включать в конструкцию for() выражения, не относящиеся к управлению циклом.

Sum = 0; // НЕЛЬЗЯ: for (i = 0, sum = 0; i < 100; i++) for (i = 0; i < 100; i++) sum += value[i]; sum += value[i];
Улучшайте поддержку и читаемость. Строго разделяйте контроль над циклом и то, что в нём содержится.

56. Переменные, относящиеся к циклу, следует инициализировать непосредственно перед ним.

57. Можно избегать циклов do-while.

Такие циклы хуже читаемы, поскольку условие описано после тела. Читающему придётся просмотреть весь цикл, чтобы понять его работу.

Циклы do-while вообще не являются острой необходимостью. Любой такой цикл может быть заменён на цикл while или for.

Меньшее число используемых конструкций улучшает читаемость.

58. Следует избегать использования break и continue в циклах.

Такие выражения следует использовать только тогда, когда они повышают читаемость.

(Пункт № 59 отсутствует.- Примечание переводчика.)

60. Для бесконечных циклов следует использовать форму while (true) .

While (true) { : } for (;;) { // НЕТ! : } while (1) { // НЕТ! : }
Проверка на единицу не является необходимой и бессмысленна. Форма for (;;) не очень читаема; также не является очевидным, что цикл бесконечный.

5.4 Условные выражения
61. Строго избегайте сложных уловных выражений. Вместо этого вводите булевы переменные.

Bool isFinished = (elementNo < 0) || (elementNo > maxElement); bool isRepeatedEntry = elementNo == lastElement; if (isFinished || isRepeatedEntry) { : } // NOT: if ((elementNo < 0) || (elementNo > maxElement)|| elementNo == lastElement) { : }
Задание булевых переменных для выражений приведёт к самодокументированию программы. Конструкцию будет легче читать, отлаживать и поддерживать.

62. Ожидаемую часть следует располагать в части if, исключение - в части else.

Bool isOk = readFile (fileName); if (isOk) { : } else { : }
Это позволяет убедиться, что исключения не вносят неясности в нормальный ход выполнения. Важно для читаемости и производительности.

63. Условие следует размещать в отдельной строке.

64. Следует строго избегать исполнимых выражений в условиях.

File* fileHandle = open(fileName, "w"); if (!fileHandle) { : } // НЕЛЬЗЯ: if (!(fileHandle = open(fileName, "w"))) { : }
Исполняемые выражения в условиях усложняют читаемость. Особенно это касается новичков в С/С++.

5.5 Разное
65. Следует избегать «магических» чисел в коде. Числа, отличные от 0 или 1, следует объявлять как именованные константы.

Если число само по себе не имеет очевидного значения, читаемость улучшается путём введения именованной константы. Другой подход - создание метода, с помощью которого можно было бы осуществлять доступ к константе.

66. Константы с плавающей точкой следует записывать с десятичной точкой и с указанием по крайней мере одной цифры после запятой.

Double total = 0.0; // НЕ РЕКОМЕНДУЕТСЯ: double total = 0; double speed = 3.0e8; // НЕ РЕКОМЕНДУЕТСЯ: double speed = 3e8; double sum; : sum = (a + b) * 10.0;
Это подчёркивает различные подходы при работе с целыми числами и числами с плавающей точкой. С точки зрения математики, эти две модели совершенно различны и не совместимы.

А также (как это показано в последнем примере выше) делается акцент на типе переменной (sum) в том месте, где это не является очевидным.

67. Константы с плавающей точкой следует всегда записывать, по крайней мере, с одной цифрой до десятичной точки.

Система чисел и выражений в C++ заимствована из математики, и следует придерживаться традиционных форм записи, где это возможно. Помимо прочего, 0.5 - более читаемо, чем.5 (первый вариант никак не спутать с числом 5).

68. У функций нужно обязательно указывать тип возвращаемого значения.

Int getValue() // НЕЛЬЗЯ: getValue() { : }
Если это не указано явно, C++ считает, что возвращаемое значение имеет тип int. Никогда нельзя полагаться на это, поскольку такой способ может смутить программистов, не знакомых с ним.

69. Не следует использовать goto.

Этот оператор нарушает принципы структурного программирования. Следует использовать только в очень редких случаях (например, для выхода из глубоко вложенного цикла), когда иные варианты однозначно ухудшат читаемость.

70. Следует использовать «0» вместо «NULL».

NULL является частью стандартной библиотеки C и устарело в C++.

6 Оформление и комментарии

6.1 Оформление
71. Основной отступ следует делать в два пробела.

For (i = 0; i < nElements; i++) a[i] = 0;
Отступ в один пробел достаточно мал, чтобы отражать логическую структуру кода. Отступ более 4 пробелов делает глубоко вложенный код нечитаемым и увеличивает вероятность того, что строки придётся разбивать. Широко распространены варианты в 2, 3 или 4 пробела; причём 2 и 4 - более широко.

72. Блоки кода следует оформлять так, как показано в примере 1 (рекомендуется) или в примере 2, но ни в коем случае не так, как показано в примере 3. Оформление функций и классов должно следовать примеру 2.

While (!done) { doSomething(); done = moreToDo(); }

While (!done) { doSomething(); done = moreToDo(); }

While (!done) { doSomething(); done = moreToDo(); }
Пример 3 использует лишние отступы, что мешает ясному отображению логической структуры кода.

73. Объявления классов

Class SomeClass: public BaseClass { public: ... protected: ... private: ... }
Частное следствие из правила, указанного выше.

74. Определения методов следует оформлять следующим образом:

Void someMethod() { ... }

75. Конструкцию if-else следует оформлять следующим образом:

If (condition) { statements; } if (condition) { statements; } else { statements; } if (condition) { statements; } else if (condition) { statements; } else { statements; }
Следствие из правила, указанного выше. Причём написание else на той же строке, где стоит закрывающая фигурная скобка первого блока, не является запрещённым:

If (condition) { statements; } else { statements; }
Лучше каждую часть if-else помещать на отдельной строке. Это упрощает действия с кодом, например, перемещение блока else .

76. Цикл for следует оформлять следующим образом:

For (initialization; condition; update) { statements; }
Следствие из правила, указанного выше.

77. Цикл for с пустым телом следует оформлять следующим образом:

For (initialization; condition; update) ;
Делает акцент для читающего на том, что тело пусто. Однако циклов, не имеющих тела, следует избегать.

78. Цикл while следует оформлять следующим образом:

While (condition) { statements; }
Следствие из правила, указанного выше.

79. Цикл do-while следует оформлять следующим образом:

Do { statements; } while (condition);
Следствие из правила, указанного выше.

80. Конструкцию switch следует оформлять следующим образом:

Switch (condition) { case ABC: statements; // Отсутствует "break" case DEF: statements; break; case XYZ: statements; break; default: statements; break; }
Обратите внимание, что каждое слово case имеет отступ относительно всей конструкции, что помогает её выделить. Также обратите внимание на пробелы перед двоеточиями. Если где-то отсутствует ключевое слово break , то предупреждением об этом должен служить комментарий. Программисты часто забывают ставить это слово, поэтому случай нарочного его пропуска должен описываться специально.

81. Конструкцию try-catch следует оформлять следующим образом:

Try { statements; } catch (Exception& exception) { statements; }
Следствие из правила, указанного выше. Вопросы, касающиеся закрывающих фигурных скобок у конструкции if-else , применимы и здесь.

82. Если конструкция if-else содержит только одно выражение в теле, фигурные скобки можно опускать.

If (condition) statement; while (condition) statement; for (initialization; condition; update) statement;
Рекомендуется всё же не опускать фигурные скобки.

83. Возвращаемый функцией тип может располагаться над именем самой функции.

Void MyClass::myMethod(void) { : }
Так функции выровнены в одну колонку.

6.2 Пробелы
84.

Операторы следует отбивать пробелами.
- После зарезервированных ключевых слов языка C++ следует ставить пробел.
- После запятых следует ставить пробелы.
- Двоеточия следует отбивать пробелами.
- После точек с запятой в цикле for следует ставить пробелы.

A = (b + c) * d; // НЕ РЕКОМЕНДУЕТСЯ: a=(b+c)*d while (true) // НЕ РЕКОМЕНДУЕТСЯ: while(true) { ... doSomething(a, b, c, d); // НЕ РЕКОМЕНДУЕТСЯ: doSomething(a,b,c,d); case 100: // НЕ РЕКОМЕНДУЕТСЯ: case 100: for (i = 0; i < 10; i++) { // НЕ РЕКОМЕНДУЕТСЯ: for(i=0;i<10;i++){ ...
Выделяет отдельные части выражений. Улучшает читаемость. Сложно дать всеобъемлющий набор рекомендаций относительно пробелов в языке C++. Рекомендации выше должны показать общие принципы.

85. После имён методов может идти пробел, если далее следует другое имя.

DoSomething (currentFile);
Выделяет отдельные имена. Улучшает читаемость. Если далее нет никакого имени, пробел можно опускать (doSomething()).

Другим подходом является указание пробела сразу после открывающей скобки. Использующие его также обычно ставят пробел и перед закрывающей скобкой: doSomething(currentFile);. Это позволяет выделять отдельные имена; пробел перед закрывающей скобкой выглядит неестественно, но без него выражение выглядит несимметрично (doSomething(currentFile);).

86. Логические блоки в коде следует отделять пустой строкой.

Matrix4x4 matrix = new Matrix4x4(); double cosAngle = Math.cos(angle); double sinAngle = Math.sin(angle); matrix.setElement(1, 1, cosAngle); matrix.setElement(1, 2, sinAngle); matrix.setElement(2, 1, -sinAngle); matrix.setElement(2, 2, cosAngle); multiply(matrix);
Улучшает читаемость.

Это позволяет лучше их выделять.

88. Переменные в объявлениях можно выравнивать.

AsciiFile* file; int nPoints; float x, y;
Улучшает читаемость. Чётче видны пары тип - переменная .

89. Используйте выравнивание везде, где это улучшает читаемость.

If (a == lowValue) compueSomething(); else if (a == mediumValue) computeSomethingElse(); else if (a == highValue) computeSomethingElseYet(); value = (potential * oilDensity) / constant1 + (depth * waterDensity) / constant2 + (zCoordinateValue * gasDensity) / constant3; minPosition = computeDistance(min, x, y, z); averagePosition = computeDistance(average, x, y, z); switch (value) { case PHASE_OIL: strcpy(phase, "Oil"); break; case PHASE_WATER: strcpy(phase, "Water"); break; case PHASE_GAS: strcpy(phase, "Gas"); break; }
Есть множество случаев, когда код можно дополнительно выравнивать, даже если это нарушает установленные ранее правила.

6.3 Комментарии
90. Сложный код, написанный с использованием хитрых ходов, следует не комментировать, а переписывать!

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

91. Все комментарии следует писать на английском.

В интернациональной среде английский - предпочтительный язык.

92. Используйте // для всех комментариев, включая многострочные.
// Комментарий, расположенный // на нескольких строках.
Если следовать этой рекомендации, многострочные комментарии /* */ можно использовать для отладки и иных целей.

После // следует ставить пробел, а сам комментарий следует начинать писать с большой буквы завершать точкой.

93. Комментарии следует располагать так, чтобы они относились к тому, что они описывают.

94. Комментарии к классам и заголовкам методов следует делать в соответствии с соглашениями JavaDoc.

Программисты на языке Java используют более развитый подход к документированию благодаря стандартному автоматическому средству Javadoc, которое является частью пакета разработки и позволяет автоматически создавать документацию в формате HTML из комментариев в коде.

Подобные средства есть и в C++. Они следуют тем же соглашениям о синтаксисе тегов, что и JavaDoc (см., например, Doc++ или Doxygen).

7 Ссылки

  • Code Complete, Steve McConnell - Microsoft Press
  • Programming in C++, Rules and Recommendations, M Henricson, e. Nyquist, Ellemtel (Swedish telecom):

Дадим некоторые пояснения. В языке Си любая пограмма, состоит из нескольких программных едениц и каждая из них - функция. Функции в Си подобны функциям или подпрограммам в Фортране или процедурам в Паскале, Имена функций выбираются произвольно (только латинскими буквами), но одно из них main , именно с нее начинается выполнение программы. Такая главная функция обычно обращается к другим функциям, которые находятся в одном файле с головной программой или извлекают из библиотеки предварительно подготовленных функций.Функция main не имеет аргументов, поэтому список ее выглядит так: () . Скобки { } обрамляют операоры, которые реализуют собственно алгоритм. Эти скобки аналогичны BEGIN - END в Паскале.
Строка int a,b,c; объявляет a,b,c переменными целого типа. Все используемые в программе переменные должны быть объявлены. Далее идут опрераторы присваивания к a значение 5 , а к b - 7 , с - значение их суммы. Значения переменных типа int лежат в диапазоне [-32768; 32767]. Функция printf выводит на экран: СУММА = 12 .

Рассмотрим теперь функцию scanf предназначенную для форматного ввода данных. Функция scanf в качестве фактических параметров использует адреса переменных, а не их значения. Для этого перед соответствующим параметром ставят знак & - символ взятия адресса. Например, &XL означает "адрес перменной XL ", а не значение, которое переменная имеет в данный момент.

Строка форматов функции scanf указывает, какие данные ожидаются на входе. Если функция встречает в форматной строке знак % , за которым следует символ преобразования, то она будет пропускать на входе символы до тех пор, пока не встретит какой-нибудь не пустой символ.

Предыдущяя программа страдает одним недостатком: программа вычисления суммы годится только для одного конкретного случая, когда a=5, b=7 . Улучшим ее, заменив соответствующие операторы присваивания вызовом функции scanf (пример 1.2) :

Форматная строка предписывает функции scanf ввести десятичное число, которое надо поместить в переменную a , затем через пробел ввести второе десятичное число, которое надо присвоить переменной b .Обратите внимание, что программа начинается со строки коминтарием: /* .. */ , транслятор пропускает любые символы между /* и */ и их можно использовать для пояснений.