Объявление пользовательского (typedef) типа структуры. Объявление переменных и массивов данного типа. Привести примеры

typedef-объявление
На стадии компиляции производится полная идентификация типов всех входящих в программу выражений. Даже отсутствие имени типа в объявлении как, например,
unsigned long MMM; // Вместо имени типа - комбинация модификаторов unsigned long.
восстанавливается транслятором в соответствии с принятыми в C++ правилами умолчания.
Помимо явного объявления типа в C++ предусмотрены дополнительные средства описания имён типов. Таким средством является typedef-объявление. С его помощью в программу можно ввести новые имена, которые затем используются для обозначения производных и основных типов.
typedef-объявление - это инструмент объявления. Средство ввода новых имён в программу, средство замены громоздких последовательностей имён в объявлениях (но не определениях!) новыми именами.
Синтаксис typedef-объявления как подмножества объявления представляется внушительным списком форм Бэкуса-Наура. Но при известной степени концентрации это нагромождение БНФ всё же можно разобрать:
Объявление::= [СписокСпецификаторовОбъявления][СписокОписателей]; СписокСпецификаторовОбъявления::= СпецификаторОбъявления [СписокСпецификаторовОбъявления] СпецификаторОбъявления::= typedef::= ***** СписокОписателей::= [СписокОписателей,] ОписательИнициализатор ОписательИнициализатор::= Описатель [Инициализатор] Описатель::= dИмя::= ***** dИмя::= Имя::= ОписанноеИмяТипа::= ***** ОписанноеИмяТипа::= Идентификатор СписокСпецификаторовТипа::= СпецификаторТипа [СписокСпецификаторовТипа] СпецификаторТипа::= ИмяПростогоТипа::= СпецификаторКласса::= *****
Таким образом, typedef-объявление является объявлением, которое начинается спецификатором typedef и состоит из последовательностей разнообразных спецификаторов объявления и описателей. Список описателей (элементы списка разделяются запятыми) может содержать языковые конструкции разнообразной конфигурации. В него могут входить описатели (в конце концов, это всего лишь разнообразные имена) с символами ptrОпераций (* и &), описатели, заключённые в круглые скобки, описатели в сопровождении заключённых в скобки списков объявлений параметров, описателей const и volatile, а также заключённых в квадратные скобки константных выражений (последние, надо полагать, предназначены для спецификации массивов).
В качестве примера рассмотрим, следующее typedef-объявление:
typedef int Step, *pInteger;
Это объявление начинается спецификатором typedef, содержит спецификатор объявления int и список описателей, в который входит два элемента: имя Step и имя pInteger, перед которым стоит символ ptrОперации *.
Объявление эквивалентно паре typedef-объявлений следующего вида:
typedef int Step; typedef int *pInteger;
В соответствии с typedef-объявлениями, транслятор производит серию подстановок, суть которых становится понятной из анализа примера, в котором пара операторов объявления
Step StepVal; extern pInteger pVal;
заменяется следующими объявлениями:
int StepVal; extern int * pVal;
На основе этого примера можно попытаться воспроизвести алгоритм подстановки:
после возможного этапа декомпозиции списка описателей typedef-объявления, в результате которого может появиться новая серия typedef-объявлений, транслятор переходит к анализу операторов объявлений;
в очередном операторе объявления выделяется идентификатор, стоящий на месте спецификатора объявления;
среди typedef-объявлений производится поиск соответствующего объявления, содержащего вхождение этого идентификатора в список описателей. Таким образом, транслятор находит соответствующий контекст для подстановки. Мы будем называть этот контекст контекстом замены. Контекст замены оказывается в поле зрения транслятора вместе с оператором объявления, в котором транслятор различает спецификатор объявления и описатель;
оператор объявления заменяется контекстом замены, в котором совпадающий со спецификатором объявления идентификатор заменяется соответствующим описателем.
//Функция Ввода
void Input()
{
cout << endl;
cout << "Imya - "; cin >> Imya;
cout << "Kolichestvo - "; cin >> Kol;
cout << "Cena - "; cin >> Cena;
cout << "Strana - "; cin >>Strana;
cout << endl;
}
//Функция Вывода
void Output()
{
cout << endl;
cout << "Imya -: '" << Imya << "'"<< endl;
cout << "Kolichestvo -: " << Kol << " shtuk" << endl;
cout << "Cena -: $" << Cena << endl;
cout << "Strana -: " << Strana << endl;
cout << endl

#include "stdafx.h"
#include "conio.h"
#include "iostream"
#include "string"
using namespace std;
//Создание структуры
struct Tovar
{
string Imya;
int Kol;

float Cena;
string Strana;
//Начальное значение структуры
Tovar ()
{
Imya = "NoName";
Kol = 0;
Cena = 0;
Strana = "None";
}
//Функция Ввода
void Input()
{
cout << endl;
cout << "Imya - "; cin >> Imya;
cout << "Kolichestvo - "; cin >> Kol;
cout << "Cena - "; cin >> Cena;
cout << "Strana - "; cin >>Strana;
cout << endl;
}
//Функция Вывода
void Output()
{
cout << endl;
cout << "Imya -: '" << Imya << "'"<< endl;
cout << "Kolichestvo -: " << Kol << " shtuk" << endl;
cout << "Cena -: $" << Cena << endl;
cout << "Strana -: " << Strana << endl;
cout << endl;
}
};
// Функция определения соответствия заданной и вводимой цены
void RaschodTovar (Tovar *mas, int size, float cen)
{
bool eden = false;
for (int i = 0; i cen) { mas[i].Output(); eden = true;}
}
if (eden == false) cout << "Net tovarov s cenoy vishe ukazannoy" << endl << endl;
}
//Главная функция
int main()
{
cout << "Vvedite probniy tovar - odin:" << endl;
Tovar PervTov; //Обращение к функциям ввода и вывода
PervTov.Input();
PervTov.Output();
int p;
cout << "Vvedite kolichestvo elementov tipa 'Tovar' - "; cin >> p;
Tovar *MnTovar = new Tovar[p]; //Создание динамического массива
for (int i=0;i<p;i++)
{
MnTovar [i].Input();
}
for (int i=0;i<p;i++)
{
MnTovar [i].Output();
}
float cen;
cout << "Vvedite cenu. Vse tovari, s cenoi vishe ukazannoy? budut vivedeni na ekran - "; cin >> cen;
RaschodTovar (MnTovar, p, cen); //Обращение функции отвеч за цену
return 0;
}

