Главная > Разное > Принципы программирования в машинной графике
<< Предыдущий параграф
Следующий параграф >>
<< Предыдущий параграф Следующий параграф >>
Макеты страниц

Приложение. КРАТКОЕ ВВЕДЕНИЕ В ЯЗЫК СИ

Это приложение нельзя рассматривать как полное описание языка Си. Оно предназначено для читателей, уже знакомых с каким-либо другим современным языком программирования, но нуждающихся в кратком обзоре элементов языка Си, применяемых в данной книге. Читателям, которые собираются активно работать с языком Си, автор настоятельно рекомендует обратиться к специальному учебнику, например: Керниган Б., Ритчи Д. Язык программирования Си (Пер. с англ. — М.: финансы и статистика, 1985).

А.1. ОСНОВНЫЕ ТИПЫ ДАННЫХ

Перед использованием любых переменных сначала необходимо определить их типы, или, говоря профессиональным языком, объявить их

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

ожидаемого. Типы “Boolean” (“Булевский”) и “logical” (“логический”) в языке Си отсутствуют. Целочисленное значение 0 воспринимается как false (“ложь”), а значение 1 (или любое другое ненулевое значение) — как true (“истина”).

А.2. НЕКОТОРЫЕ ОПЕРАТОРЫ

Рассмотрим фрагмент программы

Смысл этих трех условных операторов интуитивно ясен. Заметим, что после ключевого слова в условном операторе всегда следует выражение в круглых скобках. Часть условного оператора else (“иначе”) является необязательной.

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

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

является составным оператором. Фигурные скобки обязательно применяются в условном операторе если при удовлетворении условия необходимо выполнить более одного оператора. Например, если бы было записано

то оператор

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

присваивает значение 10 обеим переменным тип.

Примитивный цикл можно образовать с помощью условного оператора и оператора безусловного перехода как в примере

Здесь сокращенная форма записи операторов соответственно. Последние две строки не надо воспринимать как образец хорошо структурированного программирования. Они приведены здесь лишь для объяснения двух последующих новых концепций языка.

Первая из них — оператор цикла while:

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

Другой важной конструкцией является оператор цикла

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

Если имеет значение 0 (или меньше, чем 0), сумма 5 также будет равна 0, поскольку во всех трех случаях проверка на завершение цикла выполняется в начале цикла. Но такого не произойдет при использовании оператора цикла как в примере:

Здесь проверка на завершение цикла выполняется в конце, как и предполагает запись. Поэтому значение присваивается переменной , только когда положительно. Если будет равно 0 (или меньше 0), то переменная получит значение 1, так как внутренняя часть этого цикла выполняется, по крайней мере, один раз.

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

применяется при необходимости безусловного завершения выполнения ближайшего вложенного оператора оператора или оператора Так, для фрагмента программы

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

или

Для немедленного перехода на проверку условия завершения цикла можно использовать оператор продолжения

Например, оператор

можно заменить на

А.3. ОПЕРАТОРЫ И ВЫРАЖЕНИЯ

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

Запись

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

эквивалентно

Однако есть операции, которые выполняются в порядке справа налево. Примером может служить операция присваивания следовательно, оператор присваивания

даст такой же эффект, как

Заметим, что символ должен использоваться для проверки равенства двух величин, для этой цели служит операция

Перечислим теперь все операции языка смысл некоторых из них ясен немедленно, другие будут пояснены позже. Операции перечислены в порядке уменьшения приоритета, но для группы операций, расположенных между двумя соседними горизонтальными линиями, приоритеты одинаковы; в таблице также указан порядок выполнения операций внутри группы (слева направо и СПРАВА НАЛЕВО) при отсутствии скобок.

(см. скан)

(см. скан)

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

Это выражение с присваиванием, которое увеличивает значение переменной на 1. Менее очевидно, но это выражение также имеет и значение, а именно — новое значение переменной и Следовательно, имеет смысл запись

которая, в частности, может быть заменена на

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

означает приращение переменной и использование ее нового значения;

означает использование старого значения переменной и затем приращение этой переменной. Так, после выполнения операций

будем иметь

Выражение присваивания и знак точки с запятой именно в этом порядке, образуют оператор присваивания. Так,

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

Он работает аналогично строке

(запись означает означает и так далее).

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

вычисляется первое выражение означающее условие. Затем вычисляется либо либо в зависимости от того, будет ли значение не нулевое или нуль, соответственно. Напомним, что в языке Си значение true (“истина”) обозначается через через 0, так что вместо

можно записать

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

которое следует читать как

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

может произойти деление на нуль.

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

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

Здесь использованы операции “сдвиг влево” и “побитовая операция Значения переменных к можно представить в двоичном формате

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

будет равно 3 типа integer (“целый”).

Если обе переменные имеют тип то результат деления иметь целое значение усеченного частного. Так, 8/3 имеет значение 2 типа даже в том контексте, где ожидается тип как в выражении где х имеет тип вещественной переменной. Если, по крайней мере, один из операндов имеет тип то частное а также имеет тип float и не усекается.

Константы с плавающей точкой содержат десятичную точку или букву (или Е); целые константы их не содержат. Так,

Операция деления по модулю может применяться только для целочисленных операндов:

В последнем примере оператор преобразования типа имеет приоритет перед операцией деления по модулю

Если значение с плавающей точкой присваивается целочисленной переменной, то будет иметь место усечение. Оператор

присваивает значение 3 переменной и Если переменная х имеет тип то при выполнении следующего оператора переменной х будет присвоено значение 7 (превращенное в число с плавающей точкой 7.0)

А.4. ЛЕКСИЧЕСКИЕ ВОПРОСЫ И СТРУКТУРА ПРОГРАММЫ

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

компилятором игнорируется. Так обозначаются комментарии, предназначенные для человека.

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

Идентификаторы могут также применяться для обозначения констант, например

После такой “препроцессорной управляющей строки” можно будет использовать идентификатор просто как иное обозначение для числа 1000. Это только первый пример макрокоманды. Вот более интересная макрокоманда, названная и представляемая строкой

которая означает, что в любом месте программы любая встречающаяся строка вида

автоматически заменяется на строку

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

Существуют управляющие строки для включения файлов. Часто применяется строка

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

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

Стандартные функции будут описаны в параграфе

А.5. МАССИВЫ И УКАЗАТЕЛИ

После объявления массива

доступны следующие пять переменных типа

Можно также записать где индекс любое целочисленное выражение, значение которого не может быть ни отрицательным, ни больше 4. Индексы всегда начинают счет с 0. Между скобками в объявлении массива могут появиться только целые константы. В объявлениях вместо чисел часто используются имена констант, как в следующем примере, где также показано, что массивы могут иметь более одного индекса:

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

Это не совсем практичная программа, но она показывает, как можно получить доступ к переменной, не используя ее имени. Здесь является указателем на переменную поэтому выражение эквивалентно Это означает, что переменная в конце выполнения программы будет иметь значение 789 вместо 123.

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

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

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

А.6. ФУНКЦИИ

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

Для выполнения этого же действия можно написать и более короткий вариант с применением условного выражения

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

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

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

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

После выполнения строки программы

значениями переменных будут 2 и 1, соответственно. Заметим, что аргументы представляют собой указатели, которые обозначаются через внутри функции. Значения двух элементов целочисленного массива можно взаимно поменять друг на друга

Напомним, что к является сокращенной записью для

Говорят, что переменная в вышеприведенной функции относится к автоматическому классу памяти, то есть для нее не отводится постоянное место в памяти. Кроме того, существуют статический и внешний классы памяти, для переменных такого вида отводится постоянное место в памяти. Массивы автоматического класса не могут быть инициализированы. Простые переменные можно инициализировать всегда. Если переменная автоматическая и объявлена в функции то она будет иметь это инициализируемое значение при каждом обращении к функции Если же она объявлена статической, она получит указанное значение только при первом обращении к функции. Если функция завершает работу, а после к ней снова обратились, то статическая переменная будет иметь свое последнее значение, тогда как автоматическая переменная является неопределенной (если она не инициализирована).

А.7. СТРУКТУРЫ

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

Теперь можно использовать компоненты структуры совершенно так же, как и другие переменные, например

Вместо такого объявления можно записать либо

либо

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

Заметим, что при таком объявлении нужно обязательно использовать ключевое слово

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

Теперь будет новым именем для нашего типа структуры и далее можно пользоваться новым видом объявления

Массив например, для 1000 точек может быть объявлен как

или, если выше было объявлено имя

или, если применен оператор

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

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

где два символа - напоминают стрелку и образуют единый символ операции.

А.8. ДИНАМИЧЕСКОЕ РАСПРЕДЕЛЕНИЕ ПАМЯТИ

Предположим, что было объявлено

Тогда будет указателем на символ и можно записать

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

памяти недоступен, то переменной будет присвоено значение специальное значение для “пустого” указателя, не указывающего на реальный объект; это позволяет выполнить проверку (например, для выдачи сообщения о недостаточном объеме памяти)

Недостаточно памяти Отведенные таким путем байтов теперь доступны через указатель

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

Отведенную оператором malloc память можно освободить оператором

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

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

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

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

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

А.9. ВВОД/ВЫВОД

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

Файл заголовков содержит декларацию в виде

таким образом, если напишем

то переменная будет иметь тип “указатель на файл FILE”, при этом компьютер будет знать все подробности о типе структуры Структура фактически доступна через указатель после того, как файл открыт оператором

в котором аргументы имеют следующий смысл:

- строка, содержащая имя файла в том виде, в каком он записан в каталоге;

символы или для форматированного ввода или вывода; при неформатированном вводе-выводе формат обозначения зависит от используемого компилятора.

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

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

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

Фактически запись эквивалентна записи где указатель файла для стандартного вывода, объявленного в хидерном файле Первым аргументом функции (вторым аргументом функции будет форматная строка, содержащая часть текста, который должен появиться в литеральной форме на выходе, и элементы формата, относящиеся к остальным аргументам. Здесь элементами формата являются обозначения Они определяют, что значениями переменных будут целые числа, которые должны быть напечатаны в одной и двух десятичных позициях соответственно. Все другие символы в форматной строке печатаются буквально, включая пробелы, символ новой строки обозначается через В элементе формата букву необходимо заменить на букву если ассоциированный элемент данных имеет тип float вместо Чтение данных производится при обращении к аналогичным функциям

если данные находятся в файле на диске, или

если данные должны быть введены с клавиатуры пользователя. Однако остальные аргументы теперь должны быть указателями, поскольку функции fscanf и должны иметь возможность присваивать значения переменным через эти аргументы. Например, если некоторое число должно бытьзадано пользователем, то можно записать

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

В обозначении формата необходима буква так как имеет тип на

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

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

Одиночный символ может быть прочитан и записан более примитивным образом:

Здесь запись совершенно эквивалентна записи а запись записи

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

Функции выполняют так называемый форматированный ввод/вывод, на что указывает последняя буква в их именах. Форматированные файлы имеют линейную

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

Аргументы имеют следующие типы и смысл:

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

А.10. СТАНДАРТНЫЕ МАТЕМАТИЧЕСКИЕ ФУНКЦИИ

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

(см. скан)

<< Предыдущий параграф Следующий параграф >>
Оглавление