Будь умным!


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

Лекция 8. Указатели

Работа добавлена на сайт samzan.net:


Лекция 8.   Указатели. Динамические массивы. Подпрограммы

Указатели

Указатель – это переменная, которая ссылается на другую переменную. Значением указателя является  адрес  переменной, на которую он ссылается.

Описание переменной типа указатель:

тип  *  имя;

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

int * p1;     /*   p1 – указатель на целочисленную переменную */

float * p2;  /* указатель на вещественную переменную */

char * p3;  /* указатель на символьную переменную */

Так же, как и другие переменные, указатели при объявлении можно инициализировать, например:

int n=0, k=1;

int * p1 = &n;   /*  указателю p1 присваивается адрес n  */

Пример расположения переменных в памяти:

Адрес     Память

   |   …       |

      -------------------

4100     |    0         |   n

      -------------------

4102     |    1        |   k

      -------------------

4104    |  4100    |   p1

      -------------------

     |    …      |

Изменить значение указателя можно с помощью оператора присваивания, например:

    p1 = &k;

Через указатель можно обратиться к той переменной, на которую он ссылается. Для этого используется операция разадресации  “*”. Например,  *p1  - переменная, на которую ссылается указатель p1.

    (*p1)++;   /* значение  k увеличивается на 1 */

    printf (“%d”, *p1);   /* вывод значения 2 */

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

 Пример 8.1:

int  m[5] = {10,20,30,40,50};

/*      m  = &m[0],

       *m   эквивалентно   m[0],

       *(m+1)  эквивалентно   m[1],

       *(m+2)   эквивалентно   m[2],

  …    */

int  i;

   /* вывод массива m */

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

 printf (“%d  ”, *(m+i));       /*  или    printf (“%d  ”, m[i]);  */

Статические и динамические массивы

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

Память для массива распределяется статически на этапе трансляции (до выполнения программы), такие массивы называются статическими. В явном виде в языке  С/С++ можно использовать только статические массивы.

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

Для создания динамического массива объявляется  переменная типа указатель.

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

В языке С++ можно динамически выделить память, выполнив операцию new с названием типа в качестве операнда. Если операция выполнилась успешно, результатом является адрес выделенной памяти, иначе - пустой указатель, поэтому результат операции необходимо проверять.

Пример создания динамического массива  на языке С++ с помощью операции new:

int n;      

float  *z;    // указатель (ссылка)     

cin >> n;     

z = new float [n];   // выделение памяти

if (z == NULL) { cout<< “No memory”;  return 1;} // ошибка

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

  cin  >>  z[i];    

….

delete [] z;    // освобождение памяти

return 0;    // успешное завершение

Пояснения к фрагменту программы.

1. Объявляется целочисленная величина int n  - количество элементов массива, значение которой будет введено во время выполнения программы: cin >> n;

2. Объявляется указатель на вещественную величину:       float *z;

3. Операция new выделяет память для n вещественных чисел и возвращает начальный адрес выделенной памяти, который присваивается указателю z. Результат операции проверяется: в случае получения пустого указателя программа завершается.

3. Доступ к элементам динамического массива такой же, как к элементам статического массива. Например, к элементу с номером i в массиве z можно обратиться как  z[i] или с помощью указателей *(z + i) (к начальному адресу прибавлено смещение элемента  i).

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

Память, выделенная под динамический массив, должна быть освобождена перед завершением программы. Память, выделенная с помощью операции new [], освобождается оператором delete[], а память выделенная вызовом функции malloc()  - вызовом функции free().

Пример создания динамического массива  на   С++ с помощью функции malloc():

int n;      

int  *m;      // указатель (ссылка)  

cin >> n;     

m = (int *) malloc ( n * sizeof(int));  // выделение памяти

if (m == NULL) { cout<< “No memory”;  return 1;} // ошибка  

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

  cin  >>  m[i];    

free (m);    // освобождение памяти

return 0;    // успешное завершение

Пример создания динамического массива  на языке С с помощью функции malloc():

int n;      

int  *m;      /* указатель (ссылка)  */

scanf(“%d,  &n);     

