Индексация с помощью указателей



Мы поможем в написании ваших работ!


Мы поможем в написании ваших работ!



Мы поможем в написании ваших работ!


ЗНАЕТЕ ЛИ ВЫ?

Индексация с помощью указателей



Указатели и массивы очень тесно связаны между собой. Имя массива без индекса – это указатель на первый элемент массива. Пусть имеется массив

 

char p[10];


тогда следующие значения идентичны:

 

p

&p[0]


Выражение

 

p == &p[0]


выдаёт истину, поскольку адрес первого элемента и адрес массива совпадают.

Справедливо и обратное. Любой указатель может быть проиндексирован, как будто это массив. Например,

 

int *p, i[10];

 

p = i;

p[5] = 1000;

*(p+5) = 1000;


Оба оператора присваивания помещают значение 1000 в шестой элемент i. Первый оператор использует индексацию с р, а второй – арифметику указателей. Результат одинаков.

Данные способы индексации совершенно справедливы для массивов размерности 2 и более. Предположим, что а – это целочисленный массив 10 на 10. Тогда нижеприведенные операторы эквивалентны:

 

(int *)a

&a[0][0]


К элементу 0, 4 массива а можно обратиться или с помощью индексации массива – a[0][4], или с помощью указателя - *((int *)a+4). Аналогично к элементу 1, 2 можно обратиться или с помощью индексации массива – a[1][2], или с помощью указателя - *((int *)a+12).

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

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

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

 

int num[10][10];

void pr_row(int j)

{

int *p, t;

 

p = num[j];

 

for(t=0; t<10; ++t) printf(“%d “, *(p+t));

}

 

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


void pr_row(int j, int row_dimension, int *p)

{

int t;

 

p = p + (j * row_dimension);

for(t=0; t< row_dimension; ++t)

printf(“%d “, *(p+t));

}


Размещение массивов

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

 

int *p;

try{ p = new int[100]; }

catch(…){ puts(“Cannot allocate”); exit(1); }


В дальнейшем доступ осуществляется, как при помощи указателя. Например, доступ к 6 элементу:

 

p[5]

 

Для освобождения памяти необходимо пользоваться оператором delete.

 

delete [] p;

 

Оператор delete должен освобождать память, только ранее выделенную оператором new, иначе это может привести к краху программы.

 

Функции

Функции – это базовые блоки С, в которых выполняются все операции. Стандартный вид функций следующий:

 

спецификатор_типа имя_функции(список параметров)

{

тело функции

}

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

Оператор return

 

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

 

Выход из функции

 

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

 

void pr_reverse(char *s)

{

register int t;

 

for(t=strlen(s)-1; t>-1; t--) printf(“%c”, s[t]);

}

 

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

Большинство функций использует оператор return для окончания исполнения с целью возвращения значения или упрощения кода функции и увеличения его эффективности путём создания нескольких точек выхода. Например, функция, показанная ниже, возвращает или индекс первого появления подстроки, указываемой в s2, в строке, указываемой в s1, или -1, если не обнаружено совпадений:

 

int find_substr(char *s1, char *s2)

{

register int t;

char *p, *p2;

for(t=0; s1[t]; t++) {

p = &s1[t];

p2 = s2;

while(*p2 && *p2==*p) {

p++;

p2++;

}

if(!*p2) return t;

}

return -1;

}

Надо обратить внимание, как два оператора return упрощают функцию.

 

Возвращаемые значения

 

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

 

x = power(y);

 

if(max(x, y) > 100) printf(“greater”);

 

for(ch=getchar(); isdigit(ch);) …;

Функция не может стоять с левой стороны оператора присваивания. Оператор типа

 

swap(x, y) = 100;


неправилен. Компилятор выдаст ошибку.

Если функция определена как void, она не может использоваться в выражениях. Предположим, что f() объявлена как void. Следующие операторы не будут компилироваться:

 

int t;

 

t = f();

 

f() + f();

 

Если не указано, чему присваивается возвращаемое значение, то оно просто отбрасывается. Рассмотрим следующую программу, использующую mul():

 


#include <stdio.h>

 

int mul(int a, int b);

 

int main(void)

{

int x, y, z;

 

x = 10; y = 20;

z = mul(x, y); /* 1 */

printf(“%d “, mul(x, y)); /* 2 */

mul(x, y); /* 3 */

 

return 0;

}

 

int mul(int a, int b)

{

return a*b;

}

 

Строка 1 присваивает возвращаемое функцией mul() значение переменной z. В строке 2 возвращаемое значение используется функцией printf(). В строке 3 возвращаемое значение теряется.

 

Значения, возвращаемые функцией main()

Когда используется оператор return в main(), программа возвращает код завершения вызывавшему процессу. Возвращаемое значение должно быть целого типа. Большинство ОС трактуют 0 как нормальное завершение программы. Остальные значения воспринимаются как ошибки.

 

Правила видимости для функций

 

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

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

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

 

Аргументы функции

 

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

 

Передача по значению и передача по указателю

 

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

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

Рассмотрим следующую функцию:

 

#include <stdio.h>

 

int sqr(int x);

 

int main(void)

{

int t=10;

 

printf(“%d %d”, sqr(t), t);

return 0;

}

int sqr(int x)

{

x = x*x;

return x;

}

 

На экране появится 100 10.



Последнее изменение этой страницы: 2016-08-15; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.236.98.69 (0.023 с.)