3.Ввод-вывод массива структур. Привести пример программы подсчета среднего арифметического по числовому полю в массиве структур.

Как вводить и выводить информацию
Операции ввода/вывода в языке Си организованы посредством библиотечных функций (причем их довольно много).
Самый простой механизм ввода - чтение по одному символу из стандартного входного потока (с клавиатуры) с помощью функции getchar(). Она имеет следующий прототип (т.е. описание заголовка):
int getchar(void);
Здесь определен тип единственного аргумента (void) и тип возвращаемого функцией значения (int).
Оператор вида:
х = getchar();
присваивает переменной х очередной вводимый символ. Переменная х должна иметь символьный или целый тип.
Другая функция - putchar(х) выдает значение переменной x в стандартный выходной поток (на экран дисплея). Функция putchar() имеет прототип:
int putchar(int);
Объявления getchar() и putchar() сделаны в заголовочном файле stdio.h, содержащем описания заголовков библиотечных функций стандартного ввода/вывода. Чтобы библиотечные функции стали доступны программе, к ней необходимо подключить данный файл. Подключение осуществляется с помощью директивы препроцессора
#include <stdio.h>
помещаемой в начало программы (подробнее см. в разделе 5).
Заметим, что для функции getchar() после выбора символа необходимо нажать клавишу <Enter>. Иногда это создает определенные неудобства. Функции getch() и getche() устраняют их. Они имеют следующие прототипы:
int getch(void);
int getche(void);
Обе эти функции вводят символ сразу же после нажатия соответствующей клавиши (здесь не надо дополнительно нажимать клавишу <Enter>). Отличие между ними заключается в том, что getche() отображает вводимый символ на экране дисплея, а getch() - нет. Прототипы этих функций содержатся в файле conio.h (консольный ввод/вывод). Для их использования файл conio.h также следует подключить к программе с помощью директивы #include.
Форматированный вывод данных
Функция printf() (прототип содержится в файле stdio.h) обеспечивает форматированный вывод. Ее можно записать в следующем формальном виде:
рrintf ("управляющая строка", аргумент _1, аргумент _2,...);
Управляющая строка содержит компоненты трех типов: обычные символы, которые просто копируются в стандартный выходной поток (выводятся на экран дисплея); спецификации преобразования, каждая из которых вызывает вывод на экран очередного аргумента из последующего списка; управляющие символьные константы.
Каждая спецификация преобразования начинается со знака % и заканчивается некоторым символом, задающим преобразование. Между знаком % и символом преобразования могут встречаться другие знаки в соответствии со следующим форматом:
% [признаки] [ширина_поля] [точность] [F|N|h|l|L] c_n
Все параметры в квадратных скобках не являются обязательными.
На месте параметра c_n (символ преобразования) могут быть записаны:
с - значением аргумента является символ;
d или i - значением аргумента является десятичное целое число;
е - значением аргумента является вещественное десятичное число в экспоненциальной форме вида 1.23e+2;
Е - значением аргумента является вещественное десятичное число в экспоненциальной форме вида 1.23E+2;
f - значением аргумента является вещественное десятичное число с плавающей точкой;
g (или G) - используется, как е или f, и исключает вывод незначащих нулей;
о - значением аргумента является восьмеричное целое число;
s - значением аргумента является строка символов (символы строки выводятся до тех пор, пока не встретится символ конца строки или же не будет, выведено число символов, заданное точностью);
u - значением аргумента является беззнаковое целое число;
х - значением аргумента является шестнадцатеричное целое число с цифрами 0,..., 9, а, b, с, d, е, f;
X - значением аргумента является шестнадцатеричное целое число с цифрами 0,..., 9, А, В, С, О, Е, F;
р - значением аргумента является указатель;
n - применяется в операциях

Ввести целое число (int a;), символ (char b;) и вещественное число (float t;) можно так:
scanf("%d", &a);
scanf("%c", &b);
scanf("%d%c%f",&a, &b, &t);

4.Общий формат объявление функции пользователя. Прототип функции. Типы возвращаемых значений. Привести примеры.

Вызов функции может обрабатываться двумя разными способами. Если она объявлена встроенной (inline), то компилятор подставляет в точку вызова ее тело. Во всех остальных случаях происходит нормальный вызов, который приводит к передаче управления ей, а активный в этот момент процесс на время приостанавливается. По завершении работы выполнение программы продолжается с точки, непосредственно следующей за точкой вызова. Работа функции завершается выполнением последней инструкции ее тела или специальной инструкции return.

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

Объявление функции состоит из типа возвращаемого значения, имени и списка параметров. Вместе эти три элемента составляют прототип. Объявление может появиться в файле несколько раз.

В нашем примере файл main.C не содержит определений abs(), min() и gcd(), поэтому вызов любой из них приводит к ошибке компиляции. Чтобы компиляция была успешной, их необязательно определять, достаточно только объявить:

int abs(int);

int min(int, int);

int gcd(int, int);

(В таком объявлении можно не указывать имя параметра, ограничиваясь названием типа.)

Объявления (а равно определения встроенных функций) лучше всего помещать в заголовочные файлы, которые могут включаться всюду, где необходимо вызвать функцию. Таким образом, все файлы используют одно общее объявление. Если его необходимо модифицировать, изменения будут локализованы. Вот так выглядит заголовочный файл для нашего примера. Назовем его localMath.h:

// определение функции находится в файле gcd.С

int gcd(int, int);

inline int abs(int i) {

return(i<0? -i: i);

}

inline int min(int vl.int v2) {

return(vl<v2? vl: v2);

}

В объявлении функции описывается ее интерфейс. Он содержит все данные о том, какую информацию должна получать функция (список параметров) и какую информацию она возвращает. Для пользователей важны только эти данные, поскольку лишь они фигурируют в точке вызова. Интерфейс помещается в заголовочный файл, как мы поступили с функциями min(), abs() и gcd().

При выполнении наша программа main.C, получив от пользователя значения:

Введите первое значение: 15

Введите второе значение: 123

выдаст следующий результат:

mm: 15

НОД: 3                                                                           

4. прототип функции и типы возвращаемых значений

Прототип функции

Прототип функции описывает ее интерфейс и состоит из типа возвращаемого функцией значения, имени и списка параметров. В данном разделе мы детально рассмотрим эти характеристики.

7.2.1. Тип возвращаемого функцией значения

Тип возвращаемого функцией значения бывает встроенным, как int или double, составным, как int& или double*, или определенным пользователем – перечислением или классом. Можно также использовать специальное ключевое слово void, которое говорит о том, что функция не возвращает никакого значения:

#include <string>

#include <vector> class Date { /* определение */ };

bool look_up(int *, int);

double calc(double);

int count(const string &, char);

Date& calendar(const char);

void sum(vector<int>&, int);

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

// массив не может быть типом возвращаемого значения

int[10] foo_bar();

Но можно вернуть указатель на первый элемент массива:

// правильно: указатель на первый элемент массива

int *foo_bar();

(Размер массива должен быть известен вызывающей программе.)

Функция может возвращать типы классов, в частности контейнеры. Например:

// правильно: возвращается список символов

list<char> foo_bar();

(Этот подход не очень эффективен. Обсуждение типа возвращаемого значения см. в разделе 7.4.)

Тип возвращаемого функцией значения должен быть явно указан. Приведенный ниже код вызывает ошибку компиляции:

// ошибка: пропущен тип возвращаемого значения

const is_equa1(vector<int> vl, vector<int> v2);

В предыдущих версиях С++ в подобных случаях считалось, что функция возвращает значение типа int. Стандарт С++ отменил это соглашение. Правильное объявление is_equal() выглядит так:

// правильно: тип возвращаемого значения указан

const bool is_equa1(vector<int> vl, vector<int> v2);

5.Формальные и фактические параметры в функции. Основные способы передачи параметров в функцию.

#include <iostream>
using namespace std;
int actual_p(int n)
{n = 1;
return n;
}
int formal_p(int &n)
{n = 1;
return n;
}
int main()
{int iActual = 0;
int iFormal = 0;
cout << "Начальное значение переменной: " << iActual << endl;
cout << "Аргумент передан как фактический параметр и изменён: " << actual_p(iActual) << endl;
cout << "Конечное значение переменной: " << iActual << endl;
cout << "Начальное значение переменной: " << iFormal << endl;
cout << "Аргумент передан как формальный параметр и изменён: " << formal_p(iFormal) << endl;
cout << "Конечное значение переменной: " << iFormal << endl;
return 0;
}
Результат работы программ:
Начальное значение переменной: 0
Аргумент передан как фактический параметр и изменён: 1
Конечное значение переменной: 0
Начальное значение переменной: 0
Аргумент передан как формальный параметр и изменён: 1
Конечное значение переменной: 1

Список-формальных-параметров - это последовательность объявлений формальных параметров, разделенная запятыми. Формальные параметры - это переменные, используемые внутри тела функции и получающие значение при вызове функции путем копирования в них значений соответствующих фактических параметров. Список-формальных-параметров может заканчиваться запятой (,) или запятой с многоточием (,...), это означает, что число аргументов функции переменно. Однако предполагается, что функция имеет, по крайней мере, столько обязательных аргументов, сколько формальных параметров задано перед последней запятой в списке параметров. Такой функции может быть передано большее число аргументов, но над дополнительными аргументами не проводится контроль типов.
Если функция не использует параметров, то наличие круглых скобок обязательно, а вместо списка параметров рекомендуется указать слово void.
Порядок и типы формальных параметров должны быть одинаковыми в определении функции и во всех ее объявлениях. Типы фактических параметров при вызове функции должны быть совместимы с типами соответствующих формальных параметров. Тип формального параметра может быть любым основным типом, структурой, объединением, перечислением, указателем или массивом. Если тип формального параметра не указан, то этому параметру присваивается тип int.
Для формального параметра можно задавать класс памяти register, при этом для величин типа int спецификатор типа можно опустить.
Идентификаторы формальных параметров используются в теле функции в качестве ссылок на переданные значения. Эти идентификаторы не могут быть переопределены в блоке, образующем тело функции, но могут быть переопределены во внутреннем блоке внутри тела функции.
При передаче параметров в функцию, если необходимо, выполняются обычные арифметические преобразования для каждого формального параметра и каждого фактического параметра независимо. После преобразования формальный параметр не может быть короче чем int, т.е. объявление формального параметра с типом char равносильно его объявлению с типом int. А параметры, представляющие собой действительные числа, имеют тип double.
Преобразованный тип каждого формального параметра определяет, как интерпретируются аргументы, помещаемые при вызове функции в стек. Несоответствие типов фактических аргументов и формальных параметров может быть причиной неверной интерпретации.
Тело функции - это составной оператор, содержащий операторы, определяющие действие функции.
Все переменные, объявленные в теле функции без указания класса памяти, имеют класс памяти auto, т.е. они являются локальными. При вызове функции локальным переменным отводится память в стеке и производится их инициализация. Управление передается первому оператору тела функции и начинается выполнение функции, которое продолжается до тех пор, пока не встретится оператор return или последний оператор тела функции. Управление при этом возвращается в точку, следующую за точкой вызова, а локальные переменные становятся недоступными. При новом вызове функции для локальных переменных память распределяется вновь, и поэтому старые значения локальных переменных теряются.
Параметры функции передаются по значению и могут рассматриваться как локальные переменные, для которых выделяется память при вызове функции и производится инициализация значениями фактических параметров. При выходе из функции значения этих переменных теряются. Поскольку передача параметров происходит по значению, в теле функции нельзя изменить значения переменных в вызывающей функции, являющихся фактическими параметрами. Однако, если в качестве параметра передать указатель на некоторую переменную, то используя операцию разадресации можно изменить значение этой переменной.
Пример:
/* Неправильное использование параметров */
void change (int x, int y)
{ int k=x;
x=y;
y=k;
}

5. про фактические вроде как.
При вызове функции ей при помощи аргументов (формальных параметров) могут быть переданы некоторые значения (фактические параметры), используемые во время выполнения функции. Функция может возвращать некоторое (одно!) значение. Это возвращаемое значение и есть результат выполнения функции, который при выполнении программы подставляется в точку вызова функции, где бы этот вызов ни встретился. Допускается также использовать функции не имеющие аргументов и функции не возвращающие никаких значений. Действие таких функций может состоять, например, в изменении значений некоторых переменных, выводе на печать некоторых текстов и т.п..
С использованием функций в языке СИ связаны три понятия - определение функции (описание действий, выполняемых функцией), объявление функции (задание формы обращения к функции) и вызов функции.
Определение функции задает тип возвращаемого значения, имя функции, типы и число формальных параметров, а также объявления переменных и операторы, называемые телом функции, и определяющие действие функции. В определении функции также может быть задан класс памяти.
Пример:
int rus (unsigned char r)
{ if (r>='А' && c<=' ')
return 1;
else
return 0;
}

