АЛМАТИНСКИЙ ИНСТИТУТ ЭНЕРГЕТИКИ И СВЯЗИ

 

Кафедра электроники и

компьютерных технологий

 

 

 

 

 

 

 

 

ПРОГРАММИРОВАНИЕ НА ЯЗЫКЕ Си

 

Методические указания программирование на языке Си

(для студентов всех форм обучения специальностей 050719 – Радиотехника, электроника и телекоммуникации (бакалавриат),050704 – Вычислительная техника и программное обеспечение(бакалавриат ))

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Алматы 2005

 

                                                                   

СОСТАВИТЕЛЬ: Т.С. Бимагамбетов.  Программирование на языке Си.  Методические указания программирование на языке Си (для студентов всех форм обучения специальностей 050719 – Радиотехника, электроника и телекоммуникации (бакалавриат), 050704 – Вычислительная техника и программное обеспечение (бакалавриат)). -Алматы: АИЭС, 2005. –50 с.

 

 

 

 

 

Методические указания предназначено для изучения программирования на стандартном языке Си. Ориентация сделана как на изложение синтаксиса и семантики конструкций языка, так и на их практическое использование при решении типовых задач программирование. Методическое указание написаны по рабочем программе «Спецкурс информатики 2».

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

 

 

 

 

 

          Рецензент: ст.преподаватель Г.Т. Мусатаева

 

 

 

 

 

 

 

          Печатается по плану издания Алматинского института энергетики и связи на 2005 г.

 

 

 

 

 

 

 

 

 

 

 

ã Алматинский институт энергетики и связи

         

          1 Основные понятия языка Си. Алфавит, идентификаторы, служебные слова. Константы и переменные. Основные типы данных. Операция и выражения. Программы с линейной структурой.

 

1.1   Алфавит, идентификаторы, служебные слова

Алфавит. В алфавит языка Си входят:

-  прописные и строчные буквы латинского алфавита;

-  цифры: 0, 1,……9;

-  специальные знаки: &, {}, [] (), # и др;

-  не изображаемые символы: пробел, табуляция, переход на новую

строку и др.

Комментарий формируется как последовательность знаков, ограниченная слева знаками /*, а справа - */.  Пример-  /*Это комментарий */.

          Идентификатор. Последовательность букв, цифр и символов подчеркивания «_» , начинающаяся с буквы или символа подчеркивания, считается идентификатором языка Си. Примеры- KOM_16, size88, _MIN, TIME, time. Прописные и строчные буквы различаются, т.е. два последних идентификатора различные.

          Служебные слова. Идентификаторы, зарезервированные в языке, т.е.

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

 

auto

break

case

char

const

continue

default

do

double

else

enum

extern

float

for

goto

if

int

long

register

return

short

signed

sizeof

static

struct

switch

typedef

union

unsigned

void

volatile

while

 

1.2   Константы и переменные

Целые константы. Синтаксисом языка определены целые константы: десятичные, шестнадцатеричные и восьмеричные. Десятичные целые определены как последовательности десятичных цифр, начинающиеся не с нуля (если это не число нуль): 44, -19,  0 , 1024 и т.д.

Вещественные константы. Каждая вещественная константа состоит из следующих частей: целая часть, десятичная точка, дробная часть. Наряду с десятичной также используется экспоненциальная форма записи. Экспоненциальная форма записи предназначена для представления очень больших или очень маленьких чисел, чтобы не выписывать большое количество нулей. Примеры констант с плавающей точкой- 3.14159, 0.031419Е2, 314.19Е-2.

Именованные константы. Целочисленные именованные константы можно вводить с помощью перечисления: enum тип_перечисления {список_именованных_констант}. Пример- enum day {Sunday, Monday,…}.

Вторую возможность вводить именованные константы обеспечивают определения такого вида: const тип имя_константы = значение_константы. Пример- const float pi = 3.14.

Третью возможность вводить именованные константы обеспечивает директива препроцессор: #define имя_константы значение_константы. Пример- #define pi 3.14.

Символьные и строковые константы. Символьные константы

записываются в одиночных кавычках: ‘a’, ‘d’ и т.д. Примерами могут быть наборы литер, начинающиеся с символов ‘\’, и их называют управляющими последовательностями:

‘\n’ – перевод строки;

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

‘\r’ – возврат каретки к началу строки;

‘\0’ – нулевой символ;

‘\a’ – сигнал звонок;

‘\b’ – возврат на одну позицию;

‘\f’ – перевод страницы;

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

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

“У каждой эпохи свой язык \n”. Представления строковых констант в памяти ЭВМ подчиняются следующим правилам: все символы строки размещаются подряд, и каждый символ занимает один байт; в конце записи строковой константы компилятор помещает символ  ‘\0’.

Таким образом, количество байтов, выделяемое в памяти ЭВМ на 1 больше, чем число символов в записи строковой константы.        

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

          Определение переменных: тип, список_имен_переменных, где имена переменных – это выбранные программистом идентификаторы, которые в списке разделяются запятыми. Пример- int x, symbol; int – тип.

         

1.3 Основные типы данных

Определены целочисленные типы: char - целый длиной не менее 8 бит; short int - короткий целый; int - целый; long - длинный целый. 

Каждый из целочисленных типов может быть определен либо как знаковый signed, либо беззнаковый unsigned (по умолчанию signed).

Вещественные типы: float - вещественный одинарной точности; double

- вещественный удвоенной точности; long double - вещественный максимальной точности.

 

Основные типы данных

Тип данных

Размер, бит

Диапазон значений

unsigned char

8

0……255

char

8

-128…127

enum

16

-32768…32767

unsigned int

16

0….65535

short int (short)

16

-32768…32767

unsigned short

16

0….65535

int

16

-32768…32767

unsigned long

32

0…4294967295

long

32

-2147483648….2147483647

float

32

3.4 E – 38…3.4 E + 38

double

64

1.7 E – 308…1.7 E + 308

Long double

80

3.4 E- 4932…1.1 E+ 4932

 

1.4  Операции и выражения

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

 

Ранг

Операции

Ассоциативность

1

()  [] ->   .

®

2

!  ~  +  -  ++  --  &  *  (mun)   sizeof

¬

3

*  /    %   (мультипликативные бинарные)

®

4

+   -         (аддитивные бинарные)

®

5

<<   >>    (поразрядного сдвига)

®

6

<   <=     >=   >     (отношения)

®

7

==    !=    (отношения)

®

8

&             (поразрядная конъюнкция «И») 

®

9

^              (поразрядная исключающее «ИЛИ»)

®

10

|               (поразрядная дизъюнкция «ИЛИ»)

®

11

&&         (конъюнкция «И»)

®

12

||              (дизъюнкция «ИЛИ»)

®

13

?:             (условная операция)

¬

14

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

¬

15

,               (операция «запятая»)

®

 

Опишем кратко возможности отдельных операций.

Операция sizeof позволяет определить, сколько памяти занимает то или иное значение.

Арифметические операции: + сложение, - вычитание, * умножение, / деление, % остаток – операция нахождения остатка от деления одного целого числа на другое, - минус – это унарная операция, при которой знак числа изменяется на противоположный, + плюс, ++ (--) увеличить (уменьшить) на единицу, префиксная и постфиксная формы. Последняя операция уваливает (уменьшает) операнд на единицу. Разница между постфиксной (например, х++) и префиксной (++х)  операциями заключается в том, что в первом случае результатом является значение операнда до изменения на единицу, а во втором случае – после изменения на единицу.

Операции сравнения: == равно, != не равно, > меньше, < больше,  <= меньше или равно, >= больше или равно.

Логические операции: &&- логическое И, ||- логическое ИЛИ, ! -логическое НЕ.

Битовые операции: &- битовые И, |- битовые ИЛИ, ^- битовое ИСКЛЮЧАЮЩЕЕ ИЛИ,  ~- битовые НЕ, <<- сдвиг влево, >>- сдвиг вправо. Побитовый сдвиг левого операнда на количество разрядов, соответствующее значению правого операнда. Результатом является целое число.

Условная операция. ?: условное выражение. Трехарная операция, если значение первого операнда – истина, то результат – второй операнд; если ложь – результат – третий операнд.

Операция. Выполнить выражение до запятой, затем выражение после запятой.

Операции присваивания. Присвоить значение правого операнда левому. Результат операции присваивания – это значение правого операнда. +=, -=, *=, /=, %=, |=, &=, ^=, <<=, >>= выполнить операцию и присвоить.

Скобки в качестве операции. Круглые () и квадратные [] скобки играют роль бинарных операций при вызове функций и индексировании элементов массивов.

Выражения. Выражения – это переменные, функции и константы, называемые операндами, объединенные знаками операций. Приведем примеры выражений: х*12 +y, val<3, -9.

Выражение, после которого стоит точка с запятой, – это оператор выражения.

Присваивание – это тоже операция, оно является частью выражения. Значение правого операнда присваивается левому операнду. Пример- х=х+5.

 

1.5 Программы с линейной структурой

1.5.1 Текст программы и препроцессор. Программа на языке Си – это текстовый файл. Текстовый файл проходит три обязательных этапа обработки (рисунок 3):

    - препроцессорное преобразование текста;

    - компиляция;

    - компоновка.

 Исходный   

 текст на 

 СИ

Препроцессор

Включаем

ые файлы

 Полный текст  про

граммы (единица 

  транс

  ляции)

Компилятор

Исходный текст на Си

Объектный   

      код программы

Стандарт

ные биб

лиотеки

 

Компоновщик

Исполняемая программа

 


Задача препроцессора – преобразование текста программы до ее компиляции. Правила препроцессорной обработки определяет программист с помощью директив препроцессора. Каждая препроцессорная директива начинается с символа «#».

 


Рисунок 3

 

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

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

 

1.5.2 Структура программы.

/* Простейшая программа/

# include<stdio.h>

void main ()

{ printf (“Здравствуй, мир! \n”);}

Первая строчка в программе – комментарий. # include – директива препроцессора. stdio.h - имя заголовочного файла, который содержит в себе функцию printf ().void – это тип функции main (). В этом типе функция не

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

В задачах для вычисления используются спецификаторы:

d – для целых десятичных чисел (тип int); u – для целых чисел десятичных чисел без знака (тип unsigned); f – для вещественных чисел в форме с фиксированной точкой ( типы float и double); e – для вещественных чисел в форме с плавающей точкой (с мантиссой и порядком  для типов float и double . В качестве модификаторов в спецификации преобразования используются символы:

h – для вывода значений типа short int; l - для вывода значений типа long; L - для вывода значений типа long double.

          Ширина поля – целое положительное число, определяющее длину (в позициях на экране) представления выводимого значения.

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

          Пример -

          # include<stdio.h>

          void main ()

          {

          float c, e;

          int k;

          c=48.3; k=-83; e=16.33;

          printf (“ c=%5.2f\tk=%5d\te=%8.2f\te=%11.4e”, c, k, e, e);

          }

Результат на экране:

с= 48.30     k =    -83     e =    16.33    e =  1.6330Е+01.

          Программирование алгоритмов с линейной структурой

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

.

 

Для ввода данных с клавиатуры в программе используется функция scanf (), она описана в заголовочном файле stdio.h. Математические функции описаны

8

в файле math.h   % - спецификации преобразования. Значение z выводится с шириной поля 5 (позиций на экране) и с точностью в два знака в дробной части. На рисунке 4 представлена блок-схем алгоритма.

 

     Конец

I=1,N-1

Ввод x, y

    Начало

x

Вывод z

/*  Программа пример */

# include <stdio.h>

# include <math.h>

          void main()

{

int x;

float y,z, z1,z2;

const float pi=3.14;

printf (“Введите значения x, y\n”);

scanf (“%d%f ”, &x&y);

z1=sqrt(x*x+y*y);

z2=sin(x*x+y*y);

z3= exp(5*log(x))*log(y)/pi;

z=z1/z2+z3;

printf (“z= %5.2f”, z);

}

   1.6 Задачи

1.               2.        

3.        4.

 

5.               6.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


Рисунок 4

 

2 Управляющие операторы: условные операторы, операторы циклов, операторы перехода

 

Операторы выбора – это условный оператор (if) и переключатель (switch).

Операторы циклов – это оператор с предусловием (while), оператор с постусловием (do-while) и параметрический оператор (for).

Операторы перехода выполняют безусловную передачу управления: goto (безусловный переход), continue (завершение текущей итерации цикла), break (выход из цикла или переключателя), return (возврат из функции).

2.1 Условный оператор

2.1.1 Оператор  if имеет сокращенную форму: if (выражение условия)

оператор и полную форму if (выражение условия) оператор_1; else

оператор_2; (Рисунок 5).

Если значение условия «истинно», то выполняется оператор, следующий за условием, если же условие принимает значение «ложно», то выполняется оператор, следующий за ключевым словом else. Часто встречается необходимость использовать конструкцию if- elseif:

if (условие) оператор;

else if (условие) оператор;

else if (условие) оператор;

…… else оператор.

В этой форме условия операторов if проверяются сверху вниз.

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

if (х)

  {

 if (y) оператор 1;

}

else оператор 2.

Условие

Оператор

Истинно

Ложно

(0)

(!=0)

(if)

(0)

Оператор 2

Условие

Истинно

Ложно

(!=0)

(if)

Оператор 1

 


 

 

 

 

 

 

 

 

 

 

Рисунок 5

 

          2.1.2 Оператор switch

                Язык Си имеет встроенный оператор множественного выбора, называемый switch. Основная форма оператора имеет такой вид

          switch (выражение)

{

case constant1:

последовательность операторов

break;

case constant2:

последовательность операторов

break;

…..

case constantN :

последовательность операторов

break;

default

последовательность операторов

}.

          Сначала вычисляется выражение в скобках за ключевым словом switch. Затем просматривается список меток до тех пор, пока не находится метка, соответствующая значению вычисленного выражения. Далее происходит выполнение соответствующей последовательности операторов, следующих за двоеточием. Если же значение выражения не соответствует ни одной из меток оператора switch, то выполняется последовательность операторов, следующая за ключевым словом default.

          Когда после последовательности операторов встречается ключевое слово break, то выполнение оператора break приводит к выходу из оператора

switch и переходу к следующему оператору программы.

Пример, предположим, что в переменной х хранится целое  число от 0 до 2, и нам нужно выполнить различные действия в зависимости от ее значения:


# include <conio.h>

# include <stdio.h>

void main ()

{

int x;

printf (“Введите х\n”);

scanf (“%d”, &x);

switch (x)

{

case 0:

printf (“%d”,x);

break;

case 1:

printf (“%d”,x);

break;

case 2:

printf (“%d”,x);

break;

defult:

printf (“Необрабатываемое значение);

} getch ();

}


 

          2.2 Операторы цикла

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

          2.2.1 Цикл while имеет вид:  

while (выражение условия);

тело_цикла.

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

          2.2.2 Второй формой оператора цикла является цикл do while. Он имеет форму

 do,

тело_цикла,

while (выражение условия).

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

          2.2.3 Цикл for имеет вид:

          for (выражение_1; выражение условия; выражение_3);

          тело_цикла.

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

Условие

Тело цикла

Истинно

Ложно

(0)

(!=0)

(0)

Условие

  Тело цикла

Истинно

Ложно

(!=0)

Условие

Истинно

Ложно  (0)

(!=0)

  Тело цикла

  Выражение 3

  Выражение 1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


Рисунок 6

 

          Проиллюстрируем особенности трех типов цикла на примере вычисления приближенного значения

 

 

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

Оператро for.


# include <stdio.h>

# include <conio.h>

void main ()

{

clrscr ();

float x,y,s;

int i,n,k;

printf (“Введите х\n);

scanf  (“%f ”,&x);

printf (“Введите n\n”);

scanf  (“%d ”,&n);

s=0;

for (i=0; i<=n; i++)

{

if (i= =0) k=1; // факториал нуля, единицы

else k=k*i; // факториал

y = exp (i*log (x))/ k; // xi = exp (i*log (x))

s=s+y;

}

printf (“s=%1.3f ”, s);

getch ();

}


 

