Б. Керриган, Д. Ритчи Язык программирования C. Б. Керниган, Д. зык программирования и . Издание 3е, исправленное Перевод с английского под редакцией Вс. С. Штаркмана СанктПетербург 2003
Скачать 31.48 Mb.
|
Глава Обзор языка Начнем, пожалуй Единственный способ выучить новый язык программирования - это писать на нем программы. При изучении любого языка первой, как пра- вило, предлагают написать приблизительно следующую программу: слова здравствуй, мир Вот первое препятствие, и чтобы его преодолеть, вы должны суметь где- то создать текст программы, успешно его скомпилировать, загрузить, за- пустить на выполнение и разобраться, куда будет отправлен результат. Как только вы овладеете этим, все остальное окажется относительно про- сто. Си-программа, печатающая мир", выглядит так: } Как запустить эту программу, зависит от системы, которую вы исполь- зуете. Так, в операционной системе UNIX необходимо сформировать ис- ходную в файле с именем, заканчивающимся с", например в файле с, который затем компилируется с помощью команды CG Если вы все сделали правильно - не пропустили где-либо знака и не до- пустили орфографических ошибок, то компиляция пройдет "молча" и вы получите файл, готовый к исполнению и названный Если вы те- перь запустите этот файл на выполнение командой out программа напечатает здравствуй, мир В других системах правила запуска программы на выполнение могут быть иными; чтобы узнать о них, поговорите со специалистами. Теперь поясним некоторые моменты, касающиеся самой программы. Программа на Си, каких бы размеров она ни была, состоит из функций и переменных. Функции содержат инструкции, описывающие вычисле- ния, которые необходимо выполнить, а переменные хранят значения, ис- пользуемые в процессе этих вычислений. Функции в Си похожи на под- Начнем, пожалуй программы и функции Фортрана или на процедуры и функции Паскаля. Приведенная программа - это функция с именем main. Обычно вы воль- ны придумывать любые имена для своих функций, но - особое имя: любая программа начинает свои вычисления с первой инструкции функ- ции main. Обычно main для выполнения своей работы пользуется услугами дру- гих функций; одни из них пишутся самим программистом, а другие бе- рутся готовыми из имеющихся в его распоряжении библиотек. Первая строка программы: сообщает компилятору, что он должен включить информацию о стан- дартной библиотеке ввода-вывода. Эта строка встречается в начале мно- гих исходных файлов Си-программ. Стандартная библиотека описана в главе 7 и приложении В. Один из способов передачи данных между функциями состоит в том, что функция при обращении к другой функции передает ей список значе- ний, называемых аргументами. Этот список берется в скобки и помеща- ется после имени функции. В нашем примере определена как функ- ция, которая не ждет никаких аргументов, что отмечено пустым списком Первая программа на Си Включение информации о стандарт- ной библиотеке. Определение функции с именем main, не получающей никаких аргументов. { Инструкции main заключаются в фи- гурные скобки. printf ("здравствуй, Функция main вызывает библиотеч- ную функцию для печати задан - } ной символов; \п - символ новой строки. Инструкции функции заключаются в фигурные скобки Функция main содержит только одну инструкцию printf ("здравствуй, Функция вызывается по имени, после которого, в скобках, указывается список аргументов. Таким образом, приведенная выше строка - это вызов 20 _ Глава Обзор языка функции p r i n t f с аргументом "здравствуй, Функция библиотечная функция, которая в данном случае напечатает последова- тельность символов, заключенную в двойные кавычки. Последовательность символов в двойных кавычках, такая как "здрав- ствуй , называется строкой символов, или строковой константой. Пока что в качестве аргументов для и других функций мы будем использовать только строки символов. В Си комбинация внутри строки символов обозначает символ новой строки и при печати вызывает переход к левому краю следующей строки. Если вы удалите \п (стоит поэкспериментировать), то обнаружите, что, закончив машина не переходит на новую строку. Символ новой строки в текстовый аргумент p r i n t f следует включать явным образом. Если вы попробуете выполнить, например, мир компилятор выдаст сообщение об ошибке. Символ новой строки никогда не вставляется автоматически, так что одну строку можно напечатать по шагам с помощью нескольких обраще- ний к printf. Нашу первую программу можно написать и так: flinclude h> { В результате ее выполнения будет напечатана та же строка, что и раньше. Заметим, что \п обозначает только один символ. Такие особые комби- нации символов, начинающиеся с обратной наклонной черты, как \п, и называемые широко применяются для обозначения трудно или невидимых символов. Среди про- чих в Си имеются символы \t, \b, \\, обозначающие соответственно возврат на один символ назад ("забой" последнего двойную кавычку, саму наклонную черту. Полный список таких симво- лов представлен в параграфе 2.3. Упражнение 1.1. Выполните программу, печатающую в вашей системе. Поэкспериментируйте, удаляя некоторые части прог- раммы, и посмотрите, какие сообщения об ошибках вы получите. 1.2. Переменные и арифметические выражения 21 Упражнение 1.2. Выясните, что произойдет, если в строковую константу аргумента вставить где с - символ, не входящий в представлен- ный выше список. 1.2. Переменные и арифметические выражения Приведенная ниже программа выполняет вычисления по формуле (5/9) (Т- 32) и печатает таблицу соответствия температур по Фа- ренгейту температурам по Цельсию: О ' -17 20 -6 40 4 60 15 80 26 100 37 120 48 140 60 160 71 180 82 200 93 220 104 240 260 126 280 137 300 148 Как и предыдущая, эта программа состоит из определения одной-един- ственной функции main. Она длиннее программы, печатающей мир", но по сути не сложнее. На ней мы продемонстрируем несколько новых возможностей, включая комментарий, объявления, переменные, арифметические выражения, циклы и форматный вывод. /* печать таблицы температур по Фаренгейту и Цельсию для fahr = 0, 300 */ { int fahr, celsius; int lower, upper, step; lower = 0; /* нижняя граница таблицы температур */ , upper = 300; /* верхняя граница */ Глава Обзор языка step = 20; /* шаг */ fahr = lower; while <= upper) { Celsius = 5 * (fahr-32) / 9; printf fahr, celsius); fahr = fahr + step; } } Две строки: /* печать таблицы температур по Фаренгейту и Цельсию для fahr = 0, 20 300 */ являются комментарием, который в данном случае кратко объясняет, что делает программа. Все символы, помещенные между /* и игнорируют- ся компилятором, и этим можно свободно пользоваться, чтобы сделать программу более понятной. Комментарий можно располагать в любом месте, где могут стоять символы пробела, табуляции или символ новой строки. В Си любая переменная должна быть объявлена раньше, чем она будет использована; обычно все переменные объявляются в начале функции перед первой исполняемой инструкцией. В объявлении описываются свой- ства переменных. Оно состоит из названия типа и списка переменных, например: int fahr, celsius; int lower, upper, step; Тип означает, что значения перечисленных переменных есть целые, в отличие от него тип указывает на значения с плавающей точкой, т. е. на числа, которые могут иметь дробную часть. Диапазоны значений обоих типов зависят от используемой машины. Числа типа бывают как (лежат в диапазоне от -32768 до так и 32-разрядные. Числа типа обычно представляются 32-разрядными словами, имеющими по крайней мере 6 десятичных знача- щих цифр (лежат приблизительно в диапазоне Помимо int и в Си имеется еще несколько базовых типов для данных, это: char - символ - единичный байт; short - короткое целое; long - длинное целое; double - с плавающей точкой с двойной точностью. Переменные и арифметические выражения __ 23 Размеры объектов указанных типов также зависят от машины. Из базо- вых типов можно создавать: массивы, структуры и объединения, указа- тели на объекты базовых типов и функции, возвращающие значения этих типов в качестве результата. Обо всем этом мы расскажем позже. Вычисления в программе преобразования температур начинаются с инструкций присваивания: lower = 0; upper = 300; step = 20; = lower; которые устанавливают указанные в них переменные в начальные значе- ния. Любая инструкция заканчивается точкой с запятой. Все строки таблицы вычисляются одним и тем же способом, поэтому мы воспользуемся циклом, повторяющим это вычисление для каждой строки. Необходимые действия выполнит цикл while: while (fahr <= upper) { Он работает следующим образом. Проверяется условие в скобках. Если оно истинно (значение f a h r меньше или равно значению upper), то вы- полняется тело цикла (три инструкции, заключенные в фигурные скоб- ки). Затем опять проверяется условие, и если оно истинно, то тело цикла выполняется снова. Когда условие становится ложным превысило upper), цикл завершается, и вычисления продолжаются с инструкции, следующей за циклом. Поскольку никаких инструкций за циклом нет, программа завершает работу. Телом цикла while может быть одна или несколько инструкций, за- ключенных в фигурные скобки, как в программе преобразования темпе- ратур, или одна-единственная инструкция без скобок, как в цикле while (i < i = 2 •* i; И в том и в другом случае инструкции, находящиеся под управлением мы будем записывать со сдвигом, равным одной позиции табуля- ции, которая в программе указывается четырьмя пробелами; благодаря этому будут ясно видны инструкции, расположенные внутри цикла. От- ступы подчеркивают логическую структуру программы. Си-компилятор не обращает внимания на внешнее оформление программы, но наличие в нужных местах отступов и пробелов существенно влияет на то, насколько легко она будет восприниматься человеком при просмотре. Чтобы лучше 24 Глава Обзор языка была видна логическая структура мы рекомендуем на каждой строке писать только по одной инструкции и с обеих сторон от операто- ров ставить пробелы. Положение скобок не так важно, хотя существуют различные точки зрения на этот счет. Мы остановились на одном из не- скольких стилей их применения. Выберите тот, кото- рый больше всего вам нравится, и строго ему следуйте. часть вычислений выполняется теле цикла. Температура по Фа- ренгейту переводится в температуру по Цельсию и присваивается пере- менной посредством инструкции Celsius = 5 * (fahr-32) / 9; Причина, по которой мы сначала умножаем и затем делим на 9, не сразу умножаем на 5/9, связана с тем, что в Си, как и во многих других языках, деление целых сопровождается отбрасыванием, т. е. потерей дробной час- ти. Так как 5 и 9 - целые, отбрасывание в 5/9 дало бы нуль, и на месте температур по Цельсию были бы напечатаны нули. Этот пример прибавил нам еще немного знаний о том, как работает функция Функция p r i n t f - это универсальная функция формат- ного ввода-вывода, которая будет подробно описана в главе 7. Ее первый аргумент - строка символов, в которой каждый символ % соответствует одному из последующих аргументов (второму, а информа- ция, расположенная за символом указывает на вид, в котором выводит- ся каждый из этих аргументов. Например, %d специфицирует выдачу ар- гумента в виде целого десятичного числа, и инструкция fahr, celsius); печатает целое выполняет табуляцию (\t) и печатает целое celsius. В функции rintf каждому спецификатору первого аргумента (конст- рукции, начинающейся с %) соответствует второй аргумент, третий аргу- мент и т. д. Спецификаторы и соответствующие им аргументы должны быть согласованы по количеству и типам: в противном случае напечатано будет не то, что нужно. Кстати, printf не является частью языка Си, и вообще в языке нет ни- каких специальных конструкций, определяющих ввод-вывод. Функция printf -- лишь полезная функция стандартной библиотеки, которая обычно для Си-программ. Поведение функции p r i n t f , однако, оговоре- но стандартом ANSI, и ее свойства должны быть одинаковыми во всех Си-системах, удовлетворяющих требованиям стандарта. Желая сконцентрировать ваше внимание на самом Си, мы не будем мно- го говорить о вводе-выводе до главы 7. В частности, мы отложим разго- вор о форматном вводе. Если вам потребуется ввести числа, советуем про- Переменные и арифметические выражения 25 читать в параграфе 7.4 то, что касается функции Эта функция от- личается от p r i n t f лишь тем, что она вводит данные, а не выводит. Существуют еще две проблемы, связанные с программой преобразова- ния температур. Одна из них (более состоит в том, что выводи- мый результат выглядит несколько неряшливо, поскольку числа не ровнены по правой позиции колонок. Это легко исправить, добавив в каж- дый из спецификаторов формата %d указание о ширине поля; при этом программа будет печатать числа, прижимая их к правому краю указан- ных полей. Например, мы можем написать Celsius); чтобы в каждой строке первое число печатать в поле из трех позиций, а второе - из шести. В результате будет напечатано: О -17 20 -6 40 4 60 15 80 26 100 37 Вторая, более серьезная проблема связана с тем, что мы пользуемся целочисленной арифметикой и поэтому не совсем точно вычисляем тем- пературы по шкале Цельсия. Например, 0 самом деле (с точностью до десятой) равно -17.8 °С, а не -17. Чтобы получить более точные значе- ния температур, нам надо пользоваться не целочисленной арифметикой, а арифметикой с плавающей точкой. Это потребует некоторых измене- ний в программе. /* печать таблицы температур по Фаренгейту и Цельсию для fahr = 0, 20 300; вариант с плавающей точкой */ { float fahr, Celsius; int lower, upper; step; • • lower = 0; /* нижняя граница таблицы температур */ upper = 300; •/* верхняя граница */ step = 20; /* шаг */ fahr = lower; while (fahr <= upper) { 26 Глава Обзор языка celsius = * fahr, celsius); fahr = fahr + step; Программа мало изменилась. Она отличается от предыдущей тем, что f ah и celsius объявлены как 3 oat, а формула преобразования напи- сана в более естественном виде. В предыдущем варианте нельзя было ци- сать 5/9, так как целочисленное деление в результате обрезания дало бы нуль. Десятичная точка в константе указывает на то, что последняя рас- сматривается как число с плавающей и таким образом, есть частное от деления двух значений с плавающей точкой, которое не предполагает отбрасывания дробной части. В том случае, когда ариф- метическая операция имеет целые операнды, она выполняется по прави- лам целочисленной арифметики. Если же один операнд с плавающей точ- кой, а другой - целый, то перед тем, как операция будет выполнена, по- следний будет преобразован в число с плавающей точкой. Если бы мы написали f ah г-32, то 32 автоматически было бы преобразовано в число с плавающей точкой. Тем не менее при записи констант с плавающей точ- кой мы всегда используем десятичную точку, причем даже в тех случаях, когда константы на самом деле имеют значения. Это делается для того, чтобы обратить внимание читающего программу на их природу. Более подробно правила, определяющие, в каких случаях целые пере- водятся в числа с плавающей точкой, рассматриваются в главе 2. А сейчас заметим, что присваивание = lower; и проверка while (fahr <= upper) работают естественным образом, т. е. перед выполнением операции зна- int приводится к float. Спецификация %3. Of в printf определяет печать числа с плавающей точкой (в данном случае числа в поле шириной не более трех пози- ций без десятичной точки и дробной части. Спецификация f описы- вает печать другого числа (celsius) в поле из шести позиций с одной циф- рой после десятичной точки. Напечатано будет следующее: О -17.8 20 -6.7 40 4.4 Инструкция 27 Ширину и точность можно не задавать: означает, что число будет за- нимать не более шести позиций; 2f - число имеет две цифры после де- сятичной точки, но ширина не ограничена; %f просто указывает на печать числа с плавающей точкой. %d - печать десятичного целого. %6d - печать десятичного целого в поле из шести позиций. - печать числа с плавающей точкой. f - печать числа с плавающей точкой из шести позиций. 2f - печать числа с плавающей точкой с двумя цифрами после десятичной точки. 2f - печать числа с плавающей точкой и двумя цифрами после десятичной точки в поле из шести позиций. Кроме f допускает следующие спецификаторы: %о для восьме- ричного числа; %х для числа; %с для символа; %s для строки символов и %% для самого %. Упражнение 1.3. Усовершенствуйте программу преобразования темпе- ратур таким образом, чтобы над таблицей она печатала заголовок. Упражнение 1.4. Напишите программу, которая будет печатать таблицу соответствия температур по Цельсию температурам по Фаренгейту. 1.3. Инструкция for Существует много разных способов для написания одной той же про- граммы. Видоизменим нашу программу преобразования температур: печать таблицы температур по Фаренгейту и Цельсию */ main() int fahr; for (fahr = 0; fahr <= 300; fahr = fahr + 20) fahr, } Эта программа печатает тот же результат, но выглядит она, несомнен- но, по-другому. Главное отличие заключается в отсутствии большинства переменных. Осталась только переменная f a h r , которую мы объявили как int. Нижняя и верхняя границы и шаг присутствуют в виде констант 28 ; Глава Обзор языка в инструкции f or - новой для нас а выражение, вычисляю- щее температуру по теперь задано третьим аргументом функ- ции f, а не в отдельной инструкции присваивания. Последнее изменение является примером применения общего прави- ла: в любом контексте, возможно использовать значение переменной какого-то типа, можно использовать более сложное выражение того же Так, на месте третьего аргумента функции согласно специ- фикатору f должно быть значение с плавающей точкой, следователь- но, здесь может быть любое выражение этого типа. Инструкция f o r описывает цикл, который является обобщением цик- ла while. Если вы сравните его с ранее написанным while, то вам станет ясно, как он работает. Внутри скобок имеются три выражения, разделяе- мые точкой с запятой. Первое выражение - инициализация fahr = О выполняется один раз перед тем, как войти в цикл. Второе - проверка условия продолжения цикла . fahr <= 300 Условие вычисляется, и если оно истинно, выполняется тело цикла (в нашем случае это одно обращение к Затем осуществляется при- ращение шага: fahr = fahr + 20 и условие вычисляется снова. Цикл заканчивается, когда условие стано- вится ложным. Как и в случае с while, тело может состоять из одной инструкции или из нескольких, заключенных в фигурные скоб- ки. На месте этих трех выражений (инициализации, условия и прираще- ния шага) могут стоять произвольные выражения. Выбор между while и определяется соображениями ясности програм- мы. Цикл более удобен в тех случаях, когда инициализация и прира- щение шага логически связаны друг с другом общей переменной и выра- жаются единичными инструкциями, поскольку названный цикл компакт- нее цикла while, а его управляющие части сосредоточены в одном месте. |