5. способы передачи параметров в функцию
Способ передачи параметров процедуре или функции указывается при описании ее аргументов: имени аргумента может предшествовать явный описатель способа передачи. Описатель ByRef задает передачу по ссылке, a ByVal — по значению. Если же явное указание способа передачи параметра отсутствует, то по умолчанию подразумевается передача по ссылке.
Поясним сказанное на примере. Пусть имеются следующие описания двух процедур:
Sub Main() а = 10 b = 20 с = 30 Call Examplel(a, b, с) Call MsgBox(a) Call MsgBox(b) Call MsgBox(c) End Sub Sub Example1(x, ByVal y, ByRef z) x = x + 1 у = у + 1 z = z + 1 Call MsgBox(x) Call MsgBox(y) Call MsgBox(z) End Sub
Вспомогательная процедура Examplel использует в качестве формальных аргументов три переменные, описанные по-разному. Далее в теле этой процедуры каждый из них увеличивается на единицу, а затем их значения выводятся на экран с помощью функции MsgBox. Основная процедура Main устанавливает значения переменных a, b и с, а затем передает их в качестве (фактических) аргументов процедуре Examplel. При этом первый аргумент передается по ссылке (действует умолчание), второй — по значению, а третий — снова по ссылке. После возврата из процедуры Examplel основная процедура также выводит на экран значения трех переменных, передававшихся в качестве аргументов. Всего на экран выводится шесть значений:
сначала это числа 11, 21 и 31 (все полученные значения увеличены на 1 и выводятся процедурой Examplel);
затем это числа 11, 20 и 31 (эти значения выводятся процедурой Main, причем переменные, переданные по ссылке, увеличились, а переменная, переданная по значению — нет).
Программа может состоять (и обычно состоит) из многих процедур и функций, которые могут располагаться в одном или нескольких модулях. Модули группируются в проекты, при этом в одном проекте могут мирно сосуществовать несколько различных программ, использующих общие модули или процедуры.
Каждая из процедур, находящихся в одном модуле, должна иметь уникальное имя, однако в проекте может содержаться несколько различных модулей. Обычно рекомендуется использовать только уникальные имена процедур в одном проекте, но допустимы и исключения. В том случае, если в проекте содержится несколько различных процедур с одним и тем же именем, необходимо для уточнения имени использовать при вызове процедуры следующий синтаксис:
<имяМодуля>.<имяПроцедуры>
Если при этом имя модуля состоит из нескольких слов, следует заключить это имя в квадратные скобки. Например, если модуль называется "Графические процедуры", а процедура — "Крестик", вызов может выглядеть следующим образом:
[Графические процедуры].Крестик
Допускается также использование процедур, расположенных и в других проектах. При этом может потребоваться еще один уровень уточнения имени:
<имя Проекта> <имя Модуля> <имяП роцедуры>

6.Способы передать в функцию массивов. Привести примеры.