Оператор do-while.


# include <stdio.h>

# include <conio.h>

void main ()

{

clrscr ();

float x,y,s;

int i,n,k;

const double e = 0.0001; // 

точность вычисления

printf (“Введите х\n);

scanf  (“%f ”,&x);

s=1.0; i=1; y=0.0; k=1;

 

do

{

s=s+y;

k=k*i;

y = exp (i*log (x))/ k;

i++;

}

while (y>e);

printf (“s=%1.3f ”, s);

printf (“s=%1.3d ”, i);

getch ();

}


 

Оператор while.

# include <stdio.h>

# include <conio.h>

void main ()

{

clrscr ();

float x,y,s;

int i,n,k;

const double e = 0.0001; // точность вычисления

printf (“Введите х\n);

scanf  (“%f ”,&x);

s=0.0; i=1; y=0.1; k=1;

while (y>e);

{

y = exp (i*log (x))/ k;

s=s+y;

i++;

k=k*i;

}

printf (“s=%1.3f ”, s);

printf (“s=%1.3d ”, i);

getch ();

}

 

2.3 Операторы перехода

2.3.1 Оператор goto

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


if (x>=0)

goto positiv;

x=-x;

positiv : // объявление метки

abs=x;


2.3.2 Оператор continue

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

итерацию цикла и не выполнять оставшиеся операторы, а сразу

перейти к следующей итерации цикла. Например, найдем сумму всех целых чисел от 0 до 100, которые не делятся на 7.


int sum=0;

for (int i=1; i<=100;  i++)

{

if (i%7 = = 0) continue;

sum=sum+1;

}


 

2.3.3 Оператор return.

Оператор return завершает выполнение функции и возвращает управление в ту точку, откуда она была вызвана. Его форма: return выражение, где выражение – это результат функции. Если функция не возвращает никакого значения, то оператор возврата имеет форму return;. Например, вычислим факториал целого числа.


 

int fact(int n)

{

int k;

if (n= =1)

{

k=1;


else k=n*fact (n-1); }

return k; }

 

 


 

          2.4 Задачи

         

2.4.1 Решить задачу с использованием операторов if и switch

1.             2.               3.               4.                  

 

2.4.1 Решить задачу с использованием операторов while, do-while и for

5.                                                6.          

7.                               8.                                  9.                          10.        

 

 

2.4.1 Решить задачу с использованием операторов goto, continue, return, break

1. Вычислить сумму заданного количества (k) первых членов ряда Фибоначчи, если известны первые два (использовать goto).

2. Введя значения переменных n и x, вычислить сумму   , где  n>0. Если n<0, использовать оператор прерывания break.

3. Используя оператор continue, произвести суммирование положительных чисел.

4. Используя оператор return, вычислить объем цилиндра.

 

3 Массивы

 

3.1 Общие сведения

Массив – это структурированный тип данных, который представляет фиксированное число элементов, связанных между собой по определенному правилу и имеющих один и тот же тип. Каждый элемент массива определяется именем массива и порядковым номером элемента, который называется индексом. Индекс- всегда целое число. Размеры массивов задаются константами или константными выражениями. Различают одномерные массивы (векторы) и двумерные массивы (матрицы). Индекс элемента одномерного массива указывает на порядковый номер элемента в нем. В двумерном массиве первый индекс соответствует номеру строки, а второй – номеру столбца, в которых стоит элемент. Элементы массива всегда нумеруются, начиная с нуля.

Определение массива. Основная форма определения массива:  тип <имя массива>[размер1] [размер2]…. [размерN]. В одномерном массиве: тип <имя массива>[размер]; тип – базовый тип элементов массива, размер – количество элементов одномерного массива. При двумерном: тип <имя массива>[размер1][размер2]. Объявление двумерного массива можно трактовать как объявление массива массивов, т.е. массив размера [размер2], элементами которого являются одномерные массивы <имя массива>[размер1].

Инициализация массива. Инициализация – это объединение определения объекта с одновременным присваиванием его элементам значений. Значения, которыми инициализируется, массив, заключаются в фигурные скобки и разделяются запятыми. Например, int A []= {1, 2, 5, 9, 10}. В данном примере не указано количество элементов одномерного массива, оно определяется по количеству начальных значений, перечисляемых в фигурных скобках A [0] = 1, A [1] = 2 и т.д. до A [4]= 10. Если количество начальных значений меньше, чем объявленная длина массива, то начальные значения получат только первые элементы массива int A [8] = {8,4,2}. Инициализируются только переменные A [0]= 8, A [1]= 4, A [2]= 2, а остальные элементы A [3],…, A [7] не инициализируются.

Двумерный массив можно инициализировать, рассматривая его как массив массивов, int A [3][5]= {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} и int A [3][5]= {{1, 2, 3, 4, 5}, {6, 7, 8, 9, 10}, {11, 12, 13, 14, 15}} эквивалентны. В то же время инициализации int A [3][5]= {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11} и int A [3][5]= {{1, 2, 3}, {4, 5, 6, 7, 8}, {9, 10, 11}} различны.

Символьные массивы могут инициализироваться как обычный массив

shar str[15]={‘C’,’+’,’+’}, а могут – как строка символов shar str[15]=“C++”. Отличие этих двух способов состоит в том, что во втором случае будет добавлен еще и нулевой байт. К тому же второй способ короче. Допускаются также объявление и инициализация массива без явного указания размера массива.

В языке Си не проверяется выход индекса за пределы массива. Если массив А [100] описан как целочисленный массив, имеющий 100 элементов, а вы в программе укажете А [200], то сообщение об ошибке не будет выдано, а в качестве значения элемента А [200] будет выдано некоторое число, занимающее соответствующие 2 байта.

 

3.2 Одномерные массивы

 

Ввод и вывод массивов осуществляются с помощью оператора цикла:

for (i=0; i<=4; i++)

scanf (“%d”, &A [i]);  ввод одномерного массива А [5].

for (i=0; i<=4; i++)

printf (“%d”, &A [i]);  вывод одномерного массива А [5].

Массив состоит из 5 элементов: A [0]- первый элемент, A [1]-второй,…, A [4] пятый элемент.

Примеры.

1 Ввод и вывод элементов массива

# include<stdio.h>

void main ()

{

int age [4];

for (int i=0; i<3; i++)

{

printf (“\n Введите возраст: ”);

scanf (“%d”, &age[i]);

}

for (int i=0; i<3; i++)

printf (“\n Вы ввели %d”, age[i]);

}

Результат.   Введите возраст: 44

          Введите возраст: 16

                    Введите возраст: 23

          Введите возраст: 68

 

          Вы ввели: 44

          Вы ввели: 16

                    Вы ввели: 23

                    Вы ввели: 68

 

2 Упорядочение элементов массива по убыванию

# include <stdio.h>

# include <conio.h>

void main ()

{ int n, i, j;

float A[100],k;

printf (“Введите количество элементов n:\n”);


scanf (“%d ”, &n);

 

for (i=0; i<n; i++)

{

printf (“A [%d]”, i);

scanf (“%f ”, &A [i]);

}

for (j=0; j<n-1; j++)

for (i=1; i<n; i++)

if (A [j])>A [i]

{

k=A [j]; A [j] = A [i]; A [i] = k;

}

printf (“Упорядочный  

массив\n”);

for (i=0; i<n; i++)

printf (“A [%d]=%f\n”, i, A [i]);

getch ();

}

Результат. A [0] = 32.3

                  A [1] = -4

                  A [2] = 0.11

 Упорядочный массив

                  A [0] = -4

                  A [1] = 0.11

                  A [2] =32.3

Каждый элемент A [j], начиная с

A [0], сравнивается со всеми последующими, и на место A [j] выбирается минимальный.


 

         


3 Определить минимальное число и их количество среди элементов массива

# include <stdio.h>

# include <conio.h>

void main ()

{

int n, i, k;

float A[100], min;

printf (“Введите количество

элементов n:\n”);

scanf (“%d ”, &n);

for (i=0; i<n; i++)

{

printf (“A [%d]”, i);

scanf (“%f ”, &A [i]);

}

min = A [0], k=0;

for (i=1; i<n; i++)

if (min>A [i] min = A [i];

for (i=0; i<n; i++)

if (min= = A [i]) k=k+1;

printf (“Упорядочный

массив\n”);

for (i=0; i<n; i++)

printf (“\n min=%f , коли-

чество min=%5d ”, min, k);

getch ();

}


 


4 Объединение двух массивов

в один

# include <stdio.h>

# include <conio.h>

# define B 3

      void main ()

{

clrscr ();

int a[B]; int b[B]; int c[2*B];

int i,j,k;                                          { if (k<B) c[k++]= a[i++];

for (i=0; i<B; i++)                          else c[k++] = b[j++];

scanf (“%d ”, &a[i]);                          }

printf (“\n”);                                    while (k<2*B);

for (j=0; j<B; j++)                          for (i=0; i<2*B; i++)

scanf (“%d ”, &b[j]);                      printf (“\t%d “, c[i]);

k = i = j = o;                                    getch ();

do                                                                }




5 Вывести на экран одномерный массив в обратном порядке

# include <stdio.h>

# include <conio.h>

# define B 2

      void main ()

{

clrscr ();

int a[B]; int i;

for (i=0; i<B; i++)

scanf (“%d ”, &a[i]);

printf (“\n”);

for (i=B; i>=0; i--)

printf (“\t%d “, a[i]);

getch ();

}


 


6 Разделить а[8] на два массива, записать в один из них элементы с четными номерами, а в другой – с нечетными

# include <stdio.h>

# include <conio.h>

      void main ()

{

clrscr ();

const int n=7;

int a[100]; b[100]; c[100];

int i,j,k,s;

j=k=s=0;

for (i=0; i<=7; i++)

{

a[i]=rand ()/100; //псевдо-

случайное целое число

printf (“\t%d “, a[i]);

if (a[i]%2 = =0) // четные

элементы

{ b[j]=a[i]; j++;k++; } //к –

число четных элементов

else {c[s]=a[i]; s++}// число

нечетных элементов

}

printf (“\n”);

for (j=0; j<k; j++)

printf (\t%d ”, b[j]);

printf (“\n”);

for (s=0; s<=n-k; s++)

printf (\t%d ”, c[s]);

getch ();

}


 

3.2 Двумерные массивы

При обработке элементов двумерных массивов необходимо открыть два цикла для изменения номера строки (i) и номера столбца (j), т.е. в одном цикле организовать другой.

Элементы главной диагонали имеют равные индексы строк и столбцов (i=j), поэтому при их переборе можно использовать только один цикл. Для


 

элементов, расположенных выше главной диагонали, номер строки меньше номера столбца (i<j), а для элементов ниже главной диагонали–наборот (i>j).

Для элементов побочной диагонали номера строки и столбца меняются одновременно, номер строки изменяется от 1 до n с шагом +1, а номер столбца от n до 1 с шагом –1. Кроме того, для элементов побочной диагонали характерно i+j=n+1 (n- порядок матрицы).

Примеры


1 Дана матрица a[4][4]. Все элементы ниже главной диагонали обнулить, выше – заменить на 3, а на главной диагонали заменить на 7.

#include <stdio.h>

#include <conio.h>

#include <stdlib.h>

void main()

{

          clrscr();

          int a[100][100], k, i, j, n;

          printf("Bedite n=");

          scanf("%d",&n);

          k=1;

          for(i=1; i<=n; i++)

          {

          for(j=1; j<=n; j++)

          {

          a[i][j]=k; k++;

         

         printf("%d",a[i][j]);

          printf("\t");

          }

          printf("\n");

          }

          printf("\n");

          for(i=1; i<=n; i++)

          {

          a[i][i]=7;

          for(j=1;j<=n;j++)

          {

          if(i>j) a[i][j]=0;

          if(i<j) a[i][j]=3;

          printf("%d", a[i][j]);

          printf("\t");

          }

          printf("\n");

          }

          getch();

          }


 


2 Дана матрица a[4][4]. Найти сумму элементов матрицы и заменить элементы побочной диагонали на 7

#include <stdio.h>

#include <conio.h>

#include <stdlib.h>

void main()

{

          clrscr();

          int a[100][100],k,i,j,n;

          float s;

          printf("Bedite n=");

          scanf("%d",&n);

          k=1;

 

          for(i=1;i<=n;i++)

          {

          for(j=1;j<=n;j++)

          {

          a[i][j]=k;k++;

          printf("%d",a[i][j]);

          printf("\t");

          s=s+a[i][j];

          }

          printf("\n");

          }

          printf("s=%1.1f\n",s);

          printf("\n");

          for(i=1;i<=n;i++)

          {

 

          a[i][5-i]=7;

          for(j=1;j<=n;j++)

          {

          printf("%d",a[i][j]);

          printf("\t");

 

          }

          printf("\n");

          }

          getch();

           }


 

 

 


3 Дана матрица a[4][4]. Найти суммы элементов каждой строки

(каждого столбца ).

#include<conio.h>

#include<stdio.h>          

#include <stdlib.h>

void main()

{

          clrscr();

          int a[100][100],s[100],k,i,j,n;

          printf("Введите n=");

          scanf("%d",&n);

          k=1;

          for(i=1; i<=n; i++)

          {

          for(j=1; j<=n; j++)

          {

          a[i][j]=k; k++;

          printf("%d",a[i][j]);

          printf("\t");

          }

          printf("\n");

          }

          printf("\n");

         /*в циклах для столбцов i

          менять на j*/

          for(i=1; i<=n; ++)

          {

          s[i]=0;

          for(j=1; j<=n; j++)

          {

          s[i]=s[i]+a[i][j];

          }

          printf("s[%d]=%d\n",i,s[i]);

          printf("\n");

          }

          getch();

          }


 


4 Поворот массива на 180 0

#include <stdio.h>

#include <conio.h>

#include <stdlib.h>

void main()

{

          clrscr();

          int a[100][100],b[100][100];

int k, i, j, n;

          printf("Введите n=");

          scanf("%d",&n);

          k=1;

          for(i=1; i<=n; i++)

          {

         

         for(j=1; j<=n; j++)

          {

          a[i][j]=k;k++;

          printf("%d",a[i][j]);

          printf("\t");

          }

          printf("\n");

          }

          printf("\n");

          for(i=1;i<=n;i++)

          {

          for(j=1;j<=n;j++)

          {

          b[i][j]=a[n+1-i][n+1-j];

         

          printf("%d",b[i][j]);

          printf("\t");

          }

          printf("\n");

          }

          getch();

          }


 

          3.3 Задачи

3.3.1  Дан массив А(5). Найти сумму и количество положительных элементов.

3.3.2  Дан массив А(5). Найти наименьший положительный элемент среди  элементов с четными номерами массива.

3.3.3  Найти наибольший среди элементов массива А(10),  остальные обнулить.

3.3.4  Дана матрица А(5,5). Все элементы ниже главной диагонали обнулить,   выше – заменить на  3.

3.3.5  Дан массив А(4,3). Переписать все его элементы в вектор В.

3.3.6  Найти среднее арифметическое отрицательных элементов матрицы А(4,4).

3.3.7 Произвести поворот исходного массива на 900 .

 

4 Указатели

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

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

Вот наиболее частые примеры их использования:

-         доступ к элементам массива;

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

-         передача в функции массивов и строковых переменных;

-         выделение памяти;

-         создание сложных структур, таких, как связанный список.

 

4.1 Адреса и указатели

Каждая переменная в программе – это объект, имеющий имя и значение. По имени можно обратиться к переменной и получить ее значение. С точки зрения машинной реализации имя переменной соответствует адресу того участка памяти, который для нее выделен, а значение переменной - содержимому этого участка памяти.  Загружаясь в память, наша программа занимает некоторое количество этих адресов. Это означает, что каждая переменная и каждая функция нашей программы начинается с какого-либо конкретного адреса. Указатели позволяют получить значения по адресу. Чтобы получить адрес в явном виде,  применяют унарную операцию &. Адреса имеют целочисленные беззнаковые значения, и их можно обрабатывать как целочисленные величины. Имея возможность с помощью операции & определять адрес переменной или другого объекта программы, нужно уметь его сохранять, преобразовывать и передавать. Для этих целей в языке Си введены переменные типа указатель. Как и всякие переменные, указатели нужно определять и описывать, для этих целей используется разделитель “*”. Кроме разделителя, в определениях и описаниях указателей задается тип объектов, на которые ссылаются указатели. Тогда указатель объявляется следующим образом:

тип * <имя переменной>.

В объявлении переменной, являющейся указателем, очень важен базовый тип, который позволяет установить, сколько байтов занимает переменная. Если указатель имеет базовый тип int , то переменная занимает 2 байта, char-1 байт.

 

          4.2 Операции над указателями

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

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

          Примеры

char *z;

          int *k, *i;

          int x=10;

          i=&x;

          k=i;

          z= NULL;.

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

 

&A-адрес указателя

Значение указателя

Указатель А

Значение объекта

Адрес объекта

Значение указателя А

 


 

 

 

 

 


Рисунок 4

          Унарные операции “++” и “--”. Числовые значения переменных типа указатель меняются по- разному в зависимости от типа данных, с которыми связаны эти переменные. Если указатель связан с типом char, то при выполнении  операции “++” и “--” его числовое значение изменяется на 1(на 1 байт), если int – на 2, если float –3.

          Аддитивные операции. Две переменные типа указатель нельзя суммировать, однако к указателю можно прибавить целую величину. При этом вычисляемое значение зависит от типа объекта, с которым связан указатель. Например, пусть указатель Р имеет значение 2000 и указывает на целое (int). Тогда в результате выполнения оператора Р=Р+3; значение указателя Р будет 2006. Общая формула Р=Р+ n*(количество байт памяти базового типа указателя).

          Операция вычитание. В отличие от операции сложения, операция вычитания применима не только к указателю и целой величине, но и к двум указателям на объекты одного типа. С ее помощью можно находить разность двух указателей и тем самым определить “расстояние” между размещением в памяти двух объектов. При этом “расстояние” вычисляется в единицах, кратных “длине” отдельного элемента данных того типа, к которому отнесен указатель. Например, int x[5], *i, *k, j; int i=&x[0]; k=&x[4]; j=k-i; j принимает значение 4, а не 8, как можно было предположить, исходя из того, что каждый элемент массива x[ ] занимает два байта.

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

          Указатели можно сравнивать. Применимы все 6 операций: <,>, <=, >=, =, = =  и !=.

          Примеры

1 Вывод значения по адресу.

          # include <stdoi.h>

          # include <conio.h>

          void main ()

          {

          int var1=11; int *ptr;

          ptr=& var1; // помещаем в ptr адрес переменной var1

          printf (“%d”, *ptr);

          getch ();

          }

Результат 11.

2 Присвоение значения var1 в  var2 через указатель.

# include <stdoi.h>

          # include <conio.h>

          void main ()

          {

          int var1, var2; int *ptr;

          ptr=& var1; ptr=37; var2=*ptr; 

printf (“%d”, var2);

          getch ();

          }

Результат 37.

          Указатель на void. Адрес, который помещается в указатель, должен быть одного с ним типа.Нельзя присвоить указателью на int адрес перменной float.

                    float var1 = 11; int *ptrint=&var1//  так нельзя

Однако есть одно исключение. Это указатель на void и определяется следующим образом: void*ptr; f

loat var1 = 11; void *ptrint=&var1//  так можно.

 

          4.3 Указатели и массивы

 

          В языке Си существует связь между указателями и массивами, так как имя массива – это адрес памяти, начиная с которого расположен массив, т.е. адрес первого элемента массива. Таким образом, если был объявлен массив int mas[5]; то mas является указателем на массив, точнее, на первый элемент массива. Для того чтобы получить значения 3-го элемента массива mas, можно написать mas[2] или *( mas+2). Результат будет один и тот же. Рассмотрим на примерах указатели и массивы.

//Обычный доступ к элементам массива

# include <stdio.h>

# include <conio.h>

void main ()

{

int mas[5]={31, 64, 77, 52, 93 };

for (int i=0; i<5; i++)

printf (“\t%d”, mas[i]);

getch ();

}

Результат: 31  54   77  52  93.

          //Доступ к элементам массива через указатель

# include <stdio.h>

# include <conio.h>

void main ()

{

int mas[5]={31, 64, 77, 52, 93 };

for (int i=0; i<5; i++)

printf (“\t%d”, *(mas+i));

getch ();

}

Результат: 31  54   77  52  93.

          Указатели – константы и указатели – переменные. Можем ли мы записать *(mas++), т.е. вместо прибавления шага к i использовать операцию увеличения. Сделать так мы не можем, поскольку имя массива- это адрес памяти и является константой, поэтому это указатель константы. Однако мы можем увеличить указатель, который содержит этот адрес. Покажем на примере.

# include <stdio.h>

# include <conio.h>

void main ()

{

int mas[5]={31, 64, 77, 52, 93 };

int *ptrint;  ptrint= mas;

for (int i=0; i<5; i++)

printf (“\t%d”, *(ptrint++));

getch ();

}

Результат: 31  54   77  52  93.

          Здесь мы определили указатель на intptrint – и затем присвоили значение адреса массива mas. Теперь мы можем получить доступ к элементам массива, используя выражение *(mas++).

 

          4.4 Задачи

 

4.4.1 Дано х=10,2. Присвоить значение х в y и увеличить ее значения на 1 с помощью указателя.

4.4.2 Дано х= 0.5. Найти значение х по адресу.

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

4.4.5 Дан одномерный массив А[6]. Расположить элементы по убыванию.

4.4.4 Дана матрица А[4,4]. Вычислить сумму положительных элементов с помощью указателя.

 

          5 Символьные данные и строки

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

5.1 Символьные переменные. Символьные переменные – это величины размером в 1 байт, которые используются для представления битов и целых чисел в диапазоне от 0 до 255 или от –128 до 127 в зависимости от того, знаковая эта переменная или беззнаковая. Единственное, что может хранить компьютер, это - числа. Поэтому для того чтобы можно было хранить символы и манипулировать ими, символам присвоены коды – целые числа. Согласно стандарту ASCII, каждому символу и некоторым управляющим инструкциям соответствуют свои числовые коды, принимающие значения от 0 до 127. Русские буквы кодируются числами от 128 до 255. Максимальное число символов может быть 255. Для символьных данных в языке Си служит базовый тип char. Описание символьных переменных имеет вид: char список_имен перменных;. Например: char a, z;

          Ввод-вывод символьных данных. Для ввода и вывода символьных значений в форматных строках библиотечных функций scanf() и printf() используется спецификация преобразования  %c.

Например -

          # include <stdio.h>                       # include <stdio.h>   

          # include <conio.h>                      # include <conio.h>

          void main ()                                  void main ()

          {                                                    {

          char z;                                            char z;

          printf (“Введите символ”);          printf (“Нажмите какую-либо клавишу”);

          scanf (“%c” , &z);                         z=getchar(); //вводит один символ

          printf (“%c” , z);                            putchar(x);

          getch ();                                         getch ();

          }                                                      }

          Помимо scanf () и printf() для ввода и вывода символов в библиотеке предусмотрены специальные функции: getchar() – функция без параметров, которая  позволяет читать с клавиатуры по одному символу за обращение;  putchar(x) выводит символьные значения х на экран дисплея (вторая программа).

 

5.2   Символьные константы

          Для изображения отдельных знаков, имеющих индивидуальные внутренние коды, используются символьные константы. Каждая символьная константа-это лексема, которая состоит из изображения символа и ограничивающих апострофов. Например: ‘A’, ‘a’, ‘0’,’+’ и т.д. Символьная константа имеет целый тип, т.е. символы можно использовать в качестве целочисленных операндов в выражениях.

          # include <stdio.h>

          void main ()

          {

          char z;

          z=’a’;

          printf (“%c”,z);

          }

 

5.3 Строки

          В отличие от других языков (например от Паскаля (string), в языке Си нет отдельного типа для строк. Принято, что строка – это массив символов, заканчивающийся нулевым байтом, она всегда имеет тип char[]. Так как в конец строки добавлен нулевой байт ‘\0’, количество элементов в таком массиве на 1 больше. В программе строки, представляются последовательностью изображений символов, заключенной в кавычки, “любые символы”. В записи строки может быть один символ “А”, однако, в отличие от символьной константы ‘А’, длина строки равна двум байтам. В функции scanf() и printf() для символьных строк используется спецификация преобразователя %s. Пример-

          # include <stdio.h>

          void main ()

          { char A[17]=”Сезам, откройся!”;

          printf (“%s” ,A); }

В приведенном примере при инициализации массива выделяются 17 байтов, из них 16 символов и плюс нулевой байт окончания строки. Кроме функций scanf() и printf(), для ввода-вывода используется gets() и puts().

Пример -

          # include <stdio.h>

          void main() {shar str[80];

          printf (“Введите Ваше имя:”) ; getch (str);

          printf(“Ваше имя:”); puts(str); }

           

          5.3.1 Строки и указатели

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

          сhar A[]=”Hello, word!” – массив в который записана строка char*A=”Hello, word!” –  строка записана с помощью указателя. В следующем примере, использующем приведенные функции, в массиве result будет образована строка «1 января 1998 года, 12 часов»:

          char result[100];

          char*date = “1 января 1998 года ”;

          char*time = “12 часов”;

          strspy(result, date);

          strcat(result, “,”); strcat(result, time);

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

длины. В следующей программе определены и при инициализации связаны набор строки, одномерный массив А[] указателя char*.

          # include <stdio.h>

          void main ()

 

{char*A[] = {“one”, “two”, “three”, “four”, “five”};

          int i,n;

          for(i=0;i<=4; i++)

          printf(“\t%s”, A[i]); }

Результат: one, two, three, four, five.

         

5.3.2 Функции для работы со строками

Для работы со строками существует специальная библиотека, описание которой находится в файле string.h (см.приложение 2). Рассмотрим наиболее часто использующиеся функции.

Функция strcpy(). Вызов функции strcpy () имеет вид strcpy(s1,s2). Она используется для копирования содержимого строки s2 в  строку s1.

Функция strcat() имеет вид strcat(s1,s2). Функция присоединяет строку s2 к  строке s1 и помещает ее в массив, где находилась строка s1, при этом строка s2 не изменяется. Нулевой байт , который завершал строку s1, будет заменен первым символом строки s2.

Функция strlen() имеет вид strlen(s). Функция возвращает длину строки s, при этом завершающий нулевой байт не учитывается.

Функция strcmp() имеет вид strcmp(s1,s2); Сравнивают строки s1 и s2. Результат отрицателен, если s1<s2; равен 0, если  строки равны, и положителен , если s1>s2 (сравнение беззнаковое).

          Функция strstr() имеет вид strstr(s1,s2). Ищет в строке s1 под строку s2. Возвращает указатель на тот элемент в строке s1, с которого начинается

подстрока s2.

          Примеры

1 Копирование, объединение двух слов и определение длины строки

# include <stdio.h>

# include <conio.h>

# include <string.h>

void main ()

{ clrscr();

char s1[10], s2[10]; char des[40];

scanf (“%s”, &s1); scanf (“%s”, &s2);

strcat (des,s1); strcat (des,s2);

strcpy(s1,s2); printf (“%s”, s1); printf (“\n%s”, des);

printf(“Длина строки (число символов) s2=%d”,strlen(s2));

getch (); }

 

2 Определение количества букв «а» в данном слове

          # include <stdio.h>

# include <conio.h>

void main ()

{clrscr ();

char*s1; int i,k;

scanf (“%s”, &*s1);

for (k=0, i=0; s1[i]!=’\0’; i++)

if (s1[i]== ‘a’) k++;

printf (“%d”, k); getch (); }

 

3 Определение количества слов в данном предложении

          # include <stdio.h>

# include <conio.h>

void main ()

{clrscr ();

char s1, s2; int k;

printf (“Напишите предложения с точкой в конце: \n ”);

for (k=1, s1=s2=’ ‘; s1!=’.’; s2= s1)

{scanf (“%c”, &s1);

if (s1== ‘ ’)  &&  s2=’ ’ ) continue; if (s1==’ ’)  k++; }

printf (“%d”, k); getch (); }

 

4 Считывание символов

          # include <stdio.h>

# include <conio.h>

void main ()

{clrscr (); char s1; int k;

for (k=0, (s1=getchar())!=’.’; )

if (s1!= ‘ ’)  k++; printf (“%d”, k); getch (); }

 

          5.4 Задачи

5.4.1 Дана строка символов. Определить количество слов, начинающихся с буквы “с”.

5.4.2  Дана строка символов. Вывести на экран самое длинное слово.

5.3.3  Даны два слова, сложить и найти длину .

5.4.4  Написать программу , которая запрашивает у пользователя имя и отчество, затем здоровается с ним.

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

5.4.6      Дана строка текста , в которой слова разделены двоеточиями и пробелами. Необходимо :

а) определить количество слов в строке и вывести на экран все слова, количество букв у которых нечетное;

б) найти самое короткое слово в строке, которое заканчивается на букву «а»;

в) исключить из строки все слова, которые заканчиваются на букву «а».

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

 

6 Функции

 

6.1 Общие сведения о функциях

 

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

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

Определение функции. Определение функции состоит из заголовка и тела функции.

Тип_имя функции (список параметров)

{тело функции }

Тело функции состоит из последовательности операторов. Заголовок функции должен соответствовать ее прототипу. Список параметров состоит из перечня типов и имен параметров, разделенных запятыми. Когда функция не возвращает никакого значения, она должна быть описана как функция типа void ().

Объявление функции. Объявление функции состоит из заголовка функции. Тип_имя функции (список параметров).

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

Вызов функции. Для того чтобы вызвать функцию, необходимо указать только имя функции и круглые скобки. Имя функции (список фактических параметров). При вызове не указывается тип возвращаемого значения. Между формальным и фактическими параметрами должно быть соответствие. Лучше всего, когда тип фактического параметра совпадает с типом формального параметра. В прототипе функции имя формальных параметров можно не указывать, поэтому прототип функции может выглядеть так: int func (float a) или int func (float).

Пример -

# include <stdio.h>

# include <conio.h>

void stline (char, int); //или void stline (char ch, int n) прототип функции

void main ()

{clrscr ();

stline (‘-‘, 25); // вызов функции

printf (“\nТип данных диапазон\n”);

stline (‘=‘,17); // вызов функции

printf (“\nchar    -128 …. 127\n”);

stline (‘+‘,18); // вызов функции ; getch (); }

// Определение функции

void stline (char ch, int n) // заголовок функции

{ int i=0; i<n; i++) // тело функции

printf (“%c”, ch); }

Результат: --------------------------

                 Тип данных диапазон

       ================

        char    -128 …. …..27

      +++++++++++++++++

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

 # include <stdio.h>

# include <conio.h>

void stline (char, int);

void main ()

{clrscr ();

char chin; int n;

printf (“Введите символ\n”); scanf (“%c”, &chin);

printf (“Введите число повторений символа:\n”); scanf (“%c”, &n);

stline (chin,n);

getch (); }

void stline (char ch, int n) // заголовок функции

{ int i=0; i<n; i++) // тело функции

printf (“%c”, ch); }

 

Результат: Введите символы: +

                   Введите число повторений символов: 20

                   ++++++++++++++++++++

 

6.2 Ссылки на аргументы

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

void swap (int a, int b)                          void swap (int *a, int *b)

{                                                             {

int tpm = a;                                                int tpm = *a;

a = b;                                                         *a = *b;

b = tpm;   }                                               *b = tpm; }

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

          # include <stdio.h>

          # include <conio.h>

          void main ()

{ int k =-3;  positive (&k);

printf (“\nk=%d”, k); getch (); }

void positive (int *m)

{ *m =*m > 0 ? m : -m; }

Результат: k =3. Параметр функции positive () – указатель типа int *m. При обращении к ней из основной программы main () в качестве фактического параметра используется адрес &k переменной типа int. Внутри функции значение аргумента (т.е. адрес &k) “записывается ” в участок памяти, выделенный для указателя int *m. Разыменование  *m обеспечивает доступ к тому участку памяти, на который в этот момент “смотрит ” указатель m. Тем самым в выражении *m =*m > 0 ? m : -m все действия выполняются над значениями той переменной основной программы (int k), адрес которой (&k) использован в качестве фактического параметра.

 

6.3 Указатель на функцию

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

Тип (*имя_указателя) (спецификация_параметров); спецификация_параметров определяет состав и типы параметров функции.

Пример- int (*point) (double x).

# include <stdio.h>

# include <conio.h>

void stline (char,int)

void main ()

{clrscr ();  char chin; int n;

printf (“Введите символ\n”); scanf (“%c”, &chin);

printf (“Введите число повторений символа:\n”); scanf (“%d”, &n);

(*stline) (chin,n);

getch (); }

void stline (char chin, int n) // заголовок функции

{ int i=0; i<n; i++) // тело функции

printf (“%c”, chin); }

6.4 Рекурсия

Существование функций делает возможным использование такого средства программирования, как рекурсия. Рекурсия – это вызов функции

самого себя на выполнение. Классический пример рекурсивной функции – это вычисление факториала целого числа.

          # include <stdio.h>

          # include <conio.h>

int factorial (int); // прототип

          void main ()

{

int n;

printf (“Введите целое число n=”); scanf (“%d”, &n);

printf (“Факториал числа n!=%d”, factorial (n));

getch (); }

int factorial (int n)

{if (n>1) return n*factorial (n-1); // вызов самой себя

else return 1;

}

Если функция main () вызвала функцию factorial () с аргументом , равным 5, то сначала функция factorial () вызовет себя с аргументом – 4, затем этот вызов обратится к функции factorial () с аргументом –3 и т.д. Каждый экземпляр функции хранит свое значение параметра n во время выполнения. После того функция вызывает себя четырежды, пятый вызов производится с аргументом, равным 1. Четвертый вызов хранит значение параметра, равное 2, поэтому, умножая значения 5 вызовом, он получит число 2*1=2, которое будет возращено третьему вызову. Третий вызов хранит значение параметра, равное 3, поэтому второму вызову будет возвращено значение 3*2=6 и т.д.  

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

6.5 Область видимости и классы памяти

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

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

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

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

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

auto-автоматическая;

extern-внешняя;

static-статическая;

register-регистровая.

Три класса памяти указываются модификаторами – ключевыми словами, стоящими перед спецификацией типа переменной. Например, static int sum; register int plus; extern int j. Если ключевого слова перед спецификацией типа локальной переменной при ее объявлении нет, то по умолчанию она принадлежит классу auto. Поэтому практически никогда это ключевое слово не используется. Автоматическая переменная создается при входе в блок функции. При выходе из блока автоматическая переменная пропадает, а область памяти, в которой находилась эта переменная, считается свободной и может использоваться для других целей.

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

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

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

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

 

Класс памяти

Ключевые

слова

Время существования

Область действия

Автоматический

auto

временно

блок

Регистровый

register

временно

блок

Статический локальный

static

постоянно

блок

Статический глобальный

static

постоянно

файл

Внешний

extern

постоянно

программа

 

6.6 Массивы и строки как параметры функции

6.6.1 Массивы в качестве аргументов. Если в качестве параметра функции используется обозначение массива, то на самом деле внутрь функции передается только адрес начала массива. Например, заголовок функции можно написать двумя способами: float fun(int n, float a[])  или float fun(int n, float *a). Конструкции float a[]  и float *a совершенно равноправны в спецификациях. Во втором случае a определяется как указатель типа float *.

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

# include <stdio.h>

# include <conio.h>

void quart (int, float) // прототип

void main ()

{ float z[]= {1.0, 2.0, 3.0, 4.0}; int j;

quart (4, z); // обращение к функции

for (j=0; j<4; j++)

printf (“\nz[%d]=%f ”, j,s[j]); getch (); }

void quart (int n, float*x)

{ int i; for (i=0; i<n; i++)

  *(x+1)*=*(x+1); }

В теле функции разыменовано выражение, содержащее имя массива-параметра, т.е. вместо индексированной переменной x[i] используется *(x+1).

6.6.2 Строки как параметры функции. Строки в качестве фактических параметров могут быть специфицированы либо как одномерные массивы типа char[], либо как указатели типа char*. В обоих случаях с помощью параметра в функцию передается адрес начала символьного массива, содержащего строку. В отличие от обычных массивов, для параметров строк нет необходимости явно указывать их длину. Признак ‘\0’, размещаемый в конце каждой строки, позволяет всегда определять ее длину. Приведем примеры использования функции для решения некоторых типовых задач обработки строк. 

1 Функция поиска в строке ближайшего слева вхождения другой строки. Функция index() возвращает номер позиции, начиная с которой, ctr2

полностью совпадает с частью строки ctr1. Если строка ctr2 не входит в ctr1, то вовращается значение –1.


#include <stdio.h>

#include <conio.h>

int index(char[], char[]);

void main()

{

     clrscr();

     char c1[]="сумма масс";

     char c2[]="ма";

     char c3[]="аи";

printf("%s индекс=%d", c2, index (c1,c2));

printf("%s индекс =%d", c3, index(c1,c3));

     getch();

}

int index(char *ctr1, char*ctr2)

{

 int m1, m2, i, j;

for(m1=0; ctr1[m1]!='\0'; m1++);

for(m2=0; ctr2[m2]!='\0'; m2++);

           if (m2>m1) return -1;

           for(i=0;i<=m1-m2;i++)

           {

           for(j=0; j<m2;j++)

           if (ctr2[j]!=ctr1[i+j]) break;

           if (j==m2) return i;

 }

           return -1;

}



Результат: ма индекс=3; аи индекс=-1.

 

2 Функция инвертирования строки-аргумента с параметром-массивом: 


#include <stdio.h>

#include <conio.h>

#include <string.h>

void invert(char []);

void main()

{

    clrscr();

    char ct[]="0123456789";

    printf("\n%s", ct);

    invert(ct);

    printf("\n%s", ct);

 getch(); 

}

void invert(char e[])

          {

           char s,ct;

           int i,j,m;

           for(m=0; e[m]!='\0'; m++);

           for(i=0, j=m-1; i<j; i++, j--)

           {

           s=e[i];e[i]=e[j];e[j]=s;

           } }


Результат: 9876543210.

 

3 Функция вычисления длины строки:


#include <conio.h>

    char ct[]="0123456789";


#include <string.h>

#include <stdio.h>

void len(char []);

void main()

{

clrscr();

 

     len(ct);

     getch();

    }

    void len(char e[])

   {

    int m;

37     

 for(m=0; e[m]!='\0'; m++);

 printf("%d", m);

            }


 

Результат: 10.

 

4 Функция копирования содержимого строки c2 в с1.


#include <string.h>

#include <stdio.h>

#include <conio.h>

void copy(char[], char[]);

void main()

{

     clrscr();

     char c2[]="0123456789";

     char c1[100];

     copy(c1,c2);

     printf("%s",c1);

     getch();

}

void copy(char *c1, char*c2)

          {

           int m;

           for(m=0; c2[m]!='\0'; m++)

           c1[m]=c2[m];

           c1[m]='\0';

           }


Результат: 0123456789.

 

5 Функция соединения (конкатенации) строк.  Функция «присоединяет» к первой строке-аргумента вторую строку-аргумент:


#include <string.h>

#include <stdio.h>

#include <conio.h>

void conc(char[], char[]);

void main()

{

     clrscr();

     char c2[]="01234";

     char c1[]="5678";

     conc(c1,c2);

     printf("%s", c1);

     getch();

}

void conc(char *c1, char*c2)

          {

           int m,i;

           for(m=0; c1[m]!='\0'; m++);

           for(i=0; c2[i]!='\0'; i++)

           c1[m+i]=c2[i];

           c1[m+i]='\0';

           }


Результат: 567801234.

 


 

6 Функция сравнения строк. Функция row () возвращает значение –1, если длины строк-аргументов с1, с2 различны, 0-если все символы строк совпадают. Если длины строк одинаковы, но символы не совпадают, то возвращается порядковой номер (слева) первых не совпадающих. 


#include <string.h>

#include <stdio.h>

#include <conio.h>

void row(char[], char[]);

void main()

{

     clrscr();

int m1, m2, i; char c2[]="0178";

char c1[]="5678";

row(c1,c2);

getch();

}

void row(char *c1, char*c2)

          {


 

 


 for(m1=0; c1[m1]!='\0'; m1++);                    

 for(m2=0; c2[m2]!='\0'; m2++);

 if (m1!=m2) printf("-1");

 if (m1==m2) printf("0");

 for(i=0; i<m1; i++)

 if (c1[i]++!=c2[i]++) printf("\n%d",i+1);

 }

Результат: 0 1 2.


 


 


7 Функция выделения подстроки. Результате выполнения функции – строка с2[] из k символов, выделенных из строки с1[], начиная с символа, имеющего номер n.


#include <stdio.h>

#include <conio.h>

void str(char[], char[], int, int);

void main()

{

     clrscr();

     char c1[]="Текущее время";

     char *c2;

     str(c1,c2,8,15);

     printf("\n%s",c1);

     printf("\n%s",c2);

     getch();

}

 

Результат: время.

 

void str(char *c1,char*c2,int n,int k)

          {

           int m,i;

           for(m=0; c1[m]!='\0'; m++);

           if(n<0 || n>m || k<0 || k>m-n)

           {

           c2[0]='\0';

           return;

           }

           for(i=n; i<k+n;i++)

           c2[i-n]=c1[i-1];

           c2[i-n]='\0';

           return;

 



 

6. 7 Задачи

 

6.7.1 Даны действительные числа s,t . Вычислить , 

где     .

 

6.7.2 Даны действительные числа s,t . Вычислить ,  где   .

 

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

 

 

6.7.4 Составить программу для вычисления функции (использовать рекурсию)

 

 

В функции вычисляется

 

 

     6.7.5  Вычислить значение функции

     

        

 

 

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

 

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

 

7 Структура и объединение

7.1 Структура- это объединенное в единое целое множество поименованных элементов данных. В отличие от массива, всегда состоящего из однотипных элементов, компоненты структуры могут быть разных типов и все должны иметь различные имена. В различие стандартных типов (int, float…) программист вводит производный тип, называемый структурным с помощью struct. Формат структурного типа: struct имя_структурного_типа

{    }. К примеру, опишем структуру для хранения информации о деталях изделий. 

          struct  part //  

          { int modelnumber; // объявление структуры

          int partnumber ;// номер модели изделия

          float cost; // };   // стоимость детали

 

          7.1.1 Определение структурной переменной. Формат определения структурной переменной  struct  имя_структурного_типа имя_переменной. Определение  структурной переменной по своему синтаксису идентично определению переменной стандартного типа. Конструкция struct  имя_структурного_типа играет ту же роль, что dоuble или int. С помощью struct  part можно либо определить конкретную структуру, либо указатель на

структуры такого типа: struct part part1 (определение структуры с именем part1); struct part *point_to (определение указателя point_to на структуры).

          7.1.2 Доступ к полям (элементам) структур. Доступ к элементам структуры осуществляется с помощью операции «точка». Например, part1. modelnumber=6244. Приведем пример полностью.

          #include<stdio.h>

          struct part

          { int modelnumber

          int partnumber

          float cost };

          int main()

          { part patr1;

          part1.modelnumber=6244;

          part1.partnumber=373;

          part1.cost=217.55;

          // Вывод значений полей на экран

          printf (“Модель %d”, part1.modelnumbe);

printf (“,деталь %d”, part1.partnumber);

printf (“,цена $ %f”, part1.cost ); } 

Результат: Модель 6244, деталь 373, цена $217.55.

          7.1.3 Структуры, массивы, указатели и функции

Массивы структур. Массивы, как любые другие данные, могут быть элементами структуры. Приведем примеры. 

 

#include<stdio.h>

const int size=4;

struct part

{ int modelnumber ; int partnumber; float cost; };

int main()

{ int i;

struct part apart[size];

for(i=0; i<size; i++)

          {

printf (“Введите номер модели:”); scanf(“%d”,&apart[i].modelnumber);

printf (“Введите номер части:”); scanf(“%d”,&apart[i].partnumber);

printf (“Введите стоимость:” ); scanf(“%f”,&apart[i].cost);                                  }

printf(“\n”);

for(i=0; i<size; i++){

          printf (“Модель %d”, apart[i].modelnumber);

printf (“Часть %d”, apart[i].partnumber);

printf (“Стоимость %f”,  apart[i].cost);                                                

          } return 0; }.

Пользователь вводит номера моделей, номера частей и стоимости частей. Программа записывает эти данные в структуру,  запрашивает эти данные для разных частей и хранит их в четырех элементах массива apart. Затем она выводит информацию. Массив структур определен в строке struct part apart[size].

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

          Указатели на структуру. Формат указателя на структуру следующий: struct part *p_part;  p_part – переменная типа «указатель на структуру struct part». Если мы перейдем на структуру по значению, то все элементы структуры заменяются в стеке. Если же структура в качестве своего элемента содержит массив, то стек может переполниться. При передаче по ссылке в стек занесется только адрес структуры, в этом случае появляется возможность изменять содержимое элементов структуры.

          sturct part

          {int modelnumber; float cost;} part1, part2;

struct part *a; // Объявление указателя

          a=&part1; // Указателю а присваивается адрес переменной part1

           Получить значение элемента modelnumber переменной part1 можно двумя способами: (*a).modelnumber  или a à modelnumber. Специальная операция à (стрелка, arrow). Операция «стрелка» употребляется вместо

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

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

struct part {int modelnumber; float cost;} part1[3], *part2.

/*Инициализация указателей*/

struct part p_part=&part1[1]; part2=&part1[0];

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

#include <stdio.h>

void main()

{ struct complex { float x; float y; }

array [] ={ 1.0, 2.0, 3.0, -4.0, -5.0, -6.0,-7.0, -8.0};

struct complex summa={0.0, 0.0}; struct complex *point=&array [0];

int k,i; k=sizeof(array)/sizeof(array[0]);

for (i=0; i<k; i++) { summa.x=pointàx;  summa.y=pointày; point++; }

printf (“\n Сумма: real=%f,\t  imag=%f”, summa.x, summa.y); }

Результат: real=-9.000000,   imag=-16.000000. Доступ к структуре summa реализован с помощью уточненных именем (операция «точка»). Доступ к элементам структуры, входящих в массив, осуществляется через указатель point и с помощью операции «стрелка».

Структуры и функции. Для функции «структура» может использоваться как возвращающая значение функции или в параметрах функции. Кроме того, в обоих случаях могут использоваться указатели на объекты структурного типа. Рассмотрим их.  

Определение структурного типа: struct person {char *name; int age;}.

Функция может возвращать значение функции структуре как результат: struct person fun(int N).

Функция может возвращать указатель на структуру: struct person *ffunc(void).

Параметром функции может быть структура: void fffunc(struct prson str).

Параметром функции может быть указатель на объект структурного типа: void ffffunc(struct person *pst).

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

Передача структуры в качестве аргумента функции.

          #include<stdio.h>

          struct part { int modelnumber; int partmumber; float cost;};

          void show_struct {part}; /прототип функции

          void main() { part part1; part1. modelnumber=6244;

 

part1.partmumber=373;

part1.cost=217.55;}

void show_struct (part); } // вызов функции

void show_struct (part  Part) // описание функции

{ printf (“модель %d”, Part.modelnumber);

printf (“ , деталь %d”,

Part. partmumber );

printf (“, цена$ %f”, Part.cost ); }

Результат: модель 6244, деталь 373, цена$ 217.55.

Изменение структуры внутри функции.

#include<stdio.h>

struct part { int modelnumber; int partmumber;

float cost;} part1= {6244, 373, 217.55}; // первоначальная инициализация

void change_struct (part*); //прототип функции

void main() { printf (“Модель %d”, part1.modelnumber);

printf (“ , деталь %d”, part1. partmumber );

printf (“, цена$ %f”, part1.cost );

change_struct {&part1); // вызов функции

/*После передачи функции/

printf (“Модель %d”, part1.modelnumber);

printf (“ , деталь %d”, part1. partmumber );

printf (“, цена$ %f”, part1.cost);

void change_struct (part *Part) {Partà modelnumber=24; /*Описание функции*/

Partà partmumber =200;  Partà cost =112.2;}

Результаты: Модель 6244, деталь 373, цена$ 217.55.

                     Модель 24, деталь 200, цена$ 112.2

          7.1.4 Вложенные структуры

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

          struct employee { char name [20]; int age;

unsigned employee_number; // номер служащего

          struct data { int day; int month; int year;} hire_date;

          float salary; } new_employee;

Для обращения к члену вложенной структуры с начала указываются члены внешней структуры, а затем – вложенной.

Например: new_employee.hire_date.month=12.

 

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

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

union { int number; char cost[2] part.

Байт

Байт

number

Рисунок  4


Схема размещения объединения  part в памяти приведена на рисунке 5. Информация в объединении  записана с помощью одного из элементов (наибольшей размер int – 2 байта ), а выбрать данные из того же  участка памяти можно с помощью другого элемента того же объединения.  Таким образом, основное свойство объединения состоит в том, что все его элементы размещаются от начала одного и того же участка памяти. А размер участка памяти, отводимого для объединения, определяется размером самого большого из элементов.

cost[0]

   cost[1]


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

struct value {

enum numbertype {shorttype, longtupe, doubletype};

numbertype type;

short sx;

long bx;

double dx; };

В этом случае память выделяется под все три атрибута sx, bx, dx, реально используется только один из них. Сэкономить память можно, используя объединение: 

struct value {

          enum numbertype {shorttype, longtupe, doubletype};

          numbertype type;

          union number {short sx; long bx; double dx; } val; }.

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

(в данном случае dx).

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

          enum имя_типа {список_названий} список переменных. Список переменных может быть пустым. Пример определения перечисляемого типа

и переменной данного типа: enum seasons {win, spr, sum,aut}; enum seasons s.

          Ключом к пониманию сущности перечисляемого типа является то, что каждое из имен win, spr, sum,aut представляет собой целую величину. Если эти величины не определены по-другому, то по умолчанию они соответственно равны нулю, единице, двум и трем. Во время объявления

типа можно одному или нескольким символам присвоить другие значения, например: enum value {one=1, two, three, ten=10, thousand=1000, next}, если напечатать значения, то на экране появятся числа 1, 2, 10, 1000, 1001, т.е каждый следующий символ увеличивается на единицу по сравнению с предыдущим. Приведем пример, в котором перечисление используется для создания типа данных, хранящего дни недели.

          #include<stdio.h> 

          enum days_of_week {sun, mon, tue, wed, thu, fri, sat};

          int main()

          { enum days_of_week day1,day2;

          day1=mon;day2=thu;

int diff=day2-day1;

printf (“Разница в днях:%d”, diff);

if(day1<day2) printf (“\nday1 наступит раньше, чем day2”);

return 0; }

Внутренне перечисляемые типы данных представляются и обрабатываются как целые числа. Арифметические операции над переменными перечисляе-мых типов сводятся к операциям над целыми числами. Нельзя использовать операции ++ и --.

7.4 Переименование типов- typedef. Язык Си позволяет, кроме того, дать новое название уже существующим типам данных. Для этого использу-

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

          typedef struct {определение_элементов}-

          обозначение_структурного типа.

Пример-

          typedef struct

          { double real; double imag; } complex.

Приведенное определение вводит структурный тип struct { double real; double imag; } и присваивает ему обозначение (имя) complex. С помощью этого обозначения можно вводить структуры (объекты) так же, как обычным именем структурного типа: complex sigma, alfa (определены две структуры).  

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

typedef struct st_complex

          { double real; double imag; } complex;

Теперь для определения переменной можно использовать

struct st_complex sigma, alfa или complex sigma, alfa.

          7.5 Битовые поля. В отличие от других языков программирования, язык Си обеспечивает доступ к одному или нескольким битам в байте или слове. Если переменные принимают только два значения, такие переменные называют флагами (например, логические), используют 1 бит. Один из методов, встроенных в язык Си и имеющих доступ к биту , –

это поля битов. Поля битов- это специальный тип членов структуры, в

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

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

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

          struct имя_структуры

          { тип имя1:длина_в_битах;

          тип имя2:длина_в_битах;

             ………..

          тип имя N:длина_в_битах; }, 

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

Битовое поле может быть только элементом структуры или объединения и вне объектов этих типов не встречается. Битовые поля не имеют адресов и не могут объединяться в массивы. Если вместо struct употреблять служебное слова union, то определяется объединение с

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

          struct timeanddate     

{ unsigned hours                   :5; // часы от 0 до 24

   unsigned mins                    :6; // минуты  

   unsigned secs                     :6; // секунды от 0 до 60

   unsigned weekday              :3; // день недели

   unsigned monthday            :6; // день месяца от 1 до 31

   unsigned month                  :5; // месяц от 1 до 12

   unsigned year                     :8; // год от 0 до 100

};         

          Одна структура timeanddate требует всего 39 битов, т.е. 5 байтов. Если бы мы использовали для каждого атрибута этой структуры тип char, нам бы потребовалось 7 байтов.  

 

 

8 Файлы

 

8.1 Работы с файлами

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

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

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

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

В языке Си существуют два типа потоков: текстовый и двоичный. Текстовый поток – это последовательность символов. Двоичный поток – это последовательность байтов, которые взаимооднозначно соответствуют тому, что находится на внешнем устройстве. Каждый поток, связанный с файлом, имеет управляющую структуру, называемую FILE. Она описана в заголовочном файле stdio.h. В структуре FILE содержатся компоненты, с помощью которых ведется работа с потоком, в частности, указатель на буфер, указатель текущей позиции в потоке и другая информация. При открытии потока в программу возвращается указатель на поток, являющийся указателем на объект структурного типа FILE. Этот указатель идентифицирует поток во всех последующих операциях. Указатель на поток, например f1, должен быть объявлен в программе следующим образом:

#include<stdio.h>

FILE *f1.

Указатель на поток приобретает значение в результате выполнения функции «открытие потока»:

f1= fopen(имя_файла, режим_открытия); f1= fopen(“t.txt”, “r”),

где “r” означает, что существующий текстовый файл открывается только для чтения. При открытии потока могут возникнуть ошибки. Для вывода на экран дисплея сообщения об ошибке используется стандартная

библиотечная функция perror(), прототип  void perror(const char*s). Заметим, что указатель на поток в любом режиме, отличном от аварийного, никогда не бывает равным NULL.

 

8.2 Функции и режим работы файлов

          8.2.1 Функции

Функции

                                     Действие функции

fopen()

Открыт файл

Flcose()

Создать для записи

putc()

Записать символ в поток

getc()

Прочитать символ из потока

fseek()

Изменить указатель позиции файла на указанное место

Fprintf()

Форматная запись в файл

Fscanf()

Форматное чтение из файла

feof()

Возвращает значение «истинно», если достигнут конец файла

ferror()

Возвращает значение «ложно», если обнаружена ошибка

fread()

Читает блок данных из потока

fwrite()

Записывает блок данных в поток

Rewind()

Устанавливает указатель позиции файла на начало

Remove()

Уничтожает файл

          8.2.2 Режимы работы файлов

Режим

                                          Действие

r

Открыть для чтения

w

Создать для записи

a

Открыть для добавления в существующий файл

rb

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

wb

Открыть двоичный файл для записи

ab

Открыть двоичный файл для добавления

r+“

Открыть файл для чтения и записи

w+“

Создать файл для чтения и записи

a+“

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

r+b

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

w+b

Создать двоичный файл для чтения и записи

a+b

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

rt

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

wt

Создать текстовый файл для записи

at

Открыть текстовый файл для добавления

r+t

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

w+t

Создать тестовый файл для чтения и записи

a+t

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

         

if ((f1=fopen (“t.txt”, “w”))= =NULL)

          {

          perror(“Ошибка при открытии файла t.txt \n”);

          getch(); return 1; }

Для закрытия файла используется библиотечная функция int fclose (указатель_на _поток);

 

8.3 Работа с файлами на диске. Файлы последовательного доступа.

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

fgetc(), getc() - ввод (чтение) одного символа из файла;

fputc(), putc() - запись одного символа в файл;

fprintf() - форматированный вывод в файл;

fscanf() - форматированный ввод (чтение) из файла;

fgets() - ввод (чтение) строки из файла;

fputs() - запись строки в файл.

 

8.3.1 Двоичный режим обмена с файлами. Двоичный режим обмена организуется с помощью функций getc() и putc(), обращение к которым имеет следующий формат и прототипы:

c=getc(f1), int getc (FILE *stream); putc(c,f1), int putc (int c, FILE *stream), где с- переменная типа int для приема очередного символа из файла или для записи ее значения в файл.

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

#include<stdio.h>

#include<conio.h>

int main()

{

clrscr();

FILE *f1;

char c; char fname[20];

puts("Введите имя файла:");

gets(fname); // gr.txt

if ((f1=fopen (fname,"w"))==NULL)

{perror (fname); return 1;}

while ((c=getchar ()) != '.' ) // Ввести предложение, заканчивающееся точкой

putc(c,f1);

fclose(f1);

getch();

return 0;

}

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

выводит его на экран.

#include<stdio.h>

#include<conio.h>

int main()

{

clrscr();

FILE *f1;

char c; char fname[20];

puts("Введите имя файла gr.txt:");

gets(fname); // см. предыдущую программу

if ((f1=fopen (fname,"r"))==NULL)

{ perror (fname); return 1; }

while ((c=getc(f1)) != EOF)

putchar (c);

fclose (f1);

getch();

return 0;

}

          8.3.2 Строковый обмен с файлами. Строковый режим обмена организуется с помощью функции fgets () , fputs () и их прототипов int fputs (const char *s, FILE *stream), char *fgets (char *s, int n, FILE *stream).

          Функция fputs () записывает ограниченную символом ‘\0’ строку (на которую указывает s) в файл, определенный указателем stream на поток, и возвращает неотрицательное целое. Функция fgets () читает из определенного указателем stream файла не более n-1, записывает их в строку, на которую указывает s. Функция прекращает чтение, как только прочтет n-1 символов или встретит символ перехода на новую строку ‘\n’, который переносится в строку s. В качестве примера с помощью функции переписать содержимое файла tok.txt (f1) в файл tk.txt (f2)

#include<stdio.h>

include<conio.h>

int main()

{

clrscr();

FILE *f1,*f2;

int n; char cc[256]; // массив для обмена с файлами

if ((f1=fopen ("tok.txt","r"))==NULL) // открытие входного файла

{perror ("tok.txt"); return 1;}

if ((f2=fopen ("tk.txt","w"))==NULL) // открытие выходного файла

{perror("tk.txt"); return 1;}

while (fgets(cc, 256, f1)!=NULL) // запись содержимого файла f1 в f2;

fputs(cc,f2);

fclose (f1); fclose (f2);

return 0; getch ();

}

 

          8.3.4 Режим форматного обмена с файлами. Форматный режим обмена  организуется с помощью функций fprintf () , fscanf () и их прототипов int fprintf (указатель_на_поток, форматная строка, список  переменных), int fscanf (указатель_на_поток, форматная строка, список_адресов_переменных). От функций printf (), scanf () для форматного обмена с дисплеем и клавиатурой функции fprintf () , fscanf () отличаются лишь тем, что в качестве первого параметра в них необходимо задавать указатель на поток, с которым производится обмен. Примеры.

1 В заранее созданном файле bi.txt находятся фамилии, имена и номера телефонов клиентов фирмы. Напишем программу, которая  по фамилиям клиентов находит имена и номера телефонов. Если есть люди с одинаковыми фамилиями, то программа должна вывести список этих людей. 


#include<stdio.h>

#include<conio.h>

#define FNAME "C:\\bi.txt\0"

void main()

{

clrscr();

char *fname=FNAME;

FILE*in;

char *fam;int n=0;char *obr;

puts("\nВведите фамилию клиентов");

if ((in=fopen(fname,"rt"))==NULL)

{

printf("Ошибка открытия файла для записи");

getch();

return;

}

scanf("%s",&obr);

while (!feof(in))

{

fscanf(in,"%s %s %s",&fam);

if (fam==obr)

{

printf("%s  %s  %s\n",fam);

n++;

}

}

printf("N=%i",n);

fclose(in);

getch();

}


 

2 В заранее созданном файле numbers.txt записана матрица 4х4. Напишем программу, которая вычисляет сумму элементов каждой строки.

#include<stdio.h>

#include<conio.h>

#define FNAME "C:\\numbers.txt\0"

void main()

{

clrscr();

char fname[20]=FNAME;

FILE*in;

int a;int n=0;int sum1,sum2,sum3,sum4;

puts("\n");

if ((in=fopen(fname,"rt"))==NULL)

{printf("Ошибка открытия файла для записи"); getch(); return; }

sum1=sum2=sum3=sum4=0;

while (!feof(in))

{

fscanf(in,"%i",&a);

if (n<4) sum1+=a; if (n>3 && n<8) sum2+=a;

if (n>7 && n<12) sum3+=a; if (n>11 && n<16) sum4+=a;

n++;

}

fclose(in);

printf("\nsum[1]=%i\n",sum1); printf("sum[2]=%i\n",sum2);

printf("sum[3]=%i\n",sum3); printf("sum[4]=%i\n",sum4);

getch();

}

 

3 В файле numbers.txt даны некоторые целые числа, определить сколько  чисел, равных числу, введенному пользователем.

#include<stdio.h>

#include<conio.h>

#define FNAME "C:\\numbers.txt\0"

void main()

{

clrscr();

char fname[20]=FNAME;

FILE*in;

int a;int n=0;int k;

if ((in=fopen(fname,"rt"))==NULL)

{ printf("Ошибка открытия файла для записи"); getch(); return; }

scanf("%i",&k);

while (!feof(in))

{ fscanf(in,"%i",&a);

if (k==a) n++;

}

fclose(in);

printf("\n‚Количество чисел, равных введенному числу =%i\n",n);

getch();

}

          8.4 Работа с файлами на диске. Файлы произвольного доступа

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

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

включена функция fseek() для перемещения указателя текущей позиции в потоке на нужный байт файла. Она имеет следующий прототип: int fseek (указатель_на_поток, смещение, начало_отсчета). Функция fseek() возвращает 0, если перемещение в потоке выполнено успешно, в противном случае возвращается ненулевое значение. Смещение задается переменной или выражением типа long и может быть отрицательным, т.е. возможно перемещение по файлу в прямом и обратном направлениях. Для переменного типа long выделяются 4 байта, чем и определен диапазон его значений.  Начало отсчета задается одной из предопределенных констант, размещенных в заголовочном файле stdio.h:

          SEEK_SET (имеет значение 0) – начало файла;

          SEEK_CUR (имеет значение 1) – текущая позиция;

          SEEK_END (имеет значение 2) – конец файла.

Приведем примеры       использования функции fseek(). В файле vac.txt даны цифры от 1 до 20, которые записаны через пробелы. В зависимости от значения k заданным пользователем и i прочитать нужные цифры. Если зададите k=250, то получите на дисплее цифры 4 5 6 7 8 т.к. i меняется от 0 до 9.


#include<stdio.h>

#include<conio.h>

int main()

{

clrscr();

int i,k;

char buf[30];

char *c;long pos;

char ln;

FILE*fp;

int lang;

if ((fp=fopen("vac.txt","r"))==NULL)

{

perror("vac.txt");

return 1;

}

while (1)

{

c=buf;

puts("Введите цифру (0 – для завершения):");

scanf("%d",&k);

if (k==0)

{

fclose(fp);

return 0;

}

pos=(256-k);

fseek(fp,pos,SEEK_END);

for(i=0;i<=9;i++)

*c++=getc(fp);

   c++;

   *c='0';

   printf("%d->%s\n",k, buf);

}

fclose(fp);

return 0;

getch();}


Следующая программа вычисляет сумму чисел, начиная с выбранной подстроки. Пусть  k=240, подстрока начинается с 9 и дает сумму 174. Суммирует цифры с 9 по 20 .    На экране дисплея программа выводит

значения суммы чисел и суммируемые числа.


#include<stdio.h>

#include<conio.h>

int main()

 

{

clrscr();

FILE*fp;

int a,k,i;int n=0;int sum;long pos;char


*c;char buf[30];

if ((fp=fopen("vac.txt","r"))==NULL)

{

perror("vac.txt");

return 1;

}

sum=0;

puts("Введите цифру (0 – для завершения): ");

scanf("%d",&k);

if (k==0)

{

fclose(fp);

return 0;

}

pos=(256-k);

fseek(fp,pos,SEEK_SET);

while (!feof(fp))

{

fscanf(fp,"%i",&a);

sum+=a;

}

printf("\nСумма чисел sum=%i\n",sum);

fseek(fp,pos,SEEK_SET);

{

c=buf;

for(i=0;i<256;i++)

*c++=getc(fp);

 c++;

 printf("%d->%s\n",k, buf);

}

fclose(fp);

getch();

return 0;

}


 

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

          long ftell (FILE*) – получение значения указателя текущей позиции в потоке; void rewind (FILE*) – установка указателя текущей позиции в потоке на начало потока.

 

          8.5 Задачи

          8.5.1 Создайте файл (диск С) number.txt и записывайте в него матрицу A[3,3]. Вычислите сумму элементов этой матрицы.

          8.5.2 Напишите программу, которая выводит на экран содержимое файла С:\  number.txt.

          8.5.3 Напишите программу, которая дописывает в файл находящийся на диске С, массив В[3].

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

          8.5.5 Создайте два файла f1.dat и f2.dat. Напишите программу которая переписывает содержимое  f1.dat в файл f2.dat.

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

          8.5.7 Информация об участниках спортивных соревнований содержит:

Ф.И.О. игрока, игровой номер, возраст, рост, вес.

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

         

9 Работа в графическом режиме

          9.1 Инициализация графического режима. Видеопамять, размещаемая на плате управления дисплеем – адаптерем, хранит образ экрана – страницу. Использование видеопамяти обеспечивает драйвер – программа, взаимодействующая с адаптером. Управление экраном в графическом режиме производится с помощью наборов функций, прототипы которых находятся в заголовочном файле graphics.h. Файл graphics.h должен быть подключен с помощью директивы #include < graphics.h. > препроцессора языка Си ко всем модулям, использующим графические подпрограммы. В зависимости от типа дисплея, объема видеопамяти и вариантов его расходования для представления страниц и цветности в программе указывают соответствующий драйвер и режим его работы, производят инициализацию графического режима с помощью оператора initgraph (драйвер, режим, путь к файлу драйвера), где драйвер, режим – переменные типа int. Для максимальных возможностей использования  режима работы применяются видеоадаптеры int driver=DETECT, gmode, errorcode; инициализация, графики initgraph (&graphdriver, &gmode, “..\\bgi”). Ошибку результата работы графика можно узнать с помощью функции graphresult ().

Ее шаблон

          errorcode = graphresult ();

          if (errorcode !=grOk)

          {

          printf (“Ошибка: %s\n”, grapherrormsg (errorcode ));

          printf (“Для останова нажмите любую клавишу \n”));

          getch();

          exit (1);

}

          Файлы драйверов хранятся в поддиректории BGI, имя файла драйвера определяется названием адаптера и имеет расширение BGI: CGA.BGI, EGA.BGI. От переменной «режим» зависят следующие характеристики: число различимых точек – пикселей по горизонтали и вертикали, палитра – набор цветов для линий и пикселей, число страниц видеопамяти. 

          9.2 Координаты. Создание окон. Изображение стандартных фигур, закраска. Вывод текстов в графическом режиме. Запоминание изображений. Страницы. Моделирование движения.

          9.2.1 Координаты. Создание окон. В графических функциях, например line, используются координаты точек экрана по горизонтали и вертикали; необычна направленность вертикальной оси – сверху вниз. Поэтому пиксель в левом верхнем углу экрана имеет координаты (0,0). Относительно него измеряются координаты других точек экрана. Максимальные значения координат имеет пиксель в правом нижнем углу; эти значения дают функции

getmaxx и getmaxy соответственно. В графике используется понятие текущего указателя СР, аналогичное понятию курсора; СР невидим в отличие от курсора. Например, функция moveto (x,y) помещает СР в точку (x,y), а moverel (dx,dy) изменяет текущие координаты СР на величину dx,dy.

          Действие ряда графических операторов, например moveto, ограничивается прямоугольной областью, называемой окном или областью просмотра. Это справедливо и для параметров функции line. Окно не имеет видимых границ, но, если нужно, его можно очертить с помощью функции rectangle (прямоугольник). Функция setviewport (x1,y1,x2,y2,<отсечка>) устанавливает окно, определяя точками (x1,y1) и (x2,y2) соответственно левый верхний и правый нижний его углы; если до этого было определено окно, то оно аннулируется. Параметр <отсечка> задают равным truе, если строящиеся элементы изображения, выходящие за рамки окна, нужно отбрасывать. В противном случае указывают false. Функция cleardevice () очищает экран, устанавливает текущей точкой левый верхний угол экрана, learviewport очищает текущее окно и устанавливает текущую точку в левый верхний угол окна.

          9.2.2 Изображение стандартных фигур, закраска. Функция circle (x,y,r)

рисует на экране окружность с центром в точке с координатами (x,y) и радиусом r,  ellipse (x,y, <начальн.угол>,<конеч.угол>,rx,ry) – эллипс,

linerel (dx,dy) -  линию из текущей точки в точку, задаваемую относительным расстоянием, lineto (x,y) –  линию из текущей точки до точки с координатами (x,y),  drawpoly (<число вершин>+1,<двумерный массив координат вершин>,) –  контур многоугольника, bar(x1,y1,x2,y2) – закрашенный прямоугольник, bar3d(x1,y1,x2,y2,<глубина>,<крышка>) – параллелепипед, fillelipse (x,y,rx,ry) – закрашенный эллипс, fillpoly (<число вершин>,<двумерный массив координат вершин >) – закрашенный многоугольник, pieslice (x,y, <начальн.угол>,<конеч.угол>, r) – закрашенный сектор круга.

          Тип и цвет раскраски устанавливает функция setfillstyle (<тип краски>,<цвет>). Например, закрашивание сектора круга сплошным  заданным цветом (желтым) setcolor(14), setfillstyle (1,14), pieslice (x,y, <0>,<360>, 5).

         

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

outtext (<текст>) выводит символ или строку с текущей позиции;  outtextxy(x,y,<текст>) выводит символ или строку с заданной позиции;  setttextjustify(<гор.>,<вер.>) устанавливает режим выравнивания текста; settextstyle(<шрифты>,<направление>,<размер шрифта>) устанавливает шрифт, стиль и размер шрифта. Параметр <направление> имеет значение 0 (текст идет слева направо) и 1 (текст идет снизу вверх по вертикали, символы “положены набок”), setlinestyle(<вид линии>,<образец>,<толщина линий>), параметр  <образец> обычно принимает значение 0.

 

          9.2.4 Запоминание изображений. Страницы. Моделирование движения. Содержимое всей видеопамяти или часть ее может быть перенесено в основную память, а при необходимости возвращено в видеопамять на прежнее или новое место. Запоминаются только прямоугольные фрагменты изображения; размер соответствующей памяти в байтах возвращает функция imagesize (x1,y2,x2,y2). Копию этого фрагмента в основную память помещает функция getimage (x1,y2,x2,y2, <область памяти>); параметр  <область памяти> задается в виде обычной или динамической переменной. Воспроизведение хранимого изображения осуществляет функция putimage (x,y, <область памяти>), <способ>).

          Чтобы были качественными мультипликации необходимо использовать страницу, содержащую кадры изображения. Общая идея: пока высвечивается одна страница с кадром, на другой готовится очередной, измененный кадр. Страницы меняются ролями через 20-30 мс. Страница имеет номер, нумерация начинается с 0. Функция setactivpage (<номер страницы>) делает активной указанную страницу. Другая функция setvisualpage (<номер страницы>) делает видимой указанную страницу.

          Приведем примеры программ работы в графическом режиме.

1 Затухающие колебания в колебательном контуре    


#include <iostream.h>

#include <graphics.h>

#include <conio.h>

#include<stdio.h>

#include<dos.h>

#include<math.h>

int oci()

{

    setbkcolor(11);

    setcolor(4);setfillstyle(1,4);

setlinestyle(0,0,3);

rectangle(150,30,500,300);line(323,310,323,330);line(337,310,337,330);    ellipse(205,320,0,180,8,8);

ellipse(220,320,0,180,8,8);   ellipse(235,320,0,180,8,8);

ellipse(250,320,0,180,8,8);

    rectangle(255,400,340,420);    setfillstyle(1,1);bar(155,35,494,270);

    settextstyle(0,0,2);

    outtextxy(227,332,"L     C");outtextxy(295,427,"R");

    setcolor(14);    line(200,40,200,265);

 

line(160,150,485,150);  settextstyle(0,0,2);

outtextxy(478,143,">");  settextstyle(0,1,2);

outtextxy(209,35,">");

    settextstyle(0,0,2);   outtextxy(175,38,"A");

outtextxy(475,160,"t");

    setcolor(4);   setlinestyle(0,0,3);

line(150,275,500,275);    settextstyle(0,0,1);

outtextxy(252,392,"v");

    line(250,365,355,365); line(255,365,255,395);

    setlinestyle(0,0,1);    line(150,320,150,410);

line(150,410,255,410);    line(450,320,450,410);

line(450,410,355,410);                                        

line (290, 320,290,287) line(370,320,370,287);   line(258,320,322,320);

line(338,320,450,320);  line(150,320,197,320);

 

line(355,410,355,365);    pieslice(290,289,0,360,3);

pieslice(370,289,0,360,3); pieslice(250,365,0,360,3);

pieslice(355,365,0,360,3);

    settextstyle(0,0,1);

    outtextxy(110,10,"Zatuhayhi kolebaniy");

    outtextxy(335,10,"Bbedite R ot 0 do 100 Om");

    settextstyle(0,0,2);

    outtextxy(480,350,"R=");

    settextstyle(0,0,1);

    return 0;

}

void main()

{

positiv:

    int graphdriver = DETECT, graphmode;

    initgraph(&graphdriver, &graphmode, "..\\bgi");

    float x1,x,y,k,r,A,y1;

    int n;

    char ky,chr;

    oci();

    gotoxy(65,23);

    scanf("%f",&A);

    if(A>85) A=85;  x1=-240;r=0;x=0;

   do

{

    x+=1;

    delay(10);

    setlinestyle(0,0,3);   setcolor(4);

line(255+x,365,255+x,395);

    outtextxy(252+x,392,"v");

    delay(200);

    setcolor(11);

 

line(255+x-3,367,255+x-3,393);

outtextxy(252+x-3,392,"v");

}

    while(x<A);

    do

{

    x1+=1;

    r+=0.001;

    y1=80*exp(-A*r)*sin(6.4*x1);    

    setcolor(14);setfillstyle(1,14);

    pieslice(441+x1,150+y1,0,360,1);

    delay(50);

}   while(x1<20);

setcolor(4);

outtextxy(150,450,"Dly povtoreniy upragneniy nagmite 1,bihod 0");

    gotoxy(65,24);

    scanf("%d",&n);

    if(n==1) goto positiv;

    closegraph();

}


 

2 Графики функций y=3x2 , y=x3 , y=6x2 + 3x.

#include <iostream.h>

#include <graphics.h>

#include <conio.h>

#include<stdio.h>

#include<dos.h>

int oci()

{

    setcolor(14);setbkcolor(1);setlinestyle(0,0,3);

    line(300,20,300,440);line(20,230,600,230);

    settextstyle(0,0,2);outtextxy(592,223,">");

    settextstyle(0,1,2);outtextxy(309,15,">");

    settextstyle(0,0,2);

outtextxy(274,15,"y");outtextxy(592,238,"x");

outtextxy(25,235,"-5 -4  -3 -2        1  2     4  5");

outtextxy(230,235,"-1 0         3");

    return 0;

}

int raz()

{

    float j;

    for(int i=1;i<=11;i++)

{   setlinestyle(0,0,1);

    j+=50;

    moveto(j,230);linerel(0,-6);

    moveto(300,j);linerel(6,0);

    setfillstyle(1,1);

    bar(295,450,310,460);

}

    return 0;

}

void main()

{

    int graphdriver = DETECT, graphmode;

    initgraph(&graphdriver, &graphmode, "..\\bgi");

    float x,y,k,i,y1,y2;

    oci();raz();

    printf("\tbbetite x ot (-5) do (5)\n\t");

    scanf("%f",&k);

    printf("\t");

    scanf("%f",&i);

    x=48*k;

   do

{

    x+=1;

    delay(100);

    y=0.001*3*x*x;

    y1=0.00001*x*x*x;

    y2=-0.001*6*x*x+0.02*3*x;

    setcolor(4);setfillstyle(1,4);pieslice(300+x,230-y,0,360,1);

    setcolor(12);setfillstyle(1,12);pieslice(300+x,230-y1,0,360,1);

    setcolor(10);setfillstyle(1,10);pieslice(300+x,230-y2,0,360,1);

}

    while(x<48*i);

    getch();

    closegraph();

}

60

3 Графики функций y=sin(x)/x , y=tan(x) , y=cos(x) , y=sin(x).

#include <iostream.h>

#include <graphics.h>

#include <conio.h>

#include<stdio.h>

#include<dos.h>

#include<math.h>

int oci()

{

    setcolor(14);setbkcolor(1);setlinestyle(0,0,3);

    line(300,20,300,440);line(20,230,600,230);

    settextstyle(0,0,2);outtextxy(592,223,">");

    settextstyle(0,1,2);outtextxy(309,15,">");

    settextstyle(0,0,2);

    outtextxy(274,15,"y");outtextxy(592,238,"x");

    settextstyle(0,0,1);

    outtextxy(320,10,"Grafici sin(x),cos(x),tan(x),sin(x)/x");

    return 0; }

int raz()

{

    float j;

    for(int i=1;i<=11;i++)

{   setlinestyle(0,0,1);

    j+=50;

    moveto(j,230);linerel(0,-6);

    moveto(300,j);linerel(6,0);    setfillstyle(1,1);  bar(295,450,310,460);

}

    return 0;

}

void main()

{

    int graphdriver = DETECT, graphmode;

    initgraph(&graphdriver, &graphmode, "..\\bgi");

    float x,y,k,r,A,y1,y2,y3;

    oci();raz();

    printf("\tbbetite x ot (-5) do (5)\n\t");

    scanf("%f",&k);

    printf("\t");

    scanf("%f",&r);

    printf("Bbedite razmax amplitudy ot 5 do 15\n");

    printf("\t");

    scanf("%f",&A);

    x=48*k;

   do  {

x+=5;

    delay(300);

    y=-A*500*sin(5*x)/x;

    y1=A*5*sin(5*x);

    y2=A*5*cos(5*x);

    y3=-A*5*tan(5*x);

    setcolor(15);setfillstyle(1,15);pieslice(300+x,230-y3,0,360,2);

    setcolor(12);setfillstyle(1,12);pieslice(300+x,230-y2,0,360,2);

    setcolor(10);setfillstyle(1,10);pieslice(300+x,230-y1,0,360,2);

    setcolor(4);setfillstyle(1,4);pieslice(300+x,230-y,0,360,2);

}

    while(x<48*r);    getch();    closegraph(); }

 

4 Колебания маятника


#include<conio.h>

#include <graphics.h>

#include <stdlib.h>

#include <stdio.h>

#include<math.h>

#include<dos.h>

    void kont(float a,int cvet)

{

int z,w;

double x,y;

x=300+200*sin(a); y=230+200*cos(a);

setcolor(cvet);setfillstyle(1,cvet);

line(300,120,x,y);

pieslice(300,120,0,360,5);

pieslice(x,y,0,360,20);

}

    void main()

{ int k;

double a,b,m;

  int gdriver = DETECT,gmode;

  initgraph(&gdriver, &gmode, "..\\bgi");

  a=0;b=0;k=1;m=1;

  setbkcolor(1);setactivepage(1);

  do

{

 kont(b,getbkcolor());

  b=a;a=a+m*0.02;

  if (abs(a)>0.6) m=-m;

  kont(a,14);

//  setvisualpage(k);

  setactivepage(1-k);

  delay(50);

  k=1-k;

}

  while(a<2);

  getch();

 }


 

 

          9.3 Задачи

          9.3.1 Написать программу, которая вычерчивает колебательный контур.

          9.3.2 Написать программу, которая рисует движущийся по экрану паровоз.

9.3.3 Написать программу, вывести график функции y=0.5x2 +4x-3.

9.3.4 Написать программу, график функции y=5cos(x)-2.

9.3.5 Написать программу, рисующую фигуру Лиссажу.

Приложение А

 

Препроцессорные средства

 

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

 Препроцессорные средства языка Си просматривают программу до компилятора, заменяют аббревиатуры в тексте программы на соответствующие директивы, отыскивают и подключают необходимые файлы и влияют на условие компиляции. Из рисунка 5 (стр.12 ) видно, что на входе препроцессора – текст с  препроцессорными директивами, на выходе – текст без препроцессорных директив.

Директивы препроцессора. Для управления препроцессором, используются команды (директивы), каждая из которых помещается на отдельной строке и начинается с символа # . Директивы препроцессора дают указания компилятору. Препроцессор «сканирует» исходный текст программы в поиске строк, начинающихся с символа #. Такие строки воспринимаются препроцессором как команды (директивы), которые определяют действия по преобразованию текста. Обобщенный формат директивы препроцессора:   # имя_директивы лексемы_препроцессора.

Препроцессор включает в себя следующие директивы:

# define

Определение макроса

# undef

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

# include

Включение объекта – заголовка

# if

Компиляция, если выражение истинно

# ifdef

Компиляция, если макрос определен

# ifndef

Компиляция, если макрос не определен

# else

Компиляция, если выражение в if ложно

# elif

Составная директива else/ if

# endif

Окончание группы компиляции по условию

# line

Замена новым значением номера строки или имени исходного файла

# error

Формирование ошибок трансляции

# pragma

Действия определяются реализацией

#

null - директива

Операции

Назначение

#

Придание операнду формы строки символов

##

Склеивание лексем

 

Представление # ifdef в форме выражения

Директива #include<>. Эта директива предназначена для включения в текст программы текста файла из каталога «заголовочных» файлов ,

поставленных вместе со стандартными библиотеками компилятора. #include<> не подключает к программе соответствующую стандартную библиотеку, а только позволяет вставить в текст программы описания из указанного заголовочного файла. Доступ к кодам библиотечных функций нужен только на этапе компоновки. Именно поэтому компилировать программу и устранить синтаксические ошибки в ее тексте можно без стандартной библиотеки, но обязательно с заголовочными файлами. Хотя в заголовочных файлах содержатся описания всех стандартных функций, в код программы включаются только те функции, которые используются в программе. Выбор нужных функций выполняет компоновщик.

          Включение текстов из файлов. Для включения текста из файла используется команда  #include в трех формах записи: #include <имя_файла>, #include “имя_файла”, #include имя_макроса. При первой записи препроцессор разыскивает файл в стандартных системных каталогах, во втором случае вначале препроцессор просматривает текущий каталог пользователя и только затем обращается к просмотру стандартных системных каталогов. Третья форма директивы предполагает, что первым  после пробела не будут символы < или “, кроме того, предполагается, что существует макроопределение, которое заменит имя макроса либо на < имя_файла>, либо на “имя_файла”.

          Замены в тексте. Директива #define. Когда мы рассматривали именованные константы и массивы, нами была использована эта директива. Константы и размеры массивов определялись на этапе препроцессорной обработки. Ее форма:  #define идентификатор строка_замещения.

          С помощью этой директивы программист вводит собственные обозначения базовых или производных типов. Например, директива #define REAL long double вводит название REAL для типа long double. Далее в тексте программы можно определить конкретные объекты, используя REAL в качестве обозначения их типа REAL  x, array [6]. Например:

Исходный текст                              Результат препроцессорной обработки

#define begin {

#define end }

void main()                                        void main()

begin                                                   {

операторы                                          операторы

end                                                        }

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

          Выводим на экран дисплея значения переменной:

#define PK printf (“\n N=%d”, N); int N=4; PK; результат N=4.

Директива #define имеет еще одну важную особенность. Макросы могут иметь аргументы. Каждый раз, когда происходит замена, аргументы также

заменяются на те , которые встречаются в программе.

#define min(a,b)  (a<b? b:a)

…………………………..

printf (“Минимум из x и y %d”, min(x,y));

Когда программа будет компилироваться, в выражение, определенное min(a,b),  будут подставлены соответственно x и y.

          Реакция на ошибки. Форма #error последовательности_лексем.

Например -

#define NAME 5

#if (NAME !=5)

#error NAME должно быть равно 5!

Сообщение будет выглядеть так:  error <Имя_файла>, <номер_строки>:

error directive: NAME должно быть равно 5!

 

Предопределенные макросы

__LINE__

Десятичная константа-номер текущей обрабатываемой строки файла.

__FILE__

Строка символов- имя компилируемого файла.

__DATE__

Строка символов в формате «месяц ,число, год».

__TIME__

Строка символов в формате «часы, минуты, секунды ».

__STDC__

Константа, равная 1, если компилятор работает в соответствии с ANCI- стандартом.

__BCOPT__

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

__BCPLUSPLUS__

Числовое значение соответствует версии компилятора.

__CDECL__

Идентифицирует порядок передачи параметров функциям, значение 1 соответствует порядку, принятому в языке Си.

__CONSOL__

Определено для 32- разрядного компилятора и установлено в 1 для программ консольного приложения.

__DLL__

Соответствует работе в режиме Windows DLL.

__MSDOS__

Равно 1 для 16- разрядных компиляторов Borland C++, устанавливается в 0 для 32- разрядного компилятора.

__MT__

Макрос доступен только для 32- разрядного режима.

__OVERLAY__

Значение равно 1 в оверлейном режиме.

__PASCAL__

Противоположен __CDECL__.

__TCPLUSPLUS__

Числовое значение соответствует версии компилятора.

__TLS__

Определен как истинный для 32- разрядного режима.

__TURBOC__

Числовое значение, равное 0х0400, для компилятора Borland C++ 4.0.

__WINDOWS__

Означает генерацию кода для Windows.

__WIN32__

Определен для 32- разрядного компилятора и установлен в 1 для консольных приложений.

 

Приложение Б

 

Распределение памяти

 

Автоматическое, статическое и динамическое выделения памяти. Функции malloc () и free ().

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

          Статическое распределение памяти. Если переменная определена вне функции, память для нее отводится статически один раз в начале выполнения программы, и переменная уничтожается только тогда, когда выполнение программы завершается. Можно статически выделить память под переменную внутри функции или блока. Для этого нужно использовать ключевое слово static.

Например -

          Double globalMax;

          void func (int x)

          { static bool visited=false;

          if (!visited)

{ ……..

visited=true;}

……….

}

Начальное значение visitedfalse. При первом вызове функции func условие в операторе if будет истинным, выполняется инициализация, и переменной visited будет присвоено значение true. Поскольку статическая переменная создается только один раз, ее значение между вызовами функции сохраняется. При втором и последующих вызовах функции инициализация производиться не будет. Если бы переменная visited не была объявлена static, то инициализация происходила бы при каждом вызове функции.

          Динамическое выделение памяти. Функции malloc () и free ().

          Динамическое выделение памяти используется прежде всего тогда, когда потребность в памяти заранее не известна и объем не может быть фиксированным. Наиболее важными функциями для динамического распределения памяти являются malloc () и free (). Функция malloc () выделяет память, а функция free () освобождает ее. Прототипы этих функций хранятся в заголовочном файле stdlib.h и имеют вид: void malloc(size_t size);  void free( void*p);

Функция  malloc () возвращает указатель типа void , для правильного использования значения этой функции надо преобразовать указатель на

соответствующий тип. При успешном выполнении malloc () возвращает

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

операцию sizeof.  Для примера возьмем массив с переменным количеством элементов n.  

          #include<stdio.h>

          #include<stdlib.h>

void main()

{ float *t; //  Указатель для выделяемого блока памяти

          int i, n;

          printf (“\nn=”); // n- число элементов

          scanf(“%d”, &n);

          t=(float*) malloc(n*sizeof(float));

          for (i=0; I<n; i++) // Цикл ввода чисел

          { printf (“x[%d]=”,i);

 scanf (“%f”, &t[i]); } //

          for (i=n-1; i>=0; i--) // Цикл печати результатов

          { if (i%2==0) printf (“\n”);

printf (“\tx[%d]=%f”, i, t[i] ); }

          free (t); // Освобождение памяти

          }

Результат: n=4

          x0]=10;

x[1]=20;

x[30];

x[40]=40.

          x3]=40.000000;

x[2]=30.000000;

x[1]=20.000000;

x[0]=10.000000.

В программе int n- количество вводимых чисел типа float, t- указатель на начало области, выделяемой для размещения n вводимых чисел. Указатель t принимает значение адреса области, выделяемой для n штук значений типа float. Доступ к участкам выделенной области памяти выполняется с помощью операции индексирования: t[i] и t[i-1].

 

           

 

Приложение В

 

Стандартная библиотека функций языка Си

 

 

Математические функции (файл math.h)

 

Функция

Прототип и краткое описание действий

abs

int abs (int i).  Возвращает абсолютное значение целого аргумента i.

acos

double acos (double x). Функция арккосинуса. Значение аргумента должно находиться в диапазоне от –1 до +1.

asin

double asin (double x). Функция арксинус. Значение аргумента должно находиться в диапазоне от –1 до +1.

atan

double atan (double x). Функция арктангенса.

atan2

double atan2 (double y, double x). Функция арктангенса от значения y/x.

cabs

double cabs (struct complex znum). Вычисляет абсолютное значение комплексного числа znum.

ceil

 

double ceil (double x). Вычисляет ближайшее целое не меньше, чем

 аргумент х .

сos

double cos(double x). Угол аргумента задается в радианах.

exp

double exp(double x). Вычисляет значения ехр (х).

fabs

double fabs (double x).  Возвращает абсолютное значение вещественного аргумента х двойной точности.

floor

double floor (double ). Находит наибольшее целое, не превышающее значение х. Возвращает его форме double.

fmod

double fmod (double x, y). Возвращает остаток от деления нецелое x на y.

hypot

double hypot (double x,y). Вычисляет гипотенузу прямоугольного треугольника по значениям катетов x ,y.

labs

long labs (long x). Возвращает абсолютное значение целого аргумента long x.

ldexp

double ldexp (double v, int e). Возвращает значение выражения v*2е .

frexp

double frexp (double value, int*exp). Разбивает число с плавающей точкой value на нормализованную мантиссу и целую часть как степень числа 2. Целочисленная степень записывается в область памяти, на которую указывает ехр, а мантисса используется как значение, которое возвращает функцию.

log

double log (double x). Возвращает значение натурального логарифма (ln x).

log10

double log10 (double x), Возвращает значение log 10 x.

modf

double modf (double value, double *iptr). Разделяет число с плавающей точкой value на целую и дробную части. Целая часть записывается в область памяти, на которую указывает iptr, дробная часть является значением, возвращаемым функцией.

poly

double poly (double x, int n, double c[ ] ). Вычисляет значение полинома

C[n]x+c[n-1]x+…+c[1]x+c[0].

pow

double pow (double x, double y). Возвращает значение x т.е.x в степени y.

pow10

double pow10 (int p).  Возвращает значение 10р.

sin

double sin (double x). Функция синуса. Угол (аргумент) задается в радианах.

sinh

double sinh (double x). Возвращает значение гиперболического синуса для x.

sqrt

double sqrt (double x).

Возвращает положительное значение квадратного корня x

tan

double tan (double x).

Функция тангенса. Угол (аргумент) задается в радианах.

tanh

double tanh (double x).

Возвращает значение гиперболического тангенса для x.

 

Функции и макросы проверки и преобразования символов

(файл ctype.h)

 

Функция

Прототип и краткое описание действий

isalnum

int isalnum (int c).

Дает значение не нуль, если с-код буквы или цифры (A-z,a-z,0-9), и нуль - в противном случае.

isalpha

int isalpha (int c).

Дает значение не нуль, если с-код буквы (A-Z,a-z), и нуль - в противном случае.

isascii

int isascii (int c).

Дает значение не нуль, если с-код ASCII,т.е. принимает значение от 0 до 127, в противном случае- нуль.

iscntrl

int iscntrl (int c).

Дает значение не нуль, если с- управляющий символ с кодами  0x00-0x01F или 0x7F, и нуль - в противном случае.

isdigit

int isdigit (int c).

Дает значение не нуль, если с- цифра (0-9) в коде ASCII, и нуль - в противном случае.

isgraph

int isgraph (int c).

Дает значение не нуль, если с- видимый (изображаемый) символ с кодом (0x21-0x7E ) и нуль – в противном случае.

islower

 

 

int islower (int c)

Дает значение не нуль, если с- код буквы на нижнем регистре (a- z), и нуль - в противном случае.

isprint

int isprint (int c)

Дает значение не нуль, если с- печатный символ с кодом(0x20-0x7E) и нуль - в противном случае.

ispunct

int ispunct (int c)

Дает значение не нуль, если с- символ – разделитель (т.е. соответствует iscntrl или isspace), и нуль - в противном случае.

isspace

int isspace (int c).

Дает значение не нуль, если с -обобщенный пробел, пробел, символ табуляции, символ новой строки или новой страницы ,символ возврата каретки (0x09-0x0D,0x20), и нуль - в противном случае.

isupper

 

 

int isupper (int c)

Дает значение не нуль, если с- код буквы на верхнем регистре(A-Z), и нуль - в противном случае.

sxdigit

int isxdigit (int c)

Дает значение не нуль, если с- код шестнадцатеричной цифры (0-9,A-F,a-f), и нуль - в противном случае.

 

   toascii

int toascii (int c).

Преобразует целое число с в символ кода ASCII, обнуляя все биты , кроме младших семи. Результат от 0 до 127..

tolower

int tolower (int c).

Преобразует код буквы с к нижнему регистру, не буквенные коды не изменяются.

toupper

int toupper (void).

Преобразует код буквы с к верхнему регистру, не буквенные коды не изменяются.

 

 

Функции ввода-вывода для стандартных потоков (файл stdio.h)

 

Функция 

Прототип и краткое описание действий

getch

int getch (void).

Считывает один символ с клавиатуры без отображения на экране.

getchar

int getchar (void).

Считывает очередной символ из стандартного входного потока (stdin).

gets

char*gets(char*s).

Считывает строку s из стандартного входного потока (stdin).

printf

int printf (const char*format [, argument,…]).

Функция форматированного ввода из стандартного потока stdout.

putchar

int putchar (int c).

Записывает символ с  в стандартный поток вывода (stdout).

puts

int puts(const char*s).

Записывает сроку s в стандартный поток вывода (stdout).

scanf

int scanf (cosnt char*format[, address,…]).

Функция форматированного ввода из стандартного потока stdin.

