Будь умным!


У вас вопросы?
У нас ответы:) SamZan.ru

Одномерные и двумерные массивы и указатели

Работа добавлена на сайт samzan.ru: 2016-06-09


1.Одномерные и двумерные массивы и указатели. Передача двумерных массивов  в функции.

Массив – именованная последовательность областей памяти, хранящих однотипные элементы. Каждая такая область памяти называется элементом массива. Массивы обладают размерностью (большей или равной единице), которой задается число элементов, содержащихся в них, а также измерением, что предполагает возможность описания в программе одно- и многомерных массивов. Количество элементов в массиве называется его размером.

Элементы массива в С++ нумеруется, начиная с нуля. У одномерных массивов после его имени указывается один индекс(порядковый номер), заключенный в прямоугольные скобки [ ], а у многомерных – несколько, каждый из которых заключается в [ ]. Последнее означает, что многомерный массив создается путем определения массива из элементов типа массив.

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

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

Объявление одномерных массивов

Тип ИмяМассива[ВыражениеТипаКонстанты]; или Тип ИмяМассива[];

Объявление двумерных массивов

Тип ИмяМассива[ВыражениеТипаКонстанты][ВыражениеТипаКонстанты]; или Тип ИмяМассива[][];

Одной из наиболее распространенных конструкций с использованием указателей являются массивы. Результатом использования указателей для массивов является меньшее количество используемой памяти и высокая производительность.

Указатель – это переменная, содержащая адрес другой переменной. Так как указатель содержит адрес переменной (объекта), то это дает возможность "косвенного" доступа к этой переменной (объекту) через указатель.

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

Передача двумерных массивов в функцию

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

void f (int A[10][10])

{ ... }

int main()

{    int B[10][10];

   f(B);}

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

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

Одномерный массив int A[n] это почти то же самое, что указатель на переменную типа intint * A.

Тогда двумерный массив - это массив, каждый из элементов которого является одномерным массивом, то есть указателем на какой-то адрес целого числа в памяти. То есть двумерный массив - это массив элементов типа int * или же это указатель на переменную типа int *, то есть это переменная типа int **.

Итак, двойной указатель можно объявить так:

int ** A;

Теперь выделим память для массива A. Если мы хотим, чтобы в массиве A было n элементов, каждый из которых является указателем на тип int, то сделаем это при помощи операции new:

A = new int * [n];

Теперь A указывает на область памяти, содержащей n элементов, каждый из которых имеет тип int * и указывает на некоторую область памяти, пока еще не выделенную. Выделим эту память - сделаем все A[i] указателями на область памяти из m элементов типа int:

for (int i =  0; i < n; ++i)

{

   A[i] = new int [m];

}

Функцию, получающую в качестве параметра двумерный массив, можно объявлять так:

void  f (int ** A, int n, int m)

2.Функции. Декларации  функций. Функции без параметров.

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

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

Любая программа на С++ состоит из функций, одна из которых должна иметь имя main (с нее начинается выполнение программы).

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

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

Объявление функции (прототип, заголовок, сигнатура) задает ее имя, тип возвращаемого значения и список передаваемых параметров.

Определение функции содержит, кроме объявления, тело функции, представляющее собой последовательность операторов и описаний в фигурных скобках:

[ класс ] тип имя ([ список_параметров ])[throw ( исключения )]{ тело функции }

Рассмотрим составные части определения.

  1.  С помощью необязательного модификатора класса можно явно задать область видимости функции, используя ключевые словаextern и static:
  2.  extern - глобальная видимость во всех модулях программы (по умолчанию);
  3.  static - видимость только в пределах модуля, в котором определена функция.
  4.  Тип возвращаемого функцией значения может быть любым, кроме массива и функции (но может быть указателем на массив или функцию). Если функция не должна возвращать значение, указывается тип void.
  5.  Список параметров определяет величины, которые требуется передать в функцию при ее вызове. Элементы спискапараметров разделяются запятыми. Для каждого параметра, передаваемого в функцию, указывается его тип и имя (в объявлении имена можно опускать).

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

На имена параметров ограничений по соответствию не накладывается.

Функцию можно определить как встроенную с помощью модификатора inline, который рекомендует компилятору вместообращения к функции помещать ее код непосредственно в каждую точку вызова. Модификатор inline ставится перед типом функции. Он применяется для коротких функций, чтобы снизить накладные расходы на вызов.

Директива inline носит рекомендательный характер и выполняется компилятором по мере возможности.

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

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

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

