рефераты

рефераты

 
 
рефераты рефераты

Меню

Реферат: Turbo C++ Programer`s guide рефераты

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

Указатель функции имеет тип "указатель функции, возвращающей тип type", где type есть тип возвращаемых функцией данных.

В С++, где контроль типов данных болеестрогий, указатель функции имеет тип "указатель функции принимающей агрументы типа type и возвращающей тип type". Действительно, в С функция, определенная с типами аргументов, также будет иметь данный, более узкий тип. Например,

void (*func)();

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

void(*func)(int);

*func это указатель функции, принимающей аргумент int и не возвращающей никаких значений.

Объявления указателей

Подробное описание типа void см. на стр.39 оригинала.

Объявление указателя всегда должно устанавливать его на некоторый конкретный тип, даже если этот тип void (что фактическиозначает указатель на любой тип). Однако, уже после объявления указатель обычно может быть переназначен на объект другого типа. Turbo C++ позволяет переназначать указатели без приведения в соответствие типа, но компилятор выдаст при этом предупреждение, если только первоначально указатель не был объявлен с типом void. В С (но не в С++) вы можете назначить указатель void* на указатель, не имеющий тип void*.

Если type есть любой предопределенный или определенный пользователем тип, включая void, то объявление

type *ptr;/* Опасно - неинициализированный указатель */

объявит ptr как "указатель на тип type". К объявленному таким образомобъекту ptr применимы все правила, связанные с контекстом, продолжительностью и видимостью.

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

Указатель типа "указатель на void" не следует путать с нулевым (null) указателем. Объявление

void *vptr;

объявляет, что vptr - это родовой указатель, которомуможет быть присвоено любое значение "указатель на тип type" без выдачи компилятором сообщений. Присвоения без правильного приведения типов между "указателем на тип type1" и "указателем на тип type2", где type1 и type2 это различные типы, может вызвать предупреждение или ошибку компилятора. Если type1 это функция, а type2 нет (или наоборот), присваивания указателей недопустимы. Если type1 это указатель на void, приведения типов не требуется. Если type2 это указатель на тип void, то в С приведение не нужно.

Ограничения присвоения также существуют относительно указателей разных размеров (near, far и huge). Можно присвоить меньший указатель большему, не вызвав ошибки, но нельзя выполнить обратную операцию, не выполнив явную операцию приведения. Например,

char near *ncp;

char far  *fcp;

char huge *hcp;

fcp     =   ncp;    // допустимо

hcp    =   fcp;    // допустимо

fcp     =   hcp;    // недопустимо

scp     =   fcp;    // недопустимо

scp     =   (char nesr*)fcp;  // теперь допустимо

Указатели и константы

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

int i;                      // i это целое;

int * pi;                        // pi это указатель на i

// (неинициализированный)

int * const cp = &i;   // cp это указатель-константа на int

// const

int ci = 7;                            // ci это константа int const

int * pci;                         // pci это указатель  наконстанту ci

const int * const cpc = &ci; // cpc это указатель-константа

// на константу int

Следующие присвоения допустимы:

i = ci;                      // Присвоить const int переменной int

*cp = ci;                      // Присвоение const int объекту, на

// который указывает

// указатель-константа

++pci;                   // Инкремент указателя на константу

pci = cpc;                       // Присвоение константы-указателя-на

// константу указателю-на-константу

Следующие присвоения недопустимы:

ci = 0;                   // Присвоение значений константе

// const int недопустимо

ci--;                  // Изменение константы недопустимо

*pci = 3;                    // Присвоение объекту, на который

// указывает указатель-на-константу

// недопустимо

cp = &ci;                   // Присвоение константе-указателю,

// даже если ее значение не будет

// изменено, недопустимо

cpc++;                // Изменять указатель-константу

// недопустимо

pi = pci;                     // Если бы такое присвоение было

// разрешено, вы могли бы присваивать

// *pci (константе), присваивая *pi

// что недопустимо

Аналогичные правила                 относятся   и   к   модификатору

volatile.Отметим,  что const и volatile могут  появляться  в

качестве модификаторов одного и того же идентификатора.

Арифметические операции с указателями

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

Арифметические операции                  с   указателями   ограничены

сложением,  вычитанием и сравнением. Арифметические операции

с указателями объектов типа "указатель на тип type" автоматически учитывают размер этоготипа, то есть число байт, необходимое для хранения в памяти объекта данного типа.

При выполнении арифметических операций с указателями предполагается, что указатель указывает на массив объектов. Таким образом, если указатель объявлен как указатель на type, то прибавление к нему целочисленного значения перемещает указатель на соответствующее количество объектов type. Если type имеет размер 10 байтов, то прибавление целого числа 5 к указателю этого типа перемещает указатель в памяти на 50 байт. Разность представляет собой число элементов массива, разделяющих два значения указателей. Например, если ptr1 указывает на третий элемент массива, а ptr2 на десятый, то результатом выполнения вычитания ptr2 - ptr1 будет 7э

Когда с "указателем на тип type" выполняется операция сложения или вычитание целого числа, то результат также будет "указателем на тип type". Ецли type неявляется массивом, то операнд указателя будет рассматриваться как указатель на первый элемент "массива типа type" длиной sizeof(type).

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

Для информации:P+n можно представить себе как перемещение указателя на (n*sizeof(type)) байт вперед, пока указатель остается в допустимых границах (не далее первого за концом массива элемента).

Вычитание междудвумя указателями на элементы одного и того же массива дает интегральное значение типа ptrdiff_t, определенное в stddef.h (signed long для указателей huge и far; signed intдля всех прочих). Данное значение представляет собой разность между индексами двух указанных элементов, при условии вхождения в диапазоне ptrdiff_t. В выражении P1P2, где P1 и P2 это указатели на тип type (или указатели на квалифицированный тип), P1 и P2 должны указывать на существующие элементы или на следующий за последним элемент. если P1 указывает на i-й элемент, а P2 указывает на j-й элемент, то P1-P2 имеет значение (i-j).

Преобразования указателей

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

char *str

int *ip

str = (char*)ip;

В более общем виде, приведение (type*) преобразует указатель в тип "указатель на тип type".

Объявления ссылок в С++

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

Массивы

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

Объявление

type декларатор [<выражение-типа-константы>]

объявляет масив, состоящий из элементов типа type. Массив в С состоит из непрерывной области памяти, по размеру позволяющей в точности разместить все его элементы.

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

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

type alpha [5] [7];

В определенном контексте первый декларатор массива из нескольких может не иметь выражения в квадратных скобках. Такой массив имеет неопределенный размер. Контекстом, где допустимо такое положение, является тот случай, когдадля резервирования памяти размер массива не требуется. Например, для объявление объекта типа массива extern точный размер массива не требуется; не требуется он и при передаче функции параметра типа массива. Будучи специальным расширением ANSI C, Turbo C также позволяет объявлятьв качестве последнего элемента структуры массив неопределенного размера. Такой массив не увеличивает размера структуры, а для того, чтобы обеспечить правильное выравнивание структуры, ее можно специально дополнить символами-заполнителями. Такие структуры обычно используются при динамическом распределении памяти, когда для правильного резервирования области памяти к размеру структуры следует явно прибавить фактический размер необходимого массива.

За исключением использования массива в качестве операнда операции sizeof или &, выражение с типом массива преобразуется в константу-указатель на первый элемент массива.

Функции

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

Объявления и определения

Каждая программа должна иметь одну внешнюю функцию main, содержащую точку входа в программу. Обычно функции объявляютсякак прототипы в стандартных или создаваемых пользователем файлах заголовка, либо в файлах программы. По умолчанию функции имеют тип extern, и доступ к ним возможен из любого файла программы. Функция может быть ограничена спецификатором класса памяти static (см. стр. 32 оригинала).

Функции объявляются в исходных файлах, либо делаются доступными при компоновке с откомпилированными библиотеками.

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

Данная функция может быть объявлена в программе несколько раз, при условии, что эти объявлениясовместимы. Неопределяющие объявления функции, использующие формат прототипа функции предоставляют Turbo C++ детальную информацию о параметрах, что позволяет лучшее управление числом аргументов, контролем их типа и преобразованиями типов.

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

Объявления и прототипы

В оригинальном стиле объявлений Кернигэна и Ритчи функция могла быть либо объявлена неявно, по ее вызову, либо явно:

<type> func()

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

Эта задача упрощается благодаря введению прототипа функции со следующим синтаксисом объявления:

<type> func(список-деклараторов-параметров)

При помощи IDE или опции компилятора командной строки можно разрешить выдачу следующего предупреждения: "Function called without a prototype" ("Функция вызывается без прототипа").

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

long lmax(long v1, long v2);  /* прототип */

main()

(*

int limit = 32;

char ch = 'A';

long mval;

mval = lmax(limit,ch): /* вызов функции */

Поскольку данная программа имеет прототип функции для lmax, данная программа преобразовывает limit и ch к типу long по стандартным правилам присвоения, прежде чем поместить их в стекдля вызова lmax.Без прототипа функции limit и ch были бы помещены в стек как целое и символьное значения, соответственно; в этом случае стек, переданный lmax, не соответствовал бы по размеру и содержимому тому, что ожидает на входе lmax, что привело бы к возникновению проблем. Классический стиль объявлений не позволяет выполнять контроль типа и числа параметров, поэтому использование прототипов функций существенно упрощает отслеживаниепрограммных ошибок.

Прототипы функций также упрощают документирование кодов программы. Например, функция strcpy принимает два параметра: исходную строку и строку назначения. Вопрос, где какая из них? Прототип функции

char *strcpy(char *dest, char *source);

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

Декларатор функции в круглых скобках, содержащий единственное словоvoid, указывает на функцию, вообще не принимающую аргументов:

func(void);

В С++ func() также означает функцию, не принимающую аргументов.

stdarg.h содержит макросы, которые можно использовать в функциях, определяемых пользователем, с переменным числом параметров.

Прототип функции обычно объявляет функцию, принимающую фиксированное число параметров. Для функции С, принимающей переменное число параметров (например, printf) прототип функции может заканчиваться многоточием (...), например:

f(int *const, long total, ...)

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

Ниже приводятся примеры деклараторов функций и прототипы:

int f(); /* В С это функция, возвращащая int, без информации о параметрах. Это "классический стиль" Кернигэна и Ритчи */

int f(); /* В С++ это функция, не принимающая аргументов */

int f(void); /* Функция, возвращающая int и не принимающая параметров */

int p(int,long) /* Функция с типомвозврата int,принимающая два параметра, первый типа int, и второй типа long */

int pascal q(void); /* функция типа pascal, возвращающая int и не принимающая параметров */

char far *s(char *source, int kind); /* Функция, возвращающая дальний указатель на char и принимающая два параметра: превый - дальний указатель на char, а второй int */

int printf(char *format,...); /* Функция, возвращающая int и принимающая фиксированный параметр типа указатель на char и любое число дополнительных параметров неизвестного типа */

int (*fp)(int); /* Указатель на функцию, возвращающую int и принимающую один параметр int */

Объявления

Общий синтаксис для определений внешних функций приводится в следующей таблице:

Определения внешних функций                        Таблица 1.18

файл:

внешнее-определение

файл   внешнее-определение

внешнее-определение:

определение-функции

объявление

asm-оператор

определение-функции:

<спецификаторы-объявления> декларатор <список-объявления>

составной-оператор

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

1. Опциональные спецификаторы класса памяти: extern или static. Умолчанием является extern.

2. Тип возврата, возможно void. Умолчанием является int.

Элементы из пунктов 1 и 2 можно взаимно комбинировать.

3. Опциональные модификаторы: pascal, cdecl, interrupt, near, far, huge. Умолчание зависит от модели памяти и установленных опций компилятора.

4. Имя функции.

5. Список объявления параметров, который может быть пустым, заключенный в круглые скобки. В с предпочтительно обозначать отсутствие параметров записью func(void). В С допускается и старый стиль записи func(), но это может приводить к неоднозначностям и возможным ошибкам. В С++ выдается соответствующее предупреждение.

Страницы: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40