Безтиповый (нетипизированный) указатель




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

void* ptr;

Для указателя на тип void не определена операция ->, не определена операция обращения по адресу *, не определена адресная арифметика. Использование безтиповыхуказателей ограничено работой с памятью при использовании ряда системных функций, передачей адресов в функции, написанные на языках программирования более низкого уровня, например на ассемблере.

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

voidprintbytes(void* ptr, intnbytes){if (nbytes == 1) {char* cptr = (char*)ptr;cout<< *cptr; } else if (nbytes == 2) {short* sptr = (short*)ptr;cout<< *sptr; } else if (nbytes == 4) {long* lptr = (long*)ptr;cout<< *lptr;} else {cout<< "Неверное значение аргумента"; }}

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

Нулевой указатель

В программах на языке Си++ значение указателя, равное нулю, используется в качестве "неопределенного" значения. Например, если какая-то функция вычисляет значение указателя, то чаще всего нулевое значение возвращается в случае ошибки.

long* foo(void);...long* resPtr;if ((resPtr = foo())!= 0) {// использовать результат} else { // ошибка}

В языке Си++ определена символическая константа NULL для обозначения нулевого значения указателя.

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

Строки и литералы

Для того чтобы работать с текстом, в языке Си++ не существует особого встроенного типа данных. Текст представляется в виде последовательности знаков (байтов), заканчивающейся нулевым байтом. Иногда такое представление называют Си-строки, поскольку оно появилось в языке Си. Кроме того, в Си++ можно создать классы для более удобной работы с текстами (готовые классы для представления строк имеются в стандартной библиотеке шаблонов).

Строки представляются в виде массива байтов:

char string[20];string[0] = 'H';string[1] = 'e';string[2] = 'l';string[3] = 'l';string[4] = 'o';string[5] = 0;

В массиве string записана строка "Hello". При этом мы использовали только 6 из 20 элементов массива.

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

"Это строка""0123456789""*"

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

С помощью литералов можно инициализировать массивы:

charalldigits[] = "0123456789";

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

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

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

Значениелитерала – это адрес его первого байта, указатель на начало строки. В следующем примере функцияCopyString копирует первую строку во вторую:

voidCopyString(char* src, char* dst){while (*dst++ = *src++); *dst = 0;}intmain(){char first[] = "Перваястрока";char second[100];CopyString(first, second);return 1;}

Указатель на байт (тип char*) указывает на начало строки. Предположим, нам нужно подсчитать количество цифр в строке, на которую показывает указательstr:

#include <ctype.h>int count = 0; while (*str!= 0) { // признакконцастроки – ноль if (isdigit(*str++)) count++; // проверить байт, на который // указывает str, и сдвинуть // указатель на следующий байт }

При выходе из циклаwhileпеременнаяcount содержит количество цифр в строкеstr, а сам указательstr указывает на конец строки – нулевой байт. Чтобы проверить, является ли текущий символ цифрой, используется функцияisdigit. Это одна из многих стандартных функций языка, предназначенных для работы с символами и строками.

С помощью функций стандартной библиотеки языка реализованы многие часто используемые операции над символьными строками. В большинстве своем в качестве строк они воспринимают указатели. Приведем ряд наиболее употребительных. Прежде чем использовать эти указатели в программе, нужно подключить их описания с помощью операторов #include<string.h> и #include<ctype.h>.

char* strcpy(char* target, const char* source);

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

char* strcat(char* target, const char* source);

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

intstrcmp(const char* string1, constchar* string2);

Сравнить две строки в лексикографическом порядке (по алфавиту). Если первая строка должна стоять по алфавиту раньше, чем вторая, то результат функции меньше нуля, если позже – больше нуля, и ноль, если две строки равны.

size_tstrlen(const char* string);

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

В следующем примере, использующем приведенные функции, в массиве result будет образована строка "1 января 1998 года, 12 часов":

char result[100];char* date = "1 января 1998 года";char* time = "12 часов";strcpy(result, date);strcat(result, ", ");strcat(result, time);

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

Определить массивстрок можно с помощью следующего объявления:

char* StrArray[5] = { "one", "two", "three", "four", "five" };

Операторы управления

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

Условные операторы

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

Операторif выбирает один из двух вариантов последовательности вычислений.

if (условие) оператор1else оператор2

Если условие истинно, выполняется оператор1, если ложно, то выполняется оператор2.

if (x > y) a = x;else a = y;

В данном примере переменной a присваивается значение максимума из двух величин x и y.

Конструкция else необязательна. Можно записать:

if (x < 0) x = -x;abs = x;

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

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

if (x < 0) { x = -x;cout<< "Изменить значение x на противоположное по знаку";}abs = x;

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

Условный оператор можно расширить для проверки нескольких условий:

if (x < 0)cout<< "Отрицательная величина";elseif (x > 0)cout<< "Положительная величина";elsecout<< "Ноль";

Конструкций elseif может быть несколько.

Хотя любые комбинации условий можно выразить с помощью оператора if, довольно часто запись становится неудобной и запутанной. Оператор выбораswitch используется, когда для каждого из нескольких возможных значений выражения нужно выполнить определенные действия. Например, предположим, что в переменной code хранится целое число от 0 до 2, и нам нужно выполнить различные действия в зависимости от ее значения:

switch (code) {case 0:cout<< "кодноль"; x = x + 1;break;case 1:cout<< "кододин"; y = y + 1;break;case 2:cout<< "коддва";z = z + 1;break;default:cout<< "Необрабатываемое значение";}

В зависимости от значения code управление передается на одну из меток case. Выполнение оператора заканчивается по достижении либо оператораbreak, либо конца оператора switch. Таким образом, если code равно 1, выводится " код один ", а затем переменная y увеличивается на единицу. Если бы после этого не стоял операторbreak, то управление "провалилось" бы дальше, была бы выведена фраза " код два ", и переменная z тоже увеличилась бы на единицу.

Если значение переключателя не совпадает ни с одним из значений меток case, то выполняются операторы, записанные после метки default. Метка default может быть опущена, что эквивалентно записи:

default:; // пустой оператор, не выполняющий // никаких действий

Очевидно, что приведенный пример можно переписать с помощью оператора if:

if (code == 0) {cout<< "кодноль"; x = x + 1;} else if (code == 1) {cout<< "код один"; y = y + 1;} else if (code == 2) {cout<< "коддва";z = z + 1;} else {cout<< "Необрабатываемое значение";}

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

Операторы цикла

Предположим, нам нужно вычислить сумму всех целых чисел от 0 до 100. Для этого воспользуемся оператором циклаfor:

intsum = 0;int i;for (i = 1; i <= 100; i = i + 1) // заголовокциклаsum = sum + i; // тело цикла

Оператор цикла состоит из заголовка цикла и тела цикла. Тело цикла – это оператор, который будет повторно выполняться (в данном случае – увеличение значения переменной sum на величину переменной i). Заголовок – это ключевое слово for, после которого в круглых скобках записаны три выражения, разделенные точкой с запятой. Первое выражение вычисляется один раз до начала выполнения цикла. Второе – это условие цикла. Тело цикла будет повторяться до тех пор, пока условие цикла истинно. Третье выражение вычисляется после каждого повторения тела цикла.

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

Любое из трех выражений в заголовке цикла может быть опущено (в том числе и все три). То же самое можно записать следующим образом:

intsum = 0;int i = 1;for (; i <= 100;) {sum = sum + i;i = i + 1;}

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

int sum = 0;int i = 1;for (;;) {if (i > 100)break;sum = sum + i;i = i + 1;}

В последнем примере мы опять встречаем операторbreak. Операторbreak завершает выполнение цикла. Еще одним вспомогательным оператором при выполнении циклов служит оператор продолжения continue. Операторcontinue заставляет пропустить остаток тела цикла и перейти к следующей итерации (повторению). Например, если мы хотим найти сумму всех целых чисел от 0 до 100, которые не делятся на 7, можно записать это так:

int sum = 0;for (int i = 1; i <= 100; i = i+1) {if (i % 7 == 0)continue;sum = sum + i;}

Еще одно полезное свойство цикла for: в первом выражении заголовка цикла можно объявить переменную. Эта переменная будет действительна только в пределах цикла.

Другой формой оператора цикла является операторwhile. Его форма следующая:

while (условие) оператор

Условие – как и в условном операторе if – это выражение, которое принимает логическое значение "истина" или "ложь". Выполнение оператора повторяется до тех пор, пока значением условия является true (истина). Условие вычисляется заново перед каждой итерацией. Подсчитать, сколько десятичных цифр нужно для записи целого положительного числа N, можно с помощью следующего фрагмента:

int digits =0;while (N >= 1) {digits = digits + 1;N = N / 10;}

Третьей формой оператора цикла является циклdowhile. Он имеет форму:

do{ операторы } while (условие);

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

charch;do {ch = getch(); // функцияgetchвозвращает// символ, введёныйс // клавиатуры} while (ch!= '*');

В операторах while и do также можно использовать операторы break и continue.

Как легко заметить, операторы цикла взаимозаменяемы. Операторwhile соответствует операторуfor:

for (; условие;) оператор

Пример чтения символов с терминала можно переписать в виде:

charch;ch = getch(); while (ch!= '*') {ch = getch();}

Разные формы нужны для удобства и наглядности записи.

Оператор возврата

Операторreturn завершает выполнение функции и возвращает управление в ту точку, откуда она была вызвана. Его форма:

return выражение;

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

return;

Оператор перехода

Последовательность выполнения операторов в программе можно изменить с помощью оператора переходаgoto. Он имеет вид:

goto метка;

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

if(x >= 0)gotopositiv;x = -x; // переменить знак xpositiv: // объявление меткиabs = x; // присвоить переменной abs // положительное значение

При выполнении goto вместо следующего оператора выполняется оператор, стоящий после метки positiv. Если значение x положительное, оператор x = - x выполняться не будет.

В настоящее время считается, что оператор goto очень легко запутывает программу. Без него, вообще говоря, можно обойтись, поэтому лучше его не использовать, ну разве что лишь в самом крайнем случае.

Пример:

int fact(int n){int k;if (n == 1) { k = 1; } else { k = n * fact(n – 1); }return k;}

Это функция вычисления факториала. Первый оператор в ней – это объявление переменной k, в которой будет храниться результат вычисления. Затем выполняется условный оператор if. Если n равно единице, то вычисления факториала закончены, и выполняется оператор-выражение, который присваивает переменной значение 1. В противном случае выполняется другой оператор-выражение.

Последний оператор – это оператор возврата из функции.

Все операции языка Си++

Наряду с общепринятыми арифметическими и логическими операциями, в языке Си++ имеется набор операций для работы с битами – поразрядныеИ, ИЛИ, ИСКЛЮЧАЮЩЕЕ ИЛИ и НЕ, а также сдвиги.

Особняком стоит операция sizeof. Эта операция позволяет определить, сколько памяти занимает то или иное значение. Например:

sizeof(long); // сколько байтов занимает тип long sizeof (b); // сколько байтов занимает переменная b

Операция sizeof в качестве аргумента берет имя типа или выражение. Аргумент заключается в скобки (если аргумент – выражение, скобки не обязательны). Результат операции – целое число, равное количеству байтов, которое необходимо для хранения в памяти заданной величины.

Ниже приводятся все операции языка Си++.

Арифметические операции

+ сложение- вычитание* умножение/ деление

Операции сложения, вычитания, умножения и деления целых и вещественных чисел. Результат операции – число, по типу соответствующее большему по разрядности операнду. Например, сложение чисел типа short и long в результате дает число типа long.

% остаток

Операция нахождения остатка от деления одного целого числа на другое. Тип результата – целое число.

- минус+ плюс

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

++ увеличить на единицу, префиксная и постфиксная формы-- уменьшить на единицу, префиксная и постфиксная формы

Эти операции иногда называют "автоувеличением" (инкремент) и "автоуменьшением" (декремент). Они увеличивают (или, соответственно, уменьшают) операнд на единицу. Разница между постфиксной (знак операции записывается после операнда, например x++) и префиксной (знак операции записывается перед операндом, например --y) операциями заключается в том, что в первом случае результатом является значение операнда до изменения на единицу, а во втором случае – после изменения на единицу.

Операции сравнения

== равно!= не равно< меньше> больше<= меньше или равно>= больше или равно

Операции сравнения. Сравнивать можно операнды любого типа, но либо они должны быть оба одного и того же встроенного типа (сравнение на равенство и неравенство работает для двух величин любого типа), либо между ними должна быть определена соответствующая операция сравнения. Результат – логическое значение true или false.

Логические операции

&& логическое И|| логическое ИЛИ! логическое НЕ

Логические операции конъюнкции, дизъюнкции и отрицания. В качестве операндов выступают логические значения, результат – тоже логическое значение true или false.

Битовые операции

& битовое И| битовое ИЛИ^ битовое ИСКЛЮЧАЮЩЕЕ ИЛИ~ битовое НЕ

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

<< сдвиг влево>> сдвиг вправо

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

Условная операция

операнд1?операнд2:операнд3

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

Последовательность

, последовательность

Выполнить выражение до запятой, затем выражение после запятой. Два произвольных выражения можно поставить рядом, разделив их запятой. Они будут выполняться последовательно, и результатом всего выражения будет результат последнего выражения.

Операции присваивания

= присваивание

Присвоить значение правого операнда левому. Результат операции присваивания – это значение правого операнда.

+=, -=, *=, /=, %=, |=, &=, ^=, <<=, >>= выполнить операцию и присвоить

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

https://www.intuit.ru/studies/courses/17/17/lecture/508?page=2

 

 

Литература________________________________________

______________________________________________________

______________________________________________________

 

Разработал:старшийпреподавательС.Приступа

 

«»_________________г.

 

 



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2022-11-01 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: