Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции 3-9 full.doc
Скачиваний:
20
Добавлен:
22.12.2018
Размер:
979.97 Кб
Скачать

Глава 4. Основы программирования

4.1 Структура простейшей программы

Язык С не имеет жестко заданной структуры программы со всеми блоками и разделами. В целом программа состоит из функций, главной из которых является функция main. При запуске программы управление передается этой функции.

Так как язык С сам по себе является довольно простым языком программирования и не содержит даже операторов ввода и вывода, то основную часть языка С составляют его библиотечные функции. Эти функции группируются в различные библиотеки в зависимости от своего функционального назначения. Все библиотеки языка С разделяются на две группы:

  • системные библиотеки - поставляются вместе со средой разработки;

  • пользовательские библиотеки - разрабатываются пользователем (программистом) или приобретаются отдельно от среды разработки.

В свою очередь системные библиотеки могут быть разделены на следующие две группы:

  • стандартные библиотеки - включены в стандарт языка С: С89 или С99;

  • нестандартные библиотеки - включены разработчиком среды разработки (например, библиотека conio.h - библиотека консольного ввода и вывода, разработанная фирмой Borland).

Для того чтобы использовать функции необходимо подключить соответствующую библиотеку. Это осуществляется с помощью директивы препроцессора #include <имя библиотеки>. Более подробно данная директива рассматривается в главе 10. Подключение библиотек осуществляется в самом начале программы. Наиболее часто используемыми библиотеками языка С являются:

  • stdio.h - библиотека стандартного ввода и вывода,

  • math.h - библиотека математических функций,

  • stdlib.h - библиотека стандартных функций,

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

Часто библиотеки перекрывают друг друга: содержат одни и те же функции.

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

Например, простейшая программа «Привет мир!», с которой начинают изучение всех языков программирования, имеет вид:

1 #include<stdio.h>

2 int main(int argc, char *argv[])

3 {

4 printf(“Hello world!\n”);

5 }

В строке 1 осуществляется подключение библиотеки стандартного ввода и вывода. В строке 2 осуществляется описание заголовка функции main, забегая вперед можно сказать, что данная запись означает, что функция main возвращает целочисленное значение и принимает два параметра: целочисленный argc и массив строк argv. Данное описание регламентируется стандартом С99 и является обязательным, в отличие от более раннего стандарта С89. В строке 3 осуществляется открытие тела функции main с помощью символа открывающей фигурной скобки. Само тело функции main (строка 4) состоит из одного оператора вызова функции стандартного вывода printf. В строке 5 осуществляется закрытие тела функции main с помощью символа закрывающей фигурной скобки.

В дальнейшем будут рассмотрены другие структуры программ на языка С, которые используются при разработке программ с пользовательскими функциями (глава 6) и при разработке многомодульных приложений (глава 11).

В общем случае тело функции main состоит из операторов, которые записываются в импликативной (процедурной) форме. Каждый оператор завершается знаком ‘;’ и состоит из одного или нескольких литералов (англ., token). Литералы могут разделяться любым количеством пробелов, табуляций или переводов строк. Сам литерал должен писаться слитно. Литералами могут быть:

  • ключевые (зарезервированные) слова языка С,

  • идентификаторы языка C;

  • константы;

  • строковые литералы;

  • знаки пунктуации.

К ключевым словам языка С относятся:

auto

enum

restrict

unsigned

break

extern

return

void

case

float

short

volatile

char

for

signed

while

const

goto

sizeof

_Bool

continue

if

static

_Complex

default

inline

struct

_Imaginary

do

switch

switch

double

long

typedef

else

register

union

__asm

__finally

__try

_forceinline

__cdecl

__forceinline

_asm

_inline

__declspec

__inline

_cdecl

_stdcall

__except

__leave

_declspec

__fastcall

__stdcall

_fastcall

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

Идентификатор - это сочетание букв латинского алфавита, цифр и знака подчеркивания «_», начинающееся или с буквы, или со знака подчеркивания. Длина идентификатора не ограничена. Заглавные и строчные буквы различаются. Идентификаторы используются для обозначения имен переменных, констант, функций типов. Использовать один и тот же идентификатор для обозначения двух и более сущностей запрещено.

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

Строковые литералы - это последовательности символов заключенных в двойные кавычки, которые могут рассматриваться как строковые константы.

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

[ ] ( ) { } . ->

++ -- & * + - ~ !

/ % << >> < > <= >= == != ^ | && ||

? : ; ...

= *= /= %= += -= <<= >>= &= ^= |=

, # ##

<: :> <% %> %: %:%:

Все операторы языка С можно разделить на четыре группы:

  1. операторы объявлений типов и переменных;

  2. операторы присвоений, математических и логических операций;

  3. операторы вызова функций;

  4. операторы управления.

4.2 Система базовых типов данных

Прежде чем рассматривать операторы объявлений типов и переменных, необходимо рассмотреть систему базовых типов языка С.

Тип - это описание диапазона значений, которые может принимать переменная, указанного типа. Каждый тип данных характеризуется своим размером (количество байт занимаемых значением данного типа в памяти компьютера) и диапазоном принимаемых значений.

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

  • простые (скалярные) и сложные (векторные) типы;

  • базовые (системные) и пользовательские типы.

К простым типам данных языков программирования высокого уровня обычно относятся: целые и вещественные числа, символы, строки, булевы типы. К сложным типам данных относятся: массивы, множества, перечисления, структуры, объединения, поля бит.

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

В языке С систему базовых типов образуют четыре типа данных:

  • символьный,

  • целочисленный,

  • вещественный одинарной точности,

  • вещественный двойной точности.

Символьный тип (char) предназначен для описания символьных или целочисленных значений. Размер значения данного типа составляет 1 байт, диапазон принимаемых значений от -128 до 127. Может рассматриваться как символ и как число (специфика языка программирования), т.е. не требует преобразования как в языке Pascal.

Целочисленный тип (int) предназначен для описания целочисленных значений. Размер значения данного типа зависит от разрядности системы (среды разработки) и составляет:

  • в 16-ти разрядных системах - 2 байта (диапазон принимаемых значений от -32768 до 32767);

  • в 32-ух разрядных системах - 4 байта (диапазон принимаемых значений от -2147483648 до 2147483647).

ПРИМЕЧАНИЕ: Так как 16-ти разрядные системы устарели, то в дальнейшем будем рассматривать только 32-ух разрядное представление целого числа.

Вещественный тип одинарной точности (float) предназначен для описания вещественных значений одинарной точности. Размер значения данного типа составляет 4 байта (23 бита - мантисса, 8 бит - порядок, 1 бит - знак числа), диапазон принимаемых значений ±3.4Е±38. Точность - до 7 знаков после запятой

Вещественный тип двойной точности (double) предназначен для описания вещественных значений двойной точности. Размер значения данного типа составляет 8 байт (52 бита - мантисса, 11 бит - порядок, 1 бит - знак числа), диапазон принимаемых значений ±1.7Е±308. Точность - до 17 знаков после запятой.

Помимо описанных типов данных в языке С предусмотрены две группы модификаторов типа:

  • модификаторы знака: signed и unsigned,

  • модификаторы размера: short и long.

Модификатор signed осуществляет преобразование целочисленных значений к знаковым числам. Используется совместно с типами char и int. Так как этот модификатор всегда используется по умолчанию, то указывать его необязательно.

Модификатор unsigned осуществляет преобразование целочисленных значений к незнаковым числам. Используется совместно с типами char и int. Преобразование осуществляется путем переноса бита знака числа в значащие разряды, модифицируя тем самым диапазон принимаемых значений. Тип unsigned char принимает диапазон значений от 0 до 255. А тип unsigned int:

  • для 32-ух разрядных систем: от 0 до 4294967295 (232-1),

  • для 16-ти разрядных систем: от 0 до 65535 (216-1).

Модификатор short осуществляет преобразование целого числа типа int к короткому целому:

  • для 16-ти разрядных систем: используется по умолчанию;

  • для 32-ух разрядных систем: преобразует к 2-ух байтному значению с диапазоном от -32768 до 32767.

Модификатор long осуществляет преобразование целого числа типа int к длинному целому:

  • для 16-ти разрядных систем: преобразует к 4-ех байтному значению с диапазоном от -2147483648 до 2147483647;

  • для 32-ух разрядных систем: используется по умолчанию.

Модификатор long также используется с вещественным типом двойной точности double, преобразуя последний к вещественному числу повышенной точности (формат сопроцессора) размером 10 байт (64 бит - мантисса, 15 бит - порядок, 1 бит - знак числа) и диапазоном ±3.4Е±4932.

В стандарте С99 было разрешено двукратное использование модификатора long для создания 64-ех разрядных целых чисел. Значение типа long long int имеет размер 8 байт и диапазон от -263 до 263-1.

Допускается совместное применение модификаторов типа из различных групп. Например, значение типа unsigned long long int имеет размер 8 байт и диапазон значений от 0 до 264-1.

Минимальные и максимальные значения всех базовых типов данных языка С можно узнать, используя их мнемонические обозначения, которые описаны в библиотеках (для изучения констант следует обратиться к соответствующему разделу помощи среды разработки):

limits.h - содержит диапазоны целочисленных значений,

float.h - содержит диапазоны вещественных значений.

Рассмотренные типы данных относятся к числовым и символьным типам. До появления стандарта С99 в языке С булев (логический) тип отсутствовал как таковой. Вместо логических значений использовались целочисленные значения, интерпретируемые следующим образом:

  • 0 - логическая ложь (false),

  • 1 или любое отличное от нуля - логическая истина (true).

В стандарте С99 был введен логический тип _Bool, размер которого составляет 1 байт. Для использования более привычных ключевых слов bool, true и false необходимо подключить библиотеку stdbool.h. Тем не менее, чаще всего используется старое представление логического типа (целочисленными значениями).

4.3 Операторы объявлений

Для создания переменных, констант и пользовательских типов данных в языке С используются соответствующие операторы объявлений.

Переменная - именованная область памяти вычислительной машины, предназначенная для хранения значений определенного типа, с произвольным методом доступа: чтение и запись.

Имя переменой - разрешенный идентификатор языка С не использовавшийся ранее для обозначения других переменных, типов, элементов перечислений или имен функций.

Оператор объявления переменных имеет следующий синтаксис:

тип имя1[,имя2[,...]];

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

int a, b, c;

double x, y;

char ch;

В приведенных примерах сначала создаются три переменные (a, b, и c) целого типа, затем две переменные вещественного типа двойной точности (x и y), и, в третьей строке, переменная ch символьного типа. В операторе объявлений литералами являются: тип данных, имена переменных, знаки пунктуации: запятая и точка с запятой.

Правила хорошего стиля программирования предъявляют следующие требования к форматированию операторов объявлений переменных:

  • объявление переменных нового типа всегда начинается с новой строки;

  • имя переменой должно быть информативным, т.е. из имени переменной должно быть понятно ее назначение;

  • имя переменной не должно быть слишком длинным;

  • после объявления переменной в этой же строке или на предыдущей строке в комментарии желательно дать краткое описание назначения переменной;

  • необходимо разделять имена переменных пробелами (в разумных пределах).

ПРИМЕЧАНИЕ: Здесь и далее будут даваться рекомендации по стилю оформления текстов программ, которых следует придерживаться в процессе выполнения лабораторных работ и вообще при написании программ.

При объявлении переменной на языке С значение переменной не определено, т.е. переменная содержит произвольное значение, а не нулевое, как это сделано в некоторых других языках программирования высокого уровня. Для исправления данной ситуации в языке С введена возможность инициализации переменной при ее объявлении. Оператор объявления переменных с инициализацией имеет следующий синтаксис:

тип имя1[=значение1][, имя2[=значение2][,...]];

Для инициализации переменной используется оператор присвоения, который будет рассмотрен в последующем разделе. Примеры объявления переменных с инициализацией:

int a=26, b=032, c=0x1A;

double x=2.5e2, y=0x1.ffe-3;

char ch=’Z’;

При объявлении переменной с инициализацией в качестве значения обычно указывается некоторая константа.

В языке С присутствует три вида констант:

  • целочисленные,

  • вещественные,

  • символьные.

Целочисленные константы указываются в трех системах исчисления. Десятичная константа указывается десятичным числом в обычной форме (переменная a из примера). Восьмеричная константа указывается числом, начинающимся с цифры ноль и содержащим цифры 0...7 (переменная b из примера). Шестнадцатеричная константа указывается целым числом с префиксом 0x или 0X, содержащим цифры 0...9 и буквы латинского алфавита a...f, A...F (переменная c из примера). Целочисленные константы также имеют суффиксы, приведенные в таблице 2.1. Примеры:

unsigned long int a = 123ul;

long long int b = 10LL;

unsigned long long int c = 200ull;

Таблица 2.1 - Суффиксы целочисленных констант

Вещественные константы записываются в десятичной или шестнадцатеричной системах исчисления. Позиция запятой указывается точкой, экспонента указывается после латинской буквы e (или E) (переменные x и y в примере). Суффиксы вещественных констант:

  • f или F - тип float,

  • l или L - тип long double.

Примеры:

float x = 10.5f;

long double y = 12.5e200L;

Символьные константы записываются в одинарных кавычках (переменная ch из примера). Кроме того, в языке С присутствуют специальные символы:

‘\’’ - одинарная кавычка,

‘\”’ - двойная кавычка,

‘\\’ - обратный слеш,

‘\?’ - знак вопроса,

‘\a’ - звуковой сигнал,

‘\b’ - пробел,

‘\f’ - прокрутка страницы,

‘\n’ - перевод строки,

‘\r’ - возврат каретки,

‘\t’ - горизонтальная табуляция,

‘\v’ - вертикальная табуляция.

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

int a=5, b=4, c=a+b-3;

В языке С можно также создавать переменные, имеющие константное значение (их значение нельзя изменить). Объявление таких «переменных» имеет следующий синтаксис:

const тип имя1=значение1[, имя2=значение2[,...]];

Объявление константной переменной имеет тот же синтаксис, что и объявление обычной переменной, только начинается с ключевого слова const и при объявлении такой переменной инициализация обязательна. Примеры:

const unsigned int x=80, y=25;

const double pi=3.1415;

К операторам объявлений следует также отнести и оператор создания пользовательских типов данных. Синтаксис оператора имеет следующий вид:

typedef имя_старого_типа имя_нового_типа;

Описание нового типа начинается с ключевого слова typedef, указывается имя старого типа данных, а затем имя нового типа данных. Пример:

typedef unsigned int word;

Тип word - целое незнаковое число. Объявлять переменные пользовательского типа можно только после его описания.

Согласно стандарту С99 операторы объявления переменных могут располагаться в любом месте программы, если это не противоречит синтаксису языка С. В более ранней версии (стандарт С89), операторы объявления могли располагаться только в начале тела функции.

Оператор объявления пользовательских типов также может располагаться в любом месте программы, но рекомендуется располагать его сразу после подключения библиотек (до описания функции main).

В языке С присутствует оператор определения размера значения определенного типа sizeof, который возвращает значение целого типа равное числу байт занимаемых значением в памяти.

4.4 Оператор вызова функций

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

В языке С оператор вызова функций, в принципе, выделен условно (во многих книгах по языку C он вообще не рассматривается как оператор). Оператор вызова функций просто определяет синтаксис описания вызова любой функции в тексте программы:

имя_функции(параметры);

Сначала указывается имя функции, затем, в круглых скобках, указываются параметры функции. Завершается оператор вызова функций точкой с запятой.

ПРИМЕЧАНИЕ: В языке С, если функция не имеет параметров, то пустые круглые скобки все равно должны присутствовать.

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

sqrt(pow(x,3.0));

В этом примере осуществляется вызов функции sqrt, вычисляющий квадратный корень, в качестве параметра которой осуществляется вызов функции pow, выполняющей возведение переменной x в куб. Параметрами функции pow являются число, которое необходимо возвести в степень (переменная x), и значение степени (вещественная константа 3.0). Константа 3.0 является вещественной, так как функция pow работает с вещественными параметрами. Функции sqrt и pow описаны в библиотеке математических функций языка С math.h. Следует обратить внимание на то, что точка с запятой ставится только в конце выражения.

Рассмотрим функции форматированного ввода и вывода языка С из библиотеки stdio.h: scanf и printf.

Функция scanf предназначена для форматного ввода данных и имеет следующий заголовок:

int scanf(const char * restrict format [,addresses,…]);

Первым параметром функции является строка формата ввода (строки в языке С заключаются в двойные кавычки). В данной строке содержится перечень спецификаторов типов данных, ввод которых ожидается, в определенном порядке. Далее указываются адреса, по которым будут располагаться вводимые данные. Функция возвращает фактическое число считанных и записанных значений или -1 - если достигнут конец потока ввода. Пример ввода двух переменных переменные целого типа и переменной вещественного типа:

int a,b;

double x;

scanf(“%d %d %lf”,&a,&b,&x);

В данном примере сначала вводятся два значения целого типа в переменные a и b, а затем вещественного типа в переменную x. %d указывает на то, что вводимые данные являются целыми числами, %lf - вещественным типа double. Перед именами переменных в параметрах функции scanf необходимо указывать символ ‘&’, который в языке С, в данном случае, обозначает операцию взятия адреса переменной.

Сочетание %d или любое другое называется спецификатором типа. В общем случае он имеет следующий формат:

% [*] [WIDTH] [hh|h|l|ll|L] символ_типа

* - указывает запрещенные (символы разделители) для ввода символы. Указанные здесь символы разделяют разные поля ввода, а сами игнорируются.

WIDTH - задает максимальную длину поля ввода.

L, ll, l, hh, h - преобразователи типа. Символы hh служат для преобразования целочисленных типов к типам char или unsigned char. Символ h служит для преобразования целочисленных типов к типам short int или unsigned short int. Символ l служит для преобразования к типу long int всех целочисленных типов и к типу double всех вещественных типов. Символы ll служат для преобразования целочисленных типов к типам long long int или unsigned long long int. Символ L служит для приведения к типу long double всех вещественных типов.

В таблице 2.2 приведен перечень типов и соответствующие им спецификаторы.

Таблица 2.2 - Спецификаторы типов функций форматированного ввода и вывода

Символ

Тип

d

Знаковое целое число в десятичной системе исчисления

o

Знаковое целое число в восьмеричной системе исчисления

x

Знаковое целое число в шестнадцатеричной системе исчисления

i

Знаковое целое число в любой из ранее перечисленных систем исчисления

u

Незнаковое целое число в десятичной системе исчисления

f, e или g

Вещественное число, бесконечность или не число (NAN)

a

Вещественное число в шестнадцатеричной системе исчисления

c

Символ

s

Строка

p

Указатель

n

Блокирование (пропуск) ввода

%

Ввод символа процента.

Функция printf предназначена для форматного вывода данных и имеет следующий заголовок:

int printf(const char * restrict format [,variables,…]);

Первым параметром функции является строка формата вывода. В данной строке содержится перечень спецификаторов типов данных, вывод которых ожидается, в определенном порядке. Далее указываются переменные, значения которых будут выводиться. Функция возвращает фактическое число выведенных символов. Пример вывода двух переменных целого типа и переменной вещественного типа:

int a,b;

double x;

printf(“%d %d %5.2lf”,a,b,x);

В данном примере сначала выводятся два значения целого типа (переменные a и b), а затем вещественного типа (переменная x).

Как и в функции scanf, сочетание %d или любое другое называется спецификатором типа. В общем случае он имеет следующий формат:

% [flag] [WIDTH][.PREC] [h|hh|l|ll|L] символ_типа

flag - для чисел указывает на необходимость вывода знака ‘+’ для положительных чисел, для строк управляет форматированием - по левому или по правому краю.

WIDTH - задает длину поля.

.PREC - задает количество символов после запятой для вещественных чисел и минимальное количество знаков для целых чисел.

h, hh, l, ll, L - аналогично функции scanf.

Перечень типов и соответствующих им спецификаторов приведен в таблице 2.2.

Пример: даны два вещественных числа, организовать их ввод в формате: (х,у) и вывести на экран в формате Х = значение, У = значение.

#include<stdio.h>

int main(int argc, char *argv[])

{

double x,y;

scanf(“(%lf,%lf)”,&x,&y);

printf(“X = %5.2lf, Y = %5.2lf”,x,y);

return 0;

}

Пример: даны два целых числа, организовать их ввод в формате a*b= и вывести значение их произведения.

#include<stdio.h>

int main(int argc, char *argv[])

{

int a,b;

scanf(“%d*%d=”,&a,&b);

printf(“%d\n”,a*b);

return 0;

}

В таблице 2.3 приведены основные функции для математических вычислений из библиотеки math.h.

Таблица 2.3 - Основные функции для математических вычислений

Заголовок

Описание

double fabs(double x)

Вычисляет модуль вещественного числа

double pow(double x, double y)

Вычисляет возведение числа x в степень y

double exp(double x)

Вычисляет число e в степени x

double sqrt(double x)

Вычисляет квадратный корень числа

double log(double x)

Вычисляет натуральный логарифм числа

double log10(double x)

Вычисляет логарифм по основанию 10

double log2(double x)

Вычисляет логарифм по основанию 2

double sin(double x)

Вычисляет синус угла заданного в радианах

double cos(double x)

Вычисляет косинус угла заданного в радианах

double tan(double x)

Вычисляет тангенс угла заданного в радианах

double sinh(double x)

Вычисляет гиперболический синус

double cosh(double x)

Вычисляет гиперболический косинус

double tanh(double x)

Вычисляет гиперболический тангенс

double asin(double x)

Вычисляет арксинус

double acos(double x)

Вычисляет арккосинус

double atan(double x)

Вычисляет арктангенс

double asinh(double x)

Вычисляет гиперболический арксинус

double acosh(double x)

Вычисляет гиперболический арккосинус

double atanh(double x)

Вычисляет гиперболический арктангенс

double floor(double x)

Вычисляет большее ближайшее целое

double round(double x)

Округляет до ближайшего целого

double trunc(double x)

Отбрасывает дробную часть

Все приведенные функции оперируют значениями вещественного типа двойной точности (double). В библиотеке math.h содержатся и функции, соответствующие перечисленным в таблице 2.3 функциям, но для вещественных чисел одинарной (float) и повышенной (long double) точности. Данные функции имеют то же имя, но с добавлением в конце символа ‘f’ - для вещественных чисел одинарной точности, ‘l’ - для вещественных чисел повышенной точности. Например, имя функции вычисления синуса для значения типа float будет sinf, а для значения типа long double - sinl.

В таблице 2.3 приведены далеко не все функции, содержащиеся в библиотеке math.h. С полным набором функций можно ознакомиться, обратившись к соответствующему разделу помощи среды разработки.

4.5 Операторы присвоения

В большинстве языков программирования высокого уровня присутствует оператор присвоения. Назначением этого оператора является присвоение значений одних объектов программы другим объектам. Синтаксис оператора присвоения языка С имеет вид:

LValue = RValue;

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

RValue - объект, значение которого будет присвоено. В качестве такого объекта в языке С может выступать:

  • переменная,

  • константа,

  • оператор вызова функции,

  • математическое или логическое выражение.

