Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
алгоритмы.rtf
Скачиваний:
2
Добавлен:
31.07.2019
Размер:
188.8 Кб
Скачать

9. Операции явного преобразования типа

Применение этой операции имеет следующий формат:

(имя_типа) операнд

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

Примеры использования преобразования типа:

(long)8 , (float)1 , (int)x%2

По поводу последнего выражения заметим, что приоритет опе­рации «тип» выше деления (и других бинарных арифметических операций), поэтому сначала значение переменной х приведется к целому типу (отбросится дробная часть), а затем выполнится де­ление по модулю.

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

floa t с;

in t a=l , b=2;

с=(float)a/b ;

В результате переменная с получит значение 0,5. Без преобразования типа ее значение стало бы равно 0.

10. Операции определения размера sizeof

Эта операция имеет две формы записи:

sizeof(тип) и sizeof(выражение)

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

11.Операция «запятая» и «?»

Операция «запятая». Эта необычная операция используется для связывания нескольких выражений в одно. Несколько выражений, разделенных запятыми, вычисляются последовательно слева направо. В качестве результата такого совмещенного выражения принимается значение самого правого выражения. Например, если переменная х имеет тип int , то значение выражения (х=3, 5*х) будет равно 15, а переменная х примет значение 3.

Операция «условие ?:». Это единственная операция, которая имеет три операнда. Формат операции:

выражение1 ? выражение2 : вьгражениеЗ

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

12. Операция получения адреса (&) и раскрытия ссылки(*)

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

Первый из них — оператор &, это унарный оператор, возвращающий адрес операнда в памяти[12]. (Унарной операцией называется операция, имеющая только один операнд.) Например, оператор

m = &count;

записывает в переменную m адрес переменной count. Этот адрес представляет собой адрес ячейки памяти компьютера, в которой размещена переменная. Адрес и значение переменной — совершенно разные понятия. Выражение "&переменная" означает "адрес переменной". Следовательно, инструкция m = &scount; означает: "Переменной m присвоить адрес, по которому расположена переменная count;".

Допустим, переменная count расположена в памяти в ячейке с адресом 2000, а ее значение равно 100. Тогда в предыдущем примере переменной m будет присвоено значение 2000.

Второй рассматриваемый оператор * является двойственным (дополняющим) по отношению к &[13]. Оператор * является унарным оператором, он возвращает значение объекта, расположенного по указанному адресу. Операндом для * служит адрес объекта (переменной). Например, если переменная m содержит адрес переменной count, то оператор

q = *m;

записывает значение переменной count в переменную q. В нашем примере переменная q получит значение 100, потому что по адресу 2000 записано число 100, причем этот адрес записан в переменной m. Выражение "* адрес" означает "по адресу". Наш фрагмент программы можно прочесть как "q получает значение, расположенное по адресу m".

К сожалению, символ операции раскрытия ссылки совпадает с символом операции умножения, а символ операции получения адреса — с символом операции поразрядного И. Необходимо помнить, что эти операторы не имеют никакого отношения друг к другу. Операторы * и & имеют более высокий приоритет, чем любая арифметическая операция, кроме унарного минуса, имеющего такой же приоритет.

Если переменная является указателем, то в объявлении перед ее именем нужно поставить символ *, он сообщит компилятору о том, что это указатель на переменную данного типа. Например, объявление указателя на переменную типа char записывается так:

char *ch;

Необходимо понимать, что ch — это не переменная типа char, а указатель на переменную данного типа, это совершенно разные вещи. Тип данных, на который указывает указатель (в данном случае это char), называется базовым типом указателя[14]. Сам указатель является переменной, содержащей адрес объекта базового типа. Компилятор учтет размер указателя в архитектуре компьютера и выделит для него необходимое количество байтов, чтобы в указатель поместился адрес. Базовый тип указателя определяет тип объекта, хранящегося по этому адресу.

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

int x, *y, count;

объявляет х и count как переменные целого типа, а у — как указатель на переменную целого типа.

В следующей программе операторы * и & используются для записи значения 10 в переменную target. Программа выведет значение 10 на экран.

#include<stdio.h>

intmain(void)

{

inttarget, source;

int *m;

source = 10;

m = &source;

target = *m;

printf("%d", target);

return 0;

}

13. Знаки операций () и []

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

#include<stdio.h>

char s[80];

intmain(void)

{

s[3] = 'X';

printf("%c", s[3]);

return 0;

}

значение 'Х' сначала присваивается четвертому элементу массива (в С элементы массива нумеруются с нуля), затем этот элемент выводится на экран.

14. Директива #define

Директива #define определяет идентификатор и последовательность символов, которая будет подставляться вместо идентификатора каждый раз, когда он встретится в исходном файле. Идентификатор называется именем макроса, а сам процесс замены — макрозаменой[1]. В общем виде директива выглядит таким образом:

#defineимя_макросапоследовательность_символов

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

Предположим, например, что вместо значения 1 нужно использовать слово LEFT (левый), а вместо значения 0 — слово RIGHT (правый). Тогда можно сделать следующие объявления с помощью директивы #define:

#define LEFT 1

#define RIGHT 0

В результате компилятор будет подставлять 1 или 0 каждый раз, когда в вашем файле исходного кода встречается идентификатор соответственно LEFT или RIGHT. Например, следующий код выводит на экран 0 1 2:

printf("%d %d %d", RIGHT, LEFT, LEFT+1);

После определения имя макроса можно использовать в определениях других имен макросов. Вот, например, код, определяющий значения ONE (один), TWO (два) и three (три):

#define ONE 1

#define TWO ONE+ONE

#define THREE ONE+TWO

Макроподстановка — это просто замена какого-либо идентификатора связанной с ним последовательностью символов. Поэтому если требуется определить стандартное сообщение об ошибке, то можно написать примерно следующее:

#define E_MS "стандартная ошибка при вводе\n"

/* ... */

printf(E_MS);

Теперь каждый раз, когда встретится идентификатор E_MS, компилятор будет его заменять строкой "стандартная ошибка при вводе\n". Для компилятора выражение printf() на самом деле будет выглядеть таким образом:

printf("стандартная ошибка при вводе\n");

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

#define XYZ это проверка

printf("XYZ");

вместо сообщения это проверка будет выводиться последовательность символов XYZ.

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

#define LONG_STRING "это очень длинная \

строка, используемая в качестве примера"

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

Имена макросов часто используются для определения имен так называемых "магических чисел" (встречающихся в программе). Например, имеется программа, в которой определяется массив и несколько процедур, получающих доступ к этому массиву. Вместо того чтобы размер массива "зашивать в код" в виде константы, этот размер можно определить с помощью оператора #define, а затем использовать это имя макроса везде, где требуется размер массива. Таким образом, если требуется изменить этот размер, то потребуется изменить только соответствующий оператор#define, a затем перекомпилировать программу. Рассмотрим, например, фрагмент программы

#define MAX_SIZE 100

/* ... */

floatbalance[MAX_SIZE];

/* ... */

for(i=0; i<MAX_SIZE; i++) printf("%f", balance[i]);

/* ... */

for(i=0; i<MAX_SIZE; i++) x =+ balance[i];

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