Декларация функции (прототип) – это ее заголовок с символом точка с запятой в конце.
Например, обе декларации идентичны:
void my_func (void);
void my_func ();
Функция имеет имя my_func, не имеет формальных параметров и ничего не возвращает.
Замечание! Если декларация функции записана следующим образом: 
my_func (void); 
то функция по умолчанию возвращает значение типа int.

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

Функции – это основные единицы построения программ при процедурном программировании на языке Си++.

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

Программа на языке Си++ состоит, по крайней мере, из одной функции – функции   main. С нее всегда начинается выполнение программы. Встретив имя функции в выражении, программа вызовет эту функцию, т.е. передаст управление на ее начало и начнет выполнять операторы. Достигнув конца функции или оператора return – выхода из функции, управление вернется в ту точку, откуда функция была вызвана, подставив вместо нее вычисленный результат.

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

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

Формальные параметры могут использоваться только вне тела функции, а фактические - используются как вне функции, так и внутри ее.

Формальные параметры определены в заголовке функции, а фактические - значения, с которыми функция вызывается.

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

Классическим примером рекурсивной функции является вычисление факториала (это не означает, что факториал следует вычислять именно так). Для того чтобы получить значение факториала числа n, требуется умножить на n факториал числа (n-1). Известно такжечто0!=1 и 1!=1.

