Главная страница

Шпаргалка по языку СИ. Конспект Лекций «программирование На Языке Высокого Уровня Си» П. Конспект лекций для студентов высших учебных заведений, обучающихся по специальности 230102. 65 Автоматизированные системы обработки информации и управления


Скачать 1.25 Mb.
НазваниеКонспект лекций для студентов высших учебных заведений, обучающихся по специальности 230102. 65 Автоматизированные системы обработки информации и управления
АнкорШпаргалка по языку СИ
Дата26.05.2022
Размер1.25 Mb.
Формат файлаpdf
Имя файлаКонспект Лекций «программирование На Языке Высокого Уровня Си» П.pdf
ТипКонспект лекций
#550544
страница5 из 15
1   2   3   4   5   6   7   8   9   ...   15
Тема 7. Представление основных управляющих структур
программирования
Оператор присваивания
Оператор присваивания записывается в виде:
<переменная> = <выражение>;
Значение выражения из правой части присваивается переменной из левой части оператора присваивания.
При присваивании необходимо обеспечивать совместимость типов (иногда говорят – совместимость по присваиванию), т.е. тип, полученный при вычислении выражения, должен быть совместим с типом переменной, которой это значение должно быть присвоено. Значение типа T1 является совместимым по присваиванию с типом T2 (то есть, допустим, оператор
T1=T2), если выполняется одно из следующих условий:

T1 и T2 имеют тождественные типы, и ни один из них не является файловым типом или структурным типом, содержащим компонент с файловым типом на одном из своих уровней.
87


T1 и T2 являются совместимыми порядковыми типами, и значения типа T2 попадают в диапазон возможных значений T1.

T1 и T2 являются вещественными типами, и значения типа T2 попадают в диапазон возможных значений T1.

T1 является вещественным типом, а T2 является целочисленным типом.

T1 и T2 являются строковыми типами.

T1 является строковым типом, а T2 является символьным типом
(Char).

T1 и T2 являются совместимыми множественными типами, и все члены значения типа T2 попадают в диапазон возможных значений T1.

T1 и T2 являются совместимыми типами указателей.

T1 и T2 являются совместимыми процедурными типами.

T1 представляет собой процедурный тип, а T2 – процедура или функция с идентичным типом результата, идентичным числом параметров и соответствием между типами параметров.
На этапе компиляции и выполнения программы выдается сообщение об ошибке, если совместимость по присваиванию необходима, а ни одно из условий предыдущего списка не выполнено.
Составной оператор
Синтаксис оператора
{ <операторы> }
Здесь «операторы» – один или несколько любых операторов языка Си, разделенных точкой с запятой. Составной оператор предназначен для объединения нескольких операторов в один, что имеет решающее значение там, где синтаксис языка Си допускает использование только одного оператора.
Оператор перехода Goto
Синтаксис оператора goto <метка>;
88

