- •Цикл for
- •Цикл while
- •Цикл do…while
- •Оператор goto
- •Указатели предназначены для хранения адресов областей памяти, а cсылка представляет собой синоним имени, указанного при инициализации ссылки. Далее подробно рассматриваются эти важные темы.
- •Указатель на функцию
- •Указатель на объект
- •Указатель на void
- •Локальные переменные
Указатели предназначены для хранения адресов областей памяти, а cсылка представляет собой синоним имени, указанного при инициализации ссылки. Далее подробно рассматриваются эти важные темы.
Когда компилятор обрабатывает оператор определения переменной, например, int i=10;, он выделяет память в соответствии с типом (int) и инициализирует ее указанным значением (10). Все обращения в программе к переменной по ее имени (i) заменяются компилятором на адрес области памяти, в которой хранится значение переменной. Программист может определить собственные переменные для хранения адресов областей памяти. Такие переменные называются указателями.
Указатель на функцию
Указатель на функцию содержит адрес в сегменте кода, по которому располагается исполняемый код функции, то есть адрес, по которому передается управление при вызове функции. Указатели на функции используются для косвенного вызова функции (не через ее имя, а через обращение к переменной, хранящей ее адрес), а также для передачи имени функции в другую функцию в качестве параметра. Указатель функции имеет тип «указатель функции, возвращающей значение заданного типа и имеющей аргументы заданного типа»:
тип (*имя) ( список_типов_аргументов );
Например, объявление:
int (*fun) (double, double);
задает указатель с именем fun на функцию, возвращающую значение типа int и имеющую два аргумента типа double.
Указатель на объект
Указатель на объект содержит адрес области памяти, в которой хранятся данные определенного типа (основного или составного). Простейшее объявление указателя на объект (в дальнейшем называемого просто указателем) имеет вид:
тип *имя;
где тип может быть любым, кроме ссылки и битового поля, причем тип может быть к этому моменту только объявлен, но еще не определен (следовательно, в структуре, например, может присутствовать указатель на структуру того же типа).
Звездочка относится непосредственно к имени, поэтому для того, чтобы объявить несколько указателей, требуется ставить ее перед именем каждого из них. Например, в операторе
int *a, b, *c;
описываются два указателя на целое с именами а и с, а также целая переменная b.
Размер указателя зависит от модели памяти. Можно определить указатель на указатель и т. д.
Указатель на void
Указатель на void применяется в тех случаях, когда конкретный тип объекта, адрес которого требуется хранить, не определен (например, если в одной и той же переменной в разные моменты времени требуется хранить адреса объектов различных типов).
Указателю на void можно присвоить значение указателя любого типа, а также сравнивать его с любыми указателями, но перед выполнением каких-либо действий с областью памяти, на которую он ссылается, требуется преобразовать его к конкретному типу явным образом.
9-10) Динамическое выделение памяти необходимо для эффективного использования памяти компьютера. Например, мы написали какую-то программку, которая обрабатывает массив. При написании данной программы необходимо было объявить массив, то есть задать ему фиксированный размер (к примеру, от 0 до 100 элементов). Тогда данная программа будет не универсальной, ведь может обрабатывать массив размером не более 100 элементов. А если нам понадобятся всего 20 элементов, но в памяти выделится место под 100 элементов, ведь объявление массива было статическим, а такое использование памяти крайне не эффективно.
В С++ операции new и delete предназначены для динамического распределения памяти компьютера. Операция new выделяет память из области свободной памяти, а операция delete высвобождает выделенную память. Выделяемая память, после её использования должна высвобождаться, поэтому операции new и delete используются парами. Даже если не высвобождать память явно, то она освободится ресурсами ОС по завершению работы программы. Рекомендую все-таки не забывать про операцию delete.
// пример использования операции new
int *ptrvalue = new int;
//где ptrvalue – указатель на выделенный участок памяти типа int
//new – операция выделения свободной памяти под создаваемый объект.
Операция new создает объект заданного типа, выделяет ему память и возвращает указатель правильного типа на данный участок памяти. Если память невозможно выделить, например, в случае отсутствия свободных участков, то возвращается нулевой указатель, то есть указатель вернет значение 0. Выделение памяти возможно под любой тип данных: int, float, double, char и т. д.
// пример использования операции delete:
delete ptrvalue;
// где ptrvalue – указатель на выделенный участок памяти типа int
// delete – операция высвобождения памяти
11)
#include <conio.h>
#include <iostream.h>
#include <stdlib.h>
//Сразу после описания директив объявляем прототипы наших функций
void clear(); //Прототип создаваемой функции clear()
int sum(int,int); // Прототип создаваемой функции sum(int a, int b)
int main()
{
clear();
cout<<sum(6,4); //передаем в функцию в качестве параметров 6 и 4. Функция их складывает и возвращает сумму
return 0;
}
void clear() //Создаем функцию очистки экрана.
{
system("cls"); //Очистка экрана - аналог С++ первая программа Очистка экрана clrscr();
return;
}
int sum(int a,int b) //Создаем следующую функцию сложения 2 чисел
{
return a+b; //Принимаем параметры и выполняем суммирование. После чего возвращаем результат сложения
}
======================
Вот и всё. Такой подход позволяет писать программы сверху вниз. Главное обратить внимание на то, что в объявлении прототипа функции в C++ мы после объявления ставим точку с запятой. Сами прототипы функций мы описываем подобно директивам, удобно прописывать их сразу вслед за директивами. Еще важно понимать, прототип функции указывает на тип, возвращаемый функцией и тип каждого параметра
Для более простого восприятия объясню коротко
функция int my_function (int a) - прототип int my function (int);
функция void my_function(int a,int b,int c) - прототип void my_function(int,int,int);
Какой тип у функции - такой тип у прототипа
Какие типы у параметров функции - такие типы указываем в скобках прототипа
Конечно у начинающих изучение С++ могут возникнуть вопросы по изучению функций и прототипов, но понять их не сложно. Понять что такое функции и как их использовать очень важно.
Если вы вызываете функцию до ее описания без прототипов - то компилятор сообщит о синтаксической ошибке
Если вы описали прототип, но не написали для него функцию - то компилятор сообщит об ошибке. Это стоит учитывать.
• Функции группируют связанные операторы для выполнения определенной задачи.
• Ваша программа вызывает функцию, обращаясь к ее имени, за которым следуют круглые скобки, например bеер ().
• После завершения обработки большинство функций возвращают значение определенного типа, например int или float, которое программа может проверить или присвоить переменной.
• Ваши программы передают параметры (информацию) функциям, например имя, возраст или оклад служащего, заключая параметры в круглые скобки, которые следуют за именем функции.
• C++ использует прототипы функций для определения типа возвращаемого функцией значения, а также количества и типов параметров, передаваемых функции.
функция параметров не принимает.В С++ void в этом случае, писать не обязательно, в отличие от Си.
12) ============================
#include <conio.h>
#include <iostream.h>
//Определяем прототипы функций
int sum_value(int); //Функция будет принимать обычный параметр
int sum_ref(int &); //Функция будет принимать ссылку
int sum_ptr(int *); //Функция будет принимать указатель
//Определяем функции. 3 функции по прототипам + main
int sum_value(int x) //В переменную x присваивается значение извне. x- обычный параметр
{
x=x+1; //x увеличивается на один
return x; //возвращаем полученный x
}
int sum_ref(int &x) //В функцию передается ссылка, она ссылается на объект извне
{
x=x+1; //Увеличили x на один
return x; //возвращаем полученный x
}
int sum_ptr(int *x) //В функцию передается указатель. Указатель это адрес переменной извне
{
*x=*x+1; //Для того, чтоб работать с указателем как с переменной, нужно его разыменовать
return *x; //Если не прописать звездочки, будет выведен адрес, а не значение объекта
}
//Внутри функции main функции вызываются по очереди
void main()
{
clrscr();
int value=10; //Переменная value будет выступит передаваемым параметром в функцию
cout<<sum_value(value)<<endl; //Первое обращение. Передача в функцию обычного параметра
cout<<value<<endl<<endl;
cout<<sum_ref(value)<<endl; //Второе обращение. Передача в функцию параметра ссылки
cout<<value<<endl<<endl;
cout<<sum_ptr(&value)<<endl; //Третье обращение. Передача в функцию параметра указателя
cout<<value<<endl<<endl;
getch();
return;
}
============================
При первом обращении в функцию передается параметр, равный value. Внутри функции этот параметр как эстафетную палочку принимает переменная x и вся обработка идет с этим x, а сама переменная value тихо отходит курить в сторонку "Вдруг пригожусь" При этом переменная x является локальной переменной, т.к. объявлена внутри функции, а значит всё что происходит с ней, всё происходит только в пределах этой функции. При попытке вывести на экран value - value снова играет главную роль, но она же просто курила в сторонке и никто ее не трогал. Как было ей присвоено 10, так и осталось
При втором обращении в качестве параметра передается ссылка. Так обращение к ссылке точно такое же как обращение к переменной, то такой вызов совершенно такой же как и первый, НО сама работа программы при таком вызове происходит по другому. Так как функция принимает параметр в качестве ссылки, то получается что-то типа Ссылка говорит объекту: "Тебе надо, ты и делай". Т.е. выходит так, что в отличие от первого варианта, объект только собрался курить в сторонке, а ссылка ему сразу пинком под зад: "Я курить, а если ничего не сделаем звездюлей тебе вешать будут". В общем получается, что value теперь как входной, так и выходной параметр. При попытке вывода на экран выведется 11, потому что обрабатываться будет сам параметр, а не ссылка на него
Третий вариант по работе точно такой же как и второй, только сам вызов происходит немного по другому. Так как указатель сам по себе адрес переменной, то в вызове функции идет обращение к адресу параметра. Внутри функции использовано разыменование, так как для работы с самим объектом нужно это самое разыменование.
Что я еще могу сказать. Первый вариант можно привести ко второму или третьему, если например, объявить глобальную переменную (int z), после этого внутри функции присвоить в нее полученную x, а после вызова функции из тела программы написать value=x., а оно надо? можно еще написать value=sum_value(value) , но удобнее использовать ссылки
Передача обычного параметра в функцию = входной параметр
Передача в функцию ссылки/указателя = как входной, так и выходной параметр
Говорят, что основное назначение ссылки заключается в упрощении процесса изменения параметров внутри функции. Мне кажется, что доброй половине начинающих такая фраза совершенно ни о чем
Я бы говорил так: основное назначение ссылки упростить изменение параметра передаваемого в функцию, изменяя принимаемый параметр вне функции, но обрабатывая его внутри неё.
13) ============================
#include <conio.h>
#include <iostream.h>
int func(int &N) //Параметр переданный в N будет обработан напрямую
{
N=N/2; //Делим переданный параметр в функцию пополам
return N; //Возвращаем вычисленное значение
}
void main()
{
clrscr();
int value=100; //value будем передавать как параметр вовнутрь функции
func(value, value=value/2, value=value*3); //Такой синтаксис допускается компилятором
cout<<value<<endl; //Выводим value на экран
getch();
return;
}
============================
Не знаю кому и когда такое надо, но возможность такая есть. Думаю не корректно говорить, что каждый такой элемент внутри функции обрабатывается как параметр. Насколько вижу я глазами новичка: В функцию как и должен, передается только один параметр, который обрабатывается функцией. За этот параметр передается крайний левый элемент из скобок. (value, value=...). Если изучать код дальше, то видно, что функция работает со ссылкой на то, что туда передается, значит то что я туда передаю при окончании работы функции поменяет свое значение в главной программе.
После срабатывания функции, value поменял свое значение на то что рассчитала функция и дальше я коверкаю код таким вот способом, где внутри параметров вместо параметров выполняю обычное присваивание в этот value своего значения, причем переприсваиваю значение дважды. Это можно и нужно было сделать после описания функции, но было написано внутри.
Возможно читающий посетитель этого блога встретит подобное написание программы и уже будет подготовлен к тому как правильно прочитать этот нелепый код. Мой совет - так не делать.
Я пришел к тому что:
Несмотря на то что выглядит, что функция принимает несколько параметров, принимает она только столько сколько ей сказано и эти параметры идут первыми внутри скобок
Если функции сказано принять N параметров, а принимает она N+1 или больше, то то что входит за этот N параметрами как таковыми не являются
14) Глобальные переменные
Глобальные переменные - это переменные, которые определены за пределами любой функции:
int x = 0; // глобальная переменная
int main () { код main }
void simple_function ()
{
x = 5;
}
Здесь переменная x видна из любой функции программы, в том числе и из simple_function. Что значит: "функция видит переменную"? Это значит, что внутри функции можно обращаться к этой переменной.
Глобальные переменные существуют с того момента как они встретились в коде и до конца программы. Т.е. если глобальную переменную объявить после какой-нибудь функции, то в этой функции данную еременную нельзя будет использовать:
int main () { код main }
void simple_function ()
{
x = 5 // компилятор выдаст ошибку, переменная ещё не объявлена
}
int x = 5; // объявление глобальной переменной
int getX ()
{
return x; // getx видит переменную x
}
Переменная x объявлена после функций main и simple_function, а значит, в этих функциях её не видно.