Передайте в функцию массив фиксированного размера. Прототип функции измените таким образом, чтобы он содержал аргумент соответствующего типа. Например, декларация функции, принимающей в качестве параметра массив целых числовых значений из трех элементов, может выглядеть следующим образом:
void ArrayFunction(int aNumbers[3]);
Вызов такой функции осуществляется путем передачи ей непосредственно массива в качестве аргумента:
void SomeFunction()
{
int aNumbers[] = { 1, 2, 3 };
ArrayFunction(aNumbers);
}
Передаваемые данные копируются в стек. Модификация массива в вызываемой функции не приводит к изменению источника.
2
Передавайте в функцию массивы переменной длины. Для этого просто не специфицируйте размерность соответствующего аргумента:
void ArrayFunction(int aNumbers[]);
Многомерные массивы также можно передавать подобным образом (переменным может быть только первое «измерение»):
void ArrayFunction(int aNumbers[][3][2]);
Вызов подобных функций производится тем же образом, что и в первом шаге.
Для того чтобы иметь возможность корректно обрабатывать массивы переменной длины в функции, необходимо либо явно передавать количество их элементов через дополнительный параметр, либо использовать соглашения, накладывающие ограничения на значения самих элементов (определенное значение должно являться признаком конца массива).
3
Передайте массив по указателю. Аргументом функции должен являться указатель на значение с типом, соответствующим элементам массива. Например:
void ArrayFunction(int *pNumbers);
Доступ к данным в функции может осуществляться как в нотации работы с элементами массива, так и при помощи адресной арифметики:
void ArrayFunction(int *pNumbers)
{
pNumbers[0] = 10; // доступ к элементу 0
*(pNumbers + 1) = 20; // доступ к элементу 1
}
Будьте внимательны! Поскольку в функцию передается не копия данных, а указатель на них, модификации будет подвергнут исходный массив.
Преимуществом данного метода является скорость, экономия вычислительных ресурсов и определенная гибкость. Так, можно вызывать целевую функцию, передав ей указатель на произвольный элемент массива:
void SomeFunction()
{
int aNumbers[] = { 1, 2, 3 };
ArrayFunction(aNumbers); // весь массив
ArrayFunction(&aNumbers[1]); // начиная со второго элемента
}
Данный способ также обычно предполагает передачу количества доступных элементов в дополнительном параметре или использование признака конца массива.
4
Осуществите передачу данных в функцию с параметром, являющимся объектом или ссылкой на объект класса, реализующего функционал массива. Подобные классы или шаблоны классов обычно входят в состав популярных библиотек и фреймворков (QVector в Qt, CArray в MFC, std::vector в STL, и т.д.).
Часто данные классы реализуют стратегию неявного совместного использования данных (implicit data sharing) с подсчетом ссылок (reference counting), выполняя глубокое копирование только при модификации данных (copy on write). Это позволяет минимизировать потребление вычислительных ресурсов даже в случае передачи объектов массивов по значению через аргументы функций и методов:
void ArrayFunction(QVector oArray)
{
int nItemCount = oArray.count();
int nItem = oArray[0];
}
void SomeFunction()
{
QVector oArray(10);
for(int i = 0; i oArray[i] = i;
ArrayFunction(oArray);
}
К тому же подобные классы обычно поддерживают удобные возможности, такие как динамическое изменение размера массива, поиск, сортировку и т.д.

7.Привести примеры функции вывода на печать элемента массива структур по его порядковому номеру.

????

8.Привести пример функции, которая возвращает сумму (произведение, среднеарифметическое) элементов в массиве.

Тип возвращаемого значения

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

int simple_function()

float simple_function()

char simple_function()

В первом случае функция должна вернуть целое число (int), во втором - вещественное число (float), а в третьем случае - символ (char).

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

Идентификатор или имя функции

Идентификатор (имя) функции задаётся точно также, как и любой другой идентификатор. В данном примере мы создали функцию с идентификатором simple_function (simple - простой).

Список аргументов или параметров

Список аргументов функции записывается в круглых скобках после имени функции. В данном примере список аргументов пуст.

Список аргументов записывается через запятую. Каждый элемент списка состоит из типа и идентификатора. Рассмотрим пример заголовка функции со списком из двух аргументов:

int simple (int a, float b)

В скобках мы записали два аргумента: a и b. У аргумента a тип int, а у аргумента b тип float.

Аргументы используются, когда в функцию нужно передать какие-либо данные из вызывающего окружения.

Тело функции

Тело функции располагается сразу под заголовком и заключено в фигурные скобки. В теле функции может содержаться сколько угодно операторов. Но обязательно должен присутствовать оператор return. Оператор return возвращает значение:

 

int simple_function ()

{

return 0;

}

Здесь, simple_function всегда будет возвращать 0. Надо признать, что данная функция бесполезна. Напишем функцию, которая принимает из вызывающего окружения два значения, складывает их и возвращает результат в вызывающее окружение. Назовём эту функцию sum (сумма):

 

int sum (int a, int b)

{

int c;

c = a + b;

return c;

}

В функцию передаётся два аргумента: a и b типа int. В теле функции они используются как обычные переменные (они и являются обычными переменными). Давайте договоримся: снаружи функции, переменные, которые передаются в неё, мы будем называть аргументами, а эти же переменные в теле функции - параметрами.

В теле функции определяется переменная c. А затем, в эту переменную мы помещаем значение суммы двух параметров.

Последняя строчка возвращает значение переменной c во внешнее окружение.

После ключевого слова return нужно указать значение которое будет возвращено. Можно возвращать как простые значения, так и переменные и даже выражения. Например:

return 32;

return a;

return b;

return a+b;

В последнем случае в вызывающее окружение будет возвращён результат суммы переменных a и b.

Обратите внимание, что оператор return не только возвращает значение, но и служит как бы выходом из функции, после него не будет выполнен ни один оператор:

return a;

c = a+b; // этот оператор не будет выполнен

Благодаря этому, с помощью return удобно создавать условия выхода из функций:

if (a > 0)

{

return 0;

}

else if (a < 0)

{

return 1

}

Здесь, из функции будет возвращено число в зависимости от значения переменной a: если a больше нуля, то будет возвращён 0, в противном случае - 1.

Вызов функции

После того как создано определение функции, её можно вызвать.

int sum (int a, int b)

{

int c;

c = a + b;

return c;

}

int main()

{

int s;

s = sum(2,2); // вызов функции

cout << s;

return 0;

}

 

 

В результате выполнения программы, на экран будет выведено: 4.

Вызов функции состоит из идентификатора функции и списка аргументов в круглых скобках. Вот несколько вызовов функции sum:

int x = 5;

int y = 4;

int z;

sum(0,1); // 1

sum(x,2); // 7

sum(x,y); // 9

z = sum(x,y); // z = 9

Вызывающее окружение

То место, откуда вызывается функция, называется вызывающим окружением. Вызывающим окружением функции sum является функция main, а вызывающим окружением функции main является отладчик или операционная система.

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

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

int simple (int a)

{

return 1;

}

int main ()

{

int b;

b = simple(0.5);

return 0;

}

В списке аргументов мы указали тип int, а в функцию передаётся вещественное значение 0.5. Так делать нельзя.

Рассмотрим более полезный пример: напишем функцию для передвижения персонажа:

int x; // глобальная переменная

int move_x (int dx)

{

x = x + dx;

return x;

}

int main ()

{

int ch;

char act;

while (1)

{

act = _getch();

ch = static_cast(act);

if (ch == 75) // была нажата стрелочка влево

move_x(-1);

else if (ch == 77) // была нажата стрелочка вправо

move_x(1);

} // конец while

return 0;

} // конец main

9.Рекурсия. Определение рекурсии. Привести примеры рекурсивных функций

Рекурсия
Функция называется рекурсивной, если во время ее обработки возникает ее повторный вызов, либо непосредственно, либо косвенно, путем цепочки вызовов других функций.
Прямой (непосредственной) рекурсией является вызов функции внутри тела этой функции.
int a()
{.....a().....}
Косвенной рекурсией является рекурсия, осуществляющая рекурсивный вызов функции посредством цепочки вызова других функций. Все функции, входящие в цепочку, тоже считаются рекурсивными.
Например:
a(){.....b().....}
b(){.....c().....}
c(){.....a().....}.
Все функции a,b,c являются рекурсивными, так как при вызове одной из них, осуществляется вызов других и самой себя. Задача о Ханойских башнях.
Для решения простейшего случая задачи, когда пирамида состоит только из одного диска, необходимо выполнить одно действие - перенести диск со стержня i на стержень j, что очевидно (этот перенос обозначается i -> j). Общий случай задачи изображен на рисунке, когда требуется перенести n дисков со стержня i на стержень j, считая стержень w вспомогательным. Сначала следует перенести n-1 диск со стержня i на стержень w при вспомогательном стержне j, затем перенести один диск со стержня i на стержень j и, наконец, перенести n-1 диск из w на стержень j, используя вспомогательный стержень i. Итак, задача о переносе n дисков сводится к двум задачам о переносе n-1 диска и одной простейшей задаче. Схематически это можно записать так: T(n,i,j,w) = T(n-1,i,w,j), T(1,i,j,w), T(n-1,w,j,i).
i n-1 w n-1 j
+ | -------->-+|+--------->+ |
n-1| | I






















































































































































































































































































Ш | | + / \ / \ / \ +-/-----\-+ / \ +-/-----\-+ ==+----|----+===/=========\====+----|----+====== +-------------->-------------+ П Рис.32. Схема решения задачи о Ханойских башнях. Ниже приведена программа, которая вводит число n и печатает список перемещений, решающая задачу о Ханойских башнях при количестве дисков n. Используется внутренняя рекурсивная процедура tn(n,i,j,w), печатающая перемещения, необходимые для переноса n дисков со стержня i на стержень j с использованием вспомогательного стержня w при {i,j,w} = {1,3,2}. /* ханойские башни */ #include main() /* вызывающая */ { void tn(int, int, int, int); /* функция */ int n; scanf(" %d",&n); tn(n,1,2,3); } void tn(int n, int i, int j, int w) /* рекурсивная */ { if (n>1) /* функция */ { tn (n-1,i,w,j); tn (1,i,j,w); tn (n-1,w,j,i); } else printf(" \n %d -> %d",i,j); return; } Последовательность вызовов процедуры tn при m=3 иллюстрируется древовидной структурой на рис.33. Каждый раз при вызове процедуры tn под параметры n, i, j, w выделяется память и запоминается место возврата. При возврате из процедуры tn память, выделенная под параметры n, i, j, w, освобождается и становится доступной память, выделенная под параметры n, i, j, w предыдущим вызовом, а управление передается в место возврата 10.*Указатели на функцию. Примеры указателей на функцию. Указатели на функции В Си сама функция не является переменной, но можно определить указатель на функцию и работать с ним, как с обычной переменной: присваивать, размещать в массиве, передавать в качестве параметра функции, возвращать как результат из функции и т.д. Для иллюстрации этих возможностей воспользуемся программой сортировки, которая уже встречалась в этой главе. Изменим ее так, чтобы при задании необязательного аргумента -n вводимые строки упорядочивались по их числовому значению, а не в лексикографическом порядке. Сортировка, как правило, распадается на три части: на сравнение, определяющее упорядоченность пары объектов; перестановку, меняющую местами пару объектов, и сортирующий алгоритм, который осуществляет сравнения и перестановки до тех пор, пока все объекты не будут упорядочены. Алгоритм сортировки не зависит от операций сравнения и перестановки, так что передавая ему в качестве параметров различные функции сравнения и перестановки, его можно настроить на различные критерии сортировки. Лексикографическое сравнение двух строк выполняется функцией strcmp (мы уже использовали эту функцию в ранее рассмотренной программе сортировки); нам также потребуется функция numcmp, сравнивающая две строки как числовые значения и возвращающая результат сравнения в том же виде, в каком его выдает strcmp. Эти функции объявляются перед main, а указатель на одну из них передается функции qsort. Чтобы сосредоточиться на главном, мы упростили себе задачу, отказавшись от анализа возможных ошибок при задании аргументов. #include <stdio.h> #include <string.h> #define MAXLINES 5000 /* максимальное число строк */ char *lineptr[MAXLINES]; /* указатели на строки текста */ int readlines(char *lineptr[], int nlines); void writelines(char *lineptr[], int nlines); void qsort(void *lineptr[], int left, int right, int (*comp)(void *, void *)); int numcmp(char *, char *); /* Сортировка строк */ main(int argc, char *argv[]) { int nlines; /* количество прочитанных строк */ int numeric = 0; /* 1 если необходима сортировка */ /* по числовому значению */ if (argc > 1 && strcmp(argv[1], "-n") == 0) numeric = 1; if ((nlines = readlines(lineptr, MAXLINES)) >= 0) { qsort ((void **) lineptr, 0, nlines-1, (int (*)(void *, void *))(numeric? numcmp: strcmp)); writelines(lineptr, nlines); return 0; } else { printf("Введено слишком много строк\n"); return 1; } } В обращениях к функциям qsort, strcmp и numcmp их имена трактуются как адреса этих функций, поэтому оператор & перед ними не нужен, как он не был нужен и перед именем массива. Мы написали qsort так, чтобы она могла обрабатывать данные любого типа, а не только строки символов. Как видно из прототипа, функция qsort в качестве своих аргументов ожидает массив указателей, два целых значения и функцию с двумя аргументами-указателями. В качестве аргументов-указателей заданы указатели обобщенного типа void *. Любой указатель можно привести к типу void * и обратно без потери информации, поэтому мы можем обратиться к функции из qsort, предварительно преобразовав аргументы в void. Внутри функции сравнения ее аргументы будут приведены к нужному ей типу. На самом деле эти преобразования никакого влияния на представления аргументов не оказывают, они лишь обеспечивают согласованность типов для компилятора. /* qsort: сортирует v[left]...v[right] по возрастанию */ void qsort(void *v[], int left, int right, int (*comp)(void *, void *)) { int i, last; void swap(void *v[], int, int); if (left >= right) /* Ничего не делается, если */ return; /* в массиве менее двух элементов */ swap(v, left, (left + right)/2); 11. *Пользовательские функции с переменным числом параметров Язык C++ вслед за С позволяет писать функции с переменным числом параметров. Одним из простых примеров может служить функция, вычисляющая среднее арифметическое своих аргументов. Другой уже классический пример — функция сцепления произвольного количества строк, которая является естественным обобщением функции сцепления двух строк.  Переменный список параметров задается в заголовке функции многоточием:  int f(…)  Этот заголовок не вызывает у компилятора протестов. Такая запись означает, что при определении функции компилятору неизвестны ни количество параметров, ни их типы, и он, естественно, не может ничего проверить. Количество параметров и их типы становятся известными только при вызове функции.  Однако у программиста с написанием таких функций сразу возникают проблемы. Ведь имена параметров отсутствуют! Поэтому доступ можно осуществить только одним способом – косвенным, используя указатель. Вспомним, что все параметры при вызове помещаются в стек. Если мы каким-то образом установим указатель на начало списка параметров в стеке, то, манипулируя с указателем, мы, в принципе, можем «достать» все параметры!  Таким образом, список параметров совсем пустой быть не может, должен быть прописан хотя бы один явный параметр, адрес которого мы можем получить при выполнении программы. Заголовок такой функции может выглядеть так:  int f(int k...)  Ни запятая, ни пробел после параметра не обязательны, хотя можно их и прописать.  Есть одно обстоятельство, которое ограничивает применение таких функций: при написании функции с переменным числом параметров помимо алгоритма обработки программист должен разрабатывать и алгоритм доступа к параметрам. Так что список необъявленных параметров не может быть совсем уж произвольным – в языке C++ не существует универсальных средств распознавания элементов этого списка. Это же означает, что передача аргумента не того типа, который задумывался, или не тем способом, который подразумевался при разработке, приведет к катастрофическим последствиям – компилятор-то ничего не проверяет.   double f(double n,...) //--заголовок с переменным числом параметров { double *p = &n;   //--установились на начало списка параметров double sum = 0, count = 0;    while (*p)    //--пока аргумент не равен нулю { sum+=(*p);    //--суммируем аргумент p++;        //--«перемещаемся на следующий аргумент count++;    //--считаем количество аргументов } return ((sum)?sum/count:0); //--вычисляем среднее 12. Параметры функции main(). Привести примеры обращения к параметрам функции main()  int main(int argc, char* argv[]) // параметры функции main() Эта строка - заголовок главной функции main(), в скобочках объявлены параметры argс и argv. Параметры функции main int main (int argc, char * argv[ ]) argv[ ] – массив символьных строк из командной строки DOS argv[ 0] – полное имя файла с маршрутом к каталогу, где хранится программа.  argv[ 1] первый параметр после имени программы в командной строке, argv[ 2] – второй и т.д.  Параметр argc определяет количество параметров в командной строке, не может быть меньше 1


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: