Зарегистрируйся в системе лидеров, увеличь посещаемость своего сайта на 70% - 150% и заработай денег!главнаяЛекция |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Лекция №1Тема: Правила
оформления программ на Си/Си++.
|
Операция
|
Наименование |
Операция
|
Наименование |
! |
Логическое
НЕ
|
~ |
Побитовое
дополнение
|
+ |
Сложение
|
- |
Вычитание,
логическое отрицание
|
/ |
Деление
|
% |
Остаток
|
<< |
Сдвиг
влево
|
>> |
Сдвиг
вправо
|
< |
Меньше
|
<= |
Меньше
или равно
|
> |
Больше
|
>= |
Больше
или равно
|
= |
Равно
|
!= |
Не
равно
|
& |
Побитовое
И, адресация
|
| |
Побитовое
ИЛИ
|
^ |
Побитовое
исключение ИЛИ
|
&& |
Логическое
И
|
|| |
Логическое
ИЛИ
|
, |
Последовательное
выполнение (запятая)
|
?: |
Операция
условного выражения
|
++ |
Инкремент
|
-- |
Декремент
|
= |
Простое
присваивание
|
+= |
Сложение
с присваиванием
|
-= |
Вычитание
с присваиванием
|
*= |
Умножение
с присваиванием
|
/= |
Деление
с присваиванием
|
%= |
Остаток
с присваиванием
|
>>= |
Сдвиг
вправо с присваиванием
|
<<= |
Сдвиг
влево с присваиванием
|
&= |
Побитовое
И с присваиванием
|
|= |
Побитовое
ИЛИ с присваиванием
|
^= |
Побитовое
исключающее ИЛИ с присвиванием
|
Значение логического
выражения, отличное от 0, истинно.
Си/Си++ поддерживает определения для множества базовых или "основных"
типов данных. Представление в памяти, область значений величин различных
типов, преобразование типов изложены в /1 - 4/.
Типы signed char, signed int, signed short int и signed
long int вместе с соответствующими двойниками unsigned называются
типами целых.
Спецификаторы типов float и double относятся к типу "плавающих".
Тип void может быть использован для объявления
функций, не возвращающих значения.
В целых типах ключевое слово signed может быть опущено. Если ключевое
слово unsigned опускается, то тип целого будет знаковым (signed).
В некоторых реализациях могут быть использованы опции компилятора, позволяющие
изменить умолчание для типа char со знакового на беззнаковый. Тогда сокращение
char имеет то же значение, что и unsigned char.
Для преобразования типа выражения необходимо в скобках перед ним записать
требуемый тип:
Определить размер памяти в байтах, соответствующий идентификатору или типу позволяют операция sizeof:
Операция sizeof обычно используют при выделении памяти под переменные для того, чтобы избежать машинной зависимости:
Запретить изменение
переменной позволяет модификатор const :
const float i=1;
Переменные
const имеют ту же зону видимости, что и переменные static. Const,
в отличие от директивы #define, контролирует типы.
Для изменяющихся переменных используется модификатор volatile,
показывающий, что переменные могут меняться фоновым процессом.
Для работы с адресами памяти используются указатели.
Размер памяти
для переменной-указателя зависит от конфигурации машины. Конкретный адрес
является указателем и получается с помощью операции & (адресации).
Адресация не может применяться к переменным класса памяти register и к
битовым полям структур /2/.
Разадресация * применяется для получения значения величины, указатель
которой известен. Если значение указателя нулевое, то результат разадресации
непредсказуем.
Указатель - это переменная, содержащая адрес другой переменной. Указатели
очень широко используются в языке Cи. Это происходит отчасти потому, что
иногда они дают единственную возможность выразить нужное действие, отчасти
потому, что они обычно ведут к более компактным и эффективным программам,
чем те, которые могут быть получены другими способами. Так как указатель
содержит адрес объекта, это дает возможность "косвенного" доступа
к этому объекту через указатель /1/. Предположим, что X - переменная,
например, типа int, а РX - указатель, созданный неким еще
не указанным способом. Унарная операция & выдает адрес объекта,
так что оператор РX = &X; присваивает адрес X переменной
РX; говорят, что РX "указывает" на X. Операция
& применима только к переменным и элементам массива, конструкции
вида &(X-1) и &3 являются незаконными. Нельзя также
получить адрес регистровой переменной.
Унарная операция * рассматривает свой операнд как адрес конечной цели и обращается по этому адресу, чтобы извлечь содержимое. Следовательно, если Y тоже имеет тип int, то Y = *РX; присваивает Y содержимое того, на что указывает РX. Последовательность РX = &X; Y = *РX; присваивает Y то же самое значение, что и оператор Y = X; Переменные, участвующие во всем этом, необходимо описать: int X, Y; int *РX; Описание указателя int *РX; должно рассматриваться как мнемоническое; оно говорит, что комбинация *РX имеет тип int. Это означает, что если РX появляется в контексте *РX, то это эквивалентно переменной тип int. Фактически синтаксис описания переменной имитирует синтаксис выражений, в которых эта переменная может появляться. Это замечание полезно во всех случаях, связанных со сложными описаниями. Например, double atof(), *DР; говорит, что atof() и *DР имеют в выражениях значения типа double.
Указатели могут входить в выражения. Например, если РX указывает
на целое X, то *РX может появляться в любом контексте, где
может встретиться X. Оператор Y = *РX + 1; присваивает
Y значение, на 1 большее значения X; printf("%d\n", *РX)
печатает текущее значение X; D = sqrt((double) *РX) получает
в D квадратный корень из X, причем до передачи функции sqrt
значение X преобразуется к типу double. В выражениях вида
Y = *РX + 1 унарные операции * и & связны со своим
операндом более крепко, чем арифметические операции, так что такое выражение
берет то значение, на которое указывает РX, прибавляет 1
и присваивает результат переменной Y.
Ссылки на указатели могут появляться и в левой части присваивания. Если
РX указывает на X, то *РX = 0 полагает X равным
нулю, *РX += 1 увеличивает его на единицу, как и выражение (*РX)++.
Круглые скобки в последнем примере необходимы; если их опустить, то это
выражение увеличит РX, а не ту переменную, на которую он указывает.
Если РY - другой указатель на переменную типа int, то
РY = РX копирует содержимое РX в РY, в результате чего
РY указывает на то же, что и РX.
Массив рассматривается как набор элементов одного типа. Чтобы объявить
массив, необходимо в описании поcле имени массива в квадратных скобках
указать его размерность.
Индексы элементов
изменяются от 0 до N-1, где N- размерность массива. Переменная типа массив
является указателем на элементы массива.
Многомерный массив определяется путем задания нескольких константных выражений
в квадратных скобках:
Элементы массива запоминаются в последовательных возрастающих адресах памяти. Хранятся элементы массива построчно. Для обращения к элементу массива вычисляется индексное выражение. Для a[i] индексное выражение равно *(а+ i) , где а - указатель, например, имя массива; i - целая величина, преобразованная к адресному типу; * - операция разадресации. Для двумерного массива b[i][j] индексное выражение: *(b+i+j).
В языке Cи существует сильная взаимосвязь между указателями и массивами,
указатели и массивы следует рассматривать одновременно. Любую операцию,
которую можно выполнить с помощью индексов массива, можно сделать и с
помощью указателей. Вариант с указателями обычно оказывается более быстрым,
но и несколько более трудным для непосредственного понимания. Описание
int A[10] определяет массив размера 10, т.е. Набор из 10 последовательных
объектов, называемых A[0], A[1], ..., A[9]. Запись A[I] соответствует
элементу массива через I позиций от начала. Если РA - указатель целого,
описанный как int *РA, то присваивание РA = &A[0] приводит
к тому, что РA указывает на нулевой элемент массива A; это означает, что
РA содержит адрес элемента A[0]. Теперь присваивание X = *РA будет
копировать содержимое A[0] в X.
Если РA указывает на некоторый определенный элемент массива A, то по определению РA+1 указывает на следующий элемент, и вообще РA-I указывает на элемент, стоящий на I позиций до элемента, указываемого РA, РA+I на элемент, стоящий на I позиций после. Таким образом, если РA указывает на A[0], то *(РA+1) ссылается на содержимое A[1], РA+I - адрес A[I], *(РA+I) - содержимое A[I]. Суть определения "добавления 1 к указателю", а также его распространения на всю арифметику указателей, состоит в том, что приращение масштабируется размером памяти, занимаемой объектом, на который указывает указатель /2/.
Ссылку на A[I] можно записать в виде *(A+I). При анализе
выражения A[I] в языке Cи оно немедленно преобразуется к виду *(A+I);
эти две формы эквивалентны. Имеется одно различие между именем массива
и указателем, которое необходимо иметь в виду. Указатель является переменной,
так что операции РA=A и РA++ имеют смысл. Но имя массива
является константой, а не переменной: конструкции типа A=РA или
A++, или Р=&A будут незаконными. Когда имя массив передается
функции, то на самом деле ей передается местоположение начала этого массива.
Внутри вызванной функции такой аргумент является точно такой же переменной, как и любая другая, так что имя массива в качестве аргумента действительно является указателем. Можно передать функции часть массива, если взять в качестве аргумента указатель начала подмассива. Например, если A - массив, то F(&A[2]) и F(A+2) передают функции F адрес элемент A[2], потому что и &A[2], и A+2 являются указательными выражениями, ссылающимися на третий элемент A.
Рассмотрим пример ввода/вывода значений элементов массива с помощью функций
scanf и рrintf:
Динамически выделять память под массивы и другие объекты можно с помощью функций calloc, malloc:
Тогда освобождать память необходимо функцией free:
При использовании функицй calloc, malloc, free в текст программы требуется включить файл описаний этих функций alloc.h:
В Си++ динамическое распределение памяти проводится оператором new, а освобождение - оператором delete. Выделенная по new область памяти занята до соответствующего delete или до конца программы.
Реальный размер
выделяемой памяти кратен опеределенному числу байт. Поэтому невыгодно
выделять память по new для небольших объектов. Если new не
может выделить блок памяти, то она возвращает значение NULL.
Примеры освобождения памяти оператором delete :
Результат delete
непредсказуемый, если переменная, записанная после него, не содержит
адрес блока памяти или равна NULL.
Для работы с отдельными
символами используются описания переменных типа
char sym1, sym2, sym3, c;
В программе можно явно присвоить
значение переменной типа char
sym1 = 'A'; sym2 = 'a';
sym3 = 'c'; c = '0x1B'; // ASCII символ
Ввод с клавиатуры:
Вывод на экран:
Фрагмент программы, выводящей на экране вводимые с клавиатуры символы до тех пор, пока не нажата клавиша Enter.
Esc-последовательности
- это специальные символьные комбинации, которыми представляются неграфические
и пробельные символы в строковых и символьных константах. Esc-последовательность
cостоит из \ и следующей сразу за ней буквы или комбинации цифр:
\n - новая строка, \t - горизонтальная табуляция,
\v - вертикальная табуляция, \b - пробел,
\а - звонок, \` - одиночная кавычка,
\" - двойная кавычка, \\ - наклонная черта
влево,
\ddd - восьмеричный код ASCII, \xdd -шестнадцатиричный
код ASCII,
\c - означает обычный символ с.
1. Какие операции
можно применять к указателям?
2. Каков результат выполнения оператора c[i]=i++; ?
3. Для чего и как происходит динамическое распределение памяти?
4. Что такое Esc-последовательность?
5. Для чего используются индексные выражения?
6. Каким образом выделяется память под массивы?
7. Что общего между массивами и указателями?
8. Как располагаются в памяти элементы многомерного массива?