Сам оператор присвоения в языке С обозначается знаком «равно».

Примеры присвоений:

int a, b, c;

double x, y;

a = 5; b = 4; c = a + b;

x = 5.0; y = exp(x);

В первой строке осуществляется объявление трех целочисленных переменных (a, b и c). Во второй строке осуществляется объявление двух вещественных переменных (x и y). В третьей строке переменным a и b присваиваются значения констант 5 и 4 соответственно, а переменной c - значение суммы переменных a и b. В четвертой строке осуществляется присвоение переменной x константы 5.0, а переменной y значения функции exp с переменной x в качестве параметра.

Помимо основного оператора присвоения в языке С присутствуют усовершенствованные операторы присвоения, которые имеют следующий синтаксис:

LValue X= RValue;

где X - символ, означающий определенную математическую операцию из набора: + - * / % ^ & | << >>. Каждая из этих операций будет рассмотрена в следующем подразделе.

Использование усовершенствованного оператора присвоения аналогично записи:

LValue = LValue X RValue;

Пример:

a += b; ≡ a = a + b;

4.6 Математические и логические операторы

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

4.6.1 математические

В языке С, как и в некоторых других языках программирования, математические операции делятся на две группы:

  • математические операции для вещественных и целочисленных вычислений;

  • математические операции только для целочисленных вычислений.

К математическим операциям для вещественных и целочисленных вычислений языка С относят обычные арифметические операции:

  • сложения (+),

  • вычитания (),

  • умножения (*),

  • деления (/).

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

Таблица 2.4 - Соответствие типа результата от типов операндов

Тип первого операнда

Тип второго операнда

Тип результата

Целый

Целый

Целый

Целый

Вещественный

Вещественный

Вещественный

Целый

Вещественный

Вещественный

Вещественный

Вещественный

Из-за особенности языка С в преобразовании типов в математических операциях возможны ситуации, когда начинающие изучать язык С допускают множество ошибок при построении выражений. Рассмотрим несколько примеров. Дан фрагмент программы:

int a,b; double c;

a = 10; b = 4; c = a / b;

В результате в переменной c будет содержаться значение 2. Так как операнды целочисленные, то и результат выполнения операции будет целочисленным.

Дан фрагмент программы:

double x = 1 / 3;

В переменной x будет содержаться значение 0. Это обусловлено тем, что, как и в предыдущем примере, операнды являются целыми числами и, поэтому, осуществляется выполнение целочисленной операции деления. Для исключения этой ошибки, предыдущий фрагмент программы следует записать в виде:

double x = 1.0 / 3;

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

Вторую группу математических операций составляют операции, предназначенные только для целочисленных вычислений. К ним относятся:

  • операция взятия остатка от деления,

  • побитовые операции,

  • операции сдвигов,

  • операции инкремента и декремента.

Операция взятия остатка от деления является бинарной операцией и в языке С обозначается символом процента (%). Пример вычисления:

int a = 10, b = 3, c;

c = a % b;

После выполнения данного фрагмента переменная c будет содержать значение 1.

Побитовые операции языка С представлены тремя бинарными и одной унарной операцией. К бинарным побитовым операциям относятся операции «И», «ИЛИ» и «Исключающее ИЛИ». Операция «И» обозначается символом амперсанда (&) и действует по принципу: бит результата равен единице, если оба соответствующих бита операндов равны единице. Операция «ИЛИ» обозначается символом вертикальной черты (|) и действует по принципу: бит результата равен единице, если хотя бы один из соответствующих битов операндов равен единице. Операция «Исключающее ИЛИ» обозначается символом диакритический знак (^) и действует по принципу: бит результата равен единице, если один и только один из соответствующих битов операндов равен единице. В таблице 2.5 приведены результаты побитовых операций для всех сочетаний операндов.

Таблица 2.5 - Результат побитовых операций

Первый операнд

Второй операнд

И

ИЛИ

Исключающее ИЛИ

0

0

0

0

0

1

0

0

1

1

0

1

0

1

1

1

1

1

1

0

Унарной побитовой операцией является операция отрицания, обозначаемая символом тильды (~) и действующей по принципу: бит результата равен единице, если соответствующий ему бит операнда равен нулю и наоборот. Пример:

unsigned char a = 10, b; //a: 00001010 = 10

b = ~a; //b: 11110101 = 245

Операции сдвига осуществляют побитовый сдвиг целого значения, указанного в первом операнде, вправо (символ >>) или влево (символ <<) на указанное во втором операнде целое число бит. Пример:

unsigned char a = 10, b, c; //a: 00001010 = 10

b = a << 2; //b: 00101000 = 40

c = a >> 1; //c: 00000101 = 5

Из примера можно увидеть, что сдвиг целого значения влево на n бит равносилен умножению этого значения на 2n, а сдвиг вправо - делению на 2n. Данное свойство часто рекомендуется использовать для оптимизации скорости выполнения программы, так как операции сдвигов выполняются быстрее операции умножения.

Операции инкремента (знак ++) и декремента (знак --) являются унарными и осуществляют увеличение и уменьшение целого значения на единицу соответственно. Операции инкремента и декремента могут указываться до или после операнда. Если операция указывается до операнда (пред- инкремент, декремент), то сначала осуществляется изменение значения операнда, а затем обращение к значению этого операнда. Если же операция указывается после операнда (пост- инкремент, декремент), то сначала осуществляется обращение к значению операнда, а затем изменение его значения. Рассмотрим пример пред- и пост- инкремента:

int a = 10, b, c;

b = ++a; //пред- инкремент b == 11

c = a++; //пост- инкремент с == 11

Во второй строке сначала значение переменной a увеличивается на единицу, а затем значение переменной a присваивается переменной b. В третьей строке, наоборот, сначала значение переменной a присваивается переменной c, а затем значение переменной a увеличивается на единицу. Таким образом, после выполнения данного фрагмента программы в переменных b и c будет содержаться значение 11, а в переменной a - значение 12.

