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



ЗНАЕТЕ ЛИ ВЫ?

Манипуляторы и форматирование ввода-вывода

Поиск

 

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

Манипуляторы – это объекты особых типов, которые управляют тем, как ostream или istream обрабатывают последующие аргументы. Некоторые манипуляторы могут также выводить или вводить специальные символы.

Например, один из манипуляторов - это endl. Он вызывает вывод символа новой строки. Другие манипуляторы позволяют задавать формат вывода чисел:

 

Манипулятор Описание
endl при выводе перейти на новую строку;
ends вывести нулевой байт (признак конца строки символов);
flush немедленно вывести и опустошить все промежуточные буферы;
dec выводить числа в десятичной системе (действует по умолчанию);
oct выводить числа в восьмеричной системе;
hex выводить числа в шестнадцатиричной системе счисления;
setw (int n) установить ширину поля вывода в n символов (n – целое число);
setfill(int n) установить символ-заполнитель; этим символом выводимое значение будет дополняться до необходимой ширины;
setprecision(int n) установить количество цифр после запятой при выводе вещественных чисел;
setbase(int n) установить систему счисления для вывода чисел; n может принимать значения 0, 2, 8, 10, 16, причем 0 означает систему счисления по умолчанию, т.е. 10.

 

Использовать манипуляторы просто – их надо вывести в выходной поток.

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

int x = 53;cout << "Десятичный вид: " << dec << x << endl << "Восьмиричный вид: " << oct << x << endl << "Шестнадцатиричный вид:" << hex << x << endl

 

Аналогично используются манипуляторы с параметрами.

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

double x;// вывести число в поле общей шириной // 6 символов (3 цифры до запятой, // десятичная точка и 2 цифры после запятой)cout << setw(6) << setprecision(2) << x << endl;

Те же манипуляторы (за исключением endl и ends) могут использоваться и при вводе. В этом случае они описывают представление вводимых чисел. Кроме того, имеется манипулятор, работающий только при вводе, это ws. Данный манипулятор переключает вводимый поток в такой режим, при котором все пробелы (включая табуляцию, переводы строки, переводы каретки и переводы страницы) будут вводиться. По умолчанию эти символы воспринимаются как разделители между атрибутами ввода.

 

int x;// ввести шестнадцатиричное числоcin >> hex >> x;

Указатели

 

В языке С++ существует два способа доступа к переменной - это ссылка на переменную по её имени и использование механизма указателей.

Указатель-переменная (или просто указатель) это переменная, предназначенная для хранения адреса какой-либо переменной в памяти.

Указатель-константа – это значение адреса оперативной памяти.

В языке определены две специальные операции для доступа к переменным через указатели. Это операции & и операция *.

Результатом операции & является выдача адрес объекта, к которому операция применяется. Например &var1 дает адрес, по которому переменная var1 хранится в памяти (точнее адрес её первого байта).

Операция * - это операция обращения к содержимому памяти по адресу, хранимому в переменной-указателе или равному указателю константе.

Признаком переменной-указателя для компилятора является наличие в описании переменной двух компонентов:

ü Типа объекта данных, для доступа к которому используется указатель (или, как еще говорят, на который ссылается указатель).

ü Символа * перед именем переменной.

Сочетание типа данныйх и символа * воспринимается компилятором как особый тип данных – «указатель на что-либо». Таким образом, описание

int var1,*ptr;

приводит к появлению переменной var1 и указателя-переменной ptr. Здесь переменная ptr будет иметь тип int*, т.е. тип «указатель на целое».

Указатели при их описании могут, как и обычные переменные, получать начальные значения:

int var1,*ptr=&var1;

Здесь указателю ptr присваивается начальное значение – адрес, по которому в памяти хранится переменная var1.

Еще одна инициализация при описании:

int *ptr=(int*)200;

Операцию *, пытаясь выразить словами смысл выражения, можно заменить фразой: - «взять содержимое по адресу, равному значению указателя». Например, оператор присваивания

*ptr2=*ptr1+4;

 

Можно интерпретировать так: взять содержимое памяти по адресу, равному значению указателя ptr1, прибавить к этому содержимому 4, а результат поместить по адресу, равному значению указателя ptr2.

Сам указатель-переменная тоже имеет адрес. Поэтому, например, корректным будет фрагмент:

int var1, *ptr1, *ptr2 = &var1;

ptr1 = (int*)&ptr2;

 

Здесь описываются два указателя-переменные, Указатель ptr2 инициализируется значением адреса переменной var1. Затем указателю ptr1 присваивается значение адреса, по которому в памяти располагается указатель ptr2.

Для указателей-переменных разрешены некоторые операции: присваивание, инкремент или декремент, сложение или вычитание, сравнение.

Язык разрешает операцию сравнение указателей одинакового типа (!). При выполнении присваивания значение указателя в правой части выражения пересылается в ячейку памяти, отведенную для указателя в левой части.

Важной особенностью арифметических операций с указателями является то, что физическое увеличение или уменьшение его значения зависит от типа указателя, т.е. от размера того объекта, на который ссылается указатель. Если к указателю, описанному как type *ptr, прибавить или отнять константу N, значение ptr изменяется на N* sizeof(type). Разность двух указателей type *ptr1 и type *ptr2 – это разность их значений, поделенная на sizeof(type).

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

 