«Метка» - это любой идентификатор, после которого поставлено двоеточие. Оператор goto указывает на то, что выполнение программы необходимо продолжить, начиная с оператора, перед которым записана метка.
Метку можно поставить перед любым оператором в той функции, где находится соответствующий ей оператор goto. Ее не надо объявлять.
Условный оператор If
В синтаксисе языка Си предусмотрены две конструкции для записи условного оператора: условный оператор с одним вариантом действии
(сокращенная форма) и условный оператор с двумя вариантами действии (полная форма).
Синтаксис оператора if можно представить следующим образом:
if (<условие>) <оператор1> [else <оператор2>];
Здесь «условие» – логическое выражение. В результате его вычисления должен получаться результат, имеющий стандартный логический тип. Если результатом является значение True (Истина), то выполняется «оператор1». Если логическое выражение принимает значение False, и ключевое слово else отсутствует (сокращенная форма), то управление передается оператору, следующему за оператором if. В полной форме условного оператора в этом случае сначала будет выполнен «оператор2».
В общем случае, при наличии множественных вложенных операторов if ключевое слово else связывается с ближайшим ключевым словом if, которое еще не связано с ключевым словом else.
Приведем примеры использования оператора if.
if (A != B) Z = X+Y;
else Z = 1;
if (P1 = null)
{
P1 = Pl^.next;
if (a > 0)
{
if (b > 0)
{
if (c > 0) d = a*b*c;
else
if (d < 0)
{
d = SQRT(–a*b*c);
89

c = –c*b;
}
else d = SQR(d);
}
else d = a*b;
else d = a;
}
Пример 15. Используем оператор if для разработки программы и блок- схемы алгоритма поиска действительных корней квадратного уравнения
(рис.31).
float a,b,c,z;/*Даны коэффициенты квадратного уравнения*/
float x1,x2;
/*Найти и вычислить их корни*/
printf("\nВведите коэффициент а:\n");
scanf("%f",&a);
printf("Введите коэффициент b:\n");
scanf("%f",&b);
printf("Введите коэффициент с:\n");
scanf("%f",&c);
z=b*b-4*a*c;
/*Z-дискриминант*/
if(z>=0.0)
{
x1=(-b+sqrt(z))/2/a;
x2=(-b-sqrt(z))/2/a;
printf("\nКорни уравнения:x1=%f,\nx2=%f",x1,x2);
}
else
printf("\nДействительные корни отсутствуют.");
a=0
D:=b*b-4*a*c
x1:=(-b+sqrt(D))/(2*a)
Начало
a, b, c
b=0
D
Корней нет
x2:=(-b–sqrt(D))/(2*a)
x1, x2
Корней нет
x := -b/(2*a)
x
D>0
D<0
D=0
x
Конец
x := – c / b
нет
да
да
нет
ввести a, b, c
f(x)
Рис. 31. Алгоритм вычисления корней квадратного уравнения
90

Оператор выбора switch
Оператор swith (переключатель), вызывает передачу управления к одному из нескольких операторов, в зависимости от значения выражения. Оператор имеет форму
switch (селектор)
{
case констанстное выражение_1: оператор_1; [break;]
case констанстное выражение_2: оператор_2; [break;]
. . .
case констанстное выражение_n: оператор_n; [break;]
[default: оператор; [break;]]
}
Селектор и константное выражение должны иметь тип int. Оператор помечается префиксом case и обычно является составным. Никакие две вариантные константы в одном и том же переключателе не могут иметь одинаковое значение.
При выполнении оператора swith вычисляется входящий в него селектор и сравнивается с каждой вариантной константой. Если одна из вариантных констант оказывается равной значению этого выражения, то управление передается оператору, который следует за совпадающим вариантным префиксом. Если ни одна из вариантных констант не совпадает со значением выражения и если при этом присутствует префикс default, то управление передается оператору, помеченному этим префиксом. Если ни один из вариантов не подходит и префикс default отсутствует, то ни один из операторов в переключателе не выполняется.
Сами по себе префиксы case и default не изменяют поток управления, которое беспрепятсвенно проходит через такие префиксы. Для выхода из переключателя используется оператор break.
Пример 16. Напишем два эквивалентных оператора: на основе swith и на основе последовательности условных операторов для выбора времени года в зависимости от целочисленного значения переменной months, в которой хранится номер текущего месяца.
// выбор на основе оператора switch
switch (months)
{
91

case 1 : season = “Зима”; break;
case 2 : season = “Зима”; break;
case 12: season = “Зима”; break;
case 3 : season = “Весна”; break;
case 4 : season = “Весна”; break;
case 5 : season = “Весна”; break;
case 6 : season = “Лето”; break;
case 7 : season = “Лето”; break;
case 8 : season = “Лето”; break;
default: season = “Осень”; break;
}
// теперь тоже самое на основе операторов if
if (months==1 || months==2 || months==12) season = "Зима";
else
if (months==3 || months==4 || months==5) season = "Весна";
else
if (months==6 || months==7 || months==8) season = "Лето";
else season = "Осень";
На блок-схемах алгоритмов оператор выбора изображается следующим образом (рис.32):
Операторы цикла while, do – while, for
Операторы цикла позволяют реализовать в языке Си, рассмотренные ранее алгоритмические конструкции цикла с предусловием, цикла с постусловием и цикла с заданным количеством итераций.
Цикл с предусловием while
Синтаксис while <условие> do <оператор>
Сначала вычисляется «условие», если оно истинно, то выполняется
«оператор», потом повторяется проверка «условия», и если оно истинно опять months
1,2,12 3,4,5 6,7,8
иначе
A
season = “Весна”
season = “Лето”
season = “Осень”
D
D>1, D<0
D=1
D=0
B
С
season = “Зима”
Рис.32. Примеры отображения оператора выбора на блок-схемах
92
выполняется «оператор» и так далее. Если условие ложно, то управление передается на оператор, расположенный после цикла. Вход в тело цикла возможен только через его заголовок, т.е. нельзя войти во внутрь цикла, используя оператор goto. Однако с помощью этого оператора можно досрочно выйти из цикла, что делать не рекомендуется, для этого используется оператор break.
Пример 17. Необходимо ввести вектор вещественных чисел с помощью цикла с предусловием.
int n=10;
float a[10];
int i;
printf("Введите вектор\n");
i=0;
while (i
{
scanf ("%d", &a[i]);
i++;
}
Цикл с постусловием do – while
Синтаксис do <операторы> while <условие>
Сначала выполняются «операторы», затем вычисляется «условие», если оно ложно, то опять выполняются «операторы» и т.д. Если «условие» истинно, то управление передается на оператор, расположенный после цикла. Вход в тело цикла возможен только через его заголовок, т.е. нельзя войти во внутрь цикла, используя оператор goto. Однако с помощью этого оператора можно досрочно выйти из цикла, что делать не рекомендуется, для этого используется оператор break.
Отметим два ключевых отличия циклов с предусловием и постусловием:
1.
Оператор внутри цикла с предусловием может не выполниться ни разу, если при входе в цикл условие сразу будет ложным. Операторы внутри цикла с
постусловием выполнятся как минимум 1 раз вне зависимости от истинности условия в конце цикла.
2.
Выход из цикла с предусловием происходит, когда условие ложно, а выход из цикла с постусловием – когда условие истинно, поэтому если нужно поменять
93
циклы (цикл с постусловием поменять на цикл с предусловием или наоборот), условие нужно поменять на противоположное.
Эти особенности нужно учитывать при разработке и отладке программ.
Пример 18. Пусть имеется матрица вещественных чисел A[10,10].
Используя цикл с постусловием, написать программу, которая находит в нечетных строках матрицы минимальный элемент, а в четных строках – максимальный.
int n=3;
int a[3][3];
int min,max;
int i,j,imax,jmax,imin,jmin;
// ввод матрицы пока пропускаем
max=a[2][1]; imax=2; jmax=1;
min=a[1][1]; imin=1; jmin=1; i=1;
do
{
j=1;
do
{
//если нечетная строка и элемент меньше минимального
if ((i%2)!=0 && a[i][j]
{
min=a[i][j];
imin=i; jmin=j;
}
else
if (((i%2)==0) && (a[i][j]>max))
{
max=a[i][j];
imax=i; jmax=j;
}
j++;
}
while (j>n);
i++;
}
Цикл с заданным количеством итераций (цикл с параметром) for
Цикл for лучше всего использовать, когда известно количество повторений в цикле. Программа при этом становится короче и понятнее.
Синтаксис
FOR (выражение 1; выражение 2; выражение 3) <оператор>
Здесь «выражение 1» задает начальное значение переменной цикла,
«выражение 2» – это выражение, определяющее условие, при котором тело цикла будет выполняться, «Выражение 3» определяет изменение переменных, управляющих циклом после каждого выполнения тела цикла. Значения
94

«выражения 1» и «выражения 2» должны быть типа, совместимого по присваиванию с типом переменной цикла. Таким образом, цикл for может быть реализован на основе цикла с предусловием:
<переменная>=<выражение 1>;
while <переменная> <= <выражение 2>
{
<оператор>;
<переменная>++;
}
Вход в цикл возможен только через его заголовок, т.е. нельзя войти во внутрь цикла, используя оператор goto. Однако с помощью этого оператора можно досрочно выйти из цикла, что делать не рекомендуется, для этого используется оператор break. При нормальном завершении цикла значение переменной цикла неопределенно, если цикл завершился преждевременно по оператору break или goto, то переменная цикла сохраняет свое последнее значение. В теле цикла for запрещается изменять переменную цикла.
Пример 19. Необходимо ввести и вывести матрицу вещественных чисел с помощью цикла с параметром, по пути подсчитав сумму всех ее элементов.
int n=10;
int a[10][10];
int sum=0;
int i,j;
for (i=0;i
{
for(j=0;j
{
printf("Введите элемент матрицы [%d %d]: ",i,j);
scanf("%d",&a[i][j]);
}
}
printf("Введенная матрица:");
for (i=0;i
{
for(j=0;j
{
printf("%d\t",a[i][j]);
sum = sum + a[i][j]; // считаем сумму
}
printf("\n");
}
printf("Сумма элементов = %d", sum);
95

Операторы прерывания циклов
В некоторых случаях необходимо прервать повторение цикла, проанализировав какие-то условия внутри тела цикла. Это может потребоваться в тех случаях, когда проверки условия для окончания цикла громоздкие, требуют многоэтапного сравнения и сопоставления каких-то данных, и все эти проверки просто невозможно разместить в выражении условия операторов while, do-while или for. Для этого можно использовать оператор break. Он прерывает выполнение тела любого цикла for, do-while или while и передает управление следующему за циклом выполняемому оператору. Еще один способ прерывания
– использование оператора goto, передающего управление какому-то оператору, расположенному вне тела цикла. Такой способ нарушает концепции структурного программирования и не рекомендуется к использованию.
Для прерывания циклов, размещенных в процедурах или функциях, можно воспользоваться стандартной процедурой exit. В отличие от оператора break, процедура exit прерывает не только выполнение цикла, но и выполнение той процедуры или функции, в которой расположен цикл.
Иногда внутри тела цикла возникает необходимость прервать только выполнение текущей итерации и перейти к следующей. В этом случае можно воспользоваться стандартной процедурой continue, которая передает управление в заголовок цикла.
Пример 20. Написать программу, которая находит в массиве целых чисел первое отрицательное число и выводит его на экран.
const int n=10;
int mas[n];
int i;
bool flag; //переменная–индикатор отсутствия отрицательных элементов в массиве
flag=false;
for(i=0;i
{
printf("Введите %d элемент массива: ",i);
scanf("%d",&mas[i]);
}
for (i=0;i
{
if (mas[i] > 0) continue; // переходим в заголовок цикла
printf ("Первое отрицательное число = %d. Номер = %d\n",mas[i],i);
96

flag = true;
break; // отрицательное число найдено – можно прервать цикл
}
if (!flag) printf("Отрицательных чисел нет");
Форматированный ввод данных
Функция scanf() (прототип содержится в файле stdio.h) обеспечивает форматированный ввод. Ее можно записать в следующем формальном виде: scanf ("<управляющая строка>", аргумент_1, аргумент_2,...);
Аргументы scanf() должны быть указателями на соответствующие значения. Для этого перед именем переменной записывается символ &.
Назначение указателей будет рассмотрено далее.
Управляющая строка содержит спецификации преобразования и используется для установления количества и типов аргументов. В нее могут включаться:

пробелы, символы табуляции и перехода на новую строку (все они игнорируются);

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

обычные символы, кроме % (считается, что они должны совпадать с очередными неизвестными символами во входном потоке).
Рассмотрим символы преобразования функции scanf() (указываются после символа %):
Таблица 9.
Символы преобразования функции scanf()
Символы
преобразования
Описание
c на входе ожидается появление одиночного символа;
d или i на входе ожидается десятичное целое число, и аргумент является указателем на переменную типа int;
D или l на входе ожидается десятичное целое число, и аргумент является указателем на переменную типа long;
е или Е
на входе ожидается вещественное число с плавающей точкой;
f на входе ожидается вещественное число с плавающей точкой;
g или G
на входе ожидается вещественное число с плавающей точкой;
о на входе ожидается восьмеричное целое число, и аргумент является указателем на переменную типа int;
97

О
на входе ожидается восьмеричное целое число и аргумент является указателем на переменную типа long;
s на входе ожидается появление строки символов;
x на входе ожидается шестнадцатеричное целое число и аргумент является указателем на переменную типа int;
Х
на входе ожидается шестнадцатеричное целое число и аргумент является указателем на переменную типа long;
p на входе ожидается появление указателя в виде шестнадцатеричного числа;
n применяется в операциях форматирования. Аргумент, соответствующий этому символу спецификации, должен быть указателем на целое. В него возвращается номер позиции (после ввода), в которой записана спецификация %n;
u на входе ожидается беззнаковое целое число, и аргумент является указателем на переменную типа unsigned int;
U
на входе ожидается беззнаковое целое число и аргумент является указателем на переменную типа unsigned long;
[ ]
сканирует входную строку для получения символов.
Перед некоторыми символами преобразования могут записываться следующие модификаторы:
Таблица 10.
Модификаторы функции scanf()
F
изменяет указатель, заданный по умолчанию, на указатель типа far;
N
изменяет указатель, заданный по умолчанию, на указатель типа near;
h преобразует аргумент к типу short int (может записываться перед символами d, i, о, u, х);
l преобразует аргумент к типу long int (может записываться перед символами d, i, o, u, x);
L
преобразует аргумент к типу long double (может записываться перед символами е, f, g).
Пример 21. Необходимо вести с клавиатуры целое число, символ и вещественное число.
int a;
char b;
float t;
scanf("%d", &a);
scanf("%c", &b);
scanf("%d%c%f",&a, &b, &t);
Пользователь осуществил ввод с клавиатуры в следующем виде:
431
W
3.14
В этом случае, переменные получили следующие значения:
a → 431
b → ‘W‘
t → 3.14
98

Форматированный вывод данных
Функция printf() (прототип содержится в файле stdio.h) обеспечивает форматированный вывод. Ее можно записать в следующем формальном виде: рrintf ("<управляющая строка>", аргумент _1, аргумент _2,...);
Управляющая строка содержит компоненты трех типов: обычные символы, которые просто копируются в стандартный выходной поток
(выводятся на экран дисплея); спецификации преобразования, каждая из которых вызывает вывод на экран очередного аргумента из последующего списка; управляющие символьные константы.
Каждая спецификация преобразования начинается со знака % и заканчивается некоторым символом, задающим преобразование. Между знаком
% и символом преобразования могут встречаться другие знаки в соответствии со следующим форматом:
% [признаки] [ширина_поля] [точность] [F|N|h|l|L] c_n
На месте параметра c_n (символ преобразования) могут быть записаны:
Таблица 11.
Символы преобразования функции printf()
Символы
преобразования
Описание
с значением аргумента является символ;
d или i значением аргумента является десятичное целое число; е значением аргумента является вещественное десятичное число в экспоненциальной форме вида 1.23e+2;
Е значением аргумента является вещественное десятичное число в экспоненциальной форме вида 1.23E+2;
f значением аргумента является вещественное десятичное число с плавающей точкой;
g (или G) используется, как е или f, и исключает вывод незначащих нулей;
о значением аргумента является восьмеричное целое число;
s значением аргумента является строка символов (символы строки выводятся до тех пор, пока не встретится символ конца строки или же не будет, выведено число символов, заданное точностью);
u значением аргумента является беззнаковое целое число;
х значением аргумента является шестнадцатеричное целое число с цифрами
0,..., 9, а, b, с, d, е, f;
X значением аргумента является шестнадцатеричное целое число с цифрами
0,..., 9, А, В, С, О, Е, F;
р значением аргумента является указатель;
n применяется в операциях форматирования. Аргумент, соответствующий этому символу спецификации, должен быть указателем на целое. В него возвращается номер позиции строки (отображаемой на экране), в которой
99
записана спецификация %n.
Необязательные параметры в спецификации преобразования:

признак минус (-) указывает, что преобразованный параметр должен быть выровнен влево в своем поле;

признак плюс (+) требует вывода результата со знаком;

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

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

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

символ F, определяющий указатель типа far;

символ N, определяющий указатель типа near;

символ h, определяющий аргумент типа short int (используется вместе с символами преобразования d, i, о, u, х, Х);

символ l, указывающий, что соответствующий аргумент имеет тип long
(в случае символов преобразования d, i, о, u, х, X) или double (в случае символов преобразования е, Е, f, g, G);

символ L, указывающий, что соответствующий аргумент имеет тип long double (используется вместе с символами преобразований е, Е, f, g, G);

символ #, который может встречаться перед символами преобразования g, f, е и перед символом х. В первом случае всегда будет выводиться десятичная точка, а во втором - префикс 0x перед соответствующим шестнадцатеричным числом.
Если после знака % записан не символ преобразования, то он выводится на экран. Таким образом, строка %% приводит к выводу на экран знака %.
100

Функция printf() использует управляющую строку, чтобы определить, сколько всего аргументов, и каковы их типы. Аргументами могут быть переменные, константы, выражения, вызовы функций; главное, чтобы их значения соответствовали заданной спецификации.
При наличии ошибок, например, в числе аргументов или типе преобразования, результаты будут неверными.
Среди управляющих символьных констант наиболее часто используются следующие:
Таблица 12.
Управляющие символьные константы функции printf()
Управляющие символьные
константы
Описание
\а для кратковременной подачи звукового сигнала;
\b для перевода курсора влево на одну позицию;
\f для подачи формата;
\n для перехода на новую строку;
\r для возврата каретки;
\t горизонтальная табуляция;
\v вертикальная табуляция;
\\
вывод символа \;
\'
вывод символа ' ;
\"
вывод символа ";
\?
вывод символа ?.
Пример 22. В результате вызова функции printf() сначала выполняется горизонтальная табуляция (\t), т.е. курсор сместится от края экрана, затем на экран будет выведено слово Computer, после этого курсор переместится в начало следующей строки (\n), затем будет выведено целое число i по формату %d
(десятичное целое), и, окончательно, курсор перейдет в начало новой строки (\n).
int i=100;
printf("\tComputer\n%d\n", i);
Результат работы программы будет иметь вид:

Computer

100


Преобразование типов
При выполнении операций происходят неявные преобразования типов в следующих случаях:
101


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

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

при передаче аргументов функции.
Кроме того, в Си есть возможность явного приведения значения одного типа к другому.
В операциях присваивания тип значения, которое присваивается, преобразуется к типу переменной, получающей это значение. Допускается преобразования целых и плавающих типов, даже если такое преобразование ведет к потере информации.
Преобразование целых типов со знаком. Целое со знаком преобразуется к более короткому целому со знаком, посредством усечения старших битов. Целая со знаком преобразуется к более длинному целому со знаком, путем размножения знака. При преобразовании целого со знаком к целому без знака, целое со знаком преобразуется к размеру целого без знака, и результат рассматривается как значение без знака.
Преобразование целого со знаком к плавающему типу происходит без потери информации, за исключением случая преобразования значения типа long int или unsigned long int к типу float, когда точность часто может быть потеряна.
Преобразование целых типов без знака. Целое без знака преобразуется к более короткому целому без знака или со знаком путем усечения старших битов.
Целое без знака преобразуется к более длинному целому без знака или со знаком путем дополнения нулей слева.
Когда целое без знака преобразуется к целому со знаком того же размера, битовое представление не изменяется. Поэтому значение, которое оно представляет, изменяется, если знаковый бит установлен (равен 1), т.е. когда исходное целое без знака больше чем максимальное положительное целое со знаком, такой же длины.
102

Целые значения без знака преобразуются к плавающему типу, путем преобразования целого без знака к значению типа signed long, а затем значение signed long преобразуется в плавающий тип. Преобразования из unsigned long к типу float, double или long double производятся с потерей информации, если преобразуемое значение больше, чем максимальное положительное значение, которое может быть представлено для типа long.
Преобразования плавающих типов. Величины типа float преобразуются к типу double без изменения значения. Величины double и long double преобразуются к float c некоторой потерей точности. Если значение слишком велико для float, то происходит потеря значимости, о чем сообщается во время выполнения.
Преобразование величины с плавающей точкой к целым типам. Сначала происходит преобразование к типу long (дробная часть плавающей величины при этом отбрасывается), а затем величина типа long преобразуется к требуемому целому типу. Если значение слишком велико для long, то результат преобразования не определен.
Преобразования из float, double или long double к типу unsigned long
производится с потерей точности, если преобразуемое значение больше, чем максимально возможное положительное значение, представленное типом long.
Преобразование типов указателя. Указатель на величину одного типа может быть преобразован к указателю на величину другого типа. Однако результат может быть не определен из-за отличий в требованиях к выравниванию и размерах для различных типов.
Указатель на тип void может быть преобразован к указателю на любой тип, и указатель на любой тип может быть преобразован к указателю на тип void без ограничений. Значение указателя может быть преобразовано к целой величине.
Метод преобразования зависит от размера указателя и размера целого типа следующим образом:

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


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

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

если размер целого типа отличен от размера указателя, то целый тип сначала преобразуется к размеру указателя (используются способы преобразования, описанные выше), а затем полученное значение трактуется как указатель.
Преобразования при вызове функции. Преобразования, выполняемые над аргументами при вызове функции, зависят от того, был ли задан прототип функции (объявление "вперед") со списком объявлений типов аргументов.
Если задан прототип функции и он включает объявление типов аргументов, то над аргументами в вызове функции выполняются только обычные арифметические преобразования.
Эти преобразования выполняются независимо для каждого аргумента.
Величины типа float преобразуются к double, величины типа char и short преобразуются к int, величины типов unsigned char и unsigned short преобразуются к unsigned int. Могут быть также выполнены неявные преобразования переменных типа указатель. Задавая прототипы функций, можно переопределить эти неявные преобразования и позволить компилятору выполнить контроль типов.
Преобразования при приведении типов. Явное преобразование типов может быть осуществлено посредством операции приведения типов, которая имеет формат:
(<имя типа>) операнд
В приведенной записи <имя типа> задает тип, к которому должен быть преобразован операнд.
104

Пример 23. Величины i,l,d будут явно преобразовываться к указанным в круглых скобках типам.
int i=2;
long l=2;
double d;
float f;
d=(double)i * (double)l;
f=(float)d;
Инициализация данных
При объявлении переменной ей можно присвоить начальное значение, присоединяя инициатор к описателю. Инициатор начинается со знака "=" и имеет следующие формы.
Формат 1 = <инициатор>;
Формат 2 = { <список инициаторов> };
Формат 1 используется при инициализации переменных основных типов и указателей, а формат 2 - при инициализации составных объектов.
Пример 24. Инициализация переменных.
char tol = 'N';
//Переменная tol инициализируется символом 'N'.
const long megabute = (1024 * 1024);
/*Немодифицируемая переменная megabute
инициализируется константным
выражением, после чего она не может
быть изменена.*/
static int b[2][2] = {1,2,3,4};
/*Инициализируется двухмерный массив b
целых
величин, элементам массива присваиваются
значения из списка.*/
static int b[2][2] = {{1,2},{3,4}};
/*Предыдущая инициализация может
быть выполнена следующим образом.*/
static int b[3[] = {{1,2},{3,4}};
/*При инициализации массива можно
опустить одну или несколько
размерностей.*/
static int b[2][2] = {{1,2},{3}};
/*Если при инициализации указано
меньше значений для строк, то
ставшиеся элементы инициализируются 0,
т.е. элементы первой строки получат
значения 1 и 2, а второй 3 и 0.*/
При инициализации составных объектов, нужно внимательно следить за использованием скобок и списков инициализаторов.
Пример 25. Инициализируется массив структур comp из двух строк и трех столбцов, где каждая структура состоит из двух элементов real и imag.
105

struct complex
{
double real;
double imag;
} comp [2][3] = {{{1,1},{2,3},{4,5}},{{6,7},{8,9},{10,11}}};
struct complex comp2 [2][3] = {{1,1},{2,3},{4,5}, {6,7},{8,9},{10,11}};
В этом примере компилятор интерпретирует рассматриваемые фигурные скобки следующим образом:

первая левая фигурная скобка

начало составного инициатора для массива comp2;

вторая левая фигурная скобка - начало инициализации первой строки массива comp2[0]. Значения 1,1 присваиваются двум элементам первой структуры;

первая правая скобка (после 1) указывает компилятору, что список инициаторов для строки массива окончен, и элементы оставшихся структур в строке comp[0] автоматически инициализируются нулем;

аналогично список {2,3} инициализирует первую структуру в строке comp[1], а оставшиеся структуры массива обращаются в нули;

на следующий список инициализаторов {4,5} компилятор будет сообщать о возможной ошибке так как строка 3 в массиве comp2 отсутствует.
При инициализации объединения задается значение первого элемента объединения в соответствии с его типом.
Пример 26. Инициализируется переменная pers.name.
union tab
{
unsigned char name[10];
int tab1;
} pers = {'A','H','T','O','H'};
Так как это массив, для его инициализации требуется список значений в фигурных скобках. Первые пять элементов массива инициализируются значениями из списка, остальные нулями.
Инициализацию массива символов можно выполнить путем использования строкового литерала.
106

char stroka[] = "привет";
Инициализируется массив символов из 7 элементов, последним элементом
(седьмым) будет символ '\0', которым завершаются все строковые литералы.
В том случае, если задается размер массива, а строковый литерал длиннее, чем размер массива, то лишние символы отбрасываются.
Следующее объявление инициализирует переменную stroka как массив, состоящий из семи элементов.
char stroka[5] = "привет";
В переменную stroka попадают первые пять элементов литерала, а символы 'Т' и '\0' отбрасываются.
Если строка короче, чем размер массива, то оставшиеся элементы массива заполняются нулями.
1   2   3   4   5   6   7   8   9   ...   15


написать администратору сайта