ПРИМЕЧАНИЕ: Изначально операции инкремента и декремента считались исключительно целочисленными операциями, однако в современных языках программирования (в том числе и языке С стандарта С99) данные операции могут использоваться и для вещественных значений. Пример:

double x = 12.5;

x++;

printf(“%lf\n”,x);

В результате выполнения данного фрагмента программы на экран будет выведено значение 13.5.

4.6.2 логические

В языках программирования операции отношения (сравнения) являются бинарными операциями, осуществляющими сравнение двух операндов и возвращающие результат сравнения в виде логического значения. Ранее говорилось, что язык С появился и развивался как язык системного программирования, поэтому в данном языке программирования принято логические значения ИСТИНА и ЛОЖЬ интерпретировать посредством целочисленных значений: 0 - ЛОЖЬ, 1 - ИСТИНА. Хотя, в стандарте С99 был введен специальный логический тип _Bool, но широкого применения он так и не нашел, поэтому в большинстве компиляторов все операции сравнения возвращают значение 1, если результат операции ИСТИНА и 0 в противном случае. Все операции сравнения, доступные в языке С, приведены в таблице 2.6.

ПРИМЕЧАНИЕ: Начинающим изучать язык С следует обратить внимание на обозначение операций «равно» и «не равно», так как оно отличается от традиционного математического обозначения этих операций, что, в свою очередь, вызывает появление ошибок. Так, например, для начинающих характерно ошибочно использовать оператор присвоения вместо операции «равно». Такая ошибка может не отслеживаться компилятором и вообще ее трудно обнаружить.

Таблица 2.6 - Операции сравнения языка С

Обозначение

Название

>

Больше

<

Меньше

>=

Больше или равно

<=

Меньше или равно

==

Равно

!=

Не равно

Например, при компиляции следующего фрагмента программы компилятор вообще не выдаст никакого сообщения об ошибке или предупреждения:

int main(int argc, char *argv[])

{

if(argc = 5) printf("Пять параметров!");

else printf("Не пять параметров!");

return 0;

}

Рассмотрим несколько примеров использования операций сравнения:

int a=5, b=4, c=10, x, y;

x = a > b; //x == 1

y = c == a; //y == 0

После выполнения данного фрагмента кода переменная x примет значение 1, а переменная y - 0.

Логические операции - унарные или бинарные операции, осуществляющие действия над логическими значениями и возвращающие логическое значение. Набор логических операций у разных языков программирования может быть различен. Логические операции, доступные в языке С, представлены в таблице 2.7.

Таблица 2.7 - Логические операции языка С

ПРИМЕЧАНИЕ: Следует обратить внимание на то, что логические операции «И» и «ИЛИ» в языке С обозначаются теми же символами, что и побитовые операции, только удвоенными. Это следует учитывать, так как обозначения этих операций схожи, а механизм действия в корне отличается.

Примеры логических операций:

int a=1, b=0, c, d; //a - ИСТИНА, b - ЛОЖЬ

c = a || b; //c == 1

d = !b && a; //d == 1

В результате выполнения приведенного фрагмента программы, обе переменные (c и d) примут значение 1, что соответствует значению логической истины в языке С.

4.7 Построение математических и логических выражений

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

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

Таблица 2.8 - Приоритеты операций языка С

Обозначение

Наименование

++, --

Операции пост- инкремента и декремента

( )

Вызов функции, группировка операций

[ ]

Обращение к элементу массива

>

Обращение к полю структуры или объединения через указатель

.

Обращение к полю структуры или объединения

++, --

Операции пред- инкремента и декремента

!

Логическое «НЕ»

~

Бинарное отрицание (инверсия)

+, -

Унарные плюс и минус

&

Операция взятия адреса

*

Разыменование указателя

sizeof

Оператор определения размера

(type)

Оператор преобразования типа

*

Умножение

/

Деление

%

Взятие остатка от деления

+

Сложение

-

Вычитание

<<, >>

Побитовые сдвиги влево и вправо

<, <=, >, >=

Операции сравнения

==, !=

Операции сравнения

&

Побитовое «И»

^

Побитовое «Исключающее ИЛИ»

|

Побитовое «ИЛИ»

&&

Логическое «И»

||

Логическое «ИЛИ»

?:

Условная операция

=

Оператор простого присвоения

*=, /=, %=

Усовершенствованные операторы присвоения

, +=, -=,

Усовершенствованные операторы присвоения

<<=, >>=, &=, ^=, |=

Усовершенствованные операторы присвоения

,

Запятая

ПРИМЕЧАНИЕ: Некоторые из приведенных в таблице 2.8 операторов или операций будут рассмотрены в последующих главах.

Как и в большинстве языков программирования, в языке С в основном не указывается порядок вычисления операндов в операциях (исключениями являются &&, ||, ?: и ‘,’). Например, в следующем операторе первой может вызываться как функция sin, так и функция cos:

y = sin(x) + cos(x);

Если в одной из функций, sin или cos, модифицируется переменная, от которой зависит работа другой функции, то итоговое значение y будет зависеть от порядка вызова. Чтобы гарантировать определенную последовательность операций следует поместить промежуточный результат в какую-нибудь временную переменную.

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

printf(“%d %lf\n”, ++n, pow(2.0,n));

Результат будет зависеть от того, получает ли n приращение до или после вызова функции pow. Чтобы решить проблему достаточно записать так:

n++;

printf(“%d %lf\n”, n, pow(2.0,n));

Вызовы функций, вложенные операторы присваивания, операции инкрементирования и декрементирования имеют «побочные эффекты» - в ходе вычисления выражений могут непредсказуемо модифицироваться некоторые переменные. В любом выражении, в котором возможны побочные эффекты, присутствует зависимость от порядка использования переменных в вычислениях. Одна из типичных ситуаций представлена следующим оператором:

a[i] = i++;

В данном случае неизвестно, какое значение индекса i используется для обращения к массиву - старое или новое. Компиляторы могут интерпретировать этот оператор по-разному и выдавать различные результаты в зависимости от своей интерпретации. В стандарте такие вопросы оставлены открытыми на усмотрение разработчиков компилятора. Это обусловлено тем, что наилучший порядок операций сильно зависит от аппаратно-системной архитектуры.

Также следует учитывать некоторые особенности интерпретации выражений компиляторами языка С. Например, необходимо определить, принадлежит ли значение заданное в вещественной переменной x, промежутку (2,5). Логическое выражение может быть записано в виде:

int flag = 2.0 < x < 5.0;

С математической точки зрения выражение записано корректно. Но компилятором языка С оно будет интерпретироваться следующим образом: сначала будет проведено сравнение значений 2.0 и переменной x, а затем результат сравнения будет сравнен со значением 5.0. Таким образом, приведенное выражение при любом значении переменной x будет выдавать значение 1 (логическая ИСТИНА), что говорит о некорректности построенного выражения. Правильное выражение будет иметь вид:

int flag = (2.0 < x) && (x < 5.0);

Поэтому при построении логических выражений следует придерживаться правила: операции сравнения должны разделяться логическими операциями.

4.8 Оператор приведения типов и его использование

В подразделе 2.6.1 при рассмотрении математических операций уже говорилось о том, что в языке С используется автоматическое приведение (преобразование) типов при различных типах операндов математических операций. Схема автоматического приведения типа:

  • Во-первых, если какой-либо из операндов имеет тип long double, то и другой приводится к long double.

  • Иначе, если какой-либо из операндов имеет тип double, то и другой приводится к double.

  • Иначе, если какой-либо из операндов имеет тип float, то и другой приводится к float.

  • Иначе, для обоих операндов выполняется расширение целого типа; затем, если один из операндов имеет тип unsigned long int, то другой преобразуется в unsigned long int.

  • Иначе, если один из операндов имеет тип long int, а другой - unsigned int, то результат зависит от того, представляет ли long int все значения unsigned int; если это так, то операнд типа unsigned int приводится к типу long int; если нет, то оба операнда преобразуются в unsigned long int.

  • Иначе, если один из операндов имеет тип long int, то и другой приводится к long int.

  • Иначе, оба операнда имеют тип int.

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

int a = 15, b = 2; double r = 0.0;

r = a / b;

В результате в переменной r будет содержаться значение 7.0, а не 7.5. Это обусловлено тем, что будет проводиться операция целочисленного деления, так как операнды являются целочисленными переменными. Исправить эту ситуацию можно, например, введением фиктивного действия: умножением на вещественную единицу:

r = 1.0 * a / b;

В таком случае сначала будет произведено вещественное умножение константы 1.0 на значение переменной a, а затем уже вещественное деление результата умножения на целое число, содержащееся в переменной b. В результате в переменной r будет содержаться значение 7.5. Для предотвращения введения фиктивного действия можно использовать оператор приведения типа, который имеет следующий синтаксис:

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

Здесь тип - это тип, к которому приводится значение выражения указанного справа. Следует учитывать, что если выражение является сложным (содержит одну и более операцию), то оно должно быть заключено в круглые скобки.

Рассматриваемое выражение, в таком случае, примет вид:

r = (double)a / b;

Здесь указывается, что значение переменной a приводится к вещественному типу double и делится на целое значение переменной b. Деление в данном случае вещественное, и в результате переменная r примет значение 7.5. Если же выражение записать в виде:

r = (double) (a / b);

то в результате в переменной r будет содержаться значение 7.0. Это обусловлено тем, что в данном случае сначала выполнится целочисленное деление, а затем результат деления будет преобразован в вещественное значение.

Преобразования типов осуществляются по следующим правилам:

  • Расширение целочисленных типов: если тип int позволяет представить все значения исходного типа операнда, то операнд приводится к типу int, в противном случае - к unsigned int.

  • Преобразование вещественных чисел в целые: дробная часть числа отбрасывается; если полученное при этом значение нельзя представить величиной заданного целочисленного типа, то результат не определен.

  • Преобразование знакового целого числа к незнаковому: ищется конгруэнтное (т.е. имеющее то же двоичное представление) наименьшее неотрицательное значение, а затем получается остаток от деления его на UMAX, где UMAX - наибольшее число в данном типе без знака. При использовании двоичного представления в дополнительном коде для этого либо отбрасываются лишние левые разряды, если двоичное представление беззнакового типа уступает в длине исходному типу, либо недостающие старшие разряды заполняются нулями (в числах без знака) или значением знака (в числах со знаком), если тип без знака шире исходного.

  • Преобразование незнакового целого числа к знаковому: его значение не меняется, если оно представимо в этом новом типе; в противном случае результат зависит от реализации.

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

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

4.9 Условная операция

В языке С присутствует так называемая условная операция, которая имеет следующий синтаксис:

условие ? выражение №1 : выражение №2;

Принцип выполнения условной операции: вычисляется условие, если оно истинно (имеет не нулевое значение), то вычисляется и возвращается значение выражения №1, в противном случае - выражения №2. Предполагается, что условие является выражением, значение которого можно интерпретировать как логическое.

Например, необходимо ввести с клавиатуры два вещественных значения и вывести на экран максимальное из этих значений:

#include <stdio.h>

int main(int argc, char *argv[])

{

double x,y;

printf(“Введите значения: ”);

scanf(“%lf %lf”,&x,&y);

double max = (x > y) ? x : y;

printf(“Максимальное значение: %lf\n“,max);

return 0;

}

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

Например, необходимо ввести с клавиатуры три вещественных значения и вывести на экран максимальное из этих значений:

#include <stdio.h>

int main(int argc, char *argv[])

{

double x, y, z;

printf(“Введите значения: ”);

scanf(“%lf %lf %lf”,&x,&y,&z);

double max = (x > y) ?

((x > z) ? x : z):

((y > z) ? y : z);

printf(“Максимальное значение: %lf\n“,max);

return 0;

}

4.10 Примеры

Задача 2.1: Вещественное число вводится с клавиатуры. Возвести число в четвертую степень, используя только две операции умножения.

Программа для задачи

#include <stdio.h> //Библиотека ввода и вывода

int main(int argc, char *argv[])

{

double a; //Объявление переменной

printf("Введите значение: "); //Приглашение к вводу

scanf("%lf",&a); //Ввод вещественного числа

a *= (a *=a); //Вычисление

printf("Значение: %lf\n",a); //Вывод результата на экран

return 0;

}

Задача 2.2: Квадратное уравнение вида задается коэффициентами A, B и C. Определить какое количество корней имеет данное уравнение.

Программа для задачи

#include <stdio.h> //Библиотека ввода и вывода

int main(int argc, char *argv[])

{

//Объявление трех вещественных переменных

double a,b,c;

//Приглашение к вводу коэффициентов уравнения

printf("Введите коэффициенты A, B и С: ");

//Ввод трех вещественных значений

scanf("%lf %lf %lf",&a,&b,&c);

//Вычисление дискриминанта

double d = b*b-4*a*c;

/* --------------------------------------------------

Используя условную операцию, определяем количество

корней уравнения:

меньше нуля - нет корней

больше нуля - два корня

равен нулю - один корень

-------------------------------------------------- */

int n = (d < 0.0)?0:(d > 0.0)?2:1;

//Вывод результата на экран

printf("Количество корней: %d\n",n);

return 0;

}

Задача 2.3: Вычислить значение функции: . Значение аргумента x вводит пользователь. Результат вывести с точностью то третьего знака после запятой.

Программа для задачи

#include <stdio.h> //Библиотека ввода и вывода

#include <math.h> //Библиотека математических функций

int main(int argc, char *argv[])

{

//Объявление вещественной переменной (аргумент)

double x;

//Приглашение к вводу значения

printf("Введите значение аргумента: ");

//Ввод вещественного значения

scanf("%lf",&x);

//Определение значения функции

double y = (x<0.0)?x*x: //если x < 0

(x<4.0)?0.5*x: //если 0 ≤ x < 4

sqrt(x); //если x ≥ 4

//Вывод результата на экран с заданной точностью

printf("Значение: %.3lf\n",y);

return 0;

}

Задача 2.4: Координаты клетки шахматной доски задаются в виде двух символов. Первый символ - буква латинского алфавита от A до H. Второй символ - цифра от 1 до 8. Заданы координаты двух клеток. Определить: возможен ли переход конем из одной клетки в другую за один ход. При вводе данных символы могут вводиться как строчными, так и заглавными буквами.

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

Программа для задачи

#include <stdio.h> //Библиотека ввода и вывода

#include <ctype.h> //Библиотека символьных функций

#include <stdlib.h> //Библиотека стандартных функций

int main(int argc, char *argv[])

{

//Объявление переменных символьного типа для

//хранения координат клеток

char x1,y1,x2,y2;

//Приглашение к вводу координат первой клетки

printf("Первая клетка: ");

//Ввод двух символов

scanf("%c%c",&x1,&y1);

// Приглашение к вводу координат второй клетки

printf("Вторая клетка: ");

//Ввод двух символов с упреждающим вводом символа

//«перевод строки»

scanf("\n%c%c",&x2,&y2);

//Преобразование символьных координат к нижнему регистру

x1 = tolower(x1); x2 = tolower(x2);

/* ---------------------------------------------------

Определение возможности хода конем из одной клетки

в другую путем вычисления произведения изменения

координат клеток взятых по модулю. Полученное

значение должно равняться двум. Результат логического

выражения записывается в целочисленную переменную.

------------------------------------------------------ */

int f = (abs(x1-x2)*abs(y1-y2) == 2);

//Вывод результата на экран. Используется условная

//операция как аргумент функции printf

printf("%s\n",(f)?"Yes!":"No!");

return 0;

}

Задача 2.5: Тело бросают с высоты H0 (в метрах) под углом  (в градусах) к горизонту с начальной скоростью V0 (в метрах в секунду). Определить: максимальную высоту, на которую поднимется тело; время и дальность полета.

Решив задачу, используя законы кинематики, получим следующие выражения:

 максимальная высота: ,

 время всего полета: ,

 дальность полета: ,

где H0 - высота, с которой бросили тело;

V0 - скорость, с которой бросили тело;

 - угол к горизонту, под которым бросили тело;

g - ускорение свободного падения (9.81 м/с2).

Программа для задачи

#include <stdio.h>//Библиотека ввода и вывода

#include <math.h> //Библиотека математических функций

int main(int argc, char *argv[])

{

/* Объявление констант: pi и ускорения

pi - число pi

g - ускорение свободного падения */

const double pi = 3.1415, g = 9.81;

/* Объявление переменных

H0 - начальная высота

V0 - начальная скорость

alpha - угол к горизонту */

double H0, V0, alpha;

//Ввод исходных данных к задаче

printf("Введите высоту: "); scanf("%lf",&H0);

printf("Введите скорость: "); scanf("%lf",&V0);

printf("Введите угол: "); scanf("%lf",&alpha);

//Перевод угла из градусов в радианы

alpha *= pi/180;

/* Вычисление вертикальной составляющей скорости

(для оптимизации расчетов) */

double Vy = V0*sin(alpha);

//Вычисление максимальной высоты подъема

double H = H0 + pow(Vy,2.0)/(2.0*g);

//Вычисление времени всего полета

double T = (Vy+sqrt(Vy*Vy+g*H0))/g;

//Вычисление дальности полета

double S = V0*T*cos(alpha);

//Вывод полученных результатов

printf("Максимальная высота: %.2lf\n",H);

printf("Общее время полета : %.2lf\n",T);

printf("Дальность полета: %.2lf\n",S);

return 0;}