sprintf

int sprintf(char*s, const char *format[, argument,…]).

Функция форматированной записи в строку s.

sscanf

int sscanf (const char *s, const char *format[, address,…]).

Функция форматированного чтения из строки s.

ungetch

int ungetch(int c).

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

 

Функции для работы со строками

(файлы string.h stdlib.h )

 

Функция

Прототип и краткое описание действий

atof

double atof (const char*str).  

Преобразует строку str в вещественное число типа double.

atoi

int atoi (const char*str).

 Преобразует строку str в целое  число типа int.  

atol

long atoll (const char*str).  

Преобразует строку str в целое  число типа int .

itoa

char*itoa (int v, char*str, int baz).

Преобразуете целое v в строку str,  при изображении числа используется основание baz (2 ≤ baz ≤ 36) для отрицательного числа и   baz =10 первый символ «минус» (-).

ltoa

сhar* ltoa (long v, char*str, int baz).

Преобразует длинное целое v в строку str .При изображении числа используется основание baz (2 ≤ baz ≤ 36).

strcat

сhar* strcat(char *sp,const char*si).

Приписывает строку si к строке sp (конкатенация строк).

strchr

сhar* strchr (const char *str, int c ).

Ищет в строке  str первое вхождение символа c.

strcmp

int strcmp (const char*strl, const char*str2).

Сравнивает строки str1 и str2. Результат отрицателен, если str1<str2. равен нулю, если str1==str2, и положителен, если str1>str2 (сравнение беззнаковое).    

strcpy

char* strcpy(char *sp,const char*si). Копирует байты строки si в строку sp

strcspn

int strcspn (const char*strl, const char*str2).

Определяет длину первого сегмента строки str1, содержащего символы, не входящие во множество символов строки str2.

strdup

char* srdup (const char*str).  

Выделяет память и переносит в нее копию строки str.

strlen

unsigned strlen (const char*str).

 Вычисляет длину строки str.

strlwr

char*strlwr (char*str). Преобразует буквы верхнего регистра в строке в соответствующие буквы нижнего регистра.

strncat

сhar*strncat (char*sp,const char*si, int kol).

Приписывает kol символов строки si к строке sp (конкатенация).

strncmp

int strncmp (const char*, int kol). Сравнивает части строк strl и str2, причем рассматриваются первые kol символов. Результат отрицателен, если strl< str2, равен нулю, если   str1== str2, и положителен, если str1>str2 .    

strnchy

char* strnchy (char*sp,const char*si, int kol).

Копирует kol символов строки si в строку sp (хвост отбрасывается или дополняется пробелами).

strnicmp

int strnicmp (const char*strl, const char*str2, int kol).

Сравнивает не более kol символов строки str1 и строки str2, не делая различия регистров (см. функцию strncmp ()).

strnset

char* strnset( char*str, int c, int kol).

Заменяет первые kol символов строки str символом с.

strpbrk

char* strpbrk (const char*strl, const char*str2 ).

Ищет в строке strl  первое появление любого из множества символов, входящих в строку str2.

strrchr

сhar* strrchr (const char *str, int c ).

Ищет в строке  str последнее вхождение символа c.

strset

сhar* strset ( char *str, int c ).

Заполняет строку заданным символом с.

strspn

int strspn (const char*str1,const char *str2).

Определяет длину первого сегмента строки str1, содержащего только символы ,из множества символов строк str2.

strstr

char* strstr (const char*strl, const char*str2 ).

Ищет в строке str1 подстроку str2. Возвращает указатель на тот элемент в строке str1, с которого начинается подстрока str2.

startod

double strtod (const char*str, char**endptr).

Преобразует символьную строку str в число двойной точности. Если endptr не равен  NULL, то *endptr возвращается как указатель на символ, при достижении которого прекращено чтение строки str.

strtok

char* strtok  (char*strl, const char*str2 ).

Ищет в строке  str1 лексемы, выделенные символами из второй строки.

strtol

long strtol (const char *str, char **endptr, int baz).

Преобразует символьную строку str в значение «длинное число» с основанием  baz (2 ≤ baz ≤ 36). Если endptr не равен NULL, то *endptr возвращается как указатель на символ, при достижении которого прекращено чтение строки str. 

strupr

char*strupr(char*str).

Преобразует буквы нижнего регистра в строке str в буквы верхнего регистра.

ultoa

char*ultoa(unsigned long v, char *str, int baz).

Преобразует беззнаковое длинное целое v в строку str.

 

Функция для выделения и освобождения памяти

(файлы alloc.h,stdlib.h)

 

Функция

Прототип и краткое описание действий

calloc

void*calloc(unsigned n,unsigned m).

Возвращает указатель на начало области динамически распределенной памяти для размещения n элементов по m байт каждый. При неудачном завершении возвращает значение NULL.

coreleft

unsigned coreleft(void) - для схем распределения памяти в TurboC: tiny, small, medium.

unsigned long coreleft (void) - для других схем распределения памяти.

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

free

void free(void*bl).

Освобождает ранее выделенный блок динамически распределяемой памяти с адресом первого байта bl.

malloc

void*malloc(unsigned s).

Возвращает указатель на блок динамически распределяемой памяти длиной s байт. При неудачном завершении возвращает значение  NULL.

realloc

void*realloc(void*bl,unsigned ns). Изменяет размер ранее выделенной динамической памяти с адресом начало bl на  ns байт. Если bl равен NULL, то функция выполняется как malloc (). При неудачном завершении возвращает значение NULL.

 

 

 

 

 

 

Функция для работы с терминалом в текстовом режиме

(файл conio.h )

 

 

Функция

Прототип и краткое описание действий

clreol

voil clreol (voild).

Стирает символы от позиции курсора до конца строки в текстовом окне.

clrscr

voil clrscr (voild).

Очищает экран.

cgets

char*cgets(char*str).

Выводит на экран строку str.

cprintf

int cprintf (const char *format [, argumtnt,…]). выводит  форматированную строку в текстовое окно, созданное функцией window().

cputs

int cputs (char *str).

Помещает в символьный массив str строку с клавиатуры (консоли).

cscanf

int cscanf (const char *format [, address,…]).

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

delline

voil delline (voild).

Удаляет строку в текстовом окне (где находится курсор).

gotoxy

void gotoxy (int x, int y).

Перемещает курсор в позицию текстового окна с координатами (x,y).

highvideo

voil highvideo (voild).

Повышает яркость символов, выводимых на экран после ее вы зова.

movetext

int movetext(int x0, int y0, int x1, int y2, int x, int y).

Переносит  текстовое окно в область экрана, правый верхний угол которого имеет координаты (x,y).Координаты угловых точек окна - (x0,y0), (x1,y1).

normvideo

voil normvideo (voild).

Устанавливает нормальную яркость выводимых на экран символов.

textattr

void textattr (int newattr ).

Устанавливает атрибуты (фон, цвет) символов, выводимых на экран.

textbackg

round

voil textbackground (int c).

Устанавливает цвет фона по значению параметра с.

textcolor

voil textcolor (int c).

Устанавливает цвет символов по значению параметра с.

textmode

voil textmode (int m).

Переводит экран в текстовый режим по значению параметра m.

wherex

int wherex (voild).

Возвращает значение горизонтальной координаты курсора.

wherey

int wherey (voild).

Возвращает значение вертикальной координаты курсора.

window

woid window(int x0, int y0, int x1, int y1).

Создает текстовое окно по координатам угловых точек (x0,y0), (x1,y1)

 

 

 

 

 

Специальные функции

 

Функция

Прототип и краткое описание действий

Местонахождение прототипа

 

delay

void delay(unsigned x).

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

dos.h

 

getenv

char*getenv (const char*name).

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

stdlib.h

 

kbhit

int kbhit(void).

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

conio.h

 

memcmp

int memcmp(const void*s1, const void*s2, unsigned n). Сравнивает посимвольно две области памяти s1 и s2 длиной n байт. Возвращает значение меньше нуля, если  s1<s2, нуль, если s1= =s2, и больше нуля, если s1>s2.

mem.h

 

memcpy

void  *memcpy (void*p,const void*i, unsigned n).

Копирует блок длиной n байт из области памяти I в область памяти p.

mem.h

 

memicmp

int memicmp(const void*s1, const void*s2, unsigned n).

Подобна memcmp, за тем исключением, что игнорируются различия между буквами верхнего и нижнего регистра.

mem.h

 

memmove

void *memove (void*dest, const void*src, int n).

Копирует блок длиной n байтов из src в dest. Возвращает указатель dest.

mem.h

 

memset

void *memset(void*s, int c, unsigned n).

Записывает во все байты области памяти s значения c. Длина области s равна n байт.

mem.h

 

nosound

void nosound (void).

Прекращает подачу звукового сигнала, начатую функцией sound ().

dos.h

 

peek

int peek (unsigned s, unsigned c ).

Возвращает целое значение (слов),записанное в сегменте s со смещением c.

dos.h

 

peekb

char peekb (unsigned s, unsigned c ).

 Возвращает один байт, записанный в сегменте s со смещением c, т.е. по адресу s:c.

 

dos.h

 

poke

void poke (unsigned s, unsigned c, int v ).  Помещает значение v в слове сегмента s со смещением c ,т.е. по адресу s:c.

dos.h

pokeb

void pokeb (unsigned s, unsigned c, char v ); то же, что и poke ,но помещает один байт v по адресу s:c.

dos.h

puteny

int puteny (const char*name ).

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

stdlib.h

rand

int rand (void).

Возвращает псевдослучайное целое число из диапазона 0 - (215-1).

stdlib.h

signal

int signal (int sig).

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

signal.h

sound

void sound (unsigned f,). Вызывает звуковой сигнал с частотой f Гц.

dos.h

srand

void  srand (unsigned seed,).

Функция инициализации генератора случайных чисел (rand). seed - любое беззнаковое целое число.

stdlib.h

 

 

Функции для работы с файлами, связанными с потоками (файл stdio. h)

 

Функция

Прототип и краткое описание действий

clearerr

void clearerr (FILE*stream).

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

fclose

int fclose (FILE *stream). Закрывает файл, связанный с потоком stream.

feof

int feof (FILE *stream).

Проверяет, достигнут ли конец файла, связанного с потоком stream.

ferror

int ferror (FILE *stream). Проверяет, не возникла ли ошибка записи или чтения при доступе к файлу, связанному с потоком stream.

fgetc

int fgetc (FILE*stream,).

Считывает символ из файла, связанного с потоком stream.

fgetpos

int fgetpos  (FILE *stream, fops_t*pos).

Копирует значение указателя текущей позиции в файле, связанном с потоком stream, в объект, на который указывает pos. Тип принимающей переменной задан в заголовочном файле stdio. h

gets

char *fgets(char*string, int n, FILE  *stream).

Читает не более n-1 символов из файла, связанного с потоком stream, в массив, адресуемый указателем string. За последним символом, помещенным в массив, записывается  «/0».

fopen

FILE *fopen (const char *filename, const char*mode).

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

fprintf

int fprintf (FILE *stream, const char *format[, argument,…]). Функция форматного вывода в файл, связанный с потоком  stream ( см.printf ()).

fputc

int fprintf (FILE *stream).

Выводит символ с в файл, связанный с потоком stream.

fputs

шnt fputs (const char *string, FILE *string).

Записывает символьную строку, на которую указывает string, в файл, связанный с потоком stream.

fscanf

шnt fscanf (FILE *stream, const char*format[, argument,…]).

Функция форматного ввода из файла, связанного с потоком

stream ( см.scanf ()).

fseek

int fseek (FILE *stream, long offset, int point).

Перемещает указатель текущей позиции в файле, связанном с потоком stream, на offset байт относительно точки отсчета, определенной значением point (0-от начала файла, 1-от текущей позиции, 2-от конца файла).

ftell

long ftell(FILE  *stream).

Возвращает значение указателя текущей позиции в файле, связанном с потоком stream.

 

 

Функции низкого уровня для работы с файлами

(файлы io.h,fcntl.h,unisd.h)

 

Функция

Прототип и краткое описание действий

close

int close (int handle).Закрывает файл, связанный с дескриптором  handle.

creat

int creat (const char *path, int mode).

Создает новый файл в режиме, заданном в mode.

eof

 

int eof (int handle).

Проверяет, достигнут ли конец файла, связанного с дескриптором handle.

lseek

long lseek (int handle, long offset, int point).

Перемещает указатель текущей позиции в файле, связанном с дескриптором handle на offset байтов относительно точки отсчета, определенной значением point (0-от начала файла, 1-от текущей позиции, 2-от конца файла).

open

int open (const char *path, int access [, unsigned mode]).

Открывает файл, на имя которого указывает path в режиме, заданном в access. Возвращает дескриптор файла. Мode определяет тип файла и права доступа.

read

int read (int handle, void*buf, unsigned len ).

Читает len байтов из файла, связанного с дескриптором handle в буфер, на который указывает buf.

sopen

int sopen (const char *path, int access, int shflag, [,unsigned mode]).

Открывает файл, на имя которого указывает path, для совместного использования несколькими процессами. Аccess- режим доступа. shflag-режим разделения файла. mode определяет тип файла и права доступа. Возвращает дескриптор файла.

tell

long tell (int handle).

Возвращает значение указателя текущей позиции в файле, связанном  с дескриптором handle.

write

int write (int handie, void *buf, unsigbed nbyte).

Записывает nbyte байтов из буфера, на который указывает buf, в файл, связанный с дескриптором handle.

 

Список литературы

 

1. Подбельский В.В., Фомин С.С. Программирование на языке Си: Учебное пособие. –М.: Финансы и статистика, 2000.

2. Культин Н. С/С++ в задачах и примерах. –Санкт-Петербург: БХВ-Петербург, 2004.

3. Березин Б.И., Березин С.Б. Начальный курс Си и С++: Учебное пособие. –М.: ДИАЛОГ-МИФИ, 2004.

4. Шиманович. Е.Л. С/С++ в примерах и задачах: Учебное пособие. –Минск: ООО «НОВОЕ ЗНАНИЕ», 2004.

5. Блоски М.И. Язык программирования Си. –М.: Радио и связи. 1986.

6. Керниган Б., Ритчи Д. Язык программирования Си. –М.: Финансы и статистика, 1992.

 

Содержание

 

1 Основные понятия языка Си……………………………………….….3

2 Управляющие операторы………………………………………………9

3 Массивы. Одномерные и двумерные массивы………………………16

4 Указатели. Операция над указателями. Указатели и массивы……..23

5 Символьные данные и строки………………………………………..26

6 Функция. Указатель на функцию. Рекурсия……………….. ………31

7 Область видимости функций и классы памяти…………….……….35

8 Структуры и объединения…………………………………………….41

9 Файлы. Работы с файлами. Двоичный режим обмена с файлами.

    Строковой обмен с файлами…………………………………………49

10 Работы в графическом режиме……………………………….. ……56

Приложение А. Препроцессорные средства……….. ….. ……………63

Приложение Б. Распределение памяти…………………….…………..66

Приложение В. Стандартная библиотека функции языка Си………..68

Список литературы……………………………………………….. ……77

 

 

 

 

Сводный план 2005 г., доп______

 

 

 

 

Бимагамбетов Толеугали Сапарович

 

 

 

 

 

ПРОГРАММИРОВАНИЕ НА ЯЗЫКЕ Си

 

Методические указания программирование на языке Си

(для студентов всех форм обучения специальностей

050719 – Радиотехника, электроника и телекоммуникации (бакалавриат),

050704 – Вычислительная техника и программное обеспечение(бакалавриат))

 

 

 

         

 

Редактор        Ж.М. Сыздыкова

 

 

 

 

 

Подписан в печать ____________                    Формат 60х84    12/16

Тираж 50 экз                                                       Бумага типографская №1

Объем 3 уч.-изд.л.                                               Заказ _____. Цена

 

 

 

 

Копировально-множительное бюро

Алматинского института энергетики и связи

05480013, Алматы, Байтурсынова 126