Студопедия.Орг Главная | Случайная страница | Контакты | Мы поможем в написании вашей работы!  
 

Правильно Неправильно. 5. РАЗНОСТЬ. Можно находить разность двух указателей



ptr1 ++; urn++;

х++; 3++;

ptr2 = ptr1 + 2; ptr2 = urn++;

ptr2 = urn + 1; x = у + 3++;

5. РАЗНОСТЬ. Можно находить разность двух указателей. Обычно это делается для указателей, ссылающихся на элементы одного и того же массива, чтобы определить, на каком расстоянии друг от друга находятся элементы. Помните, что результат имеет тот же тип, что и переменная, содержащая размер массива.

7.20.10. Сложности при использовании операций ++ и --.

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

*pc++=getchar();

*++pc=getchar();

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

7.20.11. Сравнение указателей.

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

· Вычитание целого числа из указателя

· Вычитание одного указателя из другого (обычно они указывают на один и тот же объект)

· Сравнение указателей при помощи операций отношения, например, = или >=

Указатели одинаковых типов (то есть указатели, которые ссылаются на один и тот же тип данных) можно также сравнивать между собой. Полученный результат — ИСТИНА (!0) или ЛОЖЬ (0) — можно или анализировать, или присвоить некоторой целой переменной — так же, как и результат любого другого логического выражения.

7.20.12. Переносимость указателей.

В приведенных примерах адреса представлялись целыми числами. Вы можете предположить, что в С указатели имеют тип int. Это не так. Указатель содержит адрес переменной некоторого типа, однако сам указатель не относится ни какому простому типу данных вроде int, float и им подобному. В конкретной системе С указатель может копироваться в переменную типа int, а переменная int может копироваться в указатель; однако, язык С не гарантирует, что указатели могут храниться в переменных типа int. Для обеспечения переносимости программного кода таких операций следует избегать.

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

7.20.13. Использование функции sizeof() с указателями в среде DOS.