long fact(long n){

if (n==0 || n==l) return 1;

return (n * fact(n - 1);

}

To же самое можно записать короче: long fact(long n){return (n>l) ? n * fact(n - 1)  : 1;}

Рекурсивные функции чаще всего применяют для компактной реализации рекурсивных алгоритмов, а также для работы со структурами данных, описанными рекурсивно. Любую рекурсивную функцию можно реализовать без применения рекурсии, для этого программист должен обеспечить хранение всех необходимых данных самостоятельно. Достоинством рекурсии является компактная запись, а недостатками – расход времени и памяти(возможно переполнение стека) на повторные вызовы функции и передачу.

4.Указатели и ссылки. Указатели и адресная арифметика. Указатели на одномерные массивы.

Указатели – это переменные специального типа, значениями которых является адреса различных объектов программы. Если мы используем имя того или иного объекта для извлечения его значения или для изменения его значения, то принято говорить о непосредственном (прямом) доступе к объекту. В том случае, когда адрес объекта помещен в указатель, то речь идет о косвенном доступе к объекту, на который "смотрит" указатель.

В языках C, C++ различают три категории указателей. Первая категория указателей предназначена для хранения адресов данных определенного типа (по терминологии языка Паскаль – типизированные указатели). При их объявлении указывается тип данных, на которые эти указатели могут "смотреть". Ко второй категории относятся указатели, которые могут "смотреть" на данные любого типа (по терминологии языка Паскаль – не типизированные указатели). При их объявлении используется служебное слово void. Наконец, третью группу составляют указатели, значениями которых могут быть только адреса точек входа в функции (по терминологии языка Паскаль – данные процедурного типа). Объявление и использование указателей разных категорий имеет свою специфику.

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

Основные операции, чаще всего применяемые к указателям – сложение указателя с целым числом или вычитание из указателя целого числа. 

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

int x;

int &rx=x; //объявление и инициализация ссылки

Адресная арифметика

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

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

Указатели одного и того же типа можно друг из друга вычитать. Разность указателей показывает, сколько объектов соответствующего типа может поместиться между указанными адресами.

В языке С++ между указателями и массивами существует тесная связь. Например, когда объявлен массив int array[25], этим определяет не только выделение памяти для двадцати пяти элементов массива, но и для указателя с именем array. В языке С++ имя массива без индексов трактуется как адрес начального элемента. То есть имя массива является указателем на массив. Таким образом, доступ к элементам массива осуществляется через указатель с именем array.

Поскольку имя массива является указателем, допустимо, например, такое присваивание:

int arrаy[25];

int *ptr;

ptr=array;

5.Операции с указателями. Разыменование указателей.

Определение адреса указателя: &p, где p – указатель (&p – адрес ячейки, в которой находится указатель).

Присваивание. Указателю можно присвоить адрес переменной p=&q, где p – указатель, q – идентификатор переменной.

Определение значения, на которое ссылается указатель: *p (операция косвенной адресации).

Увеличение (уменьшение) указателя. Увеличение выполняется как с помощью операции сложения (+), так и с помощью операции инкремента (++). Уменьшение – с помощью операции вычитания (–) либо декремента (––).

Например, пусть p1 – указатель, тогда р1++ перемещает указатель на:

o    1 байт, если *p1 имеет тип char;

o    4 байта, если *p1 имеет тип int (в 32 разрядной операционной системе) или 2 байта (в 16 разрядной операционной системе);

o    4 байта, если *p1 имеет тип float.

Разность двух указателей. Пусть р1 и р2 – указатели одного и того же типа. Можно определить разность р1 и р2, чтобы найти, на каком расстоянии друг от друга находятся элементы массива.

Пример программы.

Даны адреса переменных &a=63384,&b=64390,&c=64404. Что напечатает ЭВМ?

include <stdio.h>

int main()

{

float a,*p1;

int b,*p2;

char c,*p3;

a=2.5; b=3; c='A';

p1=&a; p2=&b; p3=&c;

p1++; p2++; p3++;

printf("\n p1=%u, p2=%u, p3=%u",p1,p2,p3);

return 0;

}

Ответ: р1=63388, р2=64392, р3=64405.

Основной операцией над указателем является разыменование, т. е. ссылка на объект, на который указывает указатель. Эту операцию также именуют косвенным обращением. Например:

char c1 = ‘a’;
char* p = &c1 -- в p хранится адрес c1
char c2 = *p; -- c2 = ‘
a’

Над указателями можно осуществлять определенные арифметические действия. К примеру, функция, подсчитывающая число символов в строке (не считая завершающего 0):

int strlen(char* p)
{
int i = 0;
while (*p++) i++;
return i;
}

Два структурных типа различны, даже когда они имеют одинаковые члены. К примеру:

struct s1 {int a;};
struct s2 {int a;};

являются двумя разными типами, поэтому

s1 x;
s2 y = x; -- ошибка: несоответствие типов.

Структурные типы отличаются и от основных типов, поэтому
s1 x;
int i = x; -- ошибка: несоответствие типов

Но существует механизм описания нового имени для типа, который не требует введения нового типа. Описание с префиксом typedef вводит не новую переменную данного типа, а новое имя этого типа. К примеру:

6. Арифметика указателей. Применение к указателям оператора sizeof.

Существует возможность складывать целые с указателями и вычитать их из указателей. Компилятор рассматривает такие операции достаточно интеллектуально. Например, предположим, что у вас есть указатель на int, и вы пытаетесь прибавить к нему 1. В этом случае компилятор предполагает, что вы хотите посмотреть, что находится в памяти, расположенной за этим значением int, а потому увеличит это значе¬ние на 4 байта — то есть на размер int. Если же это будет указатель на double, то до¬бавление 1 на самом деле увеличит значение указателя на 8 байт — на размер double. И только если указатель будет указывать на byte или sbyte (тип длиной в 1 байт), то прибавление 1 к такому указателю на самом деле увеличит его на 1.

 

Вы можете применять операции +, +=, -=, ++ и -- с указателями, если правый операнд будет типа long или ulong.

Не допускается выполнять арифметические действия с указателями void.

Например, предположим, что имеются следующие определения:

uint и = 3; byte b = 8; double d = 10.0;

uint* pUint= &u;// размер uint равен 4

byte* pByte = &b;// размер byte равен 1

double* pDouble = &d; // размер double равен 8

Далее предположим, что в этих указателях содержатся такие адреса:

pUint: 1243332

pByte: 1243328

pDouble: 1243320

Затем выполним приведенный ниже код:

++pUint;// добавить (1*4) = 4 байта к pUint

pByte -= 3;// вычесть (3*1) =3' байта из pByte

double* pDouble2 = pDouble +4; // pDouble2 = pDouble + 32 байта (4*8 байта)

Теперь указатели будут содержать вот что:

pUint: 1243336

pByte: 1243325

pDouble2: 1243352

Основное правило выглядит так: добавление числа х к указателю на тип т со значением р в результате дает р + х* (sizeof (Т)).

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

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

double* pDl = (double*)1243324;// обратите внимание, что совершенно законно

// инициализировать указатель таким образом.

double* pD2 = (double*)1243300;

long L = pDl-pD2; // дает в результате 3 (=24/sizeof (double) )

Применение sizeof к указателю дает размер самого указателя, а не объекта, на который он указывает:

int *pi = new int[ 3 ];

size_t pointer_size = sizeof ( pi );

7. Указатели на указатели. Указатели на функции.

Указатели на указатели

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

тип **имя_указателя_на_указатель;

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

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

Например:

int iV, jV;

int* pV=&iV;     // pV - это адрес,

                    //а *pV - значение

int** ppV=&pV;

int*** pppV=&ppV;

iV=77;    // Эквивалентно **ppV=77;

jV=*pV;   // Эквивалентно jV=**ppV;

         // или jV=***pppV;

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

Определение указателя на функцию имеет вид:

 

int         (*pf)();                           // без контроля параметров вызова

int         (*pf)(void);                      // без параметров, с контролем по прототипу

int         (*pf)(int, char*);              // с контролем по прототипу

В соответствии с принципом контекстного определения типа данных эту конструкцию следует понимать так: pf - переменная, при косвенном обращении к которой получается функция с соответствующим прототипом, например int_F(int, char*), то есть pf содержит адрес функции или указатель на функцию. Следует обратить внимание на то, что в определении указателя присутствует прототип - указатель ссылается не на произвольную функцию, а только на одну из функций с заданной схемой формальных параметров и результата.

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

 

int         INC(int а) { return a+1; }

extern   int  DEC(int);

int         (*pf)(int);

pf = &INC;

pf = INC;                                   // присваивание указателя

int         (*pp)(int) = &DEC;          // инициализация указателя

 

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

 

n = (*pf)(1) + (*pp)(n);                 // эквивалентно

n = INC(1) + DEC(n);

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

8. Строки. Функции для работы со строками. Массивы символов в С++.

Строка – это последовательность символов, заключенная в двойные кавычки (" ").

Размещая строку в памяти, транслятор автоматически добавляет в ее конце символ '\0' (нулевой символ или нулевой байт, который является признаком конца строки). В записи строки может быть и один символ: "А" (заключен в двойные кавычки), однако, в отличие от символьной константы 'А' (используются апострофы), длина строки "А" равна 2 байтам.

В языке С++ строка – это пронумерованная последовательность символов (массив символов), она всегда имеет тип char[]. Все символы строки нумеруются, начиная с нуля. Символ конца строки также нумеруется – ему соответствует наибольший из номеров. Таким образом, строка считывается значением типа "массив символов". Количество элементов в таком массиве на 1 больше, чем изображение соответствующей строки, так как в конец строки добавлен нулевой символ '\0'

 Для работы со строками в языке С есть специальные функции, которые описаны в библиотечном файле string.h. Наиболее часто используются такие функции как: 
strcpy(), strcat(), strcmp(). 
    Рассмотрим их поподробнее. Вызов функции strcpy() имеет вид: 
strcpy(s1, s2); 
    Эта функция позволяет копировать содержимое строки s2 в строку s1. Массив s1 должен быть достаточно вместительным, чтобы в него поместилась строка s2. Если нужного места не хватает, то компилятор не выдает сообщение об ошибке или хотя бы предупреждение. Программа не прервет свою работу. Однако же все это вполне может привести к порче данных или самой программы. В результате программа может работать, но неправильно. И никто об этом не узнает. 
    Функция strcat() имеет вызов: 
strcat(s1, s2); 
    Функция strcat() присоединяет строку s2 к строке s1 и помещает новую строку в массив, где находилась строка s1. Строка s2 не изменяется. При этом, как и в предшествующем случае, нет никакого контроля. Нулевой байт строки s1 заменяется первым символом строки s2. Новая строка автоматически завершится нулевым байтом. 

В языке Си нет отдельного типа для строк. Работа со строками реализована через массивы. Хотя в других языках програмимирования имеется такой тип данных как string - строки. В Си символьная строка - это одномерный массив типа char, заканчивающийся нулем - нулевым байтом. Символьная константа '\0' определена для нулевого байта. Поэтому, если в массиве должно содержаться N символав, то нужно опеределять массив как массив для N+1 элемента. Например, когда мы говорим, что массив содержит 100 элементов: a[1], a[2], ..., a[99], то это значит, что элемент a[100] содержит ноль. Обычный одномерный массив можно трактовать как строку символов. Язык С допускает строковые константы, хотя в языке и нет специального типа для таких констант. Делается это через массив. Строковая константа - это набор литер. В конце строковой константы не нужно ставить символ '\0'. Это сделает сам компилятор при обработке массива.
    Существуют очень простые способы ввода строки с клавиатуры. Первый способ - воспользоваться функцией scanf() со спецификацией ввода %s. При этом очень хорошо надо помнить, что функция вводит символы до первого пробела. Второй способ - это воспользоваться специальной библиотечной функцией gets(). Она находится в файле stdio.h. Данная функция позволяет вводить строки, содержащие пробелы. При этом можно исправлять введенные символы пока не нажата клавиша ENTER. Ввод информации заканчивается нажатием клавиши ENTER. Обе указанные функции автоматически ставят в конце строки (массива) нулевой байт. Но важно не забыть зарезервировать для строки место в памяти компьютера. Здесь используется, как было раньше показано, имя массива. Вывод строк организуется через функции printf() или puts(). Обе эти функции выводят информаци строки до нулевого байта. Функция puts() предусматривает вывод в конце выводимой строки символа новой строки. Функция printf() не выводит этого символа вонце строки. Поэтому нужно это предусмива в программе. То есть это значит, что надо предусматривать самим символы "\n" в конце формата для вывода строки. Закрепим полученные знания на задаче. 

9.Определение длины строк. Копирование и конкатенация строк. Сравнение строк. 

Определение длины строк.

    Длина строки определяется просто. Для этого нужно передать строковый указатель функции strlen(), которая возвратит длину строки, выраженную в символах. После объявления

  char *с = "Любая старая строка";

  int len;

следующий оператор установит переменную len равной длине строки, адресуемой указателем с:

  len = strlen(с);

Копирование строк.

    Оператор присваивания для строк не определен. Если с1 и с2 - символьные массивы, вы не сможете скопировать один в другой следующим образом:

   с1 = с2; //???

    Но если с1 и с2 объявить как указатели типа char *, компилятор согласится с этим оператором, но вряд ли вы получите ожидаемый результат. Вместо копирования символов из одной строки в другую оператор с1 = с2 скопирует указатель с2 в указатель с1, перезаписав, таким образом, адрес в с1, потенциально потеряв информацию, адресуемую указателем.

    Чтобы скопировать одну строку в другую, вместо использования оператора присваивания вызовите функцию копирования строк strcpy(). Для двух указателей с1 и с2 типа char * оператор

   strcpy(с1, с2);

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

    Аналогичная функция strncpy() ограничивает количество копируемых символов. Если источник (source) и приемник (destination) являются указателями типа char * или символьными массивами, то оператор

   strcpy(destination, source, 10);

скопирует до 10 символов из строки, адресуемой указателем source, в область памяти, адресуемую указателемdestination. Если строка source имеет больше 10 символов, то результат усекается. Если же меньше - неиспользуемые байты результата устанавливаются равными нулю.

Конкатенация строк.

    Конкатенация двух строк означает их сцепление, при этом создается новая, более длинная строка. При объявлении строки

   char original[128] = "Проверка";

оператор

    strcat(original, " один, два, три!");

превратит значение первоначальной строки original в "Проверка один, два, три!"

    При вызове функции strcat() убедитесь, что первый аргумент типа char * инициализирован и имеет достаточно места, чтобы запомнить результат. Если c1 адресует строку, которая уже заполнена, а c2 адресует ненулевую строку, операторstrcat(c1, c2); перезапишет конец строки, вызвав серьезную ошибку.

    Функция strcat() возвращает адрес результирующей строки (совпадающий с ее первым параметром) и может использоваться как каскад нескольких вызовов функций:

    strcat(strcat(c1,c2),c3)

Этот оператор добавит строку, адресуемую c2, и строку, адресуемую c3, к строке, адресуемой c1, что эквивалентно двум отдельным операторам:

    strcat(c1,c2);

    strcat(c1,c3);

Сравнение строк.

    Используя функцию GetStringAt() из предыдущего раздела, можно написать программу, которая предлагает ввести пароль. Чтобы определить правильность введенного пароля, воспользуемся функцией strcmp(), которая сравнивает две строки.

    Чтобы программа успешно завершилась, аккуратно введите текст Borland C++, используя прописные и строчные буквы. Если вы введете пароль неправильно, программа отобразит сообщение об ошибке и потребует начать сначала. Как и другие подобные программы с парольным входом.

10. Преобразование строк. Обращение строк. Поиск символов.

Преобразование строк
Элементы символьных строк могут быть преобразованы из одного регистра в другой. Для этого используются стандартные функции _strlwr() и _strupr(). Следует отметить, что в некоторых версиях компиляторов имена данных функций могут следовать без ведущего символа подчеркивания.
Функция _strlwr () принимает в качестве параметра указатель на строку символов, преобразует эту строку к нижнему регистру (строчные символы) и возвращает указатель на полученную строку. Данная функция имеет следующий прототип:
char* _strlwr(char*  str)
Следующий фрагмент показывает применение функции _strlwr():
char str[]   =   "ABRACADABRA";
_strlwr(str);

После вызова функции строка str будет преобразована в "abracadabra".
Функция _strupr () объявлена следующим образом:
char* _strupr (char*  str)
Данная функция преобразует строку символов, на которую указывает str, в прописные буквы (к верхнему регистру).


Обращение строк
Функция обращения строки strrev() меняет порядок следования символов на обратный (реверс строки). Данная функция имеет прототип:
char*  strrev(char*  str)
Следующий пример демонстрирует работу функции strrev ().
char str [6] ;
strcpy(str, RUS("Привет"));
cout << strrev(str);

В результате на экране будет выведена строка "тевирП". Эта функция изменяет строку-оригинал.


Поиск символов
Одна из часто встречаемых задач при работе со строками - поиск отдельного символа или даже группы символов. Библиотека string.h предлагает следующий набор стандартных функций.
Функция нахождения символа в строке strсhr () имеет следующий прототип:
char*  strchr(const char*  string,   int c)
Данная функция производит поиск символа с в строке string и в случае успешного поиска возвращает указатель на место первого вхождения символа в строку. Если указанный символ не найден, функция возвращает NULL. Поиск символа осуществляется с начала строки.
Ниже рассматривается фрагмент, осуществляющий поиск заданного символа в строке.
char str[6] ;
strcpy(str, "Привет");
char* pStr;
pStr = strchr(str,'и');
cout << RUS(pStr);

11. Поиск подстрок. Функции преобразования типа.

Поиск подстрок

При необходимости поиска в одной строке последовательности символов, заданной в другом символьном массиве (подстроке, лексеме), стандартная библиотека string.h предлагает воспользоваться одной из следующих функций.
Функция strstr () описана следующим образом:
char*  strstr(const char*  str, const char*   substr)
Данная функция осуществляет сканирование строки str и находит место первого вхождения подстроки substr в строку str. В случае успешного поиска функция strstr возвращает указатель на первый символ строки str, начиная с которого следует точное совпадение части str обязательно со всей лексемой substr. Если подстрока substr не найдена в str, возвращается NULL.
Функция strtok () имеет синтаксис:
char*  strtok(char*  str,   const char* delim)
Эта функция выполняет поиск в строке str подстроки, обрамленной с обеих сторон любым символом-разделителем из строки delim. В случае успешного поиска данная функция обрезает строку str, помещая символ ' \0' в месте, где заканчивается найденная лексема. Таким образом, при повторном поиске лексемы в указанной строке str первым параметром следует указывать NULL. Так как strtok () модифицирует строку-оригинал, рекомендуется предварительно сохранять копию последней. Приведенный ниже пример иллюстрирует вышесказанное.


Функции преобразования типа
Функции преобразования данных довольно часто используются, как следует из названия, для преобразования одного типа данных в другой тип. В приведенной ниже табл. перечислены основные функции, их прототипы подключаются в заголовочном файле stdlib. h.

Преобразование данных

Наименование

Краткое описание

atof

преобразует строку символов в число с плавающей точкой

atoi

преобразует строку символов в строку типа int

atol

преобразует строку символов в число типа long

ecvt

преобразует число с плавающей точкой типа double в строку символов; десятичная точка и знак числа не включаются в полученную строку; позиция точки и знак числа возвращаются отдельно

fcvt

идентично ecvt, но округляет полученное значение до заданного числа цифр

gcvt

преобразует число с плавающей точкой типа double в строку символов, включая символ десятичной точки и используя специфицированное число цифр

itoa

преобразует число типа int в строку символов

ltoa

преобразует число типа long в строку символов

strtod

преобразует строку символов в число с плавающей точкой типа double

strtol

преобразует строку символов в число типа long

strtoul

преобразует строку символов в число типа unsigned long

ultoa

преобразует число типа unsigned long в строку символов


12.Структуры и объединения. Структуры как аргументы фунций.

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

Структура – это способ связать воедино данные разных типов и создать пользовательский тип данных. В языке Pascal подобная конструкция носит название записи.

Структура – тип данных, задаваемый пользователем. В общем случае при работе со структурами следует выделить четыре момента:

- объявление и определение типа структуры,

- объявление структурной переменной,

- инициализация структурной переменной,

- использование структурной переменной.

Определение типа структуры представляется в виде

struct ID

{

<тип> <имя 1-го элемента>;

<тип> <имя 2-го элемента>;

…………

<тип> <имя последнего элемента>;

};

Определение типа структуры начинается с ключевого слова struct и содержит список объявлений, заключенных в фигурные скобки. За словом struct следует имя типа, называемое тегом структуры (tag – ярлык, этикетка). Элементы списка объявлений называются членами структуры или полями. Каждый элемент списка имеет уникальное для данного структурного типа имя. Однако следует заметить, что одни и те же имена полей могут быть использованы в различных структурных типах.

Определение типа структуры представляет собой шаблон (template), предназначенный для создания структурных переменных.

Объявление переменной структурного типа имеет следующий вид:

struct ID var1;

при этом в программе создается переменная с именем var1 типа ID. Все переменные, использующие один шаблон (тип) структуры, имеют одинаковый набор полей, однако различные наборы значений, присвоенные этим полям. При объявлении переменной происходит выделение памяти для размещения переменной. Шаблон структуры позволяет определить размер выделяемой памяти.

В общем случае, под структурную переменную выделяется область памяти не менее суммы длин всех полей структуры, например,

struct list

{

char name[20];

char first_name[40];

int;

}L;

Объединение – это тип данных, позволяющий переменным различного типа использовать одну и ту же область памяти. Для объявления объединения используется ключевое словоunion. Объединение, так же как и структура, содержит несколько полей. Однако в любой момент времени доступно только одно поле. Под объединение выделяется столько памяти, сколько требуется для размещения самого большого поля. Все поля поочередно используют выделенную память для хранения своих значений.

Определение типа:

union tag

{

тип1 переменная1;

тип2 переменная2;

……

};

где tag – имя типа.

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

Существует три способа передачи структур функциям:

- передача компонентов структуры по частям;

- передача целиком структуры;

- передача указателя на структуру.

13.Указатели на структур. Передача по ссылке членов массивов структур.

Указатели на структуру

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

Формат: struct point *pp;

где pp – указатель на структуру типа struct point, *pp – сама структура, (*pp).x и (*pp).y – члены структуры.

Скобки (*pp).x необходимы, так как приоритет операции (.) выше приоритета операции (*). В случае отсутствия скобок *pp.x понимается как *(pp.x).

Инициализация указателя на структуру выполняется так же, как и инициализация указателей других типов: struct point var1, *S1; здесь var1 – структурная переменная,*S1 – указатель на структуру.

Для определения значения указателя ему нужно присвоить адрес уже сформированной структуры:

S1 = &var1;

Теперь возможно еще одно обращение к элементам структуры:

(*S1).name.

Указатели на структуры используются весьма часто, поэтому для доступа к ее полям была введена короткая форма записи. Если р – указатель на структуру, то  <поле структуры> зволяет обратиться к указанному полю структурной переменной.

Знак → (стрелка) вводится с клавиатуры с помощью двух символов: '–' (минус) и '>' (больше). Например, pp  xpp  y.

Операторы доступа к полям структуры (.) и (→) вместе с операторами вызова функции () и индексами массива [] занимают самое высокое положение в иерархии приоритетов операций в языке C.

Указатели на структуру используются в следующих случаях:

·     доступ к структурам, размещенным в динамической памяти;

·     создание сложных структур данных – списков, деревьев;

·     передача структур в качестве параметров в функции.

Массивы структур

При необходимости хранения некоторых данных в виде нескольких массивов одной размерности, но разного типа, возможна организация массива структур. Наглядно массив структур можно представить в виде таблицы, где столбцы таблицы представляют собой поля структуры, а строки – элементы массива структур.

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

(*(keytab+i)).c или (keytab+i)→ c.

Массив структур также может быть передан в функцию в качестве аргумента, передача массива происходит через указатель на массив. 

14.Объединения и операции с ними. Пользовательские типы данных. Функции работы с датой и временем.

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

Структуры

Представьте, что вам надо написать библиотеку для работы с комплексными числами. Ясно, что и результаты функций, и их параметры будут состоять из реальной и мнимой частей. Можно, конечно, для представления комплексного числа пользоваться массивом из двух double:

/* Complex number as array */
double cmplx[2]; /* [0] - real part, [1] - img */

Можно, но не очень удобно - хотя бы потому, что вместо массива комплексных чисел вам придется работать с двумерным массивом вещественных

/* Array of complex numbers-arrays */
double cmplxarr[10][2]; /* [][0] - real part. [][1] - img */

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

/* complex.h */

struct COMPLEX { 
  double re;
  double im; 
};

или то же самое, но короче

/* complex.h */

struct COMPLEX { 
  double re, im;
};

Перечисления

Перечисления представляют собой список идентификаторов, введенных пользователем:

enum name_list {name1,name2,...};

За каждым таким именем по умолчанию закрепляются целочисленные константы:

  1.  имени name1 соответствует константа 0;
  2.  имени name2 соответствует константа 1;
  3.  .....................................

Объединения

Объединения – это такие наборы данных, которые компилятор должен разместить в оперативной памяти, начиная с одного и того же места. Впервые такое совмещение разных данных появилось в языке ФОРТРАН, где для этой цели использовался операторEQUIVALENCE (эквивалентность). Основным назначением этого оператора была попытка экономии оперативной памяти за счет размещения вновь используемых массивов на месте уже отработавших массивов. В последующем этот оператор использовался и для совместного доступа к одним и тем же полям оперативной памяти как к данным разного типа. Наконец, еще одна дополнительная услуга со стороны оператора EQUIVALENCE состояла в том, что программисты, создающие разные фрагменты программы могли использовать разные имена для обозначения одних и тех же физических величин. Эквивалентность двух разных имен позволяла свести к минимуму переделки при объединении фрагментов программ.

В языках C, C++ объединения создаются с помощью оператора union.

time.h — заголовочный файл стандартной библиотеки языка программирования СИ, содержащий типы и функции для работы с датой и временем.

15. Объектно-ориентированное программирования. Классы. Конструкторы и деструкторы.

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

Две роли классов

У класса две различные роли: модуля и типа данных. Класс - это модуль, архитектурная единица построения программной системы. Модульность построения - основное свойство программных систем. В ООП программная система, строящаяся по модульному принципу, состоит из классов, являющихся основным видом модуля. Модуль может не представлять собой содержательную единицу; его размер и содержание определяется архитектурными соображениями, а не семантическими. Теоретически можно построить монолитную систему, состоящую из одного модуля, решающую ту же задачу, что и система, состоящая из многих модулей. Практически большую систему, создаваемую коллективом разработчиков, без разделения системы на модули построить не удается. Модульность построения - основное средство борьбы со сложностью системы.

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

Синтаксис класса

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

[атрибуты][модификаторы]class имя_класса[:список_родителей]

{тело_класса}

При определении класса имеется возможность задать для объекта начальное значение. Специальный метод класса, называемыйконструктором, выполняется каждый раз, когда создается новый объект этого класса. Конструктор – это метод, имя которого совпадает с именем класса. Конструктор не возвращает никакого значения.

Для класса String имеет смысл в качестве начального значения использовать пустую строку:

class String

{public:

    String();   // объявление конструктора};

// определение конструктора

String::String(){

    str = 0;

    length = 0;}

Определив такой конструктор, мы гарантируем, что даже при создании автоматической переменной объект будет соответствующим образом инициализирован (в отличие от переменных встроенных типов).

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

У класса может быть только один деструктор. Его имя – это имя класса, перед которым добавлен знак "тильда" ‘ ~ ’. Для объектов класса String   деструктор должен освободить память, используемую для хранения строки:

class String {

    ~String(); };

String::~String(){

    if (str)

         delete str;

}

Если деструктор в определении класса не объявлен, то при уничтожении объекта никаких действий не производится.

Деструктор всегда вызывается перед тем, как освобождается память, выделенная под объект




1. Капитализм во Франции.html
2. Теоретические и практические основы аудита на расчетных и валютных счетах предприяти
3. Вредители почек и цветков на плодовых культурах
4. поживает воистину счастливый народ и правят им по справедливости и в радости король и королева
5. по теме- ldquo;Конкурентоспособность фирмыrdquo;
6. Реферат МЕТОДИЧЕСКИЕ УКАЗАНИЯ К САМОСТОЯТЕЛЬНОЙ РАБОТЕ по дисциплине ВВЕДЕНИЕ В СПЕЦИАЛЬНОС
7. I. По сфере регулирования общественных отношений социальные нормы подразделяются на- это правила пов
8. Анализ романа Дины Рубиной Почерк Леонардо
9. 2012 г. П О Л О Ж Е Н И Е о проведении молодежного фестиваля Рыбинская зима Место проведени
10. Причинами возникновения пожаров
11. Источники и литература по архивоведению Беларуси
12. Напрямки оптимізації системи правового регулювання оподаткування операцій на ринку цінних паперів
13. ПОЛОЖЕНИЕ О ПРОФИЛЬНОЙ СМЕНЕ «КАЗАЧИЙ КРУГ»
14. Что такое палаццо Что такое вилла Назовите отличительные черты живописи Венеции в эпоху Возрож
15. Тема 1. Прикладная политология как научное направление и учебная дисциплина.html
16. Die Sehenswurdigkeiten Leipzigs
17. Петербургский национальный исследовательский университет информационных технологий механики и оптики Сп
18. реферат дисертації на здобуття наукового ступеня доктора технічних наук Макiївка 1999 Дисертаціє
19. Дубовский педагогический колледж Отчет практиканта Вид практики
20. трезвость ума рассудительность т