Программа «Hello World» является распространенным примером, который можно просто использовать для проверки наличия компилятора и библиотеки. Он использует стандартную библиотеку C ++ с std::cout
из <iostream>
и имеет только один файл для компиляции, что минимизирует вероятность возможной ошибки пользователя во время компиляции.
Процесс компиляции C ++-программы по своей сути отличается между компиляторами и операционными системами. В разделе « Компиляция и построение» содержатся сведения о том, как скомпилировать код C ++ на разных платформах для различных компиляторов.
Версия | стандарт | Дата выхода |
---|---|---|
С ++ 98 | ISO / IEC 14882: 1998 | 1998-09-01 |
C ++ 03 | ISO / IEC 14882: 2003 | 2003-10-16 |
C ++ 11 | ISO / IEC 14882: 2011 | 2011-09-01 |
C ++ 14 | ISO / IEC 14882: 2014 | 2014-12-15 |
C ++ 17 | TBD | 2017-01-01 |
C ++ 20 | TBD | 2020-01-01 |
Эта программа печатает Hello World!
к стандартному потоку вывода:
#include <iostream>
int main()
{
std::cout << "Hello World!" << std::endl;
}
Смотрите на живую в Колиру .
Давайте подробно рассмотрим каждую часть этого кода:
#include <iostream>
- это препроцессорная директива, которая включает в себя содержимое стандартного файла iostream
заголовочного файла C ++.
iostream
- это стандартный файл заголовка библиотеки, который содержит определения стандартных потоков ввода и вывода. Эти определения включены в пространство имен std
, как описано ниже.
Стандартные потоки ввода / вывода (I / O) обеспечивают способы ввода и вывода программ во внешнюю систему - обычно терминал.
int main() { ... }
определяет новую функцию с именем main
. По соглашению main
функция называется исполнением программы. В программе на C ++ должна быть только одна main
функция, и она должна всегда возвращать число типа int
.
Здесь int
- это то, что называется возвращаемым типом функции . Значение, возвращаемое main
функцией, - это код выхода.
По соглашению код выхода программы 0
или EXIT_SUCCESS
интерпретируется как успех системой, выполняющей программу. Любой другой код возврата связан с ошибкой.
Если оператор return
не присутствует, main
функция (и, следовательно, сама программа) возвращает 0
по умолчанию. В этом примере нам не нужно явно писать return 0;
,
Все остальные функции, кроме тех, которые возвращают тип void
, должны явно возвращать значение в соответствии с их типом возвращаемого значения или иначе не должны возвращаться вообще.
std::cout << "Hello World!" << std::endl;
печатает «Hello World!» к стандартному потоку вывода:
std
- пространство имен , а ::
- оператор разрешения области, который позволяет искать объекты по имени в пространстве имен.
Существует много пространств имен. Здесь мы используем ::
чтобы показать, что мы хотим использовать cout
из пространства имен std
. Для получения дополнительной информации см. Оператор разрешения области действия - Документация Microsoft .
std::cout
- это стандартный объект потока вывода , определенный в iostream
, и он выводит на стандартный вывод ( stdout
).
<<
является в этом контексте оператором вставки потока , так называемым, потому что он вставляет объект в объект потока .
Стандартная библиотека определяет оператора <<
для выполнения ввода данных для определенных типов данных в выходные потоки. stream << content
вставляет content
в поток и возвращает тот же, но обновленный поток. Это позволяет привязывать потоковые вставки: std::cout << "Foo" << " Bar";
печатает «FooBar» на консоли.
"Hello World!"
является символьным строковым литералом или «текстовым литералом». Оператор вставки потока для символьных строковых литералов определяется в файле iostream
.
std::endl
- специальный объект манипулятора ввода-вывода , также определенный в файле iostream
. Вставка манипулятора в поток изменяет состояние потока.
Манипулятор потока std::endl
выполняет две функции: сначала он вставляет символ конца строки, а затем очищает буфер потока, чтобы текст отображался на консоли. Это гарантирует, что данные, вставленные в поток, действительно отображаются на консоли. (Данные о потоке обычно хранятся в буфере, а затем «размываются» партиями, если вы немедленно не отключите флеш.)
Альтернативный метод, который позволяет избежать флеша, заключается в следующем:
std::cout << "Hello World!\n";
где \n
- последовательность escape символов для символа новой строки.
Точка с запятой ( ;
) уведомляет компилятор о завершении оператора. Все инструкции C ++ и определения классов требуют конечной / конечной точки с запятой.
Комментарий - это способ помещать произвольный текст внутри исходного кода, не компилятор C ++ интерпретирует его с любым функциональным значением. Комментарии используются, чтобы дать представление о дизайне или методе программы.
В C ++ есть два типа комментариев:
Двойная последовательность косой черты //
будет отмечать весь текст до появления новой строки в виде комментария:
int main()
{
// This is a single-line comment.
int a; // this also is a single-line comment
int i; // this is another single-line comment
}
Последовательность /*
используется для объявления начала блока комментариев, а последовательность */
используется для объявления конца комментария. Весь текст между начальной и конечной последовательностями интерпретируется как комментарий, даже если текст в остальном является синтаксисом C ++. Они иногда называются комментариями «C-style», так как этот синтаксис комментария унаследован от языка предшественника C ++, C:
int main()
{
/*
* This is a block comment.
*/
int a;
}
В любом комментарии блока вы можете написать все, что хотите. Когда компилятор встречает символ */
, он завершает комментарий блока:
int main()
{
/* A block comment with the symbol /*
Note that the compiler is not affected by the second /*
however, once the end-block-comment symbol is reached,
the comment ends.
*/
int a;
}
Вышеприведенный пример является допустимым кодом C ++ (и C). Однако наличие дополнительного /*
внутри комментария блока может привести к предупреждению некоторых компиляторов.
Блочные комментарии также могут начинаться и заканчиваться в одной строке. Например:
void SomeFunction(/* argument 1 */ int a, /* argument 2 */ int b);
Как и во всех языках программирования, комментарии предоставляют несколько преимуществ:
Однако комментарии также имеют свои недостатки:
Потребность в комментариях может быть уменьшена путем написания четкого, самодокументирующего кода. Простым примером является использование пояснительных имен для переменных, функций и типов. Факторизация логически связанных задач в дискретные функции идет рука об руку с этим.
Во время разработки комментарии также могут использоваться для быстрого отключения частей кода без его удаления. Это часто полезно для тестирования или отладки, но не является хорошим стилем для чего угодно, кроме временных изменений. Это часто называют «комментированием».
Точно так же сохранение старых версий части кода в комментарии для справочных целей неодобрительно, поскольку оно загромождает файлы, при этом мало ценное по сравнению с исследованием истории кода через систему управления версиями.
Функция - это единица кода, представляющая последовательность операторов.
Функции могут принимать аргументы или значения и возвращать одно значение (или нет). Для использования функции для значений аргументов используется вызов функции, а использование самого вызова функции заменяется его возвращаемым значением.
Каждая функция имеет сигнатуру типа - типы ее аргументов и тип возвращаемого типа.
Функции вдохновлены понятиями процедуры и математической функции.
Функции часто предназначены для выполнения конкретной задачи. и может быть вызван из других частей программы. Функция должна быть объявлена и определена до ее вызова в другом месте программы.
Объявление функции объявляет о существовании функции с ее именем и сигнатурой типа компилятору. Синтаксис следующий:
int add2(int i); // The function is of the type (int) -> (int)
В приведенном выше примере int add2(int i)
объявляет компилятору следующее:
int
. add2
. int
. i
. Имя аргумента необязательно; декларация для функции также может быть следующей:
int add2(int); // Omitting the function arguments' name is also permitted.
В соответствии с одним правилом определения функция с определенной сигнатурой типа может быть объявлена или определена только один раз во всей кодовой базе C ++, видимой компилятору C ++. Другими словами, функции с определенной сигнатурой типа не могут быть переопределены - их нужно определять только один раз. Таким образом, недопустимо следующее: C ++:
int add2(int i); // The compiler will note that add2 is a function (int) -> int
int add2(int j); // As add2 already has a definition of (int) -> int, the compiler
// will regard this as an error.
Если функция ничего не возвращает, ее возвращаемый тип записывается как void
. Если он не принимает никаких параметров, список параметров должен быть пустым.
void do_something(); // The function takes no parameters, and does not return anything.
// Note that it can still affect variables it has access to.
Функция может быть вызвана после ее объявления. Например, следующая программа вызывает add2
со значением 2
в функции main
:
#include <iostream>
int add2(int i); // Declaration of add2
// Note: add2 is still missing a DEFINITION.
// Even though it doesn't appear directly in code,
// add2's definition may be LINKED in from another object file.
int main()
{
std::cout << add2(2) << "\n"; // add2(2) will be evaluated at this point,
// and the result is printed.
return 0;
}
Здесь add2(2)
является синтаксисом вызова функции.
Определение функции * похоже на объявление, за исключением того, что оно также содержит код, который выполняется, когда функция вызывается внутри ее тела.
Примером определения функции для add2
может быть:
int add2(int i) // Data that is passed into (int i) will be referred to by the name i
{ // while in the function's curly brackets or "scope."
int j = i + 2; // Definition of a variable j as the value of i+2.
return j; // Returning or, in essence, substitution of j for a function call to
// add2.
}
Вы можете создавать несколько функций с тем же именем, но с разными параметрами.
int add2(int i) // Code contained in this definition will be evaluated
{ // when add2() is called with one parameter.
int j = i + 2;
return j;
}
int add2(int i, int j) // However, when add2() is called with two parameters, the
{ // code from the initial declaration will be overloaded,
int k = i + j + 2 ; // and the code in this declaration will be evaluated
return k; // instead.
}
Обе функции вызываются одним и тем же именем add2
, но действительная функция, которая вызывается, напрямую зависит от количества и типа параметров в вызове. В большинстве случаев компилятор C ++ может вычислить, какую функцию вызывать. В некоторых случаях тип должен быть явно указан.
Значения по умолчанию для параметров функции могут быть указаны только в объявлениях функций.
int multiply(int a, int b = 7); // b has default value of 7.
int multiply(int a, int b)
{
return a * b; // If multiply() is called with one parameter, the
} // value will be multiplied by the default, 7.
В этом примере multiply()
можно вызвать с одним или двумя параметрами. Если задан только один параметр, b
будет иметь значение по умолчанию 7. По умолчанию аргументы должны быть помещены в последние аргументы функции. Например:
int multiply(int a = 10, int b = 20); // This is legal
int multiply(int a = 10, int b); // This is illegal since int a is in the former
Существуют специальные вызовы функций в C ++, которые имеют различный синтаксис, чем name_of_function(value1, value2, value3)
. Наиболее распространенным примером является оператор.
Некоторые специальные последовательности символов, которые будут сведены к вызову функции компилятором, например !
, +
, -
, *
, %
, и <<
и многое другое. Эти специальные символы обычно связаны с использованием не-программирования или используются для эстетики (например, символ +
обычно распознается как символ добавления как в программировании на С ++, так и в элементарной математике).
C ++ обрабатывает эти последовательности символов специальным синтаксисом; но, по существу, каждое возникновение оператора сводится к вызову функции. Например, следующее выражение C ++:
3+3
эквивалентно следующему вызову функции:
operator+(3, 3)
Все имена функций оператора начинаются с operator
.
В то время как в непосредственном предшественнике C ++ имена операторов C не могут присваиваться различным значениям, предоставляя дополнительные определения с сигнатурами разных типов, в C ++ это действительно. «Скрытие» дополнительных определений функций под одним уникальным именем функции называется перегрузкой оператора на C ++ и является относительно распространенным, но не универсальным соглашением на C ++.
Препроцессор является важной частью компилятора.
Он редактирует исходный код, вырезает несколько битов, меняет другие и добавляет другие вещи.
В исходных файлах мы можем включать директивы препроцессора. Эти директивы говорят препроцессору выполнять определенные действия. Директива начинается с # на новой строке. Пример:
#define ZERO 0
Первая препроцессорная директива, которую вы встретите, вероятно, является
#include <something>
директивы. Что он делает, так это все something
и вставляет в файл, где была указатель. Всемирная программа приветствия начинается с линии
#include <iostream>
Эта строка добавляет функции и объекты, которые позволяют использовать стандартный ввод и вывод.
Язык C, который также использует препроцессор, не имеет столько заголовочных файлов, как язык C ++, но на C ++ вы можете использовать все файлы заголовков C.
Следующей важной директивой, вероятно, является
#define something something_else
директивы. Это говорит препроцессору, что, когда он идет по файлу, он должен заменить каждое появление something
something_else
. Это также может сделать вещи похожими на функции, но это, вероятно, считается продвинутым C ++.
something_else
не требуется, но если вы определите something
как ничто, то внешние директивы препроцессора, все вхождения something
то исчезнут.
Это действительно полезно из-за #if
, #else
и #ifdef
. Формат для них будет следующим:
#if something==true
//code
#else
//more code
#endif
#ifdef thing_that_you_want_to_know_if_is_defined
//code
#endif
Эти директивы вставляют код, который находится в истинном бите, и удаляют ложные биты. это может быть использовано для того, чтобы иметь биты кода, которые включены только в определенные операционные системы, без необходимости переписывать весь код.
Исполняемый программный код C ++ обычно создается компилятором.
Компилятор - это программа, которая переводит код с языка программирования в другую форму, которая (более) выполняется непосредственно для компьютера. Использование компилятора для перевода кода называется компиляцией.
C ++ наследует форму своего процесса компиляции с его «родительского» языка, C. Ниже приведен список четырех основных шагов компиляции в C ++:
Многие компиляторы C ++ могут также объединять или объединять определенные части процесса компиляции для удобства или для дополнительного анализа. Многие программисты на С ++ будут использовать разные инструменты, но все инструменты, как правило, следуют этому обобщенному процессу, когда они участвуют в создании программы.
Ссылка ниже расширяет эту дискуссию и обеспечивает приятную графику. [1]: http://faculty.cs.niu.edu/~mcmahon/CS241/Notes/compile.html
В C ++ код должен быть объявлен или определен перед использованием. Например, следующее создает ошибку времени компиляции:
int main()
{
foo(2); // error: foo is called, but has not yet been declared
}
void foo(int x) // this later definition is not known in main
{
}
Это можно решить двумя способами: поставить определение или декларацию foo()
перед его использованием в main()
. Вот один пример:
void foo(int x) {} //Declare the foo function and body first
int main()
{
foo(2); // OK: foo is completely defined beforehand, so it can be called here.
}
Однако также возможно «форвард-объявить» функцию, помещая только объявление «прототипа» перед ее использованием и затем определяя тело функции позже:
void foo(int); // Prototype declaration of foo, seen by main
// Must specify return type, name, and argument list types
int main()
{
foo(2); // OK: foo is known, called even though its body is not yet defined
}
void foo(int x) //Must match the prototype
{
// Define body of foo here
}
Прототип должен указывать тип возвращаемого значения ( void
), имя функции ( foo
) и типы переменных списка аргументов ( int
), но имена аргументов НЕ требуются .
Одним из распространенных способов интегрирования этого в организацию исходных файлов является создание файла заголовка, содержащего все объявления прототипов:
// foo.h
void foo(int); // prototype declaration
а затем предоставить полное определение в другом месте:
// foo.cpp --> foo.o
#include "foo.h" // foo's prototype declaration is "hidden" in here
void foo(int x) { } // foo's body definition
а затем, после компиляции, связать соответствующий файл объекта foo.o
в скомпилированном объектном файле, где он используется в фазе связывания, main.o
:
// main.cpp --> main.o
#include "foo.h" // foo's prototype declaration is "hidden" in here
int main() { foo(2); } // foo is valid to call because its prototype declaration was beforehand.
// the prototype and body definitions of foo are linked through the object files
Ошибка «неразрешенного внешнего символа» возникает, когда прототип функции и вызов существуют, но тело функции не определено. Это может быть сложнее решить, поскольку компилятор не сообщит об ошибке до этапа окончательной компоновки, и он не знает, в какую строку перейти в код, чтобы показать ошибку.