
- •Простые типы данных
- •Модификаторы
- •1. Запись целых констант
- •2. Запись вещественных констант
- •3. Запись символьных констант
- •1. Арифметические операции над данными целочисленных типов
- •Простые типы данных
- •Модификаторы
- •Операция присваивания
- •Простые типы данных
- •Модификаторы
- •Операция присваивания
- •Диапазоны значений простых типов данных для ibm pc
- •5. Структура программы. Функции.
- •2. Формальные параметры функций
- •3. Возвращаемые значения функций
- •2.2 Пример простой программы на языке Си.
- •Управляющие конструкции
- •Ветвления
- •Циклы while, do.. .While и for
- •Теоретичсекий зачет по операторам цикла
- •Графика
- •Указатели
- •Преобразование типов
- •Задание:
- •Задание:
- •Задание:
- •Задание:
- •Задание:
- •Задание:
- •Задание:
- •Задание:
- •Задание:
- •Операции над указателями.
- •Структуры
- •Объединения
- •. Описания типов (typedef)
- •Поразрядные операции (bitwize)
Объединения
В Паскале нет аналогов объединениям (union), да и в Си объединения используются не слишком часто (как написано в одной из популярных книг по Си, объединения чаще всего используются для "сомнительного преобразования типов"). Мы не будем много заниматься
объединениями, но кратко поясним, что это такое.
Описание объединения похоже на описание структуры: union point { int х, у;}, но само объединение принципиально отличается от структуры тем, что все элементы объединения располагаются в памяти, начиная с одного и того же адреса. То есть переменные описанного нами объединения point будут занимать два байта памяти (элементы структуры point занимали четыре байта), и поля х и у данного объединения практически неразличимы. Приведем пример короткой программы, демонстрирующий сказанное:
#include <stdio.h>
void main(void)
{ union point { int x,y;} a;
a.x=10;
printf("\nРазмер объединения:%d a.x=%d a.y=%d",sizeof(a), a.x, a.y);
}
На печать будет выведено 2 10 10.
. Описания типов (typedef)
Выше мы фактически уже продемонстрировали описание типов (перечислимого типа, структур и объединений). В этом разделе речь пойдет о
специальном операторе typedef, который предназначен для объявления типов, но сначала приведем пример еще одного способа
"псевдоописания" типа посредством директивы #define. Для "псевдоописания" типа можно использовать следующую конструкцию:
/* Описание "типа" POINT */ #define POINT struct point
POINT { int x,y;};
/* Описание переменных типа POINT */
POINT a,b,c;
В приведенном примере тип point, конечно, не определяется, все построено на контекстных заменах. Но такой прием иногда используют, часто он встречается и в различных пособиях по языку Си,
Теперь о том, как в Си описываются именно типы. Рассмотрим сначала совсем простые примеры.
Вот описание обычной вещественной переменной с именем real: float real;. А вот описание типа real: typedef float real. После описания типа real можно описывать переменные данного типа: real а,Ь, с;
Вот описание целочисленного массива из десяти элементов: int array[10];..A вот описание типа array: typedef int array [10] . После описания типа array можно описывать переменные array ml,m2,m3; ?все это описания "целочисленных массивов ml, m2, mЗ из 10 элементов.
Макроопределения с параметрами и без них
Еще раз о директиве #include
Мы уже использовали директиву #include для включения в текст нашей программы заголовочных файлов. Эта директива просто включает текст из указанного в ней файла в данный. (В Паскале имеется аналог ? {$i}.) Следует обязательно обратить внимание на то, что недаром #include называют директивой препроцессора. Смысл этого "пре" в том, что эта директива (равно как и другие, с которыми мы далее познакомимся) выполняется до того, как текст программы поступает на вход компилятора. сначала над текстом программы работает препроцессор (обрабатывает директивы,начинающиеся со знака #), а потом то, что получилось, поступает на вход компилятора. Это, кстати, хорошо согласуется с тем, что директивы препроцессора могут располагаться только на внешнем уровне, "внутрь" функций препроцессор "не лезет", а компилятор, в свою очередь, директив препроцессора не понимает.Приведем короткий пример, поясняющий сказанное, Мы уже выносили заголовки функций в отдельный файл. Можно довести ситуацию до абсурда и вообще все функции разнести по отдельным файлам:
Файл dayftmc.h
int DayInRussian(int) ;
int DayInEnglish(int);
Файл main.с
vo.id main(void)
{ <...> }
Файл russian.c
int DayInRussian(int n)
{ <...> }
Файл english.c
int DayInEnglish(int n)
{ <...> }
И, наконец, файл program.с, содержащий только одни лишь директивы #include:
#include <stdio.h>
#include "dayfunc.h"
#include "main.c"
#include "russian.c"
#include "english.c"
Директива #define без параметров
Продолжая знакомиться с директивами препроцессора, рассмотрим директиву #define, посредством которой обычно определяются константы. Как всегда, начнем с того, что приведем простой пример:
#include <stdio.h>
#define N 10
void main(void)
{ int i;
for (i=0;i<N;i++) printf("\nПривет, мир!");
}
Неужели нельзя было просто написать i<10?
В этом примере, конечно, можно. Но представим себе другую ситуацию (пока именно представим, ибо соответствующие примеры имеются ниже). Пусть мы выполняем рисование на экране посредством некоторой "черепашки", которая передвигается с фиксированным шагом step (заданным в пикселях). Сколько раз встретится в такой программе значение шага? Уж наверняка не один раз. И не два, и не три... А если мы захотим изменить значение шага? Придется "лазить" по всей программе и искать соответствующие значения. А если мы снова захотим изменить это значение...
Кроме того, с помощью #define обычно определяются "популярные" константы (пи, е и т.п.). Ведь даже если каждая такая константа в программе используется единожды (что маловероятно), написать #define PI 3.14159265358979323846
и использовать в самой программе имя PI гораздо удобнее, чем записывать числовое значение непосредственно в программе.
Все "популярные" числовые константы и так уже определены в заголовочном файле math.h. Чтобы узнать их имена, можно посмотреть на содержимое файла. Число пи в нем определено константой М_Р1, число е ? константой М_Е. Почему перед ними стоит префикс "М_"? Достоверно не знаем, наверное, потому, что они находятся в math.h.
Директива #define с параметрами. Макроподстановка
Выше мы рассмотрели директиву препроцессора #define без параметров и выяснили, что с ее помощью выполняется обычная контекстная замена. Часто используется также "другой #define" ? директива #define с параметрами. Как всегда, приведем сначала простой пример. В отличие от Паскаля в Си нет функции sqr (возведение в квадрат), хотя для записи некоторых выражений (например, формулы расстояния между двумя точками в пространстве) эта функция очень пригодилась бы. Конечно, написать такую функцию совсем просто, но в Си такие задачки обычно решаются посредством макроподстановок: #define с параметрами. Итак, пример:
#include <stdio.h>
#define SQR(x) (x)*(х)
void main(void)
{ float a;
printf ("\nВведите число:");scanf("%f",&a) ;
printf ("Его квадрат: %f\n",SQR(a)); }
Фактически директива #define с параметрами работает так же как и без параметров, - заменяет все вхождения своего первого аргумента ( в данном случае sqr (х) ) на второй аргумент (в данном случае (х) * (х) ). Особенность этой директивы в том, что параметры ее формальные (это видно и из приведенного примера). То есть заменяются не только записи SQR (х) , но и все записи вида SQR (выражение) на записи вида (выражение) * (выражение) , Приведем еще несколько типичных примеров использования
директивы #define с параметрами:
#define ABS(х) ( (х) >=0) ? (х) : (-(х) )
#define МАХ(х,у) ( (х)>(у))?(х):(у)
Файлы
Потоки. Стандартные потоки ввода-вывода
Сначала требуется описать специальную "файловую переменную", далее установить соответствие между ней и файлом на диске, открыть файл (для чтения, записи, дозаписи и т.п.), поработать с файлом и закрыть его. Как обычно говорят, в Си используется "потоковый" ввод-вывод. Вы, возможно, будете удивлены, но в Паскале тоже используется потоковый ввод-вывод, но в книгах по Паскалю внимания самому этому термину уделяется гораздо меньше. Если не вдаваться в тонкости реализации языка, то поток - это на самом деле та самая переменная, которая используется для связи с файлом (более точно с файлом или устройством). Имеется и несколько предопределенных потоков (предопределенных файловых переменных), связанных со стандартными устройствами. Так, поток stdin изначально связан со стандартным устройством ввода (обычно клавиатурой), а поток stdout со стандартным устройством вывода (обычно терминалом). Имеется также поток stderr для вывода сообщений об ошибках. Он также изначально связан со стандартным устройством вывода (терминалом). Чтобы не сложилось впечатление, что механизм потокового ввода-вывода является исключительно особенностью Си, ниже приведены практически эквивалентные примеры на Си и Паскале.
Вот пример на Паскале, демонстрирующий, что в Паскале также имеется стандартный поток вывода output, изначально связанный с терминалом: writein (output, 'Привет, Мир!')
Аналогичный пример на Си будет выглядеть несколько иначе. Дело в том, что ряд функций вывода (например, printf) всегда работает именно со стандартным устройством вывода. Для работы с потоками, отличными от стандартных, имеются другие функции. (В данном случае мы все равно используем стандартный поток, но задать его хотим не "по умолчанию", а явно.) Поэтому вместо printf надо использовать функцию fprintf, первым параметром которой является поток. пример:
#include <stdio.h>
void main(void)
{ fprintf(stdout, "Привет, Мир!");}
Текстовые и двоичные потоки
Так сложилось, что файлы делятся на текстовые и нетекстовые (последние иногда называют двоичными, или бинарными Текстовые файлы отличаются от двоичных двумя особенностями: во-первых, они делятся на строки, каждая из которых заканчивается "переводом строки", состоящим из двух символов с кодами 0x0D 0x0A; во-вторых, текстовые файлы заканчиваются "признаком конца файла" ? символом с кодом 0х1А (точнее, должны заканчиваться, это условие соблюдается не всегда).
При чтении текстового файла (потока) функции Си преобразуют "признак конца строки", т.е. последовательность символов 0x0D 0x0A, в один символ 0x0A (' \п' ), а "признак конца файла (потока)" ? в значение EOF (End Of File). Константа EOF определена в заголовочном файле stdio.h и обычно равна?1.
При чтении двоичных потоков никаких преобразований не производится.
файловая переменная описывается как указатель на тип FILE (на самом деле это структура, описанная в stdio.h). Связывание файловой переменной с дисковым файлом и открытие файла выполняется функцией fopen. Первым параметром этой функции является имя файла, а вторым строка формата, в которой могут использоваться следующие символы:
г - открыть для чтения;
w - открыть для записи;
а - открыть для дозаписи;
t - текстовый поток;
b - двоичный (бинарный) поток.
Если тип потока явно не указан, то поток считается текстовым. После r, w и a можно добавить " + ", в этом случае файл открывается для произвольного доступа (чтения и записи).
Пример работы с текстовым фйлом
# include <stdio.h>
main()
{
int i;
char s[10];
FILE *f;
f=fopen("1.txt", "rt");
for (i=0;i<3;i++){ fscanf(f,"%s",s);
printf("%s\n",s);}
}
# include <stdio.h>
main()
{
int i;
char s[10];
FILE *f;
f=fopen("1.txt", "wt");
for (i=0;i<3;i++){
gets(s);
fprintf(f,"%s\n",s);}
fclose(f);}
задание
Записать в текстовый файл fam 5 фамилий. В текстовый файл ocenka записать 5 оценок
Вывести оценки и фамилии в виде таблицы.
Записать в текстовый файл temp величину среднемесячных температур воздуха.
Вычислить среднегодовую температуру, а также средние температуры за каждый период: зима, лето, весна, осень.
Записать в текстовый файл old 10 оценок по 12 балльной шкале. Пересчитать эти оценки по пятибалльной шкале и записать полученные результаты в файл new
Записать в текстовый файл full данные : фамилия, имя, отчество (5 записей). Пепеписать в новыц файл fio только начальные буквы : например: Иванов Петр Семенович: ИПС
Ввести произвольную строку < 30 символов.Записать в текстовый файл vibor все гласные буквы, встречающиеся в этой строке.
Cлучайным образом выбрать 10 чисел от 0 до 100. Запистаь эти числа в файл random1
Вычислить среднее арифметическое этих чисел
Заполнить таблицу чисел 3x3. Подсчитать сумму элементов главной диагонали этой таблицы и записать результат в файл glav
Из файла glav взять число и вывести его в центре экрана
В файл fams записать фамилии, в файл adress –адреса, а в файл telefon телефоны 5 человек.
Вывести в виде таблицы телефонный справочник
10. Записать в файл rand2 10 случайных чисел. Прочитать их из файла rand2 и записать в rand3 квадраты этих чисел.
Ввод-вывод в поток
Ввод-вывод в поток можно осуществлять различными способами в виде последовательности байтов, в виде символов и строк или с использованием форматных преобразований.
Операции ввода- вывода выполняются, начиная с текущей позиции потока, определяемой положением указателя потока. Указатель устанавливается при открытии на начало или конец файла ( в соответствии с режимом открытия) и изменяется автоматически при выполнении каждой операции ввода-вывода. Текущее положения указателя можно получить с помощью функций ftell и fgetpos и задать явным образом с помощью функций ftell и fgetpos. Эти функции нельзя использовать для стандартных потоков.
Основные функции ввода-вывода потока:
чтение и запись потока байтов fread ,fwrite
чтение символа из потока getc,fgets, из стандартного потока stdin –getchar
запись символов в поток – puts,fputs, в стандартный поток stdout- putchar
чтение строки из потока – fgetsБ из стандартного потока stdin – gets
запись строки в поток – fputs, в стандартный поток stdout – puts
форматированный ввод из потока – fscanf, из стандартного потока stdin –scanf, из строки sscanf
форматированный вывод в поток – fprintf, в стандартный поток stdout –printf, в строку sprintf
Закрытие потока
Потом закрывается либо при завершении программы либо явным образом с помощью фунции fclose
Обработка ошибок
Функции работы с потоком возвращают значения, которые рекомендуется анализировать в программе и обрабатывать ошибочные ситуации, возникающие. Например, при открытии файлов или чтении из потока.. при работе с файлами используют функции
Int feof(FILE*) – возвращает не равное 0 значение, если достигнут конец файла, в противном случае 0
Int ferror(FILE*) – возвращает не равное 0 значение, если обнаружена ошибка вводы- вывода, в противном случае 0.
Функции
Функция- это именованная последовательность описаний и операторов, выполняющая какое-либо законченное действие.
Функция может принимать параметры и возвращать значение.
Любая программа на С состоит из функций, одна из которых должна иметь имя main ( с не начинается выполнение программы). Функция начинает выполняться в момент вызова и должна быть объявлена и определена. Объявлений должна быть несколько, а определение только одно.
Объявление функции должно находиться в теле программы раньше ее вызова для того, чтобы компилятор мог проверить правильность вызова.
Объявление функции (прототип, заголовок) задает ее имя, тип возвращаемого значения и список передаваемых параметров.
Определение функции содержит, кроме объявления, тело функции, представляющее собой последовательность операторов и описаний в фигурных скобках.
[класс] тип имя ([список параметров]) [(throw (исключения)]
{тело функции}
класс- необязательный модификатор класса. С помощью которого можно задать область видимости функции, используя ключевое слово extern (глобальная видимость во всех модулях программы) или static(видимость в прелелах определенной функции)
Тип возвращаемого значения может быть любым, кроме массива и функции ( но может быть указателем на массив или функцию). Если функция не должна возвращать значения, указывается тип Void
Список параметров определяет значения, которые необходимо передать в функцию в момент ее вызова. Элементы списка разделяются запятыми. Для каждого параметра указывается тип и имя ( в объявлении имена можно опускать)
Существует список исключений функции
В определении, объявлении и вызове функции типы и порядок следования параметров должны совпадать.
Тип возвращаемого значения и типы параметров совместно определяют тип функции
Для вызова функции в простейшем виде можно указать ее имя. За которым в круглых скобках через запятую перечисляются имена передаваемых аргументов.
Примеры:
for (i=1;i<=a);i++) f= Вычисление суммы#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
int sum (int a,int b);ё
main()
{
int x,y,s;
scanf("%d %d",&x,&y);
s=sum(x,y);
printf("%d",s);
}
int sum(int a,int b)
{
return(a+b);
}
Вычисление факториала
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
int fact(int a);
main()
{
long x,y,s;
scanf("%d",&x);
s=fact(x);
printf("%ld",s);
}
int fact(int a)
{long f=1; int i;
f*i;
return(f);
}