Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Лекция 01

.pdf
Скачиваний:
17
Добавлен:
19.05.2015
Размер:
349.83 Кб
Скачать

Кафедра автоматизации технологических процессов Тверского государственного технического университета

Разработчик: доцент В. Г. Васильев

ЛЕКЦИЯ № 1

«Введение в язык программирования С»

по курсу «Структуры и алгоритмы обработки данных»

(для специальности «Управление в технических системах»)

СОДЕРЖАНИЕ:

1.1.История языка

1.2.От С к С++

1.3.Первая программа 1.4.Сложности С 1.5. Стандарты С

РЕКОМЕНДУМАЯ ЛИТЕРАТУРА

1.Уэйт М., Прата С., Мартин Д. Язык Си. Руководство для начинающих.

М.: Мир, 1988. -512с.

2.Шиманович Е.Л. C/C++ в примерах и задачах. - Минск: Новое знание,

2004, - 528с.

3.Шмидский Я. К. Программирование на языке С/С++. Самоучитель. –

М.: Вильямс, 2004. -352с.

4.Касаткин А.И., Вальвачев А.Н. Профессиональное программирование на языке Си: От Турбо Си к С++. – Минск: Вышэйшая школа, 1992. – 240с.

5.Павловская Т.А. С/С++. Программирование на языке высокого уровня.

СПб.: Питер, 2003. – 461с.

6.Паппас К., Мюррей У. Программирование на С и С++. - К.: BHV, 2000.

320c.

7.Савич У. С++ во всей полноте. - К.: BHV; СПб: Питер, 2005. 784 с.

8.Холзнер С. Visual C++: Учебный курс. СПб: Питер, 2000. - 576с.

9.Прата С. Язык программирования С: Лекции и упражнения = C Primer

Plus — 1-е изд. — М.: Вильямс, 2006. — С. 960. — ISBN 5-8459-0986-4. 10.Кочан С. Программирование на языке Си = Programming in C — 3-е

изд. — М.: Вильямс, 2006. — С. 496. — ISBN 0-672-32666-3.

11.Гукин Д. Язык программирования Си для «чайников» = C For Dummies — М.: Диалектика, 2006. — С. 352. — ISBN 0-7645-7068-4.

1.1 История языка

Си–стандартизированный процедурный язык программирования, разработанный в начале 1970-х годов сотрудниками Bell Labs Кеном Томпсоном и Денисом Ритчи.

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

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

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

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

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

Си (как и ОС UNIX, с которой он долгое время был связан) создавался программистами и для программистов, круг которых был бы ненамного шире круга разработчиков языка. Несмотря на это, область использования языка значительно шире задач системного программирования.

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

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

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

систему типов, предохраняющую от бессмысленных операций;

использование препроцессора для, например, определения макросов и включения файлов с исходным кодом;

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

минимальное число ключевых слов;

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

указатели на функции и статические переменные

области действия имён;

структуры и объединения — определяемые пользователем собирательные типы данных, которыми можно манипулировать как одним целым;

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

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

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

1.2 От С к С++

Язык программирования С++ произошёл от Си. Однако в дальнейшем Си и C++ развивались независимо, что привело к росту несовместимостей между ними. Последняя редакция Си — С99 — добавила в язык несколько конфликтующих с С++ особенностей. Эти различия затрудняют написание программ и библиотек, которые могли бы нормально компилироваться и работать одинаково и в Си и в C++, что, конечно, запутывает тех, кто программирует на обоих языках.

Бьёрн Страуструп, разработавший С++, неоднократно выступал за максимальное сокращение различий между Си и C++ для создания максимальной совместимости между этими языками. Противники же такой точки зрения считают, что так как Си и C++ являются двумя различными языками, то и совместимость между ними не так важна, хоть и полезна. Согласно этому лагерю, усилия по уменьшению несовместимости между ними не должны препятствовать попыткам улучшения каждого языка в отдельности.

1.3.Первая программа

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

Авторы первых программ писали их в машинном (двоичном) коде. Теперь программы пишутся на каком либо языке высокого уровня (Pascal, Delphi, C, C++, C++ Builder, Visual C++). Затем специальная программа, называемая транслятором, переводит их машинный код, который и исполняется процессором. Все трансляторы делятся на два класса:

интерпретаторы - трансляторы, которые переводят каждый оператор программы в машинный код и по мере перевода операторы выполняются процессором;

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

Особенности компиляции (перевода в машинный код) программ на языке С++ рассмотрим после написания первой программы.

Эта простая программа, появившаяся в первом издании книги «Язык программирования Си» Кернигана и Ритчи, обычно является первой программой большинства учебников Си. Она печатает сообщение «Hello, World!» на стандартном устройстве вывода (которым, как правило, является монитор (дисплей), но может быть и файл, какое-либо устройство или область в памяти, в зависимости от того, как отражается стандартное устройство вывода на данной платформе).

main()

{

printf("Hello, World!\n");

}

Несмотря на то, что на большинстве современных компиляторов эта программа может быть скомпилирована, она порождает несколько предупреждений на компиляторах стандарта ANSI C. Кроме того, этот код не будет компилироваться, если компилятор следует стандарту C99, так как в этом случае тип int больше не подразумевается для случаев, когда тип в результате функции не указан (а оформление функции main вообще описано отдельно). Эти сообщения можно убрать, если внести в эту программу несколько небольших изменений:

#include <stdio.h> int main(void)

{

printf("Hello, World!\n"); return 0;

}

В первой строке программы расположена директива препроцессора #include, встретив которую, компилятор заменяет её на полный текст файла, на

который она ссылается. В данном случае эта строка будет заменена стандартным заголовочным файлом stdio.h. Угловые скобки указывают компилятору искать файл stdio.h в каталоге стандартных заголовочных файлов.

Следующая строка является объявлением функции с именем main. Эта функция в программе Си является особенной, так как выполняется первой из написанных программистом при запуске программы. Слово int говорит, что функция main возвращает целое число. Фигурные скобки после функции main обозначают её определение.

Следующая строка «вызывает» функцию printf. Включаемый заголовочный файл stdio.h содержит информацию, описывающую то, как нужно вызывать эту функцию. В данном примере этой функции передаётся единственный аргумент, содержащий текстовую строку «Hello, World!\n». Последовательность \n транслируется в символ «новая строка», который при отображении соответственно обозначает разрыв строки. Функция printf возвращает значение типа int, которое возвращает число напечатанных символов (в этом примере возвращаемое значение игнорируется).

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

0;).

Обратите внимание что каждый оператор языка Си заканчивается символом ; (точкой с запятой). Это обязательно. Иначе компилятор выдаст вам ошибку.

1.4.Сложности С

Многие элементы Си потенциально опасны, а последствия неправильного использования этих элементов зачастую непредсказуемы. Керниган говорит: «Си — инструмент, острый, как бритва: с его помощью можно создать и элегантную программу, и кровавое месиво». В связи со сравнительно низким уровнем языка многие случаи неправильного использования опасных элементов не обнаруживаются и не могут быть обнаружены ни при компиляции, ни во время исполнения. Это часто приводит к непредсказуемому поведению программы. Иногда в результате неграмотного

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

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

Другим потенциальным источником опасных ситуаций служит механизм указателей. Указатель может ссылаться на любой объект в памяти, включая и исполняемый код программы, и неправильное использование указателей может порождать непредсказуемые эффекты и приводить к катастрофичным последствиям. К примеру, указатель может быть неинициализированным или, в результате неверных арифметических операций над указателем, указывать в произвольное место памяти; на некоторых платформах работа с таким указателем может вызвать аппаратную остановку программы, на незащищённых же платформах это может привести к порче произвольных данных в памяти, причём эта порча может проявиться в самые произвольные моменты времени и намного позже момента порчи. Также, область динамической памяти, на которую ссылается указатель, может быть освобождена (и даже выделена после этого под другой объект) — такие указатели называются «висячими». Или, наоборот, после манипуляций с указателями на область динамической памяти может не остаться ссылок, и тогда эта область, называемая «мусором» (garbage), никогда не будет освобождена, что может приводить к «утечкам памяти» в программе. В других языках подобные проблемы пытаются решить введением более ограниченных ссылочных типов.

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

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

Ещё одной распространённой проблемой является то, что память не может быть использована снова, пока она не будет освобождена программистом с помощью функции free(). В результате программист может случайно забыть освобождать эту память, но продолжать её выделять, занимая всё большее и большее пространство. Это обозначается термином утечка памяти. Наоборот, возможно освободить память слишком рано, но продолжать её использовать. Из-за того, что система выделения может использовать освобождённую память по-другому, это ведёт к непредсказуемым последствиям. Эти проблемы решаются в языках со сборкой мусора. С другой стороны, если память выделяется в функции и должна освобождаться после выхода из функции, данная проблема решается с помощью автоматического вызова деструкторов в языке C++, или с помощью локальных массивов, используя расширения С99.

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

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

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

1.5.Стандарты С

Вконце 1970-х годов Си начал вытеснять Бейсик с позиции ведущего языка для программирования микрокомпьютеров. В 1980-х годах он был адаптирован для использования в IBM PC, что привело к резкому росту его популярности. В то же время Бьярне Строуструп и другие в лабораториях Bell Labs начали работу по добавлению в Си возможностей объектноориентированного программирования. Язык, который они в итоге сделали, C++, в настоящее время является самым распространённым языком программирования. Си остаётся более популярным в UNIX-подобных системах.

В1983 году Американский Национальный Институт Стандартизации (ANSI) сформировал комитет для разработки стандартной спецификации Си. По окончании этого долгого и сложного процесса в 1989 году он был наконец утверждён как «Язык программирования Си» ANSI X3.159-1989. Эту версию языка принято называть ANSI C или C89. В 1990 году стандарт ANSI C был принят с небольшими изменениями Международной Организацией по Стандартизации (ISO) как ISO/IEC 9899:1990.

Одной из целей этого стандарта была разработка надмножества K&R C, включающего многие особенности языка, созданные позднее. Однако комитет по стандартизации также включил в него и несколько новых возможностей, таких как прототипы функций (заимствованные из С++) и более сложный препроцессор.

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

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

2.они используют специфические платформо-зависимые средства;

3.они рассчитаны на определённое значение размера некоторых типов данных или на определённый способ хранения этих данных в памяти для конкретной платформы.

C99

После стандартизации в ANSI спецификация языка Си оставалась относительно неизменной в течение долгого времени, в то время как Си++ продолжал развиваться (в 1995 году в стандарт Си была внесена Первая нормативная поправка, но её почти никто не признавал). Однако в конце 1990- х годов стандарт подвергся пересмотру, что привело к публикации ISO 9899:1999 в 1999 году. Этот стандарт обычно называют «С99». В марте 2000 года он был принят и адаптирован ANSI.

Вот некоторые новые особенности С99:

подставляемые функции (inline);

объявление локальных переменных в любом операторе программного текста (как в С++);

новые типы данных, такие как long long int (для облегчения перехода от 32- к 64-битным числам), явный булевый тип данных _Bool и тип complex для представления комплексных чисел;

массивы переменной длины;

поддержка ограниченных указателей (restrict);

именованная инициализация структур: struct { int x, y, z; } point = {

.y=10, .z=20, .x=30 };

поддержка однострочных комментариев, начинающихся на //, заимствованных из C++ (многие компиляторы Си поддерживали их и ранее в качестве дополнения);

несколько новых библиотечных функций, таких как snprintf;

несколько новых заголовочных файлов, таких как stdint.h.

C11

8 декабря 2011 опубликован новый стандарт для языка Си (ISO/IEC 9899:2011) Некоторые возможности нового стандарта уже поддерживаются компиляторами GCC и Clang. Основные изменения:

поддержка многопоточности;

улучшенная поддержка юникода;

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]