void main(void)

{

long *ptr1=(long*)100;

long *ptr2=(long*)200;

ptr1++;

ptr2-=10;

printf(“ptr2=%ld,ptr1=%ld,

ptr2-ptr1=%ld\n”,

ptr2,ptr1,ptr2-ptr1);

}

Результат: ptr2=160,ptr1=104,ptr2-ptr1=14

 

Так как указатель имеет тип long* (длина 4 байта), то «единица измерения» указателя равна 4 байтам.

В самом деле:

ptr1=100+1*4=104.

ptr2=200-10*4=160.

ptr2-ptr1=(160-104)/4=56/4=14.

 

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

int *ptr=(int*)200;

 

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

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

 

Массивы

 

Массив – это один из структурированных типов данных. Это расположенные вплотную друг за другом в памяти элементы одного и того же типа. Каждый массив имеет имя. Доступ к отдельным элементам массива осуществляется по имени массива и индексу (порядковому номеру) элемента.

Основные свойства массива:

ü Все элементы массива имеют один и тот же тип;

ü Все элементы массива расположены в памяти друг за другом; индекс первого элемента равен НУЛЮ.

ü Имя массива является указателем-константой, равной адресу начала массива (первого байта первого элемента массива)

Признаком массива при описании является наличие парных квадратных скобок [ ]. Константа или константное выражение в скобках задает число элементов массива.

Например:

char array[81];

int key[4];

 

При описании массива может быть выполнена инициализация его элементов. Существуют два метода инициализации массивов.

ü Инициализация по умолчанию. Это случай, когда все элементы массивов инициализируются нулями.

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

Существуют две формы явной инициализации элементов массива:

1) Явное указание числа элементов массива и список начальных значений, возможно, с меньшим числом элементов.

Например:

char array[10]={‘A’,’B’,’C’,’D’};

 

Здесь описывается массив из 10 элементов. Первые 4 элемента массива инициализируются символами ’A’,’B’,’C’,’D’. Значение остальных шести элементов либо равно нулю, либо не определено (в зависимости от способа объявления массива). Если список начальных значений содержит больше элементов, чем число в квадратных скобках, компилятор генерирует ошибку.

2) Неявное указание числа элементов массива со списком начальных значений. Компилятор определяет число элементов массива по списку инициализации.

Например:

 

char array[]={’A’,’B’,’C’,’D’};

 

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

 

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

 

1. Если массив объявляется как внешний, при этом в том месте, где массив описывается приведена информация о числе его элементов. Например:

extern char array[];

2. в описании функции, если указатель на первый элемент массива является аргументом функции. Например:

int function (int array[], int index)

{

// тело функции

}

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

 

Доступ к отдельным элементам массива может выполняться либо с помощью индекса, либо операцией взятия значения по адресу. В первом случае для ссылки на нужный элемент указывается его порядковый номер в массиве, заключенный в квадратные скобки. Самый первый элемент массива имеет порядковый номер 0. Например:

 

int array[]={1,2,3,4,5};

int index = 0, first, last, bad;

first = array[index]; // first = 1

last = array[4]; // last = 5

bad = a[index+6]; // bad случайно

 

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

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

 

int array[10]={1,2,3,4,5,6,7};

int members;

members=sizeof(array)/sizeof(int);

 

Нетрудно догадаться, что переменная members будет равна 10.

Другой способ доступа к элементам массива – использование механизма указателей. Так как имя массива – это указатель-константа на первый байт первого элемента массива, то, используя операцию взятия значения по адресу, можно выполнить доступ к любому из элементов массива. Так следующие выражения являются тождествами:

 

array тождественно &array[0],

(array+i) тождественно &array[i].

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

 

С++ поддерживает многомерные массивы. Размерность массива – это число индексов, используемых для ссылки на конкретный элемент массива. Многомерные массивы должны описываться и могут при описании инициализироваться. Например:

int matrix[10][20];

int m[3][3]={0, 1, 2,

10,11,12,

20,21,22};

char strings[3][80]=

{“Первая строка”,

“Втоая строка”,

“Третья строка”

};

Элементы многомерных массивов хрянятся в памяти в порядке возрастания самого правого индекса. Или, как иногда говорят, по строкам. Список начальных значений, задаваемых при описании массива, соответствует порядку элементов массива в памяти. Особый случай – инициализация двумерного массива строковыми литералами. В приведенном примере описывается двумерный массив strings, состоящий из 3-х строк по 80 символов (байт) каждая. Строки массива инициализируются символами строковых литералов. В конце каждого литерала помещается символ конца строки – ‘\0’.

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

 

int m[][3]={0, 1, 2,

10,11,12,

20,21,22};

Имя двумерного массива является указателем-константой на массив указателей констант. Элементами массива указателей являются указатели-константы на начало каждой из строк массива. Например, для двумерного массива matrix[2][2] указателями-константами на нулевую, первую и вторую строки будут соответственно matrix[0], matrix[1] и matrix[2], а следующие выражения будут тождественными:

 

matrix[0] тождественно & matrix[0][0],

matrix[1] тождественно & matrix[1][0],

matrix[2] тождественно & matrix[2][0].

 

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

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



Поделиться:


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

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