Сложное присваивание

advertisement
1.Операторы присваивания, операции
Операторы присваивания задают новое значение переменной. Присваивание делится на две категории: простое
и сложное.
Простое присваивание
Оператор простого присваивания в C# имеет вид одиночного знака равенства (=). Чтобы присваивание успешно
сработало, правая часть оператора должна относиться к типу, который может быть неявно преобразован к типу
переменной в левой части.
Сложное присваивание
Операторы сложного присваивания, помимо обычного присваивания, выполняют некоторые дополнительные
действия. Сложное присваивание выполняется следующими операторами:
+= -= *= /= %= &= = ^= <<= >>=
Оператор сложного присваивания x<операция>=y вычисляется точно так же, как команда x = x <операция>y, с
двумя исключениями:
l значение x вычисляется всего один раз, и результат используется как при выполнении операции, так и при
присваивании;
l если x содержит вызов функции или ссылку на массив, эта операция выполняется всего один раз.
При обычных правилах преобразования, если x и y относятся к типу short, следующая команда привела бы к
ошибке компиляции, поскольку сложение выполняется со значениями типа int, а результат типа int нельзя неявно
преобразовать в short:
x = x + 3;
Однако в данном случае, поскольку тип short может быть неявно преобразован в int, и вы можете написать
x = 3;
Такая операция допустима.
Операция присваивания
Присваивание – это тоже операция, она является частью выражения. Значение
правого операнда присваивается левому операнду.
x = 2;
// переменной x присвоить значение 2
cond = x < 2; // переменной cond присвоить значение true, если x меньше 2,
// в противном случае присвоить значение false
3 = 5;
// ошибка, число 3 неспособно изменять свое значение
Последний пример иллюстрирует требование к левому операнду операции
присваивания. Он должен быть способен хранить и изменять свое значение.
Переменные, объявленные в программе, обладают подобным свойством. В следующем
фрагменте программы
int
x =
x =
x =
x = 0;
3;
4;
x + 1;
вначале объявляется переменная x с начальным значением 0. После этого значение x
изменяется на 3, 4 и затем 5. Опять-таки, обратим внимание на последнюю строчку.
При вычислении операции присваивания сначала вычисляется правый операнд, а затем
левый. Когда вычисляется выражение x + 1, значение переменной x равно 4. Поэтому
значение выражения x + 1 равно 5. После вычисления операции присваивания (или,
проще говоря, послеприсваивания) значение переменной x становится равным 5.
У операции присваивания тоже есть результат. Он равен значению левого операнда.
Таким образом, операция присваивания может участвовать в более
сложном выражении:
z = (x = y + 3);
В приведенном примере переменным x и z присваивается значение y + 3.
Очень часто в программе приходится значение переменной увеличивать или уменьшать
на единицу. Для того чтобы сделать эти действия наиболее эффективными и удобными
для использования, применяются предусмотренные в Си++ специальные знаки
операций: ++ (увеличить на единицу) и --(уменьшить на единицу). Существует две
формы этих операций: префиксная и постфиксная. Рассмотрим их на примерах.
int x = 0;
++x;
Значение x увеличивается на единицу и становится равным 1.
--x;
Значение x уменьшается на единицу и становится равным 0.
int y = ++x;
Значение x опять увеличивается на единицу. Результат операции ++ – новое
значение x, т.е. переменной y присваивается значение 1.
int z = x++;
Здесь используется постфиксная запись операции увеличения на единицу. Значение
переменной x до выполнения операции равно 1. Сама операция та же –
значение x увеличивается на единицу и становится равным 2. Однако результат
постфиксной операции – это значение аргумента до увеличения. Таким образом,
переменной z присваивается значение 1. Аналогично, результатом постфиксной
операции уменьшения на единицу является начальное значение операнда, а
префиксной – его конечное значение.
Подобными мотивами оптимизации и сокращения записи руководствовались создатели
языка Си (а затем и Си++), когда вводили новые знаки операций типа "выполнить
операцию и присвоить". Довольно часто одна и та же переменная используется в левой
и правой части операции присваивания, например:
x = x + 5;
y = y * 3;
z = z – (x + y);
В Си++ эти выражения можно записать короче:
x += 5;
y *= 3;
z -= x + y;
Т.е. запись oper= означает, что левый операнд вначале используется как левый
операнд операции oper, а затем как левый операнд операции присваивания результата
операции oper. Кроме краткости выражения, такая запись облегчает оптимизацию
программы компилятором.
2. Логические выражения
Условия
и
логические
выражения.
Основанием для принятия решений в управляющих конструкциях являются условные выражения это способ
записи условий. Такие выражения, которые могут принимать значения, либо ЛОЖЬ (false), либо ИСТИНА
(true). В условных выражениях используются операторы сравнения. Т.к. они несколько различаются в разных
языках, о них чуть позже. Пример пока с математическими знаками (в примере ниже НЕ используются
программные операторы сравнения!!!), чтобы можно было понять суть условных выражений:
Пусть у вас есть переменная х=2. Вам нужно проверить выражение х>0. В данном случае х действительно
больше 0, т.к. его текущее значение 2. Следовательно выражение х>0 верно и равно (выражение равно!)
true. Если при том же значении х вам придется проверить выражение х<0, то данное выражение уже будет
неверным,
т.к.
х=0.
Значит
значение
второго
выражения
будет
false.
Если надо проверить несколько условий в одном выражении, т.е. решить сложное логическое выражение,
состоящее из нескольких простых (Простые мы разобрали в примере выше), применяются специальные
операторы. Вообще данный раздел математики принято называть булевой алгеброй по имени ученого,
который все это дело разработал. Кому будет мало имеющейся здесь таблицы- отсылаю к соответствующему
разделу математики. Итак, над условными выражениями можно выполнять действия логической математики.
Как пользоваться таблицей: любое сложное логическое выражение, составленное из нескольких простых
всегда можно свести к выражению из двух операндов, один из которых простой, а второй может быть
простым, а может быть сложным. Если продолжать этот процесс, то мы доберемся до выражения, состоящего
из
2
простых
операндов.
Вот
к
нему
и
можно
применить
данную
таблицу.
Таблица значений логических операций
оператор
операнд 1 операнд
2
AND
true
true
false
true
true
false
false
false
OR
true
true
false
true
true
false
false
false
XOR(только для VB !!!. В С/C++ для логических true
true
выражений не используется)
false
true
true
false
false
false
NOT
true
false




значение
выражения
true
false
false
false
true
true
true
false
false
true
true
false
false
true
AND- операция логическое И или логическое умножение (конъюнкция). И одно, и другое условные
выражения должны быть истинны, чтобы все сложное выражение можно было считать истиной.
OR- операция логическое ИЛИ или логическое сложение (дизъюнкция ). Достаточно, чтобы одно из
выражений было истинным, чтобы все сложное выражение было истинным.
XOR операция исключающее ИЛИ. Обычное ИЛИ дает true, когда оба операнда true, а данный
вариант исключает по принципу или-или, но не оба вместе и дает false. Итак, если одно и только
одно условное выражение имеет значение ИСТИНА, то результат будет ИСТИНА. Если оба условия
ИСТИНА или оба ЛОЖЬ, то результат будет ЛОЖЬ.
NOT- операция "логическое НЕ" или отрицание. Это операция с одним операндом. Если операнд
является истинным, то все выражение- будет ложью. И наоборот.
3.Условные операторы
Условный оператор это классика программирования, он есть в каждом языке. (собственно этот
оператор есть даже в машинных кодах). Он позволяет выполнять определенный код, в зависимости от
некоторого условия.
Есть два вида условных операторов:
1.
2.
Операторы вида if ... else
Операторы вида if ... elseif ... elseif ... else
1) Рассмотрим первый вид оператора, он присутствует в языках C++, Java, C#, VFP, Delphi:
Язык
С++,C#,Java,Transa
ct-SQL
Delphi
VFP
Естественн
ый язык
Описан
ие
if (выражение1) опе
ратор1;
if выражение1Thenопе
ратор1;
ifвыражение1 [the
n]
[elseоператор2;]
[elseоператор2;]
операторы1
ЕСЛИ а
больше 3,
ТО вывести
OK!,
ИНАЧЕ выв
ести
ERROR!,
[else
операторы2]
endif
Приме
р
int i = 2;
if(i < 1){
cout << 'i меньше
1!';
} else {
cout << 'i больше
или равно 1!';
}
i := 1;
if i < 1 Then begin
Write(i);
end;
else Write( - i);
i = -3;
if i < 2 then
MessageBox('ист
ина!')
else
MessageBox('ло
жь!')
endif
1). Если на
улице
дождь
возьми
зонт, иначе
не бери.
2) На улице
дождь!
Резуль
тат
i больше или равно
1!
-1
истина!
Нужно взять
зонт!
Суть его, если условие в выражение1 истинно, то выполняется оператор1, если условие ложно, то
выполняется оператор2.
Блок else в любом языке можно пропустить, тогда если условие выражения1 ложно просто ничего не
будет выполнятся, сразу управление передается дальше. Например на языке C++ блок if без else:
int i = 1;
if(i == 10){
cout << 'истина!';
}
Этот блок ничего не выведет на экран.
2) Рассмотрим второй вид оператора, он присутствует в языках Perl, PHP, Basic, его суть просто в
добавлении любого количество блоков elseif к конструкции if .. else:
Язык
Описани
е
Perl,PHP
Альтернативный формат в
PHP
if (выражение1) оператор1;
[else if (
выражение2) оператор2;]
if (выражение1): операторы1;
if выражение1[then]
[else
if (выражение2):операторы2;
]
операторы1
...
...
[else
if (выражениеN) операторN;
]
[else операторM;]
Basic, PL\SQL,VBScript
[elseif выражение2 [then
]
операторы2]
[else
if (выражениеN):операторыN;
]
...
[else операторыM;] endif
[elseif выражениеN
операторыN]
[else
операторыM]
endif <В PL\SQL - end
if;>
Пример
$i = 1;$j = 3;
if ($i >= 10) echo 'i больше
или равно 10!';
else if ($j < 3) {echo 'j
меньше 3,';echo 'i меньше
10!';}
else if ($j == 3) echo 'j
равно 3!';
else echo 'неверные
данные';
$i = 5;$j = 4;
if ($i >= 10): echo 'i больше
или равно 10!';
else if ($j < 3): echo 'j
меньше 3,';echo 'i меньше
10!';
else if ($j == 3): echo 'j
равно 3!';
else echo 'неверные';echo '
данные!';
endif;
-
Результа
т
j равно 3!
неверные данные!
-
В первом столбце таблицы записана стандартная форма оператора if в языке PHP ,а в втором его
альтернативная форма (Разница между стандартной формой и альтернативной только в том что при
записи нескольких операторов не нужны операторные скобки).
Отличие между синтаксисом Basic’a и PL\SQL в том, что последний закрывающий оператор в
Basic’e endif, а в PL\SQL end if; .
Данная форма работает следующим образом, если выражение1 истинно, то выполняется оператор1,а
все остальные операторы не выполняются, если нет, то проверяется выражение в первом
блоке elseif, если оно истинно, то выполняется оператор этого блока, если нет проверяется
следующий блок elseif и т.д. Количество блоков elseif не ограниченно. Выполняется оператор только
одного блока. Если все выражения в if и elseif ложны, только тогда выполняется оператор в
блоке else, если этот блок существует. Использования блоков elseif необязательно, и если не указать
ни одного блока elseif получится первая форма if ... else.
Внимание:
1) В большинстве языков выражения в if могут быть только логическими (т.е. могут принимать
значения только true или false), но есть исключения: это языки C++,PHP, Perl и VBScript. В этих
языках выражение может быть не только логическим, но почти любого типа (и хотя при написании
программы это довольно удобно, но это, к сожалению, является причиной дополнительных ошибок).
Рассмотрим, что будет истиной, а что ложью при использовании типов, отличных от логических в
каждом из этих языков:
a) В С++ : Выражение считается ложным, если это 0, указатель, не указывающий ни на какой объект,
пустая строка. Иначе оно истинно. В выражении может быть использованы данные логического,
арифметического типа, указатели или тип, который может быть неявно приведен к арифметическому
или логическому типу.
б) В PHP: Выражение считается ложным, если это 0, пустая строка, строка “0”, NULL, массив без
элементов, пустой объект (не указывающему на реальный объект). Иначе оно истинно.
в) В Perl: Выражение считается ложным, если оно равно 0 или пустой строке. Иначе оно истинно.
г) В VBScript: Выражение считается ложным, если оно равно 0, пустой строке, NULL, пустому объекту
(не указывающему на реальный объект). Иначе оно истинно.
2) Очень осторожно сравнивайте значение чисел с плавающей точкой, т.к. в этом формате числа
хранятся неточно, а сравнение выполняется точно, например числа 3,99999999999999 и 4 это два
разных числа. Поэтому лучше использовать > и < при сравнении и не использовать операторы == и #,
например вместо x == y, можно использовать (x – y < 0.00001) || (y – x < 0.00001)1.
3) В Perl:
a) везде необходимо использовать операторные скобки {}, даже если оператор только один он должен
быть заключен в операторные скобки. Кроме, того ключевое слово ifможет быть заменено
на unless, тогда оператор1 будет выполнятся, только если выражение1 ложно
(т.е. unless (выражение1) оператор1; аналогично if (!(выражение1))оператор1; ).
б) Вместо простого оператора if(выражение1) оператор1; можно использовать другую
форму оператор1; if(выражение1); (т.е. сначала идет оператор или блок операторов, а уже
потом if, if(выражение1) называется модификатором оператора). При этом, также можно
вместо if использовать unless (см. пункт а).
4.Операторы цикла
Операторы отношения языка программирования С
==
Равно
!=
Не равно
<
Меньше
<=
Меньше или равно
>
Больше
>=
больше или равно
Таблица 4.1.
Логические операторы отношения
&&
И
||
ИЛИ
!
НЕ, отрицание
3.1. Оператор while
while (expression)
program statement;
Производится расчет выражения expression, заключенного в круглые скобки. Если в результате
расчета выражения expression получается истинный результат (TRUE), то выполняется
утверждение program statement, следующее непосредственно за закрывающей круглой скобкой. После
выполнения этого утверждения вновь рассчитывается выражение expression. Если в результате
расчета будет TRUE, то вновь будут выполнены утверждения program statement. Цикл повторяется до
тех пор, пока в результате расчета выражения expression (в круглых скобках оператора while) не будет
получено значение FALSE (ложный), которое является признаком окончания цикла, после чего
выполнение программы продолжается с утверждения, следующего за утверждением program statement.
while (expression)
{
program1 statement1;
program2 statement2;
program3 statement3;
...
}
3.2. Оператор for
for (init_expression; loop_condition; loop_expression)
program statement;
Первый параметр init_expression используется для задания начального значения цикла.
Второй компонент loop_condition определяет условие или условия, в соответствии с которыми будет
происходить выход из цикла. Повторение будет происходить до тех пор, пока это условие (или
условия) выполняются. Если условие не выполняется, то цикл немедленно заканчивается.
Третий параметр loop_expression выполняется каждый раз, когда заканчивается обработка тела цикла,
т.е. program statement.
Любую из трех частей можно опустить, но точки с запятыми должны остаться на своих местах.
for (init_expression; loop_condition; loop_expression)
{
program1 statement1;
program2 statement2;
program3 statement3;
...
}
Конструкция цикла, реализованная оператором for, может быть выполнена и оператором while:
init_expression;
while (loop_condition)
{
program statement;
loop_expression;
}
Исключением является применение операции continue.
3.3. Оператор do–while
В случае необходимости производить проверку условия выполнения цикла после тела цикла прибегают
к циклу do–while.
do
program statement;
while (loop_expression);
Cначала выполняется утверждение program statement, затем производится проверка условия
выполнения цикла loop_expression с помощью оператора while. Если результатом проверки будет
значение TRUE (истина), то выполнение цикла продолжится, и утверждение program statement всякий
раз будет выполняться вновь. Повторение цикла будет продолжаться до тех пор, пока в результате
проверки условия выполнения цикла loop_expression будет получаться значение TRUE. Когда в
результате проверки условия будет вычислено значение FALSE(ложь), то выполнение цикла
прекратится.
do {
program1 statement1;
program2 statement2;
program3 statement3;
... } while (loop_expression);
Оператор цикла while называется оператором цикла с предусловием, оператор цикла for называется
оператором цикла с параметром, оператор цикла do–while называется оператором цикла c
постусловием.
5. Массивы
Массивы - это группа элементов одного типа (double, float, int и т.п.). Из
объявления массива компилятор должен получить информацию о типе элементов
Объявление массива имеет два формата:
спецификатор-типа имя_массива [константное_выражение];
спецификатор-типа имя_массива [ ];
Спецификатор-типа задает тип элементов объявляемого массива. Элементами
массива не могут быть функции и элементы типа void.
Константное_выражение в квадратных скобках задает количество элементов
массива.
int sample [I0];
#define Nmax 10
int sample [Nmax] /*равносильно объявлению int sample [I0];*/
При объявлении массива константное_выражение может быть опущено в случаях,
если:
- при объявлении массив инициализируется,
- массив объявлен как формальный параметр функции,
- массив объявлен как ссылка на массив, явно определенный в другом файле.
Обращение к элементам массива осуществляется с помощью индексированного
имени. В языке Си первый элемент массива получает индекс 0. Таким образом,
выше объявлен массив с десятью элементами: от sample[0] до sample[9].
Массив занимает непрерывную область памяти. Для одномерного массива полный
объем занимаемой памяти в байтах вычисляется по формуле:
Байты = sizeof (тип) * длина массива
Массив представляет собой набор однотипных данных, расположенных в памяти
таким образом, чтобы по индексам элементов можно было легко вычислить адрес
соответствующего значения. Например, пусть одномерный массив A состоит из
элементов, расположенных в памяти подряд по возрастанию индексов, и каждый
элемент занимает по k байт. Тогда адрес i-того элемента вычисляется по
формуле:
адрес(A[i]) = адрес(A[0]) + i*k
В языке СИ определены только одномерные массивы, но поскольку элементом
массива может быть массив, можно определить и многомерные массивы. Они
определяются списком константных-выражений следующих за идентификатором
массива, причем каждое константное-выражение заключается в свои квадратные
скобки. Каждое константное-выражение в квадратных скобках определяет число
элементов по данному измерению массива, так что объявление двухмерного
массива содержит два константных-выражения, трехмерного - три и т.д.
спецификатор-типа имя_массива [конст_выражение1] [конст_выражение2];
int a[2][3]; /* представлено в виде матрицы
a[0][0] a[0][1] a[0][2]
a[1][0] a[1][1] a[1][2]*/
Объем занимаемой памяти в байтах для двухмерного массива вычисляется по
формуле:
Байты = sizeof (тип) * конст_выражение1* конст_выражение2
3
Если мы имеем дело с двумерным массивом B размерности M×N, расположенным
в памяти по строкам, то адрес элемента B[i][j] вычисляется по формуле:
адрес(B[i][j]) = адрес(B[0][0]) + (i*N+j)*k
Так как массивы занимают непрерывный участок памяти, то двухмерный массив
можно рассматривать как одномерный, поэтому обращение к элементу
B[i][j] равносильно обращению: B[i*N + j].
Отсутствие проверки границ
В языке Си не производится проверки границ массивов: таким образом, ничто не
остановит вас при выходе за границы массива. Если переполнение массива
происходит во время выполнения оператора присваивания, то лишние значения
могут присвоиться другим переменным или включиться в текст программы. С
другой стороны, вы можете объявить массив размером N и указать индекс
элемента, выходящий за пределы N, что не приведет к появлению сообщений об
ошибке, как на шаге компиляции, так и на шаге выполнения, даже если это
послужит причиной аварийного завершения программы. Таким образом, как
программист, вы отвечаете за то, чтобы ваши массивы были достаточно велики
для содержания поступающих в них данных.
Пример:
#define N_max 25
int b1[N_max];
Так как индексы в Си всегда отсчитываются от 0, так что, например, в массиве
b1
можно манипулировать с элементами b1[0], b1[1], ...,b1[24]. Элемент b1[25]
массиву b1 уже не принадлежит и попытка записи в него может привести к
непредсказуемым последствиям.
Инициализация массивов
Термином "инициализация" обозначают возможность задать начальные значения
элементов массива без программирования соответствующих действий. Например,
не прибегая к программным средствам типа присвоения значений в цикле или
считывания данных из внешнего источника (файл, клавиатура, блок данных).
В Си одновременно с объявлением массива можно задать начальные значения
всех элементов массива или только нескольких первых его компонент:
int d[10]={1,2,3,4};
char a[7]="Привет";
char b[7]={'П','р','и','в','е','т'};
Многомерные массивы инициализируются так же, как и одномерные. Например,
так можно проинициализировать двухмерный массив:
int w[3][3] = {
{ 2, 3, 4 },
{ 3, 4, 8 },
{ 1, 0, 9 }
};
4
В последнем примере объявлен массив w[3][3]. Списки, выделенные в фигурные
скобки, соответствуют строкам массива, в случае отсутствия скобок
инициализация будет выполнена неправильно.
int w[3][5] ] = {1, 2, 3, 4, 5, 6,7, 8, 9, 10, 11};
1
2
3
4
5
6
7
8
9
10
11
int w[3][5] ] = {{1, 2, 3}, {4, 5, 6,7, 8}, {9, 10, 11}};
1
2
3
4
5
6
7
8
9
10 11
Пример: Поиск первого отрицательного элемента массива.
#define Nmax 10
#define Мmax 10
void main()
{
int a[Mmax][Nmax];
int found = 0; /* found - флаг (признак) -= 0, если нет отрицательного
элемента*/
for (i=0; i <Mmax && !found; i++ )
for (j=0; j <Nmax && !found; j++ )
found = a[i][j] < 0;
if (found) {printf (“\n найден в позиции I = %d j = %d”, i -1, j -1);
Инициализация безразмерных массивов
Предположим, что необходимо проинициализировать массивы для создания
таблицы сообщений об ошибках:
char e1[12] = "read error\n";
char e2[13] = "write error\n";
char e3[18] = "cannot open file\n";
Очевидно, что подсчитывать "вручную" количество символов в каждом
сообщении для подсчета размерности массивов очень трудоемко. Однако можно
заставить Си автоматически определить размеры этих массивов с помощью
инициализации безразмерных массивов. Для этого в операторе инициализации не
надо указывать размер массива, и Си автоматически создаст массив, который
сможет содержать присутствующий инициализатор. Используя этот подход,
получим следующую таблицу сообщений:
char e1[] = "read error\n";
char e2[] = "write error\n";
char e3[] = "cannot open file\n";
Компилятор Си сам сформирует нужное значение по количеству
инициализирующих данных В нашем случае под массив e2 будет отведено 13
байтов, включая последний байт с нулевым кодом, завершающий каждую строку.
Оператор printf("%s has length %d\n",e2,sizeof (e2)); выведет на экран
5
write error
has length 13
Метод инициализации безразмерных массивов не только менее трудоемок, но и
позволяет заменить любое сообщение, без перерасчета размера соответствующего
массива. В языке Си инициализация безразмерных массивов не ограничивается
только одномерными массивами. Однако для многомерных массивов необходимо
указывать все индексы измерений, кроме самого левого. В качестве примера
приведем объявление массива sqrs как безразмерного:
int sqrs[][2] = {1,1,
2,4,
3,9,
4,16,
5,25,
6,36
}
Преимуществом данного объявления является тот факт, что вы можете удлинить
или укоротить таблицу, не меняя индекс по первому измерению.
В языке СИ можно использовать сечения массива, как и в других языках высокого
уровня, однако на использование сечений накладывается ряд ограничений.
Сечения формируются вследствие опускания одной или нескольких пар
квадратных скобок. Пары квадратных скобок можно отбрасывать только справа
налево и строго последовательно. Сечения массивов используются при
организации вычислительного процесса в функциях языка СИ, разрабатываемых
пользователем.
int s[2][3];
Если при обращении к некоторой функции f написать f(s[0]), то будет
передаваться нулевая строка массива s.
int b[2][3][4];
При обращении к массиву b можно написать, например, b[1][2] и будет
передаваться вектор из четырех элементов, а обращение b[1] даст двухмерный
массив размером 3 на 4. Нельзя написать b[2][4], подразумевая, что
передаваться
будет вектор, потому что это не соответствует ограничению, наложенному на
использование сечений массива.
Строки
Наиболее часто одномерные массивы используются для создания символьных
строк. В языке Си строка состоит из массива символов, оканчивающихся на ноль.
Ноль обозначается символом '\0'.В связи с этим символьный массив должен
содержать на один элемент больше, чем количество символов в строке. Например,
если вы хотите объявить массив str, который будет содержать строку из десяти
символов, то необходимо записать:
char str[11];
Такое объявление оставляет место для нуля в конце строки. Несмотря на то, что
в
языке Си отсутствует тип данных "символьная строка", н позволяет записывать
6
символьные константы. Вспомним, что символьная константа это набор
символов, заключенный в двойные апострофы, например:
"hello there"
"this is a test"
В конец символьной константы не надо добавлять ноль – Си делает это
автоматически. В связи с этим строка "hello" в памяти будет выглядеть как:
H E L L O \0
Для считывания строки с клавиатуры можно воспользоваться библиотечной
функцией gets(). Формат ее следующий:
gets(имя-массива);
Функция gets() считывает символы до тех пор, пока вы не нажмете клавишу
Enter.
Для вывода строки на экран используется библиотечная функция puts(). Формат
ее следующий:
puts(имя-массива);
#include <stdio.h>
int main(void)
{ char string[80];
printf("Input a string:");
gets(string);
printf("The string input was: %s\n", string);
return 0;
}
Имейте в виду, что функция gets() не проверяет границ массива, с которым она
вызывается. Таким образом, если вы вводите строку длиннее, чем объявленный
массив, то лишние символы затрут введенные первоначально.
#include <stdio.h>
int main(void)
{ char string[] = "This is an example output string\n";
puts(string);
return 0;
}
6. Указатели
Указатели являются одним из мощнейших средств языка Си. Область
применения указателей довольно широка. Например,
• указатели позволяют изменять аргументы функций, находящиеся в вызовах,
• их можно использовать для поддержки динамического размещения памяти,
• ими можно заменять массивы с целью повышения эффективности работы
программы.
Однако, несмотря на то, что указатели являются одним из важных средств
языка Си, они в то же время - опасный инструмент. Например, использование
неинициализированных указателей может вызвать аварийное завершение работы
7
системы. Кроме того, некорректная запись указателей является источником
трудно обнаружимых ошибок.
Указатель - это адрес ячейки памяти, распределяемой для размещения
некоторого объекта (в качестве такого объекта может выступать переменная,
массив, структура, строковая константа). В том случае, если переменная
объявлена как указатель, то она содержит адрес байта памяти компьютера, по
которому может находиться скалярная величина любого типа.
При объявлении переменной типа указатель, необходимо определить тип
объекта данных, адрес которых будет содержать переменная, и имя указателя.
Общий формат объявления указатель - переменной имеет вид:
Спецификатор_типа [модификатор] *имя_переменной;
где имя_переменной – имя указатель – переменной; * означает, что следующая
за ней переменная является указателем.
Спецификатор-типа задает тип объекта и может быть любым из допустимых в
языке Си базовых типов. Задавая вместо спецификатора-типа ключевое слово
void, можно, таким образом, потом определить спецификацию типа, на который
ссылается указатель. Переменная, объявляемая как указатель на тип void, может
быть использована для ссылки на объект любого типа. Однако для того, чтобы
можно было выполнить арифметические и логические операции над
указателями или над объектами, на которые они указывают, необходимо при
выполнении каждой операции явно определить тип объектов. Такие
определения типов может быть выполнено с помощью операции
преобразования типов.
В качестве модификаторов при объявлении указателя могут выступать
ключевые слова const, near, far, huge.
Ключевое слово const указывает, что указатель не может быть изменен в
программе.
const * dr; /* Переменная dr объявлена как указатель на константное
выражение,
т.е. значение указателя может изменяться в процессе выполнения
программы, а величина, на которую он указывает, нет. */
unsigned char * const w = &obj; /* Переменная w объявлена как константый
указатель на данные типа char unsigned. Это
означает,
что на протяжение всей программы w будет указывать
на
одну и ту же область памяти. Содержание же этой
области
может быть изменено. */
Размер переменной объявленной как указатель, зависит от архитектуры
компьютера и от используемой модели памяти, для которой будет
компилироваться программа. Поскольку указатели содержат адреса, то при
использовании одной и той же модели распределения памяти переменные
указатели занимают область памяти одинакового размера независимо от типа
данных, на которые они показывают.
unsigned int * a; /*переменная а представляет собой указатель на тип unsigned
int (целые числа без знака)*/
double * x;
/*переменная х указывает на тип данных с плавающей точкой
удвоенной точности */
8
char * buffer;
на
/* объявляется указатель с именем buffer, который указывает
переменную типа char */
7. Функции
Функция - это самостоятельная единица программы, созданная для решения
конкретной задачи. Функция в языке С играет ту же роль, что и подпрограммы или
процедуры в других языках. Функциями удобно пользоваться, например, если
необходимо обработать один и тот же код программы. Как и переменные, функции надо
объявлять (declare). Функцию необходимо объявить до её использования. Запомните
это простое правило - сначала объяви, а потом используй.
Каждая функция языка С имеет имя и список аргументов (формальных параметров).
Функции могут возвращать значение. Это значение может быть использовано далее в
программе. Так как функция может вернуть какое-нибудь значение, то обязательно
нужно указать тип данных возвращаемого значения. Если тип не указан, то по
умолчанию предполагается, что функция возвращает целое значение (типа int). После
имени функции принято ставить круглые скобки (это касается вызова функции её
объявления и описания). В этих скобках перечисляются параметры функции, если они
есть. Если у функции нет параметров, то при объявлении и при описании функции
вместо <список параметров> надо поставить void - пусто.
Основная форма описания (definition) функции имеет вид:
тип <имя функции>(список параметров)
{
тело функции
}
Объявление (прототип) функции имеет вид:
тип <имя функции>(список параметров);
Обратите внимание на то, что при описании функции после заголовка функции
тип <имя функции>(список параметров)
точка с запятой не ставиться, а при объявлении функции точка с запятой ставиться.
Вызов функции делается следующим образом:
<имя функции>(параметры);
или
<переменная>=<имя функции>(параметры);
При вызове функции так же ставиться точка с запятой.
Почему надо объявлять функцию до использования? Дело в том, что для правильной
работы кода функции машине надо знать тип возвращаемого значения, количество и
типы аргументов. При вызове какой-либо функции копии значений фактических
параметров записываются в стек, в соответствии с типами указанными в ее прототипе.
Затем происходит переход в вызываемую функцию.
Приведем пример вызова функции, которая будет печатать строку "Вызвали функцию"
на экран.
/* Используем свою функцию */
#include <stdio.h>
void main(void)
{
void function1(void);
function1();
// Точка входа в программу
// Объявление функции
// Вызов функции
}
/* Описание функции */
void function1(void)
// Заголовок функции
{
// Начало тела функции
printf("Вызвали функцию\n");
}
// Конец тела функции
Результатом работы программы будет строка напечатанная на экране.
Обратите внимание на заголовок в описании функции! После него не ставится точка с
запятой.
В теле функции main() мы объявили функцию function1(), затем её вызвали. В теле
нашей функции function1() мы вызываем функцию printf(). А где же объявлена функция
printf() ? Если вы внимательно посмотрите текст программы, то увидите строку #include
<stdio.h>, как говорилось ранее эта строка говорит компилятору, чтобы тот включил в
текст программы файл с объявлениями функций стандартного ввода/вывода (standart
input/output). Аха! Значит функция printf() объявлена именно там!
Директива препроцессора (preprocessor directive) просто вставляет текстовый файл
stdio.h в текст нашей программы. Причем вставляет туда где стоит эта директива. Если
вы уберете или закоментируете строку #include <stdio.h>, то программа работать не
будет потому что функция printf() не будет объявлена. Компилятор просто выдаст
ошибку - Function 'printf' should have a prototype (Функция 'printf' должна иметь
прототип).
Обратите внимание ещё на то, что тип возвращаемого значения у нашей функции void
(пусто). Это значит, что функция не будет возвращать никакого значения.
Функции возвращающие значение.
Давайте рассмотрим пример в котором опишем две функции, соответственно объявим
их и последовательно вызовем. Но в этом примере для одной функции мы укажем тип
возвращаемого значения - int.
/* Две наших функции */
#include <stdio.h>
int x;
// Подключаем файл заголовков функций (их объявлений)
// Объявляем переменную x (глобальная переменная)
void main(void)
{
void function1(void);
// Объявляем функцию function1()
int function2();
// function2() будет возвращать значение типа int
x = 10;
// Присваиваем переменной x значение 10
printf("До вызова функции function2() x равно %d\n", x);
function1();
x = function2();
// Вызываем функцию function1()
// Вызываем функцию function2()
printf("После вызова функции function2() x равно %d\n", x);
}
/* Описание наших функций */
void function1(void)
{
printf("Сделан вызов первой функции\n");
}
int function2(void)
{
int y;
y = x + 10;
return y;
// Объявляем локальную переменную
// Возвращаем значение y
}
Теперь давайте посмотрим текст программы. После строки #include <stdio.h> мы
объявляем глобальную переменную x. Так как x - глобальная переменная, то она будет
видна всем функция нашей программы т.е. этой переменной могут пользоваться все
функции.
В теле main() мы объявляем две функции, одна из которых может возвращать значение
типа int. Далее мы присваиваем переменной x значение 10, так как x это глобальная
переменная, то эта переменная будет видна функции main() т.е. функция main() может
использовать эту переменную. После этого присвоения мы выводим значение x на
экран.
На экране монитора появится следующая строка - "До вызова функции function2() x
равно 10".
Обратите внимание на вызов функции printf() printf("До вызова функции function2() x равно %d\n", x);
В строке после сиволов %d стоит символ \n. \n - управляющий символ он означает, что
необходимо перейти на новую строку.
Далее мы вызываем функцию function1(). Эта функция просто выводит строку "Сделан
вызов первой функции\n" на экран и так как в строке стоит \n, то будет осуществлен
переход на новую строку.
В следующей строке x = function2(); переменная x принимает значение которое вернет
функция function2(). Посмотрите на описание функции function2(). В теле этой функции
мы объявляем переменную y, а дальше переменной y мы присваиваем значение
переменной x + 10. Так как x - глобальная переменная (она видна для функции
function2) все будет работать.
Далее идет строка return y; с помощью оператора return мы возвращаем значение
переменной y. Запомните, что если функция возвращает значение, то в теле этой
функции обязательно должен присутствовать оператор return (он может быть и не
один). Ну так вот с помощью оператора return мы возвращаем значение локальной
переменной y в вызывающую функцию main().
Теперь посмотрим тело функции main() Следующей строкой после x = function2();
является строка printf("После вызова функции function2() x равно %d\n", x); которая
выводи значение измененной переменной x;
На экране появится строка - "После вызова функции function2() x равно 20".
Функции с параметрами.
Функции языка С могут иметь параметры. Эти параметры передаются в функцию и там
обрабатываются. Ещё раз покажем основную форму описания функции
тип <имя функции>(список параметров)
{
тело функции
}
В списке параметров для каждого параметра должен быть указан тип.
Пример правильного списка параметров:
function(int x, char a, float z)
Пример неправильного списка параметров:
function(int x, a, float z)
Давайте рассмотрим все это на примере. Пусть у нас будет функция у которой
присутствует один параметр x. Функция будет возвращать квадрат значения x.
int square(int x)
{
x = x * x;
return x;
}
// Символ * это операция умножения
Теперь давайте рассмотри пример функции, которая будет выводить значение
переменной z типа float на экран.
void myout(float z)
{
printf("Z=%f", z);
}
// Переменная z является формальным параметром.
// %f - означает, что выводится число с плавающей точкой
Формальные и фактические параметры
Формальные параметры - это параметры которые мы объявляем в заголовке функции
при описании.
Фактические параметры - это параметры которые мы подставляем при вызове функции.
void myfunc(int x);
void main(void)
{
int a;
a=5;
myfunc(a);
}
// Объявление функции
// a- фактический параметр
// Описание функции
void myfunc(int x)
// x - формальный параметр
{
x = x + 10;
printf("Вывод x = %d",x);
}
В языке С функция может возвращать несколько значений. Чтобы функция могла
вернуть несколько значений необходимо пользоваться указателями. Этот механизм
называется - передача параметров по ссылке.
Download