Вы очень активны! это здорово!
Содержание:
Примеры того же самого без макросов
В C++ все-таки нету вложенных функций, поэтому нам придется эмулировать их синтаксис чем-либо еще. Поэтому зададимся другим вопросом: что именно выглядит так же, как функция, но не функция?
Ответа целых два: это, во-первых, вызов конструктора класса без с созданием объекта «в пустоту»:
К сожалению, есть два существенных «но», которые не позволяют использовать приведенный пример на практике:
1. Мы создаем по одному новому объекту на каждый вызов функции, что не есть хорошо. А ну как у меня цикл на много вызовов? Накладные расходы можно стерпеть на факт декларации такой штуковины, но никак не на использование.
2. Visual Studio со включенной оптимизацией компилятора просто-напросто вырежет создание inline_function() как бесполезное. Логика компилятора понятна (все равно этот создаваемый объект никто не будет использовать, так зачем его создавать?) но представляет опасность.
Однако есть еще штука под названием «переопределение операторов» — и мы можем переопределить двойные скобочки (по умному переопределенный оператор () называется «функтором», во как):
В этом примере уже почти все хорошо, кроме того, что класс можно сделать и анонимным, чтобы попусту не трепать имя вида
Вызов функции
При создании функции C ++ вы даете определение того, что должна делать функция. Чтобы использовать функцию, вам придется вызвать или вызвать эту функцию.
Когда программа вызывает функцию, управление программой передается вызываемой функции. Вызываемая функция выполняет определенную задачу, и когда выполняется оператор return или когда достигается завершающая ее конечная скобка, она возвращает управление программой обратно в основную программу.
Чтобы вызвать функцию, вам просто нужно передать необходимые параметры вместе с именем функции, а если функция возвращает значение, вы можете сохранить возвращаемое значение. Например,
Указатели типа void
В си существует особый тип указателей – указатели типа void или пустые указатели.
Эти указатели используются в том случае, когда тип переменной не известен. Так как void не имеет типа, то к нему не
применима операция разадресации (взятие содержимого) и адресная арифметика, так как неизвестно представление данных. Тем не менее,
если мы работаем с указателем типа void, то нам доступны операции сравнения.
Если необходимо работать с пустым указателем, то сначала нужно явно привести тип. Например
#include <conio.h>
#include <stdio.h>
int main() {
void *p = NULL;
int intVar = 10;
char charVar = 'A';
float floatVar = 24.3;
float *floatPtr = NULL;
p = &intVar;
*((int*) p) = 20;
printf("intVar = %d\n", intVar);
p = &charVar;
printf("charVar = %c\n", *((char*) p));
p = &floatVar;
floatPtr = (float*) p;
printf("floatVar = %.3f", *floatPtr);
getch();
}
Переменная не может иметь типа void, этот тип определён только для указателей. Пустые указатели нашли широкое применение при вызове
функций. Можно написать функцию общего назначения, которая будет работать с любым типом. Например, напишем функцию swap, которая обменивает
местами содержимое двух переменных. У этой функции будет три аргумента – указатели на переменные, которые необходимо обменять
местами и их размер.
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void swap(void *a, void *b, size_t size) {
char* tmp;
//создаём временную переменную для обмена
tmp = (char*) malloc(size);
memcpy(tmp, a, size);
memcpy(a, b, size);
memcpy(b, tmp, size);
free(tmp);
}
int main() {
float a = 10.f;
float b = 20.f;
double c = 555;
double d = 777;
unsigned long e = 2ll;
unsigned long f = 3ll;
printf("a = %.3f, b = %.3f\n", a, b);
swap(&a, &b, sizeof(float));
printf("a = %.3f, b = %.3f\n", a, b);
printf("c = %.3f, d = %.3f\n", c, d);
swap(&c, &d, sizeof(double));
printf("c = %.3f, d = %.3f\n", c, d);
printf("e = %ld, f = %ld \n", e, f);
swap(&e, &f, sizeof(unsigned long));
printf("e = %ld, f = %ld \n", e, f);
getch();
}
Наша функция может выглядеть и по-другому. Обойдёмся без дорогостоящего выделения памяти и будем копировать побайтно.
void swap(void *a, void *b, size_t size) {
char tmp;
size_t i;
for (i = 0; i < size; i++) {
tmp = *((char*) b + i);
*((char*) b + i) = *((char*) a + i);
*((char*) a + i) = tmp;
}
}
Пустые указатели позволяют создавать функции, которые возвращают и принимают одинаковые параметры, но имеют разное название. Это пригодится в дальнейшем, при изучении указателей на функции. Например
int cmpInt(void* a, void* b) {
return *((int*) a) - *((int*) b);
}
int cmpString(void *a, void* b) {
return strcmp((char*) a, (char*) b);
}
int cmpFloat(void* a, void* b) {
float fdiff = *((float*) a) - *((float*) b);
if (fabs(fdiff) < 0.000001f) {
return 0;
}
if (fdiff < 0) {
return -1;
} else {
return 1;
}
}
Q&A
Всё ещё не понятно? – пиши вопросы на ящик
Объявление переменной в C ++
Объявление переменной обеспечивает уверенность компилятора в том, что существует одна переменная, существующая с заданным типом и именем, чтобы компилятор продолжал следующую компиляцию, не требуя полной информации о переменной. Объявление переменной имеет смысл во время компиляции, компилятор нуждается в определении фактической переменной во время компоновки программы.
Объявление переменной полезно, когда вы используете несколько файлов, и вы определяете свою переменную в одном из файлов, которые будут доступны во время компоновки программы. Вы будете использовать ключевое слово extern для объявления переменной в любом месте. Хотя вы можете объявить переменную несколько раз в своей программе на C ++, но ее можно определить только один раз в файле, функцию или блок кода.
Пример:
Попробуйте следующий пример, в котором переменная была объявлена сверху, но она была определена внутри основной функции:
Когда приведенный выше код компилируется и выполняется, он производит следующий результат:
Такая же концепция применяется к объявлению функции, где вы указываете имя функции во время ее объявления, и ее фактическое определение может быть дано в другом месте. Например:
Зачем нужны лямбда-выражения?
Рассмотрим следующий пример:
#include <algorithm>
#include <array>
#include <iostream>
#include <string_view>
static bool containsNut(std::string_view str) // static в данном контексте означает внутреннее связывание
{
// Функция std::string_view::find() возвращает std::string_view::npos, если она не нашла подстроку.
// В противном случае, она возвращает индекс, где происходит вхождение подстроки в строку str
return (str.find(«nut») != std::string_view::npos);
}
int main()
{
std::array<std::string_view, 4> arr{ «apple», «banana», «walnut», «lemon» };
// std::find_if() принимает указатель на функцию
auto found{ std::find_if(arr.begin(), arr.end(), containsNut) };
if (found == arr.end())
{
std::cout << «No nuts\n»;
}
else
{
std::cout << «Found » << *found << ‘\n’;
}
return 0;
}
|
1 |
#include <algorithm> staticboolcontainsNut(std::string_view str)// static в данном контексте означает внутреннее связывание { // Функция std::string_view::find() возвращает std::string_view::npos, если она не нашла подстроку. // В противном случае, она возвращает индекс, где происходит вхождение подстроки в строку str return(str.find(«nut»)!=std::string_view::npos); } intmain() { std::array<std::string_view,4>arr{«apple»,»banana»,»walnut»,»lemon»}; // std::find_if() принимает указатель на функцию autofound{std::find_if(arr.begin(),arr.end(),containsNut)}; if(found==arr.end()) { std::cout<<«No nuts\n»; } else { std::cout<<«Found «<<*found<<‘\n’; } return; } |
Этот код перебирает массив строк в поисках первого попавшегося элемента, который содержит подстроку . Таким образом, результат выполнения программы:
Хотя это и рабочий код, но мы можем его улучшить.
Проблема кроется в том, что функция std::find() требует указатель на функцию в качестве аргумента. Из-за этого мы вынуждены определить новую функцию, которая будет использована только один раз, дать ей имя и поместить её в глобальную область видимости (т.к. функции не могут быть вложенными!). При этом она будет настолько короткой, что быстрее и проще понять её смысл, посмотрев лишь на одну строку кода, нежели из описания комментариев и её имени.
4.19 Помеченные Элементы в Инициализаторах
Стандартный C требует, чтобы элементы инициализатора появлялись в
фиксированном порядке, в том же самом, в котором элементы массива или
структуры инициализируются.
В GNU C вы можете дать элементы в любом порядке, указывая индексы
массива или имена полей структуры, к которым они применяются.
Чтобы указать индекс массива, напишите » или ‘
=’ перед значением элемента. Например,
Чтобы инициализировать диапазон элементов одним и тем же
значением, напишите ‘ = значение’. Например:
В инициализаторе структуры укажите имя инициализируемого поля с
помощью ‘имяструктуры:’ перед значением элемента. Пусть, например,
дана следующая структура:
Вы можете скомбинировать эту технику именования элементов с
обычной C инициализацией последовательных элементов. Каждый элемент
инициализатора, который не имеет метки, приеняется к следующему
элементу массива или структуры. Например,
RemarksRemarks
Оператор позволяет определить тип объекта во время выполнения.The operator allows the type of an object to be determined at run time.
Результатом является .The result of is a . Значение является ссылкой на объект, представляющий либо тип-ID , либо тип выраженияв зависимости от используемой формы.The value is a reference to a object that represents either the type-id or the type of the expression, depending on which form of is used. Дополнительные сведения см. в разделе класс type_info.For more information, see type_info Class.
Оператор не работает с управляемыми типами (абстрактными деклараторами или экземплярами).The operator doesn’t work with managed types (abstract declarators or instances). Сведения о получении Type указанного типа см. в разделе typeid.For information on getting the Type of a specified type, see typeid.
Оператор выполняет проверку во время выполнения, когда применяется к l-значению типа полиморфизма, где истинный тип объекта не может быть определен с помощью предоставленной статической информации.The operator does a run-time check when applied to an l-value of a polymorphic class type, where the true type of the object can’t be determined by the static information provided. К таким случаям относятся следующие.Such cases are:
-
Ссылка на классA reference to a class
-
Указатель, разыменованиеA pointer, dereferenced with
-
Указатель в индексе ( ).A subscripted pointer (). (Нельзя использовать индекс с указателем на полиморфизм типа.)(It’s not safe to use a subscript with a pointer to a polymorphic type.)
Если выражение указывает на тип базового класса, но объект фактически является типом, производным от базового класса, то ссылка на производный класс является результатом.If the expression points to a base class type, yet the object is actually of a type derived from that base class, a reference for the derived class is the result. Выражение должно указывать на полиморфизм типа (класс с виртуальными функциями).The expression must point to a polymorphic type (a class with virtual functions). В противном случае результатом является статический класс, на который ссылается выражение.Otherwise, the result is the for the static class referred to in the expression. Кроме того, указатель должен быть разыменован таким образом, чтобы использовался объект, на который он указывает.Further, the pointer must be dereferenced so that the object used is the one it points to. Без разыменования указателя результат будет для указателя, а не того, на который он указывает.Without dereferencing the pointer, the result will be the for the pointer, not what it points to. Например:For example:
Если выражение ссылается на указатель и значение этого указателя равно нулю, вызывает исключение bad_typeid.If the expression is dereferencing a pointer, and that pointer’s value is zero, throws a bad_typeid exception. Если указатель не указывает на допустимый объект, возникает исключение.If the pointer doesn’t point to a valid object, a exception is thrown. Указывает, что попытка анализа RTTI, вызвавшего ошибку, вызвана тем, что объект является каким-то недопустимым.It indicates an attempt to analyze the RTTI that triggered a fault because the object is somehow invalid. (Например, это неверный указатель, или код не был скомпилирован с помощью /GR).(For example, it’s a bad pointer, or the code wasn’t compiled with /GR).
Если выражение не является указателем, а не ссылкой на базовый класс объекта, результатом является ссылка, представляющая статический тип выражения.If the expression is not a pointer, and not a reference to a base class of the object, the result is a reference representing the static type of the expression. Статический тип выражения ссылается на тип выражения, так как он известен во время компиляции.The static type of an expression refers to the type of an expression as it is known at compile time. Семантика исполнения игнорируется при оценке статического типа выражения.Execution semantics are ignored when evaluating the static type of an expression. Кроме того, ссылки по возможности игнорируются при определении статического типа выражения:Furthermore, references are ignored when possible when determining the static type of an expression:
также может использоваться в шаблонах для определения типа параметра шаблона: can also be used in templates to determine the type of a template parameter:
4.4 Вложенные Функции
Вложенная функция — это функция, определенная внутри другой
функции. Имя вложенной функции является локальным в блоке, где она
определена. Например, здесь мы определяем вложенную функцию с именем
square и вызываем ее дважды:
Можно вызвать вложенную функцию из точки вне области действия ее
имени, сохранив ее адрес или передав адрес в другую функцию:
Если вы пытаетесь вызвать вложенную функцию с помощью ее адреса
после того, как объемлющая функция возвратила управление, все полетит к
чертям. Если вы пытаетесь вызвать ее после того, как объемлющая
область действия закончила работу, и если она ссылается на одну из
переменных, которые больше не лежат в области действия, может вам и
повезет, но неразумно рисковать. Однако, если вложенная функция не
ссылается ни на что, вышедшее из области действия, вы в безопасности.
GNU CC выполняет взятие адреса вложенной функции, используя
технику, называемую «trampolines». Бумага, описывающая ее, доступна из
‘maya.idiap.ch’ в директории ‘pub/tmb’ в файле ‘usenix88-lexic.ps.Z’.
Вложенная функция может делать переход на метку, наследуемую от
объемлющей функции, если метка явно объявлена в объемлющей функции
(см. Раздел ). Такой переход немедленно
возвращает в объемлющую функцию, покидая вложенную функцию, которая
сделала goto, а также любые промежуточные функции. Пример:
Параметры vs. Аргументы
В следующих 3-х уроках мы поговорим о параметрах и аргументах, поэтому давайте вкратце вспомним их определения.
Параметр функции (или «формальный параметр») — это переменная, создаваемая в объявлении функции:
void boo(int x); // объявление (прототип функции). x — это параметр
void boo(int x) // определение (также объявление). x — это параметр
{
}
|
1 |
voidboo(intx);// объявление (прототип функции). x — это параметр voidboo(intx)// определение (также объявление). x — это параметр { |
Аргумент (или «фактический параметр») — это значение, которое передает в функцию вызывающий объект (caller):
boo(7); // 7 — это аргумент, который передается в параметр x
boo(y+1); // выражение y+1 — это аргумент, который передается в параметр x
|
1 |
boo(7);// 7 — это аргумент, который передается в параметр x boo(y+1);// выражение y+1 — это аргумент, который передается в параметр x |
Когда функция вызывается, все параметры функции создаются как переменные, а значения аргументов копируются в параметры. Например:
void boo(int x, int y)
{
}
boo(4, 5);
|
1 |
voidboo(intx,inty) { boo(4,5); |
При вызове функции boo() с аргументами и , создаются параметры и функции boo() и им присваиваются соответствующие значения: и . Результатом будет и .
Примечание: В примере, приведенном выше, порядок обработки параметров в функции boo() будет справа налево, т.е. сначала создастся переменная и ей присвоится значение , а затем уже создастся переменная и ей присвоится значение . Порядок, в котором инициализируются параметры в круглых скобках функции, определяет каждый компилятор отдельно, так как С++ явно не указывает этот порядок обработки
С параметрами-переменными это не столь важно и критично, но если вы будете использовать в качестве параметров функции вызовы других функций (что является плохой практикой и не рекомендуется к использованию), то результат может быть неожиданным
Рассмотрим следующую программу:
#include <iostream>
int prinX()
{
std::cout << «x = 4\n»;
return 0;
}
int prinY()
{
std::cout << «y = 5\n»;
return 0;
}
void prinAll(int a, int b) {}
int main() {
prinAll(prinX(), prinY()); // в качестве параметров функции используются вызовы функций X() и Y()
return 0;
}
|
1 |
#include <iostream> intprinX() { std::cout<<«x = 4\n»; return; } intprinY() { std::cout<<«y = 5\n»; return; } voidprinAll(inta,intb){} intmain(){ prinAll(prinX(),prinY());// в качестве параметров функции используются вызовы функций X() и Y() return; } |
Результат выполнения программы:
Хотя параметры не объявлены внутри блока функции, они имеют локальную область видимости. Это означает, что они создаются при вызове функции и уничтожаются, когда блок функции завершается:
void boo(int x, int y) // x и y создаются здесь
{
} // x и y уничтожаются здесь
|
1 |
voidboo(intx,inty)// x и y создаются здесь { }// x и y уничтожаются здесь |
Существует 3 основных способа передачи аргументов в функцию:
передача по значению;
передача по адресу.
Мы рассмотрим каждый из этих способов по порядку.
4.22 Объявления Атрибутов Функций
В GNU C вы можете объявить определенные вещи о функциях,
вызываемых в вашей программе, которые помогают компилятору
оптимизировать вызовы функций и более внимательно проверять ваш код.
Ключевое слово __attribute__ позволяет вам указывать специальные
атрибуты при создании объявлений. За этим ключеным словом следует
описание атрибута в двойных скобках. В данный момент для функций
определены восемь атрибутов: noreturn, const, format, section,
constructor, destructor, unused и weak. Другие атрибуты, включая
section, поддерживаются для объявлений переменных (см. Раздел
) и для типов (см. Раздел ).
Вы можете указывать атрибуты с ‘__’, окружающими каждое ключевое
слово. Это позволяет вам использовать их в заголовочных файлах, не
заботясь о том, что могут быть макросы с тем же именем. Например, вы
можете использовать __noreturn__ вместо noreturn.
noreturn
Несколько стандартных библиотечных функций, таких как abort и
exit не могут вернуть управление. GNU CC знает это автоматически.
Некоторые программы определяют свои собственные функции, которые
никогда не возвращают управление. Вы можете объявить их noreturn,
чтобы сообщить компилятору этот факт. Например:
Ключевое слово noreturn указывает компилятору принять, что
функция fatal не может возвратить управление. Тогда он может делать
оптимизацию, несмотря на то, что бы случилось, если бы fatal вернула
управление. Это делает код немного лучше
Более важно, что это
помогает избегать ненужных предупреждений об инициализированных
переменных.
Атрибут noreturn не реализован в GNU C версии ранее чем 2.5.
const
Многие функции не используют никаких значений, кроме своих
аргументов, и не имеют эффекта, кроме возвращаемого значения. Такая
функция может быть объектом исключения общих подвыражений и
оптимизации циклов аналогично арифметической операции
Такую функцию
следует объявить с атрибутом const. Например,
говорит, что гипотетическую функцию square безопасно вызывать меньшее
количество раз, чем сказано в программе.
Атрибут const не реализован в GNU C версии ранее 2.5.
Заметим, что функция, которая имеет параметром указатель и
использует данные, на которые он указывает, не должна объявляться
const. Аналогично, функция, которая вызывает не-const функцию, обычно
не должна быть const.
format (тип, строка-индекс, первый-проверяемый)
Атрибут format указывает, что функция принимает аргументы в стиле
printf или scanf, которые должны быть проверены на соответствие со
строкой формата. Например, объявление
заставляет компилятор проверять параметры в вызове my_printf на
соответствие printf-стилю строки формата my_format.
Параметр ‘тип’ определяет, как строка формата интерпретируется, и
должен быть либо printf, либо scanf. Параметр ‘строка-индекс’
указывает, какой параметр является строкой формата (начиная с 1), а
‘первый-проверяемый’ является номером первого проверяемого аргумента.
Для функций, у которых аргументы не могут быть проверены (таких как
vprintf), укажите в качестве третьего параметра ноль. В этом случае
компилятор только проверяет строку формата на корректность.
Компилятор всегда проверяет формат для функций ANSI библиотеки
printf, fprintf, sprintf, scanf, vprintf, vfprintf, vsprintf, когда
такие предупреждения запрашиваются (используя ‘-Wformat’), так что нет
нужды модифицировать заголовочный файл ‘stdio.h’.
section («имя-секции»)
Обычно, компилятор помещает генерируемый код в секцию text.
Однако, иногда, вам нужны дополнительные секции или же вам нужно,
чтобы определенные функции оказались в специальных секциях. Атрибут
section указывает, что функция живет в определенной секции. Например,
объявление
помещает функцию foobar в секцию bar.
constructor
destructor
Атрибут constructor заставляет функцию вызываться автоматически
перед выполнением main (). Аналогично, атрибут destructor заставляет
функцию вызываться автоматически после того, как main () завершилась
или вызвана exit (). Функции с этими атрибутами полезны для
инициализации данных.
unused
Этот атрибут, примененный к функции, означает, что функция,
возможно, может быть неиспользуемой. GNU CC не будет порождать
предупреждение для этой функции.
weak
Атрибут weak приводит к тому, что объявление будет порождаться как
слабый символ, а не глобальный. Это прежде всего полезно для
определения бибилиотечных функций, которые могут быть переопределены
пользовательским кодом, хотя это может быть использовано и с
объявлениями не-функций.
alias («назначение»)
Атрибут alias заставляет породить объявление как синоним другого
символа, который должен быть указан. Например,
объявляет ‘f’ слабым синонимом для ‘__f’.
Лямбда-выражения и анонимные функции
Лямбда-выражения и анонимные функции — это «функциональный литерал», то есть необъявленная функция, которая немедленно
используется в качестве выражения. Рассмотрим следующий пример:
Функция является функцией высшего порядка, потому что она принимает функцию в качестве второго аргумента.
Этот второй аргумент является выражением, которое в свою очередь есть функция, то есть функциональный литерал.
Как функция он эквивалентен объявлению:
Синтаксис лямбда-выражений
Полная синтаксическая форма лямбда-выражений, таких как literals of function types, может быть представлена следующим образом:
Лямбда-выражение всегда заключено в скобки , объявление параметров при таком синтаксисе происходит внутри этих скобок и может включать в себя аннотации типов (опционально), тело функции начинается после знака . Если тип возвращаемого значения не , то в качестве возвращаемого типа принимается последнее (а возможно и единственное) выражение внутри тела лямбды.
Если мы вынесем все необязательные объявления, то, что останется, будет выглядеть следующим образом:
Передача лямбды в качестве последнего параметра
В Kotlin существует соглашение: если последний параметр функции является функцией, то лямбда-выражение, переданное в качестве соответствующего аргумента, может быть заключено в скобки:
Такой синтаксис также известен как trailing lambda.
Когда лямбда-выражение является единственным аргументом функции, круглые скобки могут быть опущены:
Ключевое слово : неявное имя единственного параметра
Очень часто лямбда-выражение имеет только один параметр.
Если компилятор способен самостоятельно определить сигнатуру, то объявление параметра можно опустить вместе с .
Параметр будет неявно объявлен под именем :
Возврат значения из лямбда-выражения
Мы можем вернуть значение из лямбды явно, используя оператор .
Либо неявно будет возвращено значение последнего выражения.
Таким образом, два следующих фрагмента равнозначны:
Это соглашение, вместе с , позволяет писать код в стиле LINQ:
Анонимные функции
Единственной особенностью синтаксиса лямбда-выражений, о которой ещё не было сказано, является способность определять и назначать возвращаемый функцией тип. В большинстве случаев в этом нет особой необходимости, потому что он может быть вычислен автоматически. Однако, если у вас есть потребность в определении возвращаемого типа, вы можете воспользоваться альтернативным синтаксисом: анонимной функцией.
Объявление анонимной функции выглядит очень похоже на обычное объявление функции, за исключением того, что её имя опущено. Тело такой функции может быть описано и выражением (как показано выше), и блоком:
Параметры функции и возвращаемый тип обозначаются таким же образом, как в обычных функциях. Правда, тип параметра может быть опущен, если его значение следует из контекста:
Аналогично и с типом возвращаемого значения: он вычисляется автоматически для функций-выражений или же должен быть определён вручную (если не является типом ) для анонимных функций, которые имеют в себе блок.
Обратите внимание, что параметры анонимных функций всегда заключены в круглые скобки. Приём, позволяющий оставлять параметры вне скобок, работает только с лямбда-выражениями
Одним из отличий лямбда-выражений от анонимных функций является поведение оператора (). Слово return , не имеющее метки (), всегда возвращается из функции, объявленной ключевым словом fun. Это означает, что return внутри лямбда-выражения возвратит выполнение к функции, включающей в себя это лямбда-выражение. Внутри анонимных функций оператор return, в свою очередь, выйдет, собственно, из анонимной функции.
Замыкания
Лямбда-выражение или анонимная функция (так же, как и или ) имеет доступ к своему замыканию, то есть к переменным, объявленным вне этого выражения или функции. Переменные, захваченные в замыкании, могут быть изменены в лямбде:
Рекомендации по созданию функций
Разрабатывая программы, вы постоянно будете работать с функциями, поэтому важно научиться делать их максимально полезными. Вот несколько рекомендаций:
- Называйте их так, чтобы было сразу понятно, что они делают. При этом лучше использовать повелительное наклонение, то есть напечатайФакториал (), а не печатьФакториала ().
- Делайте функции как можно меньше и универсальнее.
- Если есть вероятность, что часть кода может повториться несколько раз, то сразу выносите её в отдельную функцию.
И старайтесь как можно больше практиковаться, чтобы закрепить новые знания. В этой серии статей описываются базовые возможности программирования, которые пригодятся вам на протяжении всей карьеры. В следующем материале мы разберёмся, как упростить повторяющийся код с помощью циклов.
Ну а если вы хотите углубить свои знания, набраться опыта на реальных проектах и получать обратную связь по своему коду от опытных разработчиков, то записывайтесь на наш курс по программированию на C++.
Встраиваемые функции
В базовом языке Си директива препроцессора #define позволяет использовать макроопределения для записи вызова небольших часто используемых конструкций. Некорректная запись макроопределения может приводить к ошибкам, которые очень трудно найти. Макроопределения не позволяют определять локальные переменные и не выполняют проверки и преобразования аргументов.
Если вместо макроопределения использовать функцию, то это удлиняет объектный код и увеличивает время выполнения программы.
Кроме того, при работе с макроопределениями необходимо тщательно проверять раскрытия макросов, например
#define SUMMA(a, b) a + brez = SUMMA(x, y)*10;
rez = x + y*10;
inlineinline
1234
inline double SUMMA(double a, double b){ return(a + b);}
rez = SUMMA(x,y)*10;
rez=(x+y)*10;
- Определение и объявление функций должны быть совмещены и располагаться перед первым вызовом встраиваемой функции.
- Имеет смысл определять inline только очень небольшие функции, поскольку любая inline-функция увеличивает программный код.
- Различные компиляторы накладывают ограничения на сложность встраиваемых функций. Компилятор сам решает, может ли функция быть встраиваемой. Если функция не может быть встраиваемой, компилятор рассматривает ее как обычную функцию.
Таким образом, использование ключевого слова inline для встраиваемых функций и ключевого слова const для определения констант позволяют практически исключить директиву препроцессора #define из употребления.
Язык C++