Реферат Курсовая Конспект
КУРС ПРОГРАММИРОВАНИЯ НА ЯЗЫКЕ СИ - раздел Программирование, Федеральное Агентство По Образова...
|
ЮЖНО-УРАЛЬСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ
681.3(07) К289
СТ. Касюк
КУРС ПРОГРАММИРОВАНИЯ НА ЯЗЫКЕ СИ
Конспект лекций
Челябинск 2010
Министерство образования и науки Российской Федерации
Федеральное агентство по образованию
Южно-Уральский государственный университет
Кафедра информатики
681.3(07) К289
СТ. Касюк
КУРС ПРОГРАММИРОВАНИЯ НА ЯЗЫКЕ СИ
Конспект лекций
Челябинск
Издательский центр ЮУрГУ
УДК 681.3(075.8) ББКЧ23.я7 К289
Одобрено учебно-методической комиссией факультета экономики и управления
Рецензенты: д.т.н. B.C. Жабреев, к.т.н. В.Л. Федяев
ПРЕДИСЛОВИЕ
В учебном пособии представлен конспект лекция по курсу программирования на языке Си, который используется для подготовки студентов специальности «Прикладная информатика в экономике» на кафедре информатики Южно-Уральского государственного университета.
Цель настоящего учебного пособия — помочь овладеть учащимся методами составления программ и выработать навыки программирования на языке Си. Для достижения этой цели студенты должны познакомиться с базовыми конструкциями и элементами стандарта ANSI языка Си, изучить динамические информационные структуры и рассмотреть наиболее важные алгоритмы, применяемые в настоящее время для сортировки и поиска данных.
При изложении материала автор стремился учитывать, что пособие ориентировано на студентов младших курсов, только начинающих изучать программирование. По своему объему данных пособие приближается к учебникам по языку программирования Си для студентов, изучающих информатику и вычислительную технику. Однако оно отличается от них в основном менее подробным изложением материала, поскольку, по мнению автора, изучение разнообразных языковых конструкций и возможностей при начальном освоении программирования затеняет базовые понятия языка Си и «сбивает студентов с толку». Поскольку для учащихся практика программирования имеет огромное значение, в пособии приведено много примеров программ учебного и прикладного характеров, снабженных комментариями.
Учитывая, что студенты часто неверно трактуют работу с указателями, автор стремился как можно чаще приводить иллюстрации содержимого оперативной памяти для предотвращения возникающих на этой почве типичных ошибок.
В пособии даны программные реализации стеков, списков, очередей и деревьев. Кроме того, приведены программы с использованием динамических информационных структур из книги КерниганаБ. и Ритчи Д. «Язык программирования Си». Раздел сортировки и поиска разработан несколько подробнее других разделов, так приведены прикладные алгоритмы обработки данных, примеры сортировки и поиска данных, расчетные характеристики алгоритмов.
При работе над учебным пособием автор стремился использовать только качественные источники и материалы, приведенные в списке литературы.
Глава 1. Язык программирования Си
Введение в язык Си
Язык программирования Си — универсальный язык программирования, который завоевал особую популярность у программистов, благодаря сочетанию возможностей языков программирования высокого и низкого уровней.
Язык Си широко применяется при современном профессиональном программировании*. Большинство программистов предпочитают использовать язык Си для своих серьезных разработок потому, что их привлекают такие особенности языка, как свобода выражения мыслей, мобильность и чрезвычайная доступность.
Язык Си наряду с тем, что он позволяет освоить хороший стиль программирования, так же как более простые и менее мощные языки высокого уровня (Бейсик, Паскаль), даёт возможность программисту осуществлять непосредственный доступ к ячейкам памяти и регистрам компьютера, требуя при этом знания особенностей функционирования ЭВМ. В этом Си схож с языком низкого уровня — ассемблером. Поэтому язык Си иногда называют ассемблером высокого уровня, хотя на самом деле он представляет собой гораздо более мощное средство решения трудных задач и создания сложных программных систем.
Позиции языка Си++ в современном мире. Современные языки программирования:
• Си++ — язык системного программирования;
• Java — язык программирования для Internet и мобильных систем;
• Visual Basic — язык разработки Windows-приложений;
• Delphi — объектно-ориентированный язык Object Pascal. Практически все используемые в мире ключевые программные средства, в
том числе компиляторы, операционные системы, СУБД, системы телекоммуникаций написаны на Си++. Несколько примеров: а) практически все программные продукты Microsoft (Windows XP, Office XP, Internet Exploer, MS SQL Server и др.), б) ведущие продукты Adobe Systems (Photoshop, Acrobat и др.), в) базовые компиляторы Sun, г) графическая оболочка KDE для Linux, д) многие компоненты Mac OS X и т.д. Не вызывает сомнений подавляющее превосходство Си++ в области встроенных систем и индустрии компьютерных игр (Doom III, StarCraft и др.). На Си++ реализованы ведущие поисковые Web-системы и крупнейшие Web-порталы: Google, Yahoo, Amazon и др. Создатель Си++ Бьерн Страустроп приводит следующие аргументы в пользу языка: «Си++ является наилучшим языком для многих приложений, где требуется системное программирование, имеются определенные ограничения по ресурсам и выдвигаются серьезные требования к производительности. Одним из примеров служит Google, другим — встроенные системы для миниатюрных устройств».
Язык Си был разработан американцем Деннисом Ритчи в исследовательском центре Computer Science Research Center of Bell Laboratories корпорации AT&T в 1972 г. Первоначальная реализация Си была выполнена на ЭВМ PDP-11 фирмы DEC для создания операционной системы UNIX. Позже он был перенесен в среду многих операционных систем, обособился и существует независимо от любой из них. Программы, написанные на языке Си, как правило, можно перенести в любую другую операционную систему или на другой компьютер либо с минимальными изменениями, либо вовсе без них.
Диалекты языка Си.Первое описание языка Си дал его автор Деннис Ритчи совместно с Брайном Керниганом в книге «Язык программирования Си». Однако, описание не было строгим и содержало ряд неоднозначных моментов. Разработчики трактовали язык по-разному. Фактически, долгое время стандартом языка служила его реализация в UNIX. Сейчас существуют десятки реализаций языка программирования Си. Они поддерживают разные диалекты языка.
В 1983 г. при Американском Институте Национальных Стандартов (American National Standart Institute — ANSI) был создан комитет по стандартизации языка Си. В 1989 г. был утверждён окончательный вариант стандарта. Однако на сегодняшний день большинство реализаций языка Си не поддерживают стандарт в полном объёме.
Структура программы
Программа на языке Си состоит из одной или более подпрограмм, называемых функциями. Каждая функция в языке Си имеет свое имя. В любой программе одна из функций обязательно имеет имя main.
Имя функции — это коллективное имя группы объявлений и операторов, заключенных в фигурные скобки. За именем функции в круглых скобках указываются параметры функции.
Пример функции
/* | Первая программа | на Си | . */ | |
#include <stdio.h> | ||||
main() г | ||||
i | printf("n Здравствуй, | язык | Си!"); | |
} | /* Вывод на экран | сообщения | */ |
Пример программы из нескольких функций
#директивы пр | епроцессора | |
main() { | ||
} | ||
function { | _1(.. | .) |
} | ||
function { | _2 ( . . | .) |
} | ||
function { | n ( . . | .) |
} |
Функция main может вызывать для выполнения любую другую функцию. Функции function_1, function_2, ..., function_n могут вызвать любую функцию, кроме функции main. Функцию main нельзя вызывать изнутри программы, она является управляющей.
Пример объявления объектов
int n; | /* | Переменная п целого типа. */ | ||
float xl; | /* | Переменная xl типа с плавающей | точкой. | */ |
char a; | /* | Переменная а символьного типа. | */ |
Пример
50000U —константа типа unsigned int
Константе 50000U выделяются 2 байта вместо четырех, как было бы при отсутствии суффикса. В этом случае, т.е. для unsigned int, знаковый бит используется для представления одного из разрядов кода числа и диапазон значений становится от 0 до 65535.Суффикс L (или /) позволяет выделить целой константе 4 байта.
Совместное использование в любом порядке суффиксов U (или и)иЬ (или /) позволяет приписать целой константе тип unsigned long, и она займет в памяти 32 разряда, причем знаковый разряд будет использоваться для представления разряда кода (а не знака).
Пример
OLU — целая константа типа unsigned long длиной 4 байта
2424242424UL — константа типа unsigned long
Вещественные константы
Константа с плавающей точкой (вещественная константа) всегда представляется числом с плавающей точкой двойной точности, т. е. как имеющая тип double, и состоит из следующих частей [2]:
• целой части — последовательности цифр;
• десятичной точки;
• дробной части — последовательности цифр;
• символа экспоненты е или Е;
• экспоненты в виде целой константы (может быть со знаком).
• Любая часть (но не обе сразу) из нижеследующих пар может быть опущена:
• целая или дробная часть;
• десятичная точка или символ е (Е) и экспонента в виде целой константы.
Примеры
345. |
3.14159 |
2.1Е5 |
.123ЕЗ |
4037е-5 |
По умолчанию компилятор присваевает вещественному числу тип double.
Если программиста не устраивает тип, который компилятор приписывает константе, то тип можно явно указать в записи константы с помощью следующих суффиксов: F (или/) —float для вещественных, U (или и) — unsigned для целых, L (или /) — long для целых и вещественных.
Примеры:
• 3.14159F — константа типа float, занимающая 4 байта;
• 3.14L — константа типа loung double, занимающая 10 байт.
Символьные константы
Символьная константа — это один символ или обратная косая черта и символ, заключенные в апострофы (одинарные кавычки), например: 'z', ' V, ' t' и так далее. Обратная косая черта (слэш) и символ служат для обозначения управляющих символов, не имеющих графического представления, например, 'п' — переход на новую строку, 't' — табуляция. Все символьные константы имеют тип char и занимают в памяти по 1 байту. Значением символьной константы является числовое значение её внутреннего кода.
Строковые константы
Строковая константа — это последовательность символов, заключенная в кавычки, например: "Это строковая константа". Кавычки не входят в строку, а лишь ограничивают её. Технически, строковая константа представляет собой массив символов и по этому признаку может быть отнесена к разряду сложных объектов языка Си. Однако, строковую константу удобнее рассмотреть вместе с другими константами.
В конце каждой строковой константы компилятор помещает символ ' ', чтобы программе было возможно определить конец строки. Такое представление означает, что размер строковой константы не ограничен каким-либо пределом, но для определения длины строковой константы её нужно полностью просмотреть.
Поскольку строковая константа состоит из символов, то она имеет тип char. Количество ячеек памяти, необходимое для хранения строковой константы на единицу больше количества символов в ней. Следует отчетливо понимать, что символьная константа и строка из одного символа не одно и то же: Y не есть "х". Первое — это символ, использованный для числового представления буквы х, а второе — строковая константа, содержащая символ х и ' '. Если в программе строковые константы записаны одна за другой через разделители, то при выполнении программы они будут «склеены».
Переменные
Переменная — лексема, представляющая собой изображение изменяемого объекта.
С технической точки зрения, переменная — это область памяти, в которую могут помещаться различные числа (двоичные коды). Любая переменная до её использования в программе должна быть описана, т. е. для нее должены быть указаны тип и имя (идентификатор).
Пример
тип переменной имя переменной;
Предпочтительно использовать именно такой способ описания, чтобы при необходимости можно было модифицировать имя переменной. Кроме того, в этом случае каждую переменную удобно снабдить комментарием, поясняющим ее смысл.
Пример
int i; /* i - счетчик циклов */
Общий случай объявления переменных
тип переменных имя переменной 1,
имя переменной 2,
имя переменной п;
При объявлении переменных им можно задавать начальные значения — производить инициализацию.
Пример
тип переменной имя переменной = значение;
Примеры
int i=0, к, n, m=l; | |
float x=314.159E-2, | у; |
char а='а'; |
Операции
Над объектами в языке Си могут выполняться различные операции [2]:
1) арифметические;
2) логические;
3) адресные;
4) операции отношения;
5) операции присваивания.
Результат выполнения операции — всегда число.
Операции могут быть двухместными (бинарными) или одноместными (унарными). Двухместные операции выполняются над двумя объектами, одноместные — над одним.
Арифметические операции
Основные двухместные операции, расположенные в порядке уменьшения приоритета:
1) умножение — «*»;
2) деление — «/»;
3) сложение — «+»;
4) вычитание и арифметическое отрицание — «-»;
5) целочисленное деление (вычисление остатка от деления) — «%». Самый высокий приоритет у операции «умножение», самый низкий у
операции «целочисленное деление».
Основные одноместные операции:
1) приращение на единицу — «++»;
2) уменьшение на единицу — «—».
Результат вычисления выражения, содержащего операции «++» или «—», зависит от того, где расположен знак операции (до объекта или после него). Если операция расположена до переменной, то сначала происходит изменение значения переменной на 1, а потом выполняется какая-то операция; если — после переменной, то сначала выполняется операция, а потом значение переменной изменяется на 1.
Примеры:
• а*++Ь — если а=2 и Ь=3, то результат вычислений равен 8, а Ь=А;
• а*Ь++ — если а=1 и Ь=3, то результат вычислений равен 6, а Ь=4.
Логические операции
Логических операций в языке Си три:
1) «&&» — логическое «И» (конъюнкция);
2) «||» — логическое «ИЛИ» (дизъюнкция);
3) «!» — логическое «НЕ» (отрицание).
Логические операции могут выполняться над любыми объектами. Результат логической операции: единица, если выражение истинно; ноль, если выражение ложно. Вообще, все значения, отличные от нуля, интерпретируются как истинные. Логические операции имеют низкий приоритет, и поэтому в выражениях с такими операциями скобки используются редко.
Адресные операции
Адресные операции:
1) определение адреса — «&»;
2) обращение по адресу — «*». Адресные операции являются унарными.
Операции отношения
Операции отношения:
1) равно — « == »;
2) не равно — « != »;
3) меньше — « < »;
4) больше — « > »;
5) меньше или равно — « <= »;
6) больше или равно — « >= ».
Операции используются при организации условий и ветвлений. Все эти операции вырабатывают результат типа int. Если отношение между операндами истинно, то значение этого условия — единица, если ложно — ноль.
Операция присваивания
Операция присваиваниявыполняется следующим образом:
1) вычисляется выражение в правой части;
2) тип результата преобразуется к типу объекта в левой части;
3) результат записывается по адресу, где находится объект.
Пример
объект = <выражение>;
V — вертикальная табуляция;
4) 'Ь' — возврат на символ;
Пример
printf("n Здравствуй, язык Си!");
Результат работы программы
Здравствуй, язык Си!
Пример
а=5;
printf("n Значение переменной а=%о!.",а);
Результат работы программы
Значение переменной а=5.
Пример
х=2.78;
printf("n Значение переменной x=%f",x);
Результат работы программы
Значение переменной х=2.780000
При указании формата можно явным образом указать общее количество знакомест и количество знакомест, занимаемых дробной частью.
Пример
У=3;
printf("n Значение переменной у=%10.7f",х);
Пример программы
scanf("%d", &m); | |
/* Ввести целое число и присвоить | */ |
/* его значение переменной т. | */ |
scanf("%lf", &xl); | |
/* Ввести значение переменной xl, | */ |
/* имеющей тип double. | */ |
§1.7. ОператорыУсловный оператор if Общая форма записи
if(< выражение>) <оператор 1>;
[else
<оператор 2>;]
Если выражение истинно, то выполняется <оператор 1>, если выражение ложно, то выполняется <оператор 2> (при наличии опции else). Оператор z/может быть вложенным.
Пример
if | (key == | 1) | ||||||
printf("n | Выбран первый | пун] | <т ") ; | |||||
el | зе if (key | == | 2) | |||||
printf( | "n Выбран | ВТ | орой | пункт"); | ||||
else | ||||||||
printf( | "n Первый | и | вторе | эй | пункты | не | ||
выбраны") , |
Возможно использование оператора if без опции else. При использовании обеих форм оператора z/Ъпция else связывается с последним оператором if.
Пример
if | (key!= l) if (key == | 2) | ||||
printf( | "n | Выбран | второй | пункт"); | ||
else | ||||||
printf( | "n | Первый и | второй | пункты | не | |
выбраны | ") ; |
Если <onepamopl> или <оператор2> должны состоять из нескольких операторов, то необходимо использовать составной оператор (блок).
Пример
if (key =
{
n=n+l; m=l+r;
}else
{
m=m-1;
n=l-r; }
Оператор ветвления switch
Оператор if позволяет осуществить выбор только между двумя вариантами. Для того, чтобы производить выбор одного из нескольких вариантов используется оператор switch.
Пример
к=5; | ||
п=10; | ||
while(к<п) | ||
{ | ||
printf("k=%d n=%dn", | к, | п) ; |
к+=2; | ||
к=к+2; | ||
п++; | ||
} |
Цикл do...while
Общая форма записи
do <оператор>; while(<выражение>);
Цикл do...while— это цикл с постусловием, в котором истинность выражения проверяется после выполнения всех операторов, включенных в цикл. Тело цикла выполняется до тех пор, пока выражение не станет ложным, т. е. тело цикла выполнится хотя бы один раз. Использовать цикл лучше всего в тех случаях, когда должна быть выполнена хотя бы одна итерация.
Пример
printf("п | Введите число | 10") ; | |
do | scanf( | "%d",&number); | |
whi | le(number !=10); | ||
Пример
do | |||
{ | |||
printf(" | Введите | n>0"); | |
scanf("% | d", &n); | ||
} | |||
while (n<0) | r |
Цикл for Общая форма записи
for( <инициализация>; <проверка условия>; <коррекция> ) <оператор>;
Цикл for — цикл с фиксированным числом повторений. Для организации такого цикла должны рассматриваться три операции:
• инициализация счетчика;
• сравнение его величины с некоторым граничным значением;
• изменение значения счетчика при каждом прохождении тела цикла.
Эти три операции записываются в скобках и разделяются точкой с запятой. Первое выражение служит для инициализации счетчика. Инициализация осуществляется только один раз — когда цикл for начинает выполняться. Второе выражение — для проверки условия перед каждым возможным выполнением тела цикла. Когда выражение становится ложным (равным нулю), цикл завершится. Третье выражение вычисляется в конце каждого выполнения тела цикла. Счетчик может как увеличиваться, так и уменьшаться.
Пример
main () { int num; | ||
for( num=l; num<=5; num++ ) | ||
printf(" % 5d % 5d n", | num, | num*num); |
} |
В качестве третьего выражения может использоваться любое правильно составленное выражение, изменяющее значение проверяемого условия.
Пример
for (х=1;у<=35;у=5*х++ +10)
printf ("%10d %10о! п", х, у);
Можно опустить одно или несколько выражений, но нельзя опускать точку с запятой. Параметры, находящиеся в заголовке цикла, можно изменить при выполнении операций в теле цикла.
Пример
к=2; | |
for( n=3; k<=25; | ) |
{ | |
к = к*п; | |
} |
В цикле for часто используется операция «запятая» для разделения нескольких выражений. Это позволяет включить в спецификацию цикла несколько инициализирующих или корректирующих выражений. Выражения, к которым применяется операция «запятая», будут вычисляться слева направо.
Пример
for(i=0, j=l; i<n; i++, j=i);
В Си допускаются вложенные циклы, т. е. когда один цикл находится нутри другого.
Пример
for ( | i= | = 0; | i<n; | i++) | ||
{ | ||||||
for | ( j = | =0; j | <n; j | + +) | ||
{ | ||||||
} | ||||||
} |
Рекомендации по выбору цикла
При выборе цикла необходимо оценить следующие факторы. Нужен ли вам цикл с предусловием или цикл с постусловием (чаще используется цикл с предусловием). Если в цикле необходима инициализация и коррекция, то цикл for более предпочтителен. Этот цикл чаще всего используется при подсчете числа прохождений тела цикла с обновлением индекса, если в этом нет необходимости, то можно использовать while.
Операторы break и continue
В теле любого цикла можно использовать оператор break, который позволяет выйти из цикла, не завершая его. Оператор continue позволяет пропустить часть операторов тела цикла и начать новую итерацию.
Пример
while(<выражение>; {
break;
Пример
while(<выражение>^ {
continue;
При вложенных циклах действия операторов break и continue распространяется только на самую внутреннюю структуру, в которой они содержатся. Использование этих операторов возможно, но нежелательно, т.к. они ухудшают читаемость программы, увеличивают вероятность ошибок, затрудняют модификацию.
Пример
main ( ) | ||
{ | ||
int i, | j; | |
float | k; |
printf(" Введите j"); | ||||
scant("%d", &j ); | ||||
for { | ( i = -5; i <= 5; i++) | |||
if( i==0 ) | ||||
continue; | ||||
} | } | printf("n %d/%d =%f ", | j Д, | k=j/i); |
Оператор безусловного перехода goto Общая форма записи
goto метка;
метка : <оператор> ;
Выполнение оператора goto вызывает передачу управления в программе оператору, помеченному меткой. Для отделения метки от оператора используется двоеточие. Помеченный оператор может появиться в программе как до оператора goto, так и после. Имена меток образуются по тем же правилам, что и имена переменных.
Пример
if | ( | size | > | 12) | |
gc | )tO | a; | |||
el | se | ||||
gc | )tO | b; | |||
а: | cost | = | cost*3; | ||
fl | ag= | = 1; | |||
b: | s= | = cons | t* | 'flag; |
Использование goto в программе крайне нежелательно, так как он усложняет логику программы. Язык Си реализован таким образом, что можно программировать без оператора goto.
Определение функции
Каждая функция в языке Си должна быть определена, т. е. должны быть указаны:
• тип функции;
• имя функции;
• информация о формальных параметрах;
• тело функции.
Существует два формата определения функции — современный стиль определения функции и старый стиль определения функции [5].
Современный стиль определения функции
тип функции имя функции | (объявление формальных |
параметров через | |
{ /* тело функции */ | запятую) |
return(<выражение>); } |
5 -
=г :
ж
I «9 Г-
Х1
Х2
Хп
ЧЁРНЫЙ ЯЩИК
типФункция
(тип Х1, тип Х2....... тип Хп)
{ тело функции }
Р
Возврат значения в точку вызова return <выражением
Рис. 1.1. Схема работы функции
Пример функции вычисления факториала
#include <stdio.h> /* Определение функции factorial()*/ double factorial(double i)
{
double j,k;
k=l;
for (j=2;j<i+l;j=j+l)
k=k*j; return k; /* Возврат в программу */ /^вычисленного значения */ }
/* Главная функция*/ main()
{
double i;
printf("n Введите целое числоп");
scant("%lf",&i);
printf("n%lf!=%lfn",i,
factorial (i)); }
Пример использования return в середине функции
void function() {
return; }
В примере оператор return завершает выполнение функции function и передает управление в вызывающую функцию. Никакое значение при этом не передается.
Вызов функции зависит от того, возвращает она значения или нет. Если функция возвращает значения, то её вызов производится из математических и логических выражений.
Пример функции определения максимума из двух чисел: а = b*max(xy);
int max(int a, | int b) |
{ | |
if (a>b) | |
return | (a) ; |
else | |
return | (b) ; |
} |
Функции могут и не возвращать значения, а просто выполнять некоторые вычисления. В таком случае указывается пустой тип данных void, и отсутствует оператор return.
Пример функции, не возвращающей значения: spravka(a);
void spravka | (float a) |
{ | |
} |
Пример объявления функции модуля числа
int abs(int); int abs(int i);
Если прототип не задан, то он будет построен по умолчанию на основе первой ссылки на функцию. Такой прототип не всегда может быть согласован с последующим определением или вызовом функции.
Рекомендуется всегда указывать прототип. Это позволяет кампилятору выдавать диагностичесие сообщения при неправильном использовании функции, либо корректировать несоответствие аргументов при выполнении программы.
При программировании на языке Си широко используются библиотечные функции. Эти функции были предварительно разработаны и записаны в состав системы программирования. Прототипы библиотечных функций находятся в специальных заголовочных файлах с расширением h (head), которые необходимо подключать с помощью директивы Mnclude.
Рассмотрим пример программы генерации таблицы чисел 2й.
Пример программы
#include <stdio.h> | ||
int power(int base; int index); | ||
/* int | power(int, int); - второй вариант | объявления |
функции | power().*/ | |
main () | ||
{ | ||
int | i; | |
for | ( i=0; i <= 10; i++ ) | |
{ | printf("%d, ", power(2,i)); | |
} | ||
} | ||
int power(int base; int index) | ||
{ | ||
int | if p; | |
p=l | r | |
for | ( i=0; i <= 10; i++ ) | |
{ | p = p * base; | |
} | ||
return (p); | ||
} |
Результат работы программы
1, 2, 4, 8, 16, 32, 64, 128, 252, 504,
Схема программы представлена на рис. 1.2.
( начало 1
Цикл
return p;
I начало I
а)
6)
Рис. 1.2. Схема программмы: а — функция main; б — функция возведения в степень power
Пример
#include <math.h> #include "fact.с"
Директива #define позволяет вводить в текст программы макроопределения.
Общая форма записи
#define что менять на что менять
Замена будет произведена на нулевом этапе компиляции. Символы «что менять» будут изменены на символы «на что менять».
Пример
#include <stdio.h> #define pi 3.1459265 main ()
{
double x,y;
printf("n введите угол в радианах"); scanf("%lf", &x);
y=(180*x)/pi;
printf("n синус угла %lf в градусах %lf равен", у, sin(x));
}
Специальные операции
В языке Си помимо основных операций — арифметических, логических, операций отношений — существуют ещё две специальные операции:
1) операция вычисление размера объекта sizeof,
2) операция «запятая» — « , ».
Операция sizeof предназначена для определения объема оперативной памяти в байтах, необходимой для размещения объекта. Объектами могут быть типы данных, переменные и константы.
Общая форма записи
sizeof(объект)
Пример программы
#include <stdio.h> | |||||||||
#define pi | 3.14159625 | ||||||||
main() { int x; | |||||||||
printf( | "n Размер | памяти | под | целое | число | с | Id | байт.", | |
sizeof( | int)); | ||||||||
printf( | "n Размер | памяти | под | тип | double | %d | байт.", |
sizeof(double)); | ||
printf("n Размер | памяти под переменную %d байт.", | |
sizeof(x)); | ||
printf("n Размер | памяти под константу pi %d байт.", | |
} | sizeof(pi)); |
Операция «запятая»( « , » ) предназначена для связывания между собой выражений. Список, разделенный запятой, трактуется как единое выражение и вычисляется слева направо.
Пример
main() | |
{ | |
int x=3, у; | |
у=3, 4*х; | |
printf ("n Значение y=%d.", | у); |
} |
Результат работы программы
Значение у=12
Пример
main()
{
int i, b;
for (i=0, b=l; i <= 5;i++
{
b=b+i;
printf("n Значение b=%d.",b);
Результат работы программы
Значение b=l
Значение b=2
Значение b=3
Значение | Ь= | = 4. |
Значение | Ь= | =5. |
Значение | Ь= | = 6. |
Пример программы
#include <stdio.h> void autofunc(void)
{
int k=l;
printf("n k = %u", k); k=k+l; }
main()
{
int i;
for(i = 0; i<=5; i++) autofunc();
}
Результат работы программы
к= | |
к= | |
к= | |
к= | |
к= | |
к= |
Если в примере объявить переменную к как глобальную, результат работы программы будет иным.
Пример
#include <stdio.h> | ||
int k=l; | ||
void autofunc(void) | ||
{ | ||
printf("n k = %u' | ', k); | |
k=k+l; | ||
} | ||
main() | ||
{ | int i; | |
for (i = 0; i<=5; | i + +) | |
autofunc (); | ||
} |
Результат работы программы
k = | |
k = | |
k = | |
k = | |
k = |
к = б
Замечание: глобальные переменные нужны для того, чтобы организовывать обмен информацией между функциями.
Пример взаимодействия функций без использования глобальных переменных
#include <stdio.h> | |
int func(int xl, int x2) | |
{ | |
int y; | |
y=xl+x2; | |
return(y); | |
} | |
main() | |
{ | |
int xl, x2; | |
c=func( xl, x2); | |
/* вызов функции | */ |
} |
Пример взаимодействия функций с использованием глобальных переменных
#include <stdio.h> | ||
int xl, x2; | ||
int func(void) | ||
{ | ||
y=xl+x2; | ||
return(y); | ||
} | ||
main() | ||
{ | ||
xl=...; /* | изменение значений | */ |
Xz, •••/' / | глобальных */ | |
/* | переменных */ |
... | |||
c=func() ; | /* | вызов функции */ | |
} |
Программа имеет недостаток: функция func не имеет формальных параметров, обмен информацией между функциями организован через глобальные переменные х, х2. Существует опасность, что переменные х, х2 будут случайно изменены в вызываемой функции.
Пример
-32768 <= int i <= 32768
О <= unsigned int i <= 65535
Пример
unsigned int i; unsigned long j;
Явная модификация типа данныхприменяется, когда необходимо явным образом изменить тип переменной, например, в том случае, когда результат вычисления выходит за границы ранее присвоенного переменной типа.
Пример
main() | |
{ | |
int i,j; | |
long k; | |
i= 30000; | |
j= 20000; | |
k= i+j; /^ОШИБКА!! | i -k / |
printf("n%d+%d=%ld", i , j, k) ; }
Несмотря на то, что к объявлена как long, результат вычисления выражения
i+J получается типа int, поскольку / и j объявлены как int. В то же время значение
выражения выходит за границу типа int. Для того, чтобы не было ошибки,
необходимо модифицировать переменные следующим образом:
k=(long)i+(long]j;.
Таким образом, если необходимо явным образом изменить тип данных, который используется в выражениях, то перед объектами в круглых скобках нужно указать тот тип, который необходимо получить.
Модификатор extern предназначен для использования в данном программном модуле объекта, который объявлен в другом отдельном модуле.
Пример
extern тип объект 1, объект 2, ... ,объект п;
Пример
#. | include <stdio | .h> | ||
void stat( | void) | |||
{ | ||||
static | int k= | = 1; | ||
printf | ("t | k=%d" | ,k) ; | |
k++; | ||||
} |
main()
{ int i;
for( i=0; i<5; i++) stat (); }
Пример
register int i;
Объявление переменных с модификатором register приводит к помещению переменной в регистр, если регистр свободен.
Модификатор const предназначен для объявления объектов как неизменяемых. Объекту, который объявлен с данным модификатором, можно присвоить только одно значение.
Пример
const double pi = 3.14159265;
Если программист попытается изменить значение pi, то компьютер выдаст ошибку.
W.
указатель Р
Рис. 1.3. Графическое представление указателя Указатель, как и любая переменная, должен быть объявлен.
Общая форма объявления указателя
тип *имя объекта;
Пример объявления указателя
int *p; char *p;
Тип указателя — это тип переменной, адрес которой он содержит. Для работы с указателями в Си определены две операции:
1) операция «*» (звездочка) — позволяет сгенерировать значение объекта по
его адресу;
2) операция «&» (амперсант) — позволяет определить адрес объекта.
Операция «&» выдает адрес объекта, так что запись
р=&с; присваивает переменной р адрес ячейки, в которой размещается значение переменной с.
Операция «*», применённая к указателю, выдает значение объекта, на который данный указатель ссылается, например:
к=*р; —переменной к присваивается значение, размещенное по адресу, содержащемуся в указателе/?.
Пример
#include <stdio.h> main ()
{
int a, *b;
a=134; b=&a;
printf("n Значение переменной а равно %d.", a); printf("n Адрес переменной а равен %p.", &а); printf("n Данные по адресу указателя b равны
%d.",*b); printf("n Значение указателя b равно %p.",b); printf("n Адрес расположения указателя b равен
%p.", &b) ; }
Возрастание адресов
>
. . . | ... | п-2 | п-1 | ... |
Массив data[n], n — константа
Рис. 1.4 Пример
data[2]=32;
/^Второму элементу массива */ /*с именем data присваивается */ /^значение 32.*/
Элементы массива могут употребляться в программе так же, как и простые переменные.
При объявлении массива нужно обязательно указать общее количество элементов, чтобы ЭВМ могла выделить память под весь массив.
Общая форма объявления массива
тип имя[размер массива];
Пример
float data[245];
Здесь массив содержит 245 элементов типа float: data[0], data[], data[2], ..., data[244].
Связь массивов и указателей
Имя массива фактически является константой-указателем на начальный адрес данных — на адрес расположения элемента массива с нулевым индексом.
Графическое представление массива в памяти ЭВМ представлено на рис. 1.5, где data — адрес начала массива; sizeojidatd) — размер массива data в байтах; sizeojifloat) — размер памяти под один элемент массива в байтах; р и р! — указатели для работы с массивом.
Начальный адрес массива определяется компилятором в момент его объявления, и такой адрес никогда не может быть изменен. Адрес массива можно узнать, если вывести на экран значение константы с именем массива или вывести адрес нулевого элемента массива. Это значение можно присвоить указателю, имеющему другое имя, а затем, наращивая значение этого указателя, обращаться по выбору к любому элементу массива. Следовательно, в ряде случаев операции с массивами можно свести к операциям с указателями.
Возрастание адресов
о
Адрес
Начала
массива
Массивdata
о
р1 =data р2 = р1 + i
4 *
дпошаШЭЕ!
П-1
Р2-р1
-I
Общая форма инициализации массива при объявлении
тип | имя[п] | ={ значение 1, значение 2, значение 3, • • • i значение п}; |
Начальные значения элементов массива заключаются в фигурные скобки. Если программист не указал в квадратных скобках размер массива, то компилятор сам задаст размер по количеству приведенных значений. Если начальные значения в фигурных скобках не заданы, то все элементы массива будут нулевыми.
Пример инициализации массива
# include <stdio.h> main ()
{
int data [5] ={5, 4, 3, 2, lb-float a[ ]={3, 4, 2, 7, 18, 90}; char p[ ]={'f, 'f, 'c', 't', f