m = (int *) malloc ( n * sizeof(int));  /* выделение памяти  */

if (m == NULL) { printf( “No memory”);  return 1;} /* ошибка */

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

  scanf(“%d,  &m[i]);    

free (m);    /* освобождение памяти */

return 0;    /* успешное завершение */

Пояснения к фрагментам  программ.

1. Функция malloc() выделяет участок памяти, размер которого в байтах задан в виде параметра, и возвращает в качестве значения указатель (адрес) на начало выделенного участка.  Если не оказалось свободной памяти заданного размера, возвращает пустой указатель NULL.

2. Значение операции sizeof (тип) равно количеству байтов, необходимых для хранения величины указанного типа.

3. Перед вызовом функции malloc записана операция преобразования типа (int *). Функция  malloc() возвращает указатель типа void *, поэтому в примере требуется преобразование к типу указатель на целую величину.

4. Функция free() освобождает участок памяти, на которую ссылается указатель – параметр  m:     free (m);

ПОДПРОГРАММЫ

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

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

Перед использованием функции необходимо дать ее определение, например:    P(x) = x * (x + 1)

После этого ее можно использовать, например, получить значение функции P(x) при x=10: P(10) = 10 *( 10 + 1) = 110

Вызов подпрограммы (команда «выполнить подпрограмму») записывается аналогично использованию математической функции:

f (a1, a2, ... , an)

где f - имя подпрограммы (аналогично имени функции P), a1, a2, ... , an - аргументы вызова (фактические параметры), конкретные величины, подставляемые вместо формальных параметров при выполнении подпрограммы (как аргумент 10 при вычислении значения функции P).

Формальный параметр – это входная или выходная переменная подпрограммы, указанная в заголовке ее определения (как x в левой части определения функции P(x)).

При изменении имени формального параметра смысл подпрограммы не изменяется (как если бы в определении функции F(x) заменить x, например, на y).

Подпрограммы могут быть двух типов:

  •  функция – подпрограмма, обладающая значением;
  •  процедура – подпрограмма, не обладающая значением.

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

  •  функции, обладающие значением;
  •  функции, не обладающие значением.

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

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

Заголовок_ функции

{      Объявление_ переменных_ функции  // тело

       Операторы_ функции    // функции

}

Заголовок функции имеет вид:

тип_значения    имя_функции  ([тип  имя [,  тип   имя] …])

Если функция не обладает значением, в качестве типа значения пишется ключевое слово void. Тип значения функции разрешается не указывать. В этом случае (по умолчанию) подразумевается тип  int.

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

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

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

Оператор возврата имеет вид:

return  [выражение];

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

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

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

имя_функции  ([имя [,  имя] …])

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

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

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

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

имя_переменной =  имя_функции ([имя [, имя] …]);

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

z= (sqrt (x) + sqrt (y*2)) / 2;

Примеры заголовков функций, не обладающих значением:

void  p_f (float x, float *z) - процедура p_f от входного вещественного параметра x и выходного вещественного параметра z;

void  p_fakt (int k, int *f) - процедура p_fakt с входным целочисленным параметром   k и выходным целочисленным параметром f.

Примеры вызова функций p_f и p_fakt:

float a = 15.2,  b;

int   p;

p_f (a, &b);

p_fakt (4, &p);

Примеры заголовков функций, обладающих значением:

float  f (float x) - вещественная функция f от вещественного параметра x;

int fakt (int k) - целочисленная функция fakt с входным целочисленным

     параметром k.

Примеры вызова функций f и fakt:

float a, b;

int  n=4, p;

a = 15.2;  

b = f (a) *f(20.5);

p = fakt (n) + fakt(5);

 Пример описания функции:

/*  функция определения наибольшего из двух чисел  */

float  max ( float x, float y )

{  if (x>y) return x;

   else return y;

}

Пример вызова функции:

f = max(a,b) - max(a+b,c);

содержит два вызова приведенной выше функции max. При первом обращении функции  max  передаются значения переменных a и b,  она возвращает наибольшее из этих чисел, которое подставляется вместо указателя  функции  max(a,b).  При  втором вызове функции max  формальным параметрам x и y присваиваются соответственно  значения фактических параметров a+b  и  c. Оператор return возвращает наибольшее из этих значений в точку вызова функции.

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

Например,

float f (int n, float m[ ]); - прототип вещественной функции f от целочисленного входного параметра n и вещественного массива m.

Параметры подпрограмм

Параметры предназначены для передачи входных и выходных данных (переменных) подпрограммы.

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

При вызове подпрограммы происходит согласование параметров:

- порядок и типы формальных параметров в объявлении и определении подпрограммы должны совпадать с порядком и типами фактических параметров при вызове подпрограммы.

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

Передача параметров и согласование формальных и фактических параметров может осуществляться

  •  по значению;
  •  по ссылке (по адресу).

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

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

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

Упрощенное правило:

Для передачи параметра по ссылке в заголовке и теле подпрограммы перед именем параметра необходимо указывать символ *, а при вызове -  символ &.

Массивы всегда передаются по ссылке. Имя массива является адресом его первого элемента, поэтому на параметры – массивы правило не распространяется (т.е. не нужны символы * и &). Размер массива рекомендуется передавать отдельным параметром. В случае со строкой (символьным массивом) размер строки обычно не передают, т.к. конец строки определяется завершающим нуль символом –‘\0’.

Пример заголовка функции с параметром массив:

float f (int n, float m[])  - вещественная функция f от целочисленного параметра n (количество элементов массива) и вещественного массива m[].

Пример вызова этой функции:     

int  k;  float z[100],  t;  

t = f (k, z);

Область действия переменных

Каждая переменная до использования должна быть описана.

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

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

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

Контрольные вопросы и упражнения.

1. Что указывается в заголовке функции? Чем отличается определение функции от объявления функции?

2. Что такое прототип функции и когда он необходим в программе?

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

4. В чем особенность передачи параметра, являющегося массивом?

5. В чем отличие между локальными и глобальными переменными?

6. Какие переменные доступны в функциях main(), f1(), f2()?

int z;

f1 (float  t)

{ int x,  y;

}

char  s[80];

int  f2  (int n,  char m[])

{int  k=0,  p=1, z ;

}

void main ( )

{  float  b;

   int  k,  nom;

   gets (s);

   f1 ( b );

   k= f2 ( nom,  s );

}

7. Напишите определение функции, обменивающей местами значения переменных  x, y.

PAGE  86




1. совокупность устойчивых биологических свойств организма сложившихся под влиянием наследственности и усло
2. Проект землеустройства территории предприятия в случае перераспределения сельскохозяйственных угодий
3. Углерод и его соединенния.html
4. ПРАКТИКУМ ПО ДЕЛОПРОИЗВОДСТВУ Все формы управленческой деятельности находят отражение в соответствующих
5. Власть и собственност
6. Методические рекомендации по подготовке и оформлению курсовых работ по гражданскому праву для студентов ю
7. Дипломная работа- Формування економiчної ефективностi виробництва зерна в господарствi (ТОВ Великоглибочецьке)
8. Россия в мировом политическом процессе
9. Интегрированная логистическая поддержка ИЛП промышленной продукции Экономические предпосылки
10. Между ’красными’ и ’белыми’.html
11. тема 4 товарные запасы на предприятиях общественного питания План Понятие товарных ресурсов и то
12. О советской власти на Украине которая признавала самостоятельность УССР и возможность использования укра
13. тема внутрирегиональных и межрегиональных электронных платежей.html
14. философском знании такими общими характерными чертами являются понятия- бытия; сознания; системы; развития;
15. Психика и реальность
16. ПОСТНОЕ МЯСО ТЕЛЯТИНА отбивная котлеты ГОВЯДИНА вырезка бифштекс стейк говяжий филейная часть ростби
17. варіанті При його побудові використати елементи управління ListBox ComboBox CheckedListBox NumericUpDown DominUpDown та потрібні ра
18. ИТОГИ И ЗНАЧЕНИЕ
19. Если места цифр разряды пронумеровать справа налево и самой правой позиции присвоить номер ноль то можно з
20. слияние присоединение разделение выделение преобразование