Лекция №2 Приведение (преобразование) типов

Вид материалаЛекция

Содержание


Ветвление алгоритмов.
Подобный материал:

Лекция №2




Приведение (преобразование) типов



Рассматривая операцию деления, мы вроде бы упоминали, что при делении двух целых операндов и результат получится целым. Например, результатом операции 5/2 будет 2, а совсем не 2.5, как хотелось бы. Для получения вещественного результата нужно выполнять деление не целых, а очень даже вещественных чисел и результат операции 5.0/2.0 будет равен 2.5, что не может не радовать. А если мы работаем с переменными? И целый результат деления двух целочисленных объектов нас не устраивает? В таком случае нам необходимо воспользоваться приведением (или преобразованием) типов этих переменных. Синтаксис подобной операции выглядит следующим образом:


(желаемый_тип_переменной) имя_переменной_или_выражение_в_скобках;


Рассмотрим следующую простую программу для усвоения свежеприобретенных знаний:


#include «stdio.h»

void main()

{

int n=5, k=2;

double d;

int m;

d=(double)n/(double)(k*2);

m=n/k;

double i = (int)(d*3);

printf(«Result for integer: %d\n»,m);

printf(«Result for double: %lf\n»,d);

printf(«Result for integer d: %lf\n»,i);

}


Как мы видим, приведение типов используется три раза – для переменной n, выражения в скобках (k*2) и для переменной d. Во втором случае приведение типов работает и для выражений, правда для этого они должны быть естественно заключены в скобки. А вот на результате приведения double к int мы остановимся поподробнее. Как мы уже знаем, переменные занимают определенное количество байт в памяти. И при преобразовании значений, например, типа float в int необходимо это количество байт изменить. А если значение float переменной вылезает за диапазон допустимых значений типа int? Естественно это приведет к потере данных и в результате мы получим черт знает что. Именно поэтому приведение типов необходимо применять с некоторой осторожностью. В нашем примере, приводя double к int мы, зная, что значение переменной лежит в диапазонах обоих типов, можем воспользоваться подобными потерями данных уже для своих конкретных целей. В данном случае мы просто потеряем дробную часть переменной и результатом будет являться значение переменной i равное 3.

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


Вообще то говоря, деление – это только одна из бинарных операций, а для каждой из них операнды могут иметь разные типа. Но, к счастью, нам не всегда в явном виде необходимо указывать преобразования. Если у бинарной операции операнды имеют разные типы, то компилятор выполняет преобразования автоматически к какому то одному из типов. Такие приведения называются неявными. Правила, по которым они выполняются, нам на данный момент не важны, да и вообще на них редко обращают внимание, но для ознакомления, можно глянуть на стр. 57. На следующей странице можно так же заценить «безопасные» арифметические преобразования, гарантирующие сохранность точности и значений переменных.


Работая в 2008 студии вы наверное столкнулись с замечательной проблемой, когда функции sqrt() внезапно не понравились аргументы типа int.

Строка x=sqrt((y+y)*(y+y)) при том, что тип y – int, вызывает вопросы у компилятора?

Пользуясь приведением типов получим что то похожее на x=sqrt((double)((y+y)*(y+y))), уже без матюгов и лишних вопросов от студии.


Ветвление алгоритмов.



Как вы уже заметили, все операции, функции и прочие операции выполняются по тексту программы последовательно, по строкам. Поведение программы линейно. Но как быть, если нас это не устраивает? Что делать, если, например, дальнейшее действие должно зависеть от результатов предыдущего? В программе, вычисляющей корни квадратного уравнения, успех выполнения зависит от введенных данных. Если ввести коэффициенты, при которых дискриминант будет отрицательным, то собственно все закончится «разрушительным сбоем» и вылетом программы. Для разрешения подобных ситуаций нас потребуется некоторая программная логика, ветвление алгоритмов. А для этого в свою очередь требуются логические операции.


Операции отношений (сравнения):


< - меньше, чем

> - больше, чем

<= - меньше или равно

>= - больше или равно

== - равно

!= - не равно

&& - конъюнкция (И) арифметических операндов или отношений

|| - дизъюнкция (ИЛИ) арифметических операндов или отношений

! – отрицание (НЕ) арифметических операндов или отношений


Результатом данных операция является некоторая логическая величина, под которую отведен отдельный тип данных: bool. Значениями данной переменной могут являться два чего-то: true или false, истина или ложь. В численном эквиваленте значению false сопоставляют 0, а true – все остальное, чаще всего – 1.


Примеры:

Результатом операции 5>4 будет true, 5<1 = false, (5>1+1)&&(5>3) = true, (5>1)||(5<1) = true, 1==2 = false, 3!=3 = false и так далее.


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


Оператор if


Для простого ветвления алгоритмов с вариантами (если да, то одно, если нет, то второе) замечательно справляется оператор if. Его синтаксис приведен ниже:


if (логическое_условие)

{ блок_операций_если_true }

else

{ блок_операций_если_false }


Соответственно если выражение внутри скобок имеет значение true, то выполняется блок_операций_если_true, после отработки которого блок_операций_если_false уже не затрагивается. Ну а если нет, то программа переходит к блоку блок_операций_если_false и работает далее по тексту программы.


Существует также и сокращенная форма записи


if (логическое_условие)

{ блок_операций_если_да }


В данном варианте, если внутри скобок окажется false, то блок_операций_если_да благополучно пропускается мимо.


В качестве примера рассмотрим следующую программу нахождения квадратного корня из введенного с клавиатуры числа.


#include “stdio.h”

void main()

{

int a;

printf(“Input integer: ”);

scanf(“%d”,&a);

if (a>0)

{

printf(“Wow, looks like its positive!”);

printf(“Square root: %lf”,sqrt((double)a));

}

else

printf(“Umm, its negative, and there are no square root from this crap”)

}


Циклы


Далее мы переходи к самой интересной части лекции, а именно – к операторам циклов.

Довольно часто вам нужно будет «зацикливать» некоторые действия, повторять их по много много раз, причем зачастую, это количество раз будет зависеть от введенных данных и просто так прокопипейстить n строк не получится. В решения данной проблемы нам помогают циклы. В языке Си существую три формы операторов циклов. Цикл с пред-условием WHILE, цикл с пост-условием DO WHILE и параметрический цикл FOR.


Оператор WHILE


Цикл с пред-условием имеет вид


while(выражение_условие)

{

тело_цикла

}


В качестве выражения условия чаще всего используется логическое выражение. Если оно равно true, то цикл выполняется, если false – то завершает выполнение. Естественно проверка истинности выражения осуществляется каждый раз до выполнения тела цикла, причем внутри тела цикла параметры, от которых зависит логическое выражение, зачастую меняются. В качестве выражения-условия может быть и арифметическое выражение, тогда цикл выполняется, пока значение этого выражения не станет равным нулю. Для всех циклов справедливы служебные слова break и continue. Когда компилятор натыкается на слово break при прохождении тела цикла, цикл немедленно прекращается и компилятор переходит следующей строчке, не относящейся к циклу. При натыкании на continue в теле цикла компилятор не смотрит, что там идет дальше по тексту, а переходит к выполнению тела цикла с самого начала.


Рассмотрим простейшую программу вычисления факториала числа.


5! = 5*4*3*2*1 = 120, если кто не помнит


#include «stdio.h»

void main()

{

int a;

printf(«Input integer: »);

scanf(«%d»,&a);

if (a<=1)

{

printf(«WTF?!\n»);

return;

}

int res=1;

while (a>1)

{

res=res*a;

a--;

}

printf(«Result: %d\n»,res);

}


Переводя на русский язык: пока нас все устраивает, мы что то делаем

К слову говоря, для экстренного завершения программы в теле функции main можно использовать слово return. Поподробнее его мы рассмотрим в следующей лекции, ну а здесь оно просто выкидывает нас из программы, если введенное число нас не устраивает.


Если вам вдруг понадобится бесконечный цикл (не для того, чтобы зациклить машину, а когда условие выхода не поддается логике и нужно будет использоввать break) то что то вроде while(1) вам поможет. Или while(-100)... или while(200)... но никак не while(0).


Оператор DO WHILE


Занимается абсолютно тем же самым, но проверка условия производится после выполнения тела цикла. Другими словами, при любом условии выхода из цикла, само тело цикла выполнится один раз.


#include «stdio.h»

void main()

{

int a;

printf(«Input integer: »);

scanf(«%d»,&a);

if (a<=1)

{

printf(«WTF?!\n»);

return;

}

int res=1;

do

{

res=res*a;

a--;

}

while (a>1)

printf(«Result: %d\n»,res);

}


Переводя на русский язык: делаем что то, пока нас все устраивает.


Параметрический цикл FOR


Третий вид циклов – параметрический цикл for. Он имеет следующий вид:


for(выражение1; условие; выражение2)

{

тело_цикла

}


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

Условие – логическое выражение, аналогичное условиям для цикла while.

Выражение2 – выполняется сразу после отработки операторов в теле цикла. Вызывается каждый раз после очередного прохождения цикла.

Итого, когда программа встречает слово for, один раз выполняется выражение1. Далее, если условие верно, выполняется тело цикла, а потом – выражение2. После этого опять проверяется условие, если оно истинно – выполняем тело цикла, потом выражение2 и так далее, пока условие не станет ложным или программа не наткнется на служебное слово break. Обычно подобные циклы используются для определенного количества повторов следующим образом:




for(int i=0;i<5;i++)

{

printf(“Hello - %d\n”,i+1);

}




Данный кусок ввода занимается тем, что пять раз выводит на экран слово Hello, а так же номер повторения.

Как мы видим, в выражении1 мы объявили и инициализировали некоторую переменную и используем ее внутри цикла. В зависимости от компилятора такая переменная будет видна или во всей нашей программе, или только внутри тела цикла. В VisualStudio 6.0 справедлив первый вариант.