Операции над указателями. Адресная арифметика 


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



ЗНАЕТЕ ЛИ ВЫ?

Операции над указателями. Адресная арифметика



< Кратко рассказать: понятие указателя, операции взятия адреса и обращения по адресу >

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

Пример присваивания: int x = 10; int y = 10; int* xptr = &x; int* yptr = &y;

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

В приведенном примере условие if (x==y) возвратит истину, а if(xptr==yptr) – ложь (адреса разные).

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

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

Например: объявлена переменная long int x и указатель long int * xptr. Для хранения переменной типа long int в памяти отводится 4 байта. Если адрес x равен 0x1000 и xptr=&x, то в результате операции xptr+=1 указатель станет равным 0x1004 (на 4 байта больше).

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

Строки в языке C++

Для того чтобы работать с текстом, в языке Си++ не существует особого встроенного типа данных. Роль строки играет массив символов, заканчивающийся нулевым байтом (то есть байтом, значение которого равно нулю, или ‘\0’). Иногда такое представление называют Си-строки, поскольку оно появилось в языке Си.

Таким образом, если мы объявляем строку из 10 символов, то с учетом последнего нулевого байта необходимо объявить массив char str[11];

Для записи строковых констант в программе используются литералы. Литерал – это последовательность знаков, заключенная в двойные кавычки: "Это строка", "0123456789", "*"

Символ, заключенный в двойные кавычки, отличается от символа, заключенного в апострофы. Литерал "*" обозначает два байта: первый байт содержит символ звездочки, второй байт содержит ноль. Константа '*' обозначает один байт, содержащий знак звездочки.

С помощью литералов можно инициализировать массивы: char alldigits[] = "0123456789";

Размер массива явно не задан, он определяется исходя из размера инициализирующего его литерала, в данном случае 11 (10 символов плюс нулевой байт). При таком объявлении нулевой байт компилятор добавит автоматически.

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

< Дать определение указателя >

Значение литерала – это массив неизменяемых байтов нужного размера. Строковый литерал может быть присвоен указателю на char:

char* message = "Сообщение программы";

Значение литерала – это адрес его первого байта, указатель на начало строки. Указатель на байт (тип char*) указывает на начало строки.

Для ввода строк используют функции scanf() и gets(), fgets().Первая функция применяется, если вводимая строка не содержит пробелов. Пример: char name [50]; scanf(" %s", name); Так как name – массив символов, а имя массива есть указатель (адрес) первого элемента, то перед name в данном случае знак & не ставится.

Длину строки вычисляет функция strlen(): int n=strlen(str1); Завершающий нулевой символ в подсчет не включается.

Копирование строк: strcpy(str2, str1) - Копировать строку str1по адресу str2, включая завершающий нулевой байт.

Программирование ввода-вывода в файлы в языке C++

Для использования функций ввода/вывода в языке С необходимо подключить к программе заголовочный файл <stdio.h>. Информация считывается как поток байтов. Объявление файловой переменной, открытие файла и создание связи между файловой переменной в языке C производится системным вызовом fopen, возвращающим указатель на файловую переменную. Обычно в программе используется конструкция:

FILE * f = fopen("имя_файла", "режим_открытия");

Допустимые значения режима открытия файла: r – чтение, w – запись, a – добавление в конец. Для нетекстовых (двоичных) файлов соответственно rb, wb, ab. Для одновременных чтения/записи: r+, w+, a+ и r+b, w+b, a+b.

Пример: FILE *f=fopen("test-c.txt","w");

Для ввода/вывода в файл используются функции, аналогичные стандартному вводу/выводу, к их именам добавлена «f», например: fprintf(f,”формат”,список); fscanf(file,формат”,список); fgets(имя_строки, размер,f). Примеры: fprintf(f,"number: %2i",i); fgets(res_string,sizeof(res_string),file).

Закрытие файла: fclose(file);

Перегрузка функций и операторов в языке C++

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

Как и при объявлении обычных функций, каждую функцию необходимо объявить и определить. Пусть, скажем, требуется определить среднее нескольких чисел. Тогда в программе можно объявить функцию вычисления среднего значения двух целых чисел sred(int, int):

float sred(int a, int b)

{ return (a+b)/2; }

Если в программе требуется также определить среднее из трех чисел, то ее можно объявить аналогично:

float sred(int a, int b, int c)

(return (a+b+c)/3; }

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

float sred (float a, float b, float c)

{ return (a+b+c)/3; }

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

Язык C++ допускает также перегрузку операторов. Перегрузка операторов производится в тех случаях, когда в программе определяется собственный тип данных – тогда необходимо определить операции, допустимые для этого типа. Например, пусть объявлен тип данных – школьная дробь: struct Ratio {unsigned long num, denum;}; Теперь имя структуры Ratio может использоваться для объявления переменных или массивов нового типа: Ratio x,y,z; Перегрузка оператора сводится к написанию новой функции. Например, чтобы определить операцию += для дробей, необходимо написать функцию со следующим заголовком:

Ratio& operator+=(Ratio &a, const Ratio &b)

{ a.num=a.num*b.denum + b.num*a.denum; a.denum*=b.denum; return a; }

Перегрузка оператора состоит в написании функции вида тип operatorS(параметры), где S – знак операции.

Динамическое распределение памяти в языке C++

В С++ объекты могут быть размещены либо статически – во время компиляции, либо динамически – во время выполнения программы. Динамическое распределение памяти используется тогда, когда заранее неизвестно, сколько объектов понадобится в программе и понадобятся ли они вообще.

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

Основу системы динамического распределения памяти в С составляют библиотечные функции calloc(), malloc(), realloc() и free(). Первые три функции возвращают указатель на выделенную область памяти или NULL.

< Быть готовым рассказать, что такое указатель >

Функция calloc():void *calloc(size_t num, size_t size); выделяет память, размер которой равен значению выражения num * size, т.е. память, достаточную для размещения массива, содержащего num объектов размером size. Выделенная область памяти обнуляется.

Функция malloc():void *malloc(size_t size); возвращает указатель на первый байт области памяти размера size или NULL. Выделенная область памяти не инициализируется.

Функция realloc():void *realloc(void *ptr, size_t size); изменяет размер блока ранее выделенной памяти, адресуемой указателем *ptr в соответствии с заданным размером size..

Функция free():void free(void *ptr); освобождает блок памяти, адресуемый указателем *ptr. Функция free() должна вызываться только с указателем, который был ранее получен в результате вызова одной из функций динамического распределения памяти.

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

int* n = new int: // 1

int* m = new int (10); // 2

int* q = new int [10]; // 3

В операторе 1 операция new выполняет выделение достаточного для размещения величины типа int участка динамической памяти и записывает адрес начала этого участка в переменную n. В операторе 2, кроме описанных выше действий, производится инициализация выделенной динамической памяти значением 10. В операторе 3 операция new выполняет выделение памяти под 10 величин типа int (массива из 10 элементов) и записывает адрес начала этого участка в переменную q, которая может трактоваться как имя массива. Через имя можно обращаться к любому элементу массива.

Освобождение памяти, выделенной под массив, должно быть выполнено с помощью операции delete: delete адрес; или (для масива) delete [] адрес.



Поделиться:


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

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