- •ЗАМЕЧАНИЕ <...> в описании правил записи (формата) конструкций языка означают понятие языка, в программе не записываются.
- •Пример
- •Формат
- •Значение
- •Содержание
- •Формат
- •if ( <выражение> )
- •Форма представления одного данного
- •Ввод двумерного массива
- •Вопрос? А как же передать результаты работы в вызывающую процедуру, если реально можно передать только одно значение?
- •Вызывающая
- •Вызываемая
- •ВЫЧИСЛЕНИЕ КВАДРАТИЧНОЙ ФОРМЫ
В Си нет.Замена – целые. 0 false, |
(НЕ)0 true |
2.3 Указатели
Указатели (ссылки) – это переменные, показывающие место или адрес памяти, где расположены другие объекты (переменные, функции и т.п.). Т.к. указатель содержит адрес некоторого объекта, то через него можно обращаться к этому объекту.
У такого выражения есть АДРЕС, ТИП и, разумеется, ЗНАЧЕНИЕ. Но у него нет ИМЕНИ И нет ОБЛАСТИ ВИДИМОСТИ.
|
E – имя |
|
|
Программный уровень |
|
||
переменная |
|
|
|
Значение |
|
|
|
|
|
|
|
|
|
|
|
участок памяти |
Содержание |
|
|
Машинный уровень |
Формат: описания |
||
переменной указателя |
|||
|
&E – адрес |
<тип> *<имя> |
|
|
[,*<имя>]...; |
Объекты, состоящие из знака* и адреса необходимо объявлять!
Объявление вида: char *d; говорит о том, что значение, записанное по адресу d, имеет тип char.
Пример:
int *kol, *num; Признаком указателя служит *. double *lenght;
Операции над указателями: над указателями определены две унарные операции:& *
Унарная операция & возвращает адрес объекта в явном виде, поэтому опера-
тор:
y = &x;
присваивает адрес x переменной y. Операцию & нельзя применять к константам и выражениям. Операция применима только к объектам, имеющим имя и размещенным в памяти.
Унарная операция * воспринимает свой операнд как адрес некоторого объекта и использует этот адрес для выборки содержимого. Поэтому, оператор:
z = *y;
присваивает z значение переменной, записанной по адресу y.
int Объявляет переменную a типа «указатель на int», то есть со-
*a; держащую адрес некоторого целого значения.
11
&x |
Возвращает адрес переменной a |
|
|
|
|
|
|
*p |
Возвращает "переменную" по адресу, находящемуся в перемен- |
|
|
ной p. |
|
|
|
|
|
|
|
Пример: int x, y, *px; |
|
|
|
px=&x; |
|
|
|
y=*px; |
|
|
|
Это эквивалентно |
y = x; |
|
|
Указатели могут встречаться в выражениях. Если y – указатель на целое ( int |
*y; |
), то *y может появляться там же, где и любая другая переменная, не являющаяся указателем.
Пример:
*y = 7; //заносим 7 в ячейку памяти по адресу y *x = *x*5; //увеличиваем значение по адресу x в 5 раз
(*z)++; //добавляем 1 к содержимому ячейки памяти с адресом z, круглые скобки обязательны
12
Глава 3 МАССИВЫ И СТРУКТУРЫ
К совокупностям данным относятся массивы, структуры и файлы
3.1Массивы
МАССИВ – это множество переменных одного типа, характеризующихся одним именем. Как следует из их определения, массивы очень удобны, если приходится иметь дело с большими наборами однотипных данных.
•описание массива Формат: Синтаксис описания массива
<тип> <имя_переменной> [количество_элементов_массива]
[[количество_элементов_массива][количество_элементов_массива]]
•Обращение (ссылка) к элементу массива
Отдельные элементы массива собственного имени не имеют. Доступ к ним можно
получить, используя оператор [ ]. |
|
Формат: |
|
<имя_массива> [<индекс_элемента>][[<индекс_элемента>]] |
… |
Данный оператор возвращает элемент массива, соответствующий индексу. Обратите внимание: индексация в Си начинается с 0, то есть правильными значениями индекса будут все значения в интервале
от 0 до <количество_элементов_массива>–1.
Все значения в квадратных скобках при определении массивов должны быть целыми постоянными выражениями. Пока будем считать, что они целые без знака.
Пример: page[5] line[i] screen[j][k] matr[i–1][j+i–1]
Массивы можно, как и остальные переменные, инициализировать сразу при их объявлении. При этом элементы массива перечисляются через запятую, и все вместе заклю-
чаются в фигурные скобки. Пример: int a[ 6 ] = { 1, 2, 3, 4, 5, 6 };
Количество элементов в списке может быть меньше формального количества элементов массива – при этом инициализируются первые n элементов массива, где n – количество элементов в списке инициализации. С другой стороны, если указаны все элементы массива, то формальное количество элементов может отсутствовать:
int a[ ] = { 1, 2, 3}).
Теперь вопрос: что напечатает такая программа? #include <stdio.h>
void |
main |
(void) |
{ int |
i, a[10]; |
|
a[0] = 5; |
|
|
printf ( "%d\n", |
*a ); } |
На первый взгляд кажется, что она даже не будет компилироваться – ведь мы пытаемся взять значение по переменной, не являющейся адресом. Но, вопреки ожиданиям, она запустится и напечатает число 5.
Как вы можете догадаться из этого, имя массива в Си является указателем на его первый элемент. Запомните это раз и навсегда – это очень важно.
13
3.2Строки
Язык СИ не поддерживает отдельный строковый тип данных, но он позволяет опре-
делить строки двумя различными способами.
В первом используется массив символов, а во втором – указатель на первый символ массива. Объявление char а[10] указывает компилятору на необходимость резервирования места для максимум на 10 символов. Константа а содержит адрес ячейки памяти, в которой помещено значение первого объекта типа char.
Второй способ определения строки – это использование указателя на символ. Объявление char *b; задает переменную b, которая может содержать адрес некоторого объекта. Однако в данном случае компилятор не резервирует место для хранения символов и не инициализирует переменную b конкретным значением. Когда компилятор встречает инструкцию вида b=”Москва”; , он производит следующие действия. Во-первых, как и в предыдущем случае, он создает в каком-либо месте объектного модуля строку Москва, за которой следует нулевой символ. Во-вторых, он присваивает значение начального адреса этой строки (адрес символа М) переменной b.
Константа (литерал) типа строка (string): " <текст> "
3.3Структуры
СТРУКТУРА (запись) – совокупность разнотипных элементов. Элементы структуры (вернее, поля структуры), в отличие от элементов массива, не имеют индекса, но имеют собственное ИМЯ. Элементом структуры может быть массив любого типа.
Пример: Представление текущей даты:
–день месяца;
–название месяца;
–год;
Формат |
структура |
|
struct [<тип(имя)_структуры>] { |
||
<список_описаний_элементов> |
||
} [<список_объектов>]; |
|
|
<список_описаний_элементов> := |
||
|
|
<имя_типа> <имя_переменной> [, имя_переменной [,...]]; |
|
|
<имя_типа> <имя_переменной> [, имя_переменной [,...]]; |
|
|
… |
Пример: |
|
<имя_типа> <имя_переменной> [, имя_переменной [,...]]; |
|
|
|
1. struct |
data { |
/* Дата */ |
int day_nom; |
/* День месяца */ |
|
char month_name[4]; |
/* Название месяца */ |
|
int year; |
/* Год */ |
|
}; |
|
|
Таким образом, мы объявили новый тип данных – Data, представляющий из себя структуру. Если мы хотим объявить переменную day типа Data, то это следует делать так:
struct Data day;
Доступ к полям структуры осуществляется с помощью оператора точка:
14
day.day_nom = 5;
day. month_name = "март"; day.year = 2002;
Разрешается вкладывать структуры одна в другую.
Пример: |
|
|
|
|
|
1. |
|
|
struct |
star |
{ |
long nom_GC;//Номер по каталогу GC |
|||||
floatmv; |
//Блеск (звездная величина) |
||||
char spectr[5]; //Спектральный класс |
|||||
struct fist { |
|
|
|||
double |
alfa; //Прямое восхождение |
||||
double |
delta; //Склонение |
|
|||
} koord; |
|
|
|
|
|
/*переменная koord представлена составным элементом (вложенной структурой) ти- |
|||||
па fist*/ |
|
|
|
|
|
struct |
{ |
|
|
|
|
double |
alfa; |
|
//Собств. движение по alfa |
||
double |
delta; // ------ |
------- |
по delta |
||
} move; |
|
|
|
|
|
char rem[15]; |
}; //Особенности(прим) |
||||
Допустимы массивы структур. |
|||||
Пример: |
|
|
|
|
|
struct |
year { |
/* Сведения о погоде за год */ |
|||
struct |
|
{ |
|
|
|
double max, min, mid; |
|
}temperature; struct {
double max, min, mid;
}humidity; struct {
double max, min, mid;
}pressure;
int number_of_observations;
}weather[12];
15
Глава 4 ОПЕРАЦИИ И ВЫРАЖЕНИЯ
Выражение – это представление в тексте программы значения.
Каждое выражение состоит из одного или нескольких операндов, символов операций и ограничителей, в качестве которых чаще всего выступают круглые скобки ().
Назначение любого выражения – формирование некоторого значения. В зависимости от типа формируемых значений определяются типы выражений.
Если значениями выражений являются целые или вещественные числа, то говорят об арифметических выражениях.
Операндами для арифметических выражений служат константы и переменные арифметических типов, а также выражения, заключенные в круглые скобки.
Выражения отношения и логические выражения традиционно должны давать одно значение: истина или ложь. В СИ истина – всегда ненулевое значение, 0 – ложь.
Отношение определяется как пара арифметических выражений, соединенных (разделенных) знаком операции отношения.
Операндами логических выражений являются выражения отношения.
Для формирования и последующего вычисления выражений используются операции. Для изображения одной операции в большинстве случаев используется несколько символов. В таблице приведены все знаки операций, определенные стандартом языка. Операции в таблице разбиты на группы в соответствие с их рангами.
Р |
Операции |
Ассоциатив- |
анг |
|
ность |
1 |
(); [];→; . |
|
2 |
!; ~; +; –; ++; ––; &; *; (тип); sizeof |
|
3 |
*; /; % (мультипликативные бинарные) |
|
4 |
+; – (аддитивные бинарные) |
|
5 |
<<; >> (поразрядного сдвига) |
|
6 |
<; <=; >=; > (отношения) |
|
7 |
==; != (отношения) |
|
8 |
& (поразрядная конъюнкция “И”) |
|
9 |
^ (поразрядное исключающее “ИЛИ”) |
|
10 |
| (поразрядная дизъюнкция “ИЛИ”) |
|
11 |
&& (конъюнкция “И”) |
|
12 |
|| (дизъюнкция “ИЛИ”) |
|
13 |
?: (условная операция) |
|
14 |
=; *=; /=; %=; +=; –=; &=; ^=; |=; <<=; >>= |
|
15 |
, (операция запятая) |
|
За исключением операций “[]”, “()” и “?:” все знаки операций распознаются компилятором как отдельные лексемы. В зависимости от контекста одна и та же лексема может обозначать разные операции, т.е. один и тот же знак операции может употребляться в различных выражениях и по разному интерпретироваться в зависимости от контекста.
Например, бинарная операция & – это поразрядная конъюнкция, а унарная & – это операция получения адреса.
16
Порядок выполнения выражения однозначно определяется рангами и ассоциативностью входящих в него операций. Изменить данный порядок можно только используя ( ).
Операции ранга 1 имеют наивысший приоритет.
Операции одного ранга имеют одинаковый приоритет, и если их в выражении несколько, то они выполняются в соответствие с правилом ассоциативности либо слева направо (), либо справа налево (). Если один и тот же знак операции приведен в таблице дважды (например знак *), то первое появление (с меньшим по номеру, т.е. старшим по приоритету, рангом) соответствует унарной операции, а второе – бинарной.
4.1 Унарные (одноместные) операции
Для изображения одноместных префиксных и постфиксных операций используются следующие символы:
&– операция получения адреса операнда. Нельзя &(x+1).
* – операция обращения по адресу, т.е. раскрытия ссылки, иначе операция разыменования (доступа по адресу к значению того объекта, на который указывает операнд). Оператором должен быть указатель.
– – унарный минус, изменяет знак арифметического операнда.
+– унарный плюс, введен для симметрии с унарным минусом.
~ – поразрядное инвертирование внутреннего двоичного кода целочисленного аргумента – побитовое отрицание.
Пример:
Unsigned char a:
A=201: (=c9 (16) = 11001001(2)) ~A=54: (= 36 (16) = 00110110(2))
! – логическое отрицание (НЕ) значения операнда. Применяется к стандартным операндам. Целочисленный результат 0 (если операнд не нулевой, т.е. истинный) или 1 (если операнд нулевой, т.е. ложный). Напомним, что в качестве логических значений в языке используются целые числа: 0 – ложь и не нуль, т.е. (!0) – истина. Отрицание любого ненулевого числа будет 0, а отрицанием 0 будет 1. Таким образом: !1 равно 0; !2 равно
0; !(–5) равно 0; !0 равно 1;
++ – увеличение на единицу (инкремент или автоувеличение); имеет две формы: ++<имя>префиксная операция – увеличение значения операнда на 1 до его исполь-
зования. Ассоциативность справа в соответствие со стандартом.
<имя>++постфиксная операция – увеличение значения операнда на 1 после его использования. Ассоциативность слева в соответствие со стандартом.
-выражение ++m увеличивает на 1 значение m, и это полученное значение используется как значение выражения ++m (префиксная форма).
-выражение – – k уменьшает на 1 значение k, и это полученное значение используется как значение выражения – –k (префиксная форма).
-выражение i++ (постфиксная форма) уменьшает на 1 значение i, однако значением выражения i++ будет предыдущее значение i (до его увеличения).
-выражение j – – (постфиксная форма) уменьшает на 1 значение j, однако значением выражения j – – будет предыдущее значение j (до его уменьшения.
Например
17
int arg=4, rez; |
// rez=8, arg=5 |
rez= arg ++*2; |
|
rez=++ arg *2; |
// arg=6, rez=12 |
Внешнюю неоднозначность имеют выражения, в которых знак унарной операции ++ (или – –) записан непосредственно рядом со знаком бинарной операции +.
X+++b или z – – –d.
В этих случаях трактовка однозначно и полностью определяется рангами операций (бинарные аддитивные + имеют ранг 4;унарные ++ и –– имеют ранг 2).Таким образом:
x+++b эквивалентно (x++)+b z– – –d эквивалентно (z– –)–d.
Операнд для операции ++ (и для операции – – ) не может быть константой либо произвольным выражением. Записи ++5 или 84++ будут неверными. ++(j+k) также неверная запись. Операндами унарных операций ++ и – – должны быть всегда модифицируемые выражения (L– value, left value, l – значение, леводопустимое выражение).
Термины «леводопустимое выражение» и «l–значение» происходят от объяснения действия операции присваивания E=D, в которой операнд Е слева от знака операции присваивания может быть только модифицируемым l – значением. Примером модифицируемого l – значения служит имя переменной, которой выделена память. L – значение
– ссылка на область памяти, значение которой доступно изменениям.
Sizeof – операция вычисления размера (в байтах) для объекта того типа, который имеет операнд. Разрешается два формата операции:
sizeof <выражение>
sizeof (тип). |
|
|
Пример: |
|
|
int a, b[100], *x; |
|
|
sizeof (20.0) |
→8 |
байтов |
sizeof (a) |
→2 байта |
|
sizeof (b) |
→200 байтов |
|
sizeof (*x) |
→2 |
байта |
sizeof (int) |
→2 |
байта |
sizeof (date) |
→12 байтов |
4.2 Бинарные (двуместные) операции
Делятся на следующие группы:
•аддитивные;
•мультипликативные;
•сдвигов;
•поразрядные;
•операции отношений;
•логические;
•присваивания;
•выбора компонента структурированного объекта;
•операция “запятая”;
18
•скобки в качестве операций;
Аддитивные операции (операнды: арифметические выражения или указатели). + – бинарный плюс – сложение арифметических операндов или сложение указа-
теля с целочисленным операндом.
– – бинарный минус – вычитание арифметических операндов или вычитание указателей.
Мультипликативные операции.
* – умножение операндов арифметического типа.
/– деление операндов арифметического типа.
При целочисленных операндах абсолютное значение результата округляется до целого.
Например: 20/3 равно 6,
–20/3 равно –6, (–20)/3 равно –6, 20/(–3) равно –6.
% – получение остатка от деления целочисленных операндов (деление по модулю). При неотрицательных операндах остаток положительный. В противном случае остаток определяется реализацией. В компиляторе Turbo С у результата знак делимого:
13%4 равняется 1, (–13)%4 равняется –1;
13%(–4) равно +1, (–13)%(–4) равняется –1.
При ненулевом делителе для целочисленных операндов всегда выполняется соотношение (a/b)*b+a%b равно а.
Чтобы результат выполнения арифметической операции был вещественным, необходимо, чтобы вещественным был хотя бы один из операндов. Например, значением выражения 5.0/2 будет 2.5, что соответствует смыслу обычного деления.
Операции сдвига (определены только для целочисленных операндов).
Формат
<операнд_левый> <операция_сдвига><операнд_правый> << – сдвиг влево битового представления значения левого целочисленного операн-
да на количество разрядов, равное значению правого целочисленного операнда.
>> – сдвиг вправо битового представления значения левого целочисленного операнда на количество разрядов, равное значению правого целочисленного операнда.
E1>>E2, Е2 – число битов сдвига.
Результат не определен, если: Е2<0; значение Е2 >= размера Е1 в битах. E1>>E2 эквивалентно Е1 / (2E2)
E1<<E2 эквивалентно Е1 * 2E2
Пример: |
|
|
|
4<<2 |
равняется 16. |
двоичный код для 4 |
равен 100, |
5>>1 |
равняется 2. |
двоичный код для 5 |
– это 101. |
При сдвиге влево на две позиции код 100 становится равным 10000 (десятичное значение равно 16). Остальные результаты операций сдвига могут быть прослежены аналогично.
19
Обратите внимание, что сдвиг влево на n позиций эквивалентен умножению значения на 2, а сдвиг кода вправо уменьшает соответствующее значение в 2 раз с отбрасыванием дробной части результата (Поэтому 5>>1 равно 2).
Поразрядные операции.
& – поразрядная конъюнкция (И) битовых представлений значений целочисленных операндов.
| – поразрядная дизъюнкция (ИЛИ) битовых представлений значений целочисленных операндов.
^ – поразрядное исключающее ИЛИ битовых представлений целочисленных операндов.
АВ !А А&B A|B
F |
F |
T |
F |
F |
F |
T |
T |
F |
T |
T |
F |
F |
F |
T |
T |
T |
F |
T |
T |
Пример: Результат выполнения операций сдвига и поразрядных операций: 6&5 равняется 4.
6|5 равняется 7. 6^5 равняется 3.
Напоминаем, что двоичный код для 5 – это 101, для 6 – 110 и т.д.
Операции отношения (сравнения).
Операнды: арифметические выражения и указатели. Указатели можно сравнивать только с целым. Результат целочисленный: 0 (ложь) или 1 ( истина).
<меньше, чем.
>больше, чем.
<= меньше или равно. >= больше или равно.
==равно.
!= |
не равно. |
Последние две операции (операции сравнения на равенство) имеют более низкий приоритет по сравнению с остальными операциями отношений. Таким образом, выражение
( x<b = = a<x )
есть 1, тогда и только тогда, когда значение х находится в интервале от a до b и а<b (Вначале вычисляются x<b и x>a, а к результатам применяется операция сравнения на равенство ==).
Пример:
a+b<b+c = = a+c<a+b
x1<x2 = = x3<x1 есть 1 тогда и только тогда, когда х1 в интервале [x3;x2] и
x3<x2.
Логические бинарные операции.
&& – конъюнкция (И) арифметических операндов или отношений. Целочисленный результат 0 (ложь) или 1 (истина). Если 1-й операнд равен 0, то 2-й не вычисляется.
20
|| – дизъюнкция (ИЛИ) арифметических операндов или отношений. Целочисленный результат 0 (ложь) или 1 (истина). Если 1-й операнд равен 1, то 2-й не вычисляется.
Пример: |
|
3<5 |
равен 1. |
3>5.1 |
равен 0. |
3==5 |
равен 0. |
3!=5равен 1.
3!=5 || 3==5 равен 1.
3+4>5 &&3+5>4 && 4+5>3 равен 1.
Задача 1. Написать условие попадания точки (x, y) в область. y
1
1x
1.Точка в верхней полуплоскости: y ≥ 0.
2.Точка в правой полуплоскости: x ≥ 0.
3.Точка выше прямой: y ≥ 1-x
Все три условия должны выполняться одновременно:
y ≥ 0 && x ≥ 0 && y ≥ 1-x – с границами
Задача 2. Написать условие не попадания точки (x, y) в область. y
1
1x
1.Попадание внутрь круга: x2 +y2 ≤ 1.
2.Точка в I и III квадрантах: xy ≥ 0.
Оба условия должны выполняться одновременно:
!( x*x+y*y<1 && x*y > 0 ) – без границ
Задача 3 Деление нацело, кратность: m%n == 0.
21