Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Основы программирования на языке С. Учебное пособие
.pdf
сваиваться идентификаторам по умолчанию следующим образом: самый
левый в фигурных скобках идентификатор получит значение 0, а каждый
последующий увеличивается на 1. Имена перечислимых констант должны
быть уникальными, однако к значениям констант это не относится. Одно
значение могут иметь разные константы. Для перечислимых констант
можно ввести имя типа, соответствующего приведенному списку констант,
например, определение
enum week {Sunday, Monday, Tuesday, Wednesday, Thursday,
Friday, Saturday};
не только определяет константы Sunday==0, Monday==1,…, но
и вводит перечислимый тип с именем week, который может в дальнейшем
использоваться в определениях и описаниях других типов.
Тип void можно использовать только для определений некоторых
производных типов. Объектов типа void не существует, то есть описание
void i; недопустимо.
Из основных типов с помощью операций *, &, [], () и механизмов
определения структурированных
типов (классов, структур, объединений)
можно конструировать множество производных типов. Приведем несколько примеров:
int ar [5]; – массив, состоящий из пяти объектов типа int, доступ
к которым обеспечивают индексированные переменные ar[0], ar[1], ar[2]
ar[3], ar[4];
char t[]= “текст”; // массив, состоящий из пяти объектов типа char;
void a1 (double); //функция, не возвращающая никакого значения
и имеющая аргумент типа double;
char *a2; // указатель на объекты типа char (значением указателей
являются
адреса участков памяти, выделенных для объектов конкретно-
го типа);
long a3;// не инициализированная переменная типа long;
long &a4=a3; // инициализированная ссылка на объект типа long ;
int *a5[];// массив указателей на объекты типа int;
int (*a5)[]; // указатель на массив объектов типа int.
Обратите внимание, что строка может быть описана как массив типа
char. Подробно назначение и синтаксис таких типов данных как указатели,
массивы,
ссылки, структуры данных и классы будет описаны в следующих
главах пособия.
Кроме того, используя спецификатор typedef, можно вводить
в программе удобные обозначения для сложных описаний типов.
Например,
typedef unsigned char UC;
UC simbol;
Здесь вводится новый тип UC и переменная этого типа simbol, значениями которой являются беззнаковые числа в диапазоне от 0 до 255.
11

Для определения размера (в байтах) области памяти, выделяемой для
переменной определенного типа, в языке С используется операция sizeof.
Разрешены два формата операции
Sizeof (имя_переменной)
или
sizeof (тип данных).
Например, пусть определена переменная int i. Тогда операция
sizeof(i) определит, сколько байт выделено для переменой i.
Соотношение между основными типами можно записать так:
1==sizeof(char) <= sizeof(short) <= sizeof(int) <=
sizeof(long)<= <=sizeof(float) <= sizeof(double)
Каждый, описанный в программе объект (в частности, переменная),
имеет следующие основные характеристики:
– область действия идентификатора – эта та часть программы, в которой идентификатор может быть использован для доступа к связанному
с ним объекту (обычно имя переменной действует с места ее определения
и до конца блока {}, в котором она была определена);
– видимость объекта (данное понятие необходимо в связи с возможностью повторных определений идентификатора внутри вложенных блоков);
– продолжительность существования, которая определяет период,
в течение которого идентификаторам в программе соответствуют конкретные объекты в памяти. Определены три вида продолжительности: статическая, локальная и динамическая;
– класс памяти – определяет размещение объекта в памяти
и про-
должительность его существования.
Определенные операции могут в зависимости от их операндов вызывать преобразование значения операнда от одного типа к другому. Например,
char сh= ‘a’;
int k = ch; // символьный тип преобразуется к целому
printf(“%d”, k;) // на экран выведется ASCII–код символа ch
Иногда необходимо явным образом преобразовывать типы, например, для вывода на экран ASCII-кода символа ch без использования вспомогательной переменной целого типа будем писать
printf(“%d”, (int) ch); printf(“%d”, int (ch));
Использовать допустимые неявные преобразования типов необходимо с большой осторожностью.
Задание 2. Описание переменных и преобразование типов
Каждому студенту рекомендуется выполнить упражнения 1-3.
1. Опишите переменные всех типов, приведенных в табл. 2. Опреде-
лите количество байтов, которые занимают в памяти переменные этих типов. Результат выведите на экран в виде
sizeof (int)=2
sizeof (long)=4…
12

2. Опишите в программе две переменные целого типа и введите их
значения с клавиатуры. Выведите на экран вещественное число, равное делению первой переменной на вторую. (Используйте операцию деления /.
При делении целых чисел эта операция возвращает только целую часть частого. Результат операции деления / будет вещественным, если делимое
или (и) делитель –
вещественное число. Поэтому, в данной задачи необхо-
димо использовать явное преобразование типов).
3. Опишите в программе переменную символьного типа и введите ее
значение с клавиатуры. Выведите на экран ASCII–код введенного символа
в десятичном, восьмеричном и шестнадцатеричном виде, а также символы –
больший и меньший «сосед» данного по ASCII-коду.
13

3. ВВОД-ВЫВОД ЗНАЧЕНИЙ ПЕРЕМЕННЫХ
Значение переменной может быть указано при описании, а может быть
введено с клавиатуры. Рассмотрим, как происходит ввод-вывод в языке С.
В языке С не определено никаких ключевых слов, с помощью которых можно выполнять ввод-вывод. Вместо них используются библиотечные функции. Заголовочным файлом для
функций ввода-вывода является
<stdio.h>. Имеются как консольные (ввод с клавиатуры, вывод на экран),
так и файловые функции ввода/вывода. С практической точки зрения консольный и файловый ввод/вывод отличаются друг от друга очень мало.
В данной главе мы рассмотрим консольные функции.
Самыми простыми из консольных функций ввода/вывода являются
getchar(), которая читает символ с клавиатуры, и putchar(), которая отображает символ на экране. Первая из этих функций ожидает, пока не будет
нажата клавиша, а затем возвращает значение этой клавиши. Кроме того,
при нажатии клавиши на клавиатуре на экране дисплея автоматически
отображается соответствующий символ. Вторая же функция, putchar(),
отображает символ на экране в
текущей позиции курсора.
Пример использования функций getchar() и putchar():
char c;
printf("Enter symbol: ");
c=getchar();
printf("Symbol= ");
putchar(c);
Для считывания и отображения строк можно использовать функции
gets() и puts() соответственно.
Функция gets() читает строку символов, введенную с клавиатуры,
и записывает ее в память по адресу, на который указывает ее аргумент.
Символы можно вводить с клавиатуры до тех пор, пока не будет введен
символ возврата каретки. Он не станет частью строки, а
вместо него в ее конец будет помещен символ конца строки ('0'). Необходимо очень осторожно
использовать gets(), потому что эта функция не проверяет границы массива,
в который записываются введенные символы. Таким образом, может случиться, что пользователь введет больше символов, чем помещается в этом
массиве. Хотя функция gets() прекрасно подходит для программ-примеров
и
простых программ, предназначенных для «домашнего использования», но
в профессиональных программах ею лучше не пользоваться.
Функция puts() отображает на экране свой строковый аргумент, после чего курсор переходит на новую строку. Отметим, что функция выполняется быстрее, чем printf(), но не позволяет числа или делать преобразования формата.
Пример использования функций gets() и puts():
char str[10];
printf("Enter string: ");
14

gets(str);
printf("String= ");
puts(str);
Для консольного чтения и записи данных в разных форматах (форматный ввод-вывод) в языке С используются функции scanf() и printf() соответственно. Обе функции могут работать с любым встроенным типом
данных, а также с символьными строками, которые завершаются символом
конца строки ('0').
Функция printf(управляющая_строка) вывод данные на экран
и в
качестве результата возвращает число выведенных символов или отри-
цательное значение в случае ошибки.
Управляющая_строка состоит из элементов двух видов. Первый из
них – это символы, которые предстоит вывести на экран; второй – это спецификаторы преобразования, которые определяют способ вывода стоящих
за ними аргументов. Каждый такой спецификатор начинается со знака
процента,
за которым следует код формата. Аргументов должно быть ровно столько, сколько и спецификаторов, причем спецификаторы преобразования и аргументы должны попарно соответствовать друг другу в направлении слева направо. Например, в результате такого вызова printf()
printf("Мне нравится язык %c %s", 'C', "и к тому же очень
сильно!");
будет выведено
Мне нравится язык C и к тому же очень сильно!
В этом примере первому спецификатору преобразования (%c), соот-
ветствует символ 'C', а второму (%s), – строка «и к тому же очень сильно!».
Спецификаторы преобразования для функции printf() приведены
в табл. 3.
Таблица 3
Спецификаторы преобразования для функции printf()
Код Формат
%a
%A
%c
%d
%i
%e
%E
%f
%g
%G
%o
%s
%u
%x
Шестнадцатеричное в виде 0xh.hhhhp+d
Шестнадцатеричное в виде 0Xh.hhhhP+d
Символ
Десятичное целое со знаком
Десятичное целое со знаком
Экспоненциальное представление ('е' на нижнем регистре)
Экспоненциальное представление ('Е' на верхнем регистре)
Десятичное с плавающей точкой
В зависимости от того, какой вывод будет короче, используется %е или %f
В зависимости от того, какой вывод будет короче, используется %Е или %F
Восьмеричное без знака
Строка символов
Десятичное целое без знака
Шестнадцатеричное без знака (буквы на нижнем регистре)
15

Окончание табл. 3
Код Формат
%X
%p
%n
%%
Шестнадцатеричное без знака (буквы на верхнем регистре)
Выводит указатель
Аргумент, соответствующий этому спецификатору, должен быть указателем
на целочисленную переменную. Спецификатор позволяет сохранить в этой
переменной количество записанных символов (записанных до того места,
в котором находится код %n)
Выводит знак %
Например,
printf("%s%d%c%o%c%Х","This is number 10 : ",10,
',',10,',',10);
выведет на экран
This is number 10 : 10,12,A
Во многих спецификаторах преобразования можно указать модификаторы, которые меняют их значение. Например, можно указывать минимальную ширину поля, количество десятичных разрядов и выравнивание
по левому краю. Модификатор формата помещают между знаком процента
и кодом формата.
Целое число, расположенное между знаком % и кодом формата, играет роль модификатора минимальной ширины поля
. Если указан модификатор минимальной ширины поля, то чтобы ширина поля вывода была не
меньше указанной минимальной длины, при необходимости вывод будет
дополнен пробелами. Если же выводятся строки или числа, которые длиннее указанного минимума, то они все равно будут отображаться полностью. По умолчанию для дополнения используются пробелы. А
если для
этого надо использовать нули, то перед модификатором ширины поля следует поместить 0. Например, %05d означает, что любое число, количество
цифр которого меньше пяти, будет дополнено таким количеством нулей,
чтобы число состояло из пяти цифр. Например, при выполнении следующего фрагмента программы
double item;
item = 123.45;
printf("%f\n", item);
printf("%10f\n", item);
printf("%012f\n", item);
на экран выведется:
123.450000
123.450000
00123.450000
Модификатор минимальной ширины поля чаще всего используется при создании таблиц, в которых столбцы должны быть выровнены по
вертикали.
Модификатор точности следует за модификатором минимальной
ширины поля (если таковой имеется). Он состоит из точки и расположен-
16

ного за ней целого числа. Значение этого модификатора зависит от типа
данных, к которым его применяют.
Когда модификатор точности применяется к данным с плавающей
точкой, для преобразования которых используются спецификаторы преобразования %f, %e или %E, то он определяет количество выводимых десятичных разрядов. Например, %10.2f означает, что ширина поля вывода будет не менее
5 символов, причем для десятичных разрядов будет отведено
две позиции. Если модификатор точности применяется к %g или %G, то он
определяет количество значащих цифр.
Примененный к строкам, модификатор точности определяет максимальную длину поля. Например, %5.7s означает, что длина выводимой
строки будет составлять минимум пять и максимум семь символов. Если
строка окажется длиннее, чем
максимальная длина поля, то конечные сим-
волы выводиться не будут.
Если модификатор точности применяется к целым типам, то он
определяет минимальное количество цифр, которые будут выведены для
каждого из чисел. Чтобы получилось требуемое количество цифр, добавляется некоторое количество ведущих нулей.
Пример, использования модификатора точности:
printf("%.3f\n", 123.1234567);
printf("%8.8d\n", 1000);
printf("%3.7s\n", "This is string");
При выполнении этого фрагмента программы на экран будет выведена следующая информация:
123.123
00001000
This is
По умолчанию весь вывод выравнивается по правому краю. То
есть если ширина поля больше ширины выводимых данных, то эти данные
располагаются по правому краю поля. Вывод по левому краю можно
назначить принудительно, поместив знак минус прямо за %. Например,
%–5.2f означает, что число с плавающей точкой и с двумя десятичными
разрядами будет
выровнено по левому краю 5-символьного поля.
Например, при выполнении следующего фрагмента кода
printf("%15d\n", 100);
printf("%-15d\n", 100);
на экран выведется:
100
100
Непосредственное расположение # перед спецификаторами преобразования g, G, f, Е или e означает, что при выводе обязательно появится десятичная точка – даже если десятичных цифр нет. Если вы поставите # непосредственно перед x или X, то шестнадцатеричное число будет выведено
с префиксом 0x. Если # будет непосредственно предшествовать специфика-
17

тору преобразования o, число будет выведено с ведущим нулем. К любым
другим спецификаторам преобразования модификатор # применять нельзя.
Функция scanf(управляющая_строка) выполняет ввод с клавиатуры и возвращает количество тех элементов данных, которым было успешно присвоено значение, либо EOF в случае ошибки.
Управляющая строка состоит из символов трех видов: спецификаторов
преобразования, разделителей
и символов, не являющихся разделителями.
Каждый спецификатор формата ввода начинается со знака %, причем
спецификаторы формата ввода сообщают функции scanf() тип считываемых данных. Перечень этих кодов приведен в табл. 4.
Таблица 4
Спецификаторы преобразования для функции scanf()
Код Значение
%a Читает значение с плавающей точкой
%c Читает одиночный символ
%d Читает десятичное целое число
%i
%e Читает число с плавающей точкой
%f Читает число с плавающей точкой
%g Читает число с плавающей точкой
%о Читает восьмеричное число
%s Читает строку
%x Читает шестнадцатеричное число
%p Читает указатель
%n Принимает целое значение, равное количеству уже считанных символов
%u Читает десятичное целое число без знака
%[] Читает набор сканируемых символов
%% Читает знак процента
Читает целое число как в десятичном, так и восьмеричном
или шестнадцатеричном формате
Спецификаторам преобразования в порядке слева направо ставятся
в соответствие элементы, представляющие собой адреса объектов, куда
необходимо считать информацию. То есть если необходимо считать данные в переменную а, то следует указать, не ее имя, а ее адрес, т.е. &a.
Например, следующая программа читает десятичное, восьмеричное
и шестнадцатеричное число и выводит их
int a, b,c;
scanf("%d%o%x", &a, &b, &c);
printf("%d %d %d", a,b,c);
на экран в десятичном виде:
При чтении одиночного символа символы разделителей читаются так
же, как и любой другой символ, хотя при чтении данных других типов разделители интерпретируются как разделители полей. Например, при вводе
с клавиатуры "x y" фрагмент кода
scanf("%c%c%c", &a, &b, &c);
помещает символ x в a, пробел – в b, а символ y – в c.
18

Для чтения из входного потока строки можно использовать функцию
scanf() со спецификатором преобразования %s. Использование специфика-
тора преобразования %s заставляет scanf() читать символы до тех пор, пока
не встретится какой-либо разделитель. Читаемые символы помещаются
в символьный массив, на который указывает соответствующий аргумент,
а после введенных символов еще добавляется символ конца строки ('0').
Что касается scanf(),
то таким разделителем может быть пробел, разделитель строк, табуляция, вертикальная табуляция или подача страницы. В отличие от gets(), которая читает строку, пока не будет нажата клавиша
<ENTER>, scanf() читает строку до тех пор, пока не встретится первый
разделитель. Это означает, что scanf() нельзя использовать для чтения
строки "Hello, World", потому что после пробела процесс
чтения прекра-
тится. Пример ввода строки:
char str[80];
printf("Введите строку: ");
scanf("%s", str);
printf("Вот Ваша строка: %s", str);
Если введена строка "Hello, World", то на экран будет выведено "Hel-
lo,". Обратите внимание, что перед str не стоит операция взятия адреса &,
так как имя массива является адресом его первого элемента.
Ввод-вывод массив и символьных массивов это различные процеду-
ры. Например, если дан массив
int str[]={1,2,3};
то запись
printf("%i", str);
приведет не к его выводу, а к выводу на экран адреса массива, пре-
образованного к целому числу,
а запись
scanf("%i", str);
приведет к чтению только первого элемента массива.
Чтобы ввести, а затем вывести элементы данного массива, необхо-
димо использовать запись
scanf("%i%i%i", &str[0],&str[1],&str[2]);
printf("Элементы массива: %i %i %i",
str[0],str[1],str[2]);
Разделитель в управляющей строке дает scanf() указание пропустить
в потоке ввода один или несколько начальных разделителей. Разделителями являются пробелы, табуляции, вертикальные табуляции, подачи страниц и разделители строк. В сущности, один разделитель в управляющей
строке заставляет scanf() читать, но не сохранять любое количество (в том
числе и нулевое) разделителей, которые находятся перед
первым симво-
лом, не являющимся разделителем.
Если в управляющей строке находится символ, не являющийся разделителем, то функция scanf() прочитает символ из входного потока, про-
19

верит, совпадает ли прочитанный символ с указанным в управляющей
строке, и в случае совпадения пропустит прочитанный символ. Например,
"%d,%d" заставляет scanf() прочитать целое значение, прочитать запятую
и пропустить ее (если это была запятая!), а затем прочитать следующее целое значение. Если же указанный символ во входном потоке не будет
найден, то scanf() завершится.
Как и printf(), функция scanf() дает возможность модифицировать некоторое число своих спецификаторов формата. В спецификаторах формата
моно указать модификатор максимальной длины поля. Это целое число,
расположенное между % и спецификатором формата; оно ограничивает
число символов, считываемых из этого поля. Например, чтобы считывать
в переменную str не более 20 символов, пишите
scanf("%20s", str);
Если поток ввода содержит больше 20 символов, то при следующем
вызове функций ввода считывание начнется после того места, где оно закончилось при предыдущем вызове. Например, если вы в ответ на вызов
scanf() из этого примера введете
ABCDEFGHIJKLMNOPRSTUVWXYZ
то в str из-за спецификатора максимальной ширины поля будет помещено только 20 символов, то есть
символы вплоть до Т. Это значит, что
оставшиеся символы UVWXYZ пока еще не прочитаны. При следующем
вызове scanf(), например, при выполнении оператора
scanf("%s", str);
в str будут помешены буквы UVWXYZ. Ввод из поля может завершиться и до того, как будет достигнута максимальная длина поля – если встретится
разделитель. В таком случае scanf() переходит к следующему полю.
Чтобы прочитать длинное целое, перед спецификатором формата
поместите l (эль). А для чтения короткого целого значения перед спецификатором формата следует поместить n. Эти
модификаторы можно исполь-
зовать со следующими кодами форматов: d, i, o, u, x и n.
По умолчанию спецификаторы f, e и g дают scanf() указание присваивать данные переменной типа float. Если перед одним из этих спецификаторов будет помещен l (эль), то scanf() будет присваивать данные переменной типа double. Использование L дает scanf() указание, чтобы переменная,
принимающая данные, имела тип long double.
Функция scanf() может прочитать поле,
но не присваивать прочитанное значение никакой переменной; для этого надо перед литеройспецификатором формата поля поставить звездочку, *. Например, когда
выполняется оператор
scanf("%d%*c%d", &x, &y);
можно ввести пару координат 10,10. Запятая будет прочитана правильно, но ничему не будет присвоена. Подавление присвоения особенно
полезно тогда, когда нужно обработать только часть того, что вводится.
20
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