В этой программе на C++ печатается размер указателей по умолчанию, и их размер с моделями памяти __far и near. Также используется директива препроцессора (#), определяющая PRINT_SIZEOF с аргументом A_POINTER, поэтому печатается не только размер указателя, но и его название.

//10STRIZE.CPP

//sizeof с указателями

#include <stdio.h>

#define PRINT_SIZEOF(A_POINTER) \

printf("sizeof\t("#A_POINTER")\t= %d\n", \

sizeof(A_POINTER))

main()

{

char *reg_pc;

long double *reg_pldbl;

char __far *far_pc;

long double __far *far_pldbl;

char __near *near_pc;

long double __near *near_pldbl;

PRINT_SIZEOF(reg_pc);

PRINT_SIZEOF(reg_pldbl);

PRINT_SIZEOF(far_pc);

PRINT_SIZEOF(far_pldbl);

PRINT_SIZEOF(near_pc);

PRINT_SIZEOF(near_pldbl);

return (0);

}

Результат выполнения программы:

Sizeof (reg_pc) = 2
Sizeof (reg_pldbl) = 2
Sizeof (far_pc) = 4
Sizeof (far_pldbl) = 4
Sizeof (near_pc) = 2
Sizeof (near_pldbl) = 2

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

Указатель на функцию можно использовать в нескольких важных случаях. Рассмотрим, например, функцию qsort(). Одним из ее параметров является указатель на функцию. Адресуемая функция осуществляет необходимое сравнение, которое должно выполняться для элементов сортируемого массива. Применение в qsort() указателя на функцию вызвано тем, что процесс сравнения двух элементов может быть весьма сложным, и при помощи одного управляющего флага его представить нельзя. Функцию нельзя передать по значению — то есть, передать ее код. Однако, в С можно передать указатель на код или указатель на функцию.

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

Вот программа на С:

/*10FNCPTR.C

Программа на С, иллюстрирующая объявление пользовательской функции*/

#include "stdafx.h"

#include "E:\LECTURE\AlgorithmProgramming 02\Universal_HederFile.h"

#define IMAXVALUES 10

int icompare_funct(const void *iresult_a, const void *iresult_b);

int (*ifunct_ptr)(const void *,const void *);

void StopWait(void);

main()

{

int i;

int iarray[IMAXVALUES]={0,5,3,2,8,7,9,1,4,6};

ifunct_ptr=icompare_funct;

qsort(iarray,IMAXVALUES, sizeof(int), ifunct_ptr);

for(i=0; i < IMAXVALUES; i++)

printf("%d ",iarray[i]);

printf("\n");

StopWait(); /* Wait a little */

return (0);

}

int icompare_funct(const void *iresult_a, const void *iresult_b)

{

return((*(int *)iresult_a)-(*(int *) iresult_b));

}

7.22. Динамическая память.

При компиляции программы на С память компьютера разбивается на четыре области, содержащие код программы, все глобальные данные, стек и динамически распределяемую область памяти (иногда ее называют heap-куча). Динамическая память (heap) — это просто свободная область памяти, с которой работают при помощи функций динамического выделения памяти malloc() и free().

При вызове функции malloc() выделяется непрерывный блок памяти для указанного объекта и возвращается указатель на начало этого блока. Функция free() возвращает выделенный блок обратно в динамическую область памяти для повторного использования.

Аргумент, передаваемый функции malloc(), представляет собой объем необходимой памяти в байтах. В следующем фрагменте кода выделяется память для 300 чисел типа float:

float *pf;

int inum_floats = 300;

pf = (float *) malloc(inum_floats * sizeof(float));

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

free((void *) pf);

Программы на С и C++ могут в любой момент выделять и освобождать динамическую память. Важно помнить, что на переменные, память для которых выделена в динамической области, не распространяются правила области действия, которые справедливы для других переменных. Эти переменные действуют всегда; поэтому, если вы выделили динамическую область памяти, то не должны забывать о ее освобождении. Если повторно выделить динамическую память, предварительно не освободив ее, то выполнение программы, скорее всего закончится крахом.

Для распределения динамической памяти в большинстве компиляторов С используются описанные выше библиотечные функции malloc() и free(), однако, в C++ эти средства считаются настолько важными, что они включены в ядро языка. В C++ для выделения и освобождения динамической памяти используются операторы new и delete.

7.22.1. Использование указателей типа void.

Теперь, когда вы уже глубоко понимаете природу переменных-указателей, становится понятной необходимость указателей типа void. Суть указателя в том, что это переменная, в которой хранится адрес другой переменной. Если бы вы всегда знали размер указателя, то на этапе компиляции не нужно было бы определять его тип. Тогда можно было бы передать в функцию адрес данных любого типа. Затем функция могла бы преобразовать этот адрес в указатель нужного типа (на основании другой информации) и выполнить операции, определяющие результат. Этот процесс позволил бы создавать функции, работающие со множеством различных типов данных.

Следующая программа на C++ иллюстрирует использование указателей void:

// 10VOIDPT.CPP

// Программа на C++, использующая указатели void

#include "stdafx.h"

#include "E:\LECTURE\AlgorithmProgramming 02\Universal_HederFile.h"

#define ISTRING_MAX 50

void voutput(void *pobject, char cflag);

void StopWait(void);

void main()

{

int *pi;

char *psz;

float *pf;

char cresponse, cnewline;

cout << "Please enter the dynamic data type\n";

cout << "you woud like to create.\n\n";

cout << "Use (s)tring, (i)nt or (f)loat";

cout << endl;

cin >> cresponse;

cin.get(cnewline);

switch(cresponse)

{

case 's':

psz=new char[ISTRING_MAX];

cout << "\nPliase enter a string: ";

cin.get(psz,ISTRING_MAX);

voutput(psz, cresponse);

break;

case 'i':

pi=new int;

cout << "\nPliase enter an integer: ";

cin >> *pi;

voutput(pi, cresponse);

break;

case 'f':

pf=new float;

cout << "\nPliase enter a float: ";

cin >> *pf;

voutput(pf, cresponse);

break;

default:

cout << "\n\nObject type not implemented!";

}

StopWait(); /* Wait a little */

}

void voutput(void *pobject, char cflag)

{

switch(cflag)

{

cout << "\n\nThe string read in: " << (char *) pobject;

delete pobject;

break;

case 'i':

cout << "\n\nThe integer read in: " << *((int *) pobject);

delete pobject;

break;

case 'f':

cout << "\n\nThe float read in: " << *((float *) pobject);

delete pobject;

break;

}

cout << endl;

}

7.23. Указатели и массивы.

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

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

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

/* прибавление к указателю */

#include<stdio.h>

main ()

{

int dates[4], *pti, index;

float bills [4], *ptf;

pti = dates; /* присваивает адрес указателю массива */

ptf = bills;

for (index = 0; index < 4; index++)

printf(“ ukazatel + %d: %10 u %10u\n", index, pti + index, ptf + index);

}

Результат работы программы

Вот результат

указатели + 0: 56014 56026

указатели + 1: 56016 56030

указатели + 2: 56018 56034

указатели + 3: 56020 56038

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

56014 + 1 = 56016?

56026 + 1 = 56030?

В нашей системе единицей адресации является байт, но тип int использует два байта, а тип float — четыре. Что произойдет, если вы скажете: «прибавить единицу к указателю?» Компилятор языка Си добавит единицу памяти. Для массивов это означает, что мы перейдем к адресу следующего элемента, а не следующего байта. Вот почему мы должны специально оговаривать тип объекта, на который ссылается указатель; одного адреса здесь недостаточно, так как машина должна знать, сколько байтов потребуется для запоминания объекта. (Это справедливо также для указателей на скалярные переменные; иными словами, при помощи операции *pt нельзя получить значение.)

7.23.1. Функции, массивы и указатели.

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

Об этом уже говорилось в гл. 10. Сейчас, когда мы познакомились с указателями, можно заняться более глубоким изучением массивов-аргументов. Давайте проанализируем скелет программы, обращая внимание на описания:

/* массив-аргумент */

main ()

{

int ages [50]; /* массив из 50 элементов */

convert(ages);

}

convert (years)

int years []; /* каков размер массива? */

{

}

Очевидно, что массив ages состоит из 50 элементов. А что можно сказать о массиве years? Оказывается, в программе нет такого массива. Описатель int years[]; создает не массив, а указатель на него.

Вот вызов нашей функции:

convert(ages);

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

convert (уears)

int *years;

{

}

Действительно, операторы

int years [];

int *years;

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

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

Попробуем написать функцию, использующую массивы, а затем перепишем ее, применяя указатели.

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

/* X54.C */

#include <stdio.h>

void main(void)

{

int n1=9,i, *pa1,*pa,n;

int array[] = {9,5,3,1,0,-9,-5,-3,-1};

pa1=array;

int mean(int *pa,int n);

i = mean(pa1,n1);

printf("Average is %d\n",i);

}

int mean(int *pa,int n)

{

int index;

long sum;

if(n > 0)

{

for(index = 0, sum = 0; index < n; index++)

sum += *(pa+index);

return((int) (sum/n));

}

else

{

printf("Net massiva\n");

return(0);

}

}

Результат работы программы

Эту программу легко переделать, применяя указатели. Объявим ра указателем на тип int. Затем заменим элемент массива array[index] на соответствующее значение: *(ра + index).

/* Использование указателей для нахождения среднего значения

массива n целых чисел */

int mean(pa, n)

int *pa, n;

{

int index;

long sum; /*Если целых слишком много, их можно суммировать в формате long int */

if (n > 0)

{

for (index = 0, sum = 0; index < n: index++)

sum += *(pa + index);

return((int) (sum/n)); /* Возвращает целое */

}

else

{

printf(" Нет массива. \n");

return(0);

}

}

7.23.3. Строки (массивы типа char).

Многие строковые операции в С обычно выполняются с использованием указателей и арифметических операций с указателями для доступа к элементам символьного массива. Это обусловлено тем, что символьные массивы или строки, как правило, обрабатываются строго последовательно. Напоминаем, что все строки в С заканчиваются символом null (\0). Следующая программа на C++ иллюстрирует использование указателей с символьными массивами:

// 10CHRARY.CPP

// Программа на C++, печатающая массив символов в обратном порядке

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

#include "stdafx.h"

#include "E:\LECTURE\AlgorithmProgramming 02\Universal_HederFile.h"

void StopWait(void);

void main()

{

char pszpalindrome[]="POOR DAN IN A DROOP";

char *pc;

pc=&pszpalindrome[0]+(strlen(pszpalindrome)-1);

do

{

cout << *pc;

pc--;

} while (pc >= pszpalindrome);

cout << endl;

StopWait(); /* Wait a little */

}





Дата публикования: 2015-01-13; Прочитано: 709 | Нарушение авторского права страницы | Мы поможем в написании вашей работы!



studopedia.org - Студопедия.Орг - 2014-2024 год. Студопедия не является автором материалов, которые размещены. Но предоставляет возможность бесплатного использования (0.027 с)...