Справочная система C guide

Справочная система C guide


2. ОБЩИЙ СИНТАКСИС

2.1. Формат

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

2.2. Комментарии

Комментарии начинаются парой символов /*, заканчиваются парой

символов */.

Разрешены везде, где допустимы пробелы.

Примеры

/* Однострочный комментарий */

/*

* Многострочный комментарий

*/

2.3. Идентификаторы



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

Допустимые символы: цифры 0-9, латинские прописные и строчные буквы а — z, А - 2, символ подчеркивания (_). Первый символ не может быть цифрой.

Идентификатор может быть произвольной длины, но в некоторых ЭВМ не все символы учитываются компилятором и загрузчиком (см. таблицу ниже). Примеры NAME1 namel Total_5 Paper

Внешние идентификаторы: число значимых символов и вид букв (прописные/строчные) могут различаться даже на однотипных ЭВМ в зависимости от используемых компиляторов и загрузчиков. Замечание. Ожидается, что в дальнейшем в качестве стандартной будет принята длина идентификаторов больше восьми символов.

Тип ЭВМ Длина внешних идентификаторов (число символов), вид букв

3В Computer 8, прописные и строчные

DEC PDP-11 7, прописные и строчные

DEC VAX-11 -"-

HONEYWELL 6000 6. прописные

IBM 360/370 7, прописные

INTERDATA 8/32 8, прописные и строчные

MOTOROLA 68000 > 8, прописные и строчные

NSC 16000 - " -

ZILOG 8000 - " -

INTEL 80286 - " -

2.4. Зарезервированные слова Типы данных Классы памяти Операторы

char auto break

double extern case

enum register continue

float static default

int do

long else

short for

struct goto

union if

unsigned return

void switch

while sizeof typedef

Замечание, sizeof — это операция выполняющаяся во время компиляции. Описание typedef используется для определения сокращенной формы описания существующего типа данных. В некоторых реализациях, кроме того, зарезервированы слова asm и fortran.


3. ОСНОВНЫЕ ТИПЫ ДАННЫХ

К основным типам данных относятся целые числа (int, short, long, unsigned), символы (char) и числа с плавающей точкой (float, double).

На их основе строятся производные типа данных (см. с. 42). В этом разделе описаны синтаксис констант и объем памяти, занимаемой основными типами данных.

3.1. Целые константы

Десятичные: цифры 0—9;

первой цифрой не должен быть 0. Примеры

12 111 956 1007

Замечание. Если значение превышает наибольшее машинное целое со знаком, то оно представляется как длинное целое. Восьмеричные: цифры 0 - 7;

начинаются с 0. Примеры

012 =10 (десятичное);

0111 = 73 (десятичное);

076 = 62 (десятичное);

0123 = 1*64+2*8+3=83 (десятичное). Замечание. Если значение превышает наибольшее машинное целое без знака, то оно представляется как длинное целое.

Шестнадцатеричные: цифры 0-9, буквы а - Гили А - F для значений 10-15;

начинаются с Ох или ОХ. Примеры

0х12 = 18 (десятичное);

0Х12 = 18 (десятичное);

Ox2f = 47 (десятичное);

ОХАЗ = 163 (десятичное);

Ох1В9= 1*256 + 11 *1б + 9 = 441 (деся-

THTIHnftt

Замечание. Если значение превышает наибольшее машинное целое без знака, то оно представляется как длинное целое.

3.2. Длинные целые константы

Длинная целая константа явно определяется латинской буквой 1 или L, стоящей после константы.

Пр имеры

Длинная десятичная: 121 = 12 (десятичное);

956L = 956 (десятичное);

Длинная восьмеричная: 0121 = 10 (десятичное);

076L = 62 (десятичное);

Длинная шестнадиатеричная: 0х121 = 18 (десятичное);

OXA3L = 163 (десятичное).

3.3. Константы с плавающей точкой

Константа с плавающей точкой всегда представляется числом с плавающей точкой двойной точности, т. е. как имеющая тип double, и состоит из следующих частей:

целой части - последовательности цифр;

десятичной точки;

дробной части — последовательности цифр;

символа экспоненты е или Е;

экспоненты в виде целой константы (может быть со знаком).

Любая часть (но не обе сразу) из нижеследующих пар может быть опущена:

целая или дробная часть;




десятичная точка или символ е (Е) и экспонента в виде целой константы.

Примеры

345. = 345 (десятичное);

3.14159 = 3.14159 (десятичное);

2.1Е5 = 210000 (десятичное);

Л23ЕЗ = 123 (десятичное);

4037е-5 = .04037 (десятичное).

На их основе строятся производные типа данных (см. с. 42). В этом разделе описаны синтаксис констант и объем памяти, занимаемой основными типами данных.

3.1. Целые константы

Десятичные: цифры 0—9;

первой цифрой не должен быть 0. Примеры

12 111 956 1007

Замечание. Если значение превышает наибольшее машинное целое со знаком, то оно представляется как длинное целое. Восьмеричные: цифры 0 - 7;

начинаются с 0. Примеры

012 =10 (десятичное);

0111 = 73 (десятичное);

076 = 62 (десятичное);

0123 = 1*64+2*8+3=83 (десятичное). Замечание. Если значение превышает наибольшее машинное целое без знака, то оно представляется как длинное целое.

Шестнадцатеричные: цифры 0-9, буквы а - Гили А - F для значений 10-15;

начинаются с Ох или ОХ. Примеры

0х12 = 18 (десятичное);

0Х12 = 18 (десятичное);

Ox2f = 47 (десятичное);

ОХАЗ = 163 (десятичное);

Ох1В9= 1*256 + 11 *1б + 9 = 441 (деся-

THTIHnftt

Замечание. Если значение превышает наибольшее машинное целое без знака, то оно представляется как длинное целое.

3.2. Длинные целые константы

Длинная целая константа явно определяется латинской буквой 1 или L, стоящей после константы.

Пр имеры

Длинная десятичная: 121 = 12 (десятичное);

956L = 956 (десятичное);

Длинная восьмеричная: 0121 = 10 (десятичное);

076L = 62 (десятичное);

Длинная шестнадиатеричная: 0х121 = 18 (десятичное);

OXA3L = 163 (десятичное).

3.3. Константы с плавающей точкой

Константа с плавающей точкой всегда представляется числом с плавающей точкой двойной точности, т. е. как имеющая тип double, и состоит из следующих частей:

целой части - последовательности цифр;

десятичной точки;

дробной части — последовательности цифр;

символа экспоненты е или Е;

экспоненты в виде целой константы (может быть со знаком).

Любая часть (но не обе сразу) из нижеследующих пар может быть опущена:




целая или дробная часть;

десятичная точка или символ е (Е) и экспонента в виде целой константы.

Примеры

345. = 345 (десятичное);

3.14159 = 3.14159 (десятичное);

2.1Е5 = 210000 (десятичное);

Л23ЕЗ = 123 (десятичное);

4037е-5 = .04037 (десятичное).

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

Символьная константа состоит из одного символа кода ASCII', заключенного в апострофы (см. с. 89) . Примеры

'А' 'а' 'Т '$' Специальные (управляющие) символьные константы

Новая строка (перевод строки) HL (LF) '\п'

Горизонтальная табуляция НТ '\t'

Вертикальная табуляция VT '\v'

Возврат на шаг BS '\Ь'

Возврат каретки CR '\г'

Перевод формата FF '\f

Обратная косая \ '\\'

Апостроф ' '\"

Кавычки " '\" '

Нулевой символ (пусто) NUL '\0.'

Кроме этого любой символ может быть представлен последовательностью трех восьмеричных цифр: '\ddd'. Замечание. Символьные константы считаются данными типа int.

3.5. Строковые константы

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

Примеры

"This is a character string"

"Это строковая константа"

"А" "1234567890" "О" "$"

Строковая константа - это массив символов, заключенный в кавычки; она имеет тип char [ ].

В конце каждой строки компилятор помещает нулевой символ '\0', отмечающий конец данной строки.

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

3.6. Перечислимые константы

Имена, указанные в описании перечисяимых констант, трактуются как целые константы (см. с. 46) .

3.7. Размер данных

Следующая таблица дает размер в битах основных типов данных для различных ЭВМ'. Адрес слова определяется по байту с нулевым номером.




Основные типы данных 3В Computer Код ASCII DLC PDP-11 Код ASCII DEC VAX Код ASCII
char int short long float double Диапазон float Диапазон double Порядок байт в слове 8

32 16 32 32 64 ilO*38 ± Ю^"

ГоПЛ

8 16 16

32 32 64 +10*38 ± Ю*"

[ТоЩ1

8 32 16 32 32 64 ± 10*" tlO*" f32To1

' Аналогичные данные для советских ЭВМ, на которых работает компилятор языка Си, представлены в таблице на с. 17. - Прим. перев.

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

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

Символьная константа состоит из одного символа кода ASCII', заключенного в апострофы (см. с. 89) . Примеры

'А' 'а' 'Т '$' Специальные (управляющие) символьные константы

Новая строка (перевод строки) HL (LF) '\п' Горизонтальная табуляция НТ '\t' Вертикальная табуляция VT '\v' Возврат на шаг BS '\Ь' Возврат каретки CR '\г' Перевод формата FF '\f Обратная косая \ '\\' Апостроф ' '\" Кавычки " V' ' Нулевой символ (пусто) NUL '\0.'

Кроме этого любой символ может быть представлен последовательностью трех восьмеричных цифр: '\ddd'. Замечание. Символьные константы считаются данными типа int.

3.5. Строковые константы

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

Примеры

"This is a character string"

"Это строковая константа"

"А" "1234567890" "О" "$"

Строковая константа - это массив символов, заключенный в кавычки; она имеет тип char [ ].

В конце каждой строки компилятор помещает нулевой символ '\0', отмечающий конец данной строки.

' Американский стандартный код для обмена информацией. — Прим. перев.

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

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




3.6. Перечислимые константы

Имена, указанные в описании перечисяимых констант, трактуются как целые константы (см. с. 46) .

3.7. Размер данных

Следующая таблица дает размер в битах основных типов данных для различных ЭВМ'. Адрес слова определяется по байту с нулевым номером.

Основные типы данных 3В Computel Код ASCII DLC PDP-11 Код ASCII DEC VAX Код ASCII
char int short long float double Диапазон float Диапазон double Порядок байт в слове 8 32 16 32 32 64 ilO*38 ± Ю^" [ОПТ 8 16 16

32 32 64 +10*38 ± Ю*"

] [ТоЩ1

8 32 16 32 32 64 ± 10*" tlO*" f32To1

' Аналогичные данные для советских ЭВМ, на которых работает компилятор языка Си, представлены в таблице на с. 17. - Прим. перев.

Основные типы данных HONEYWELL 6000 Коя ASCII IBM 360/370 Код EBCDIC ———————————

INTER-DATA

8/32 Код ASCII

char mt short long float double Диапазон float Диапазон double

Порядок байт в слове

9 36 36 36 36 72 t 101"

iio138

[01231

8

32 16 32 32 64 ± lO*76 i Ю*76

[ОТ231

8

32 16 32 32 64 ± 10*" ± Ю*'”

Замечание. Следующая таблица представляет предварительные данные. Отдельные позиции (обозначенные * *) могут быть изменены.

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

00

^о 3

1^

?^

О

S

о — §С

s"

—1 <,

(j a

^s

§д

о u

00^

U< о а

dS

N *

00 -<

(Ч -<

о \-^

00 ОТ

щ Я

S5

char mt short long float double Диапазон float Диапазон double

Порядок байт в слове

8

32** 16 32 32 32

+Ю138 ±10138

|0123|

8 32 16 32 32 64

±10"8 +Ю*3”8

|3210|

8 16** 16

32 32 64**

llO*" +101308

|0l|23|

8

16 16

32** 32 64

iio138

^0±Э08

|32|10|

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

— 00

5 S u 0

rf” S

u

§ S

tT) 0

u и м к

о ^

§2

char mt short long float double Диапазон float Диапазон double

Порядок байт в слове

8 16 16

32 32 64 + Ю*” i Ю*38

ГЮ|32|

8 32 16 32 32 64 t Ю*76 ±101"'

fol23|

8 32 16 32 32 64 i Ю*38 tlO138

ГзТЩ

Код КОИ-8 включает в качестве подмножества код ASCII, код ДКОИ включает в качестве подмножества код EBCDIC




4. ОПЕРАЦИИ И ВЫРАЖЕНИЯ

4.1. Выражения

Выражение состоит из одного или большего числа операндов и символов операций.

Примеры

а++ b = 10 х = (y*z)/w

Замечание. Выражение, заканчивающееся точкой с запятой, является оператором (см. с. 32).

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

4.2. Метаобозначения операндов

Некоторые операции требуют операндов определенного вида. Вид операнда обозначается одной из следующих букв:

е - любое выражение;

v - любое выражение, ссылающееся на переменную, которой может быть присвоено значение Такие выражения называются адресными.

Префикс указывает тип выражения. Например, ie обозначает произвольное целое выражение. Далее описываются все возможные префиксы.

i - целое число или символ;

а - арифметическое выражение (целое число, символ или число с плавающей точкой);

р - указатель;

s - структура или объединение;

sp - указатель на структуру или объединение;

t - функция;

tp — указатель на функцию.

Обозначение япет указывает на имя элемента структуры или объединения.

Замечание. Если в выражении должно быть несколько операндов, то они отличаются номерами, например ael + ае2

4.3. Арифметические операции

+ Использование: ael + ае2 Сумма значений ael и ае2. Пример

“=J+2;

Устанавливает i равным J плюс 2. + Использование: ре + ie

Адрес переменной типа ре, больший на ie адреса, заданного указателем ре. Пример

last = arname + arsize - 1,

Присваивает переменной last адрес последнего элемента

массива arname

— Использование: ael - ае2 Разность значений ael и ае2 Пример

1=J-3,

- Использование: ре - ie

Адрес переменной типа ре, меньший на ie адреса, заданного указателем ре. Пример

first = last - Jisize + 1.

— Использование: pel - ре 2

Число переменных типа ре в диапазоне от ре2 до pel. Пример

arsize = last - first.

- Использование: -ае

Изменение знака ае. i Пример

Х= —X,

* Использование: ael * ае2

Произведение значений ael и ае2 Пример

z= 3 *х. / Использование: ael / ае2.

Частное от деления ael на ае2. Пр и м ер

l=J/5. % Использование: ael % ае2




Остаток от деления (деление по модулю) ael на ае2. Пример

minutes =s time % 60,

Замечание при выполнении операций ++ и - — появляется побочный эффект — изменяется значение переменной, используемой в качестве операнда ++ Использование: iv++

Увеличение iv на 1 Значением этого выражения является значение iv до увеличения Пример J=l++;

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

++ Использование: pv++

Увеличение указателя pv на 1, так что он будет указывать на следующий объект того же типа Значением этого выражения является значение pv до увеличения. Пример

*ptr++ =: 0;

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

++ Использование: ++iv

Увеличение iv на 1 Значением этого выражения является значение iv после увеличения Пример

1=++J.

++ Использование: ++pv

Увеличение pv на 1 Значением этого выражения является зна чение pv после увеличения. Пример

*++ptr = 0;

—- Использование: iv—

Уменьшение iv на 1 Значением этого выражения является значение iv до уменьшения. Пр имер J= 1—.

— Использование: pv—

Уменьшение указателя pv на 1 так, что он будет указывать на предыдущий объект того же типа Значением этого выражения является значение pv до уменьшения Пример

arrpos= р--.

— Использование: —iv

Уменьшение iv на 1. Значением этого выражения является значение iv после уменьшения Пример i= --J.

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

Использование: —pv

Уменьшение pv на- 1 Значением этого выражения является значение pv после уменьшения Пример

prepos= --р.

4.4. Операция присваивания

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

= Использование: v = e

Присваивание значения е переменной v. Пример х= у;

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

+= Использование: av += ae Увеличение av на ae При ме р

у+=2;

Увеличение переменной у на 2.

+= Использование: pv += ie Увеличение pv на ie. Пр имер р+= п,




-= Использование: av -= ae Уменьшение av на ae. Пример х-=3;

-= Использование: pv -= ie Уменьшение pv на ie. Пример ptr -= 2;

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

*= Использование: av *= ae Умножение av на ae. Пример timesx *= х;

/= Использование: av /= ae Деление av на ae. Пример

х/=2;

%= Использование: iv %= ie Значение iv по модулю ie. При мер

х%= 10;

”= Использование: iv ”= ie

Сдвиг двоичного представления iv вправо на ie бит. При мер х”=4;

“== Использование: iv “= ie

Сдвиг двоичного представления iv влево на ie бит. Пример х “= 1;

<Ь= Использование: iv &= ie

Побитовая операция И двоичных представлений iv и ie. При мер

remitems &= mask,

= Использование: iv "= ie

Побитовая операция исключающее ИЛИ двоичных представлений iv и je. При мер

control "= seton;

1= Использование: iv 1= ie

Побитовая операция ИЛИ двоичных представлений iv и ie. Пример

additems |= mask;

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

4.5. Операции отношения

Замечание. Логическое значение Ложь представляется целым нулевым значением, а значение Истина представляется любым ненулевым значением.

Значением выражений, содержащих операции отношения или логические операции, является 0 (Ложь) или 1 (Истина).

== Использование: iel == ie2

Истина, если iel равно ie2; иначе - Ложь. Пример if (i==0) break;

== Использование: pel === ре2

Истина, если значения указателей pel и ре2 равны. != Использование: iel != ie2

Истина, если iel не равно ie2. Пример

while (i '= 0) l = func;

!= Использование: pel != ре2

Истина, если значения указателей pel и ре2 не равны. Пример ”f(P'=q)

break, < Использование: ael < ae2

Истина, если ael меньше, чем ae2. Пример if(x<0) printf ("negative");

< Использование: pel < ре2

Истина, если значение pel (т. е. некоторый адрес) меньше, чем значение ре2. Пример

while (p < q) *р++ = 0;

24

Пока адрес, заданный р, меньше, чем адрес, заданный q, присваивать значение 0 переменной, на которую указывает р, и увеличивать значение р так, чтобы этот указатель указывал на следующую переменную.




<= Использование: ае1 <= ае2

Истина, если ае1 меньше или равно ае2.

<= Использование: pel <= ре2

Истина, если pel меньше или равно ре2.

> Использование: ае1 > ае2

Истина, если ае1 больше, чем ае2. Пример if (х > 0)

printf ("positive"),

> Использование: pel > ре2

Истина, если значение pel (т. е. некоторый адрес), больше, чем значение ре2. Пример

while (р > q) *Р—=0;

>= Использование: ае1 >= ае2

Истина, если ае1 больше или равно ае2.

>= Использование: pel >= ре2

Истина, если значение pel больше или равно значению ре2.

4.6. Логические операции

! Использование: !ае или !ре Истина, если ае или ре ложно. Пример if (!good)

prmtf ("not good"),

II Использование:el i, e2

Логическая операция ИЛИ значений е1 и e2. Вначале проверяется значение е1; значение e2 проверяется только в том случае,

25

если значение el - Ложь. Значением выражения является Истина, если истинно значение е1 или e2. Пример

if (х < А || х > В)

printf ("out of range"), && Использование: el && e2

Логическая операция И значений е1 и e2. Вначале проверяется значение е1; значение e2 проверяется только в том случае, если значение el — Истина. Значением выражения является Истина, если значения е1 и e2 - Истина. Пример

if (р! = NULL && *р > 7) п++;

Если р - не нулевой указатель и значение переменной, на которую указывает р, больше, чем 7, то увеличить п на 1. Обратите внимание, что если значение указателя р равно NULL (0), то выражение *р не имеет смысла.

4.7. Побитовые операции

~ Использование: ie

Дополнение до единицы значения ie. Значение выражения содержит 1 во всех разрядах, в которых ie содержит 0, и 0 во всех разрядах, в которых ie содержит 1. Пример

opposite = mask,

” Использование: iel ” ie2

Двоичное представление iel сдвигается вправо на ie2 разрядов. Сдвиг вправо может быть арифметическим (т. е. освобождающиеся слева разряды заполняются значением знакового разряда) или логическим в зависимости от реализации, однако гарантируется, что сдвиг вправо целых чисел без знака будет логическим и освобождающиеся слева разряды будут заполняться нулями. Пр имер х= х” 3;




26

“ Использование: iel “ ie2

Двоичное представление iel сдвигается влево на ie2 разрядов;

освобождающиеся справа разряды заполняются нулями. При мер

fourx= x“ 2;

& Использование: iel & ie2

Побитовая операция И двоичных представлений iel и ie2. Значение выражения содержит 1 во всех разрядах, в которых и iel и ie2 содержат 1, и 0 во всех остальных разрядах. П ри мер

flag = ((х & mask) != 0);

I Использование: iel | ie2

Побитовая операция ИЛИ двоичных представлений iel и ie2. Значение выражения содержит 1 во всех разрядах, в которых iel или ie2 содержит 1, и 0 во всех остальных разрядах. Пример

attrsum= attrl I attr2:

*> Использование: iel* ie2

Побитовая операция исключающее ИЛИ двоичных представлений iel и ie2. Значение выражения содержит 1 в тех разрядах, в которых iel и ie2 имеют разные двоичные значения, и 0 во всех остальных разрядах. Пример

diifbits= x'y;

4.8. Адресные операции

& Использование: &v

Значением выражения является адрес переменной v. При мер i intptr = &n;

* Использование: *ре

Значением выражения является переменная, адресуемая указателем ре. Пример *ptr = с:

27

* Использование: *fpe

Значением выражения является функция, адресуемая указателем fpe. Пример

fpe = funcname. (*fpe) (argl,arg2);

i 4.9. Операции над массивами

[ ] Использование: ре [ie]

Значением выражения является переменная, отстоящая на ie переменных от адреса, заданного ре. Это значение эквивалентно значению выражения *(ре + ie). Пример

arname [i]= 3;

Присвоить значение 3 i-му элементу массива arname. Обратите внимание, что первый элемент массива описывается выражением шпате [о].

4.10. Операции над структурами или объединениями

Использование: sv^mem

Значением выражения является элемент smem структуры или

объединения sv.

Пример

product.p_revenue = 50;

Присвоить значение 50 элементу p_revenue структурной

переменной product.

-> Использование: spe —> smem

Значением выражения является элемент smem структуры (или объединения), на которую (ое) указывает spe. Это значение эквивалентно значению выражения (*spe)^mem




Пример

prodptr -> р ^revenue = 2;

Присвоить значение 2 элементу p_revenue структурной переменной, на которую указывает prodptr.

28

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

4.11. Другие операции

?: Использование: ае ? е1 : е2 или ре ? е1 : е2

Если истинно ае или ре, то выполняется е1; иначе выполняется е2. Значением этого выражения является значение выражения е1 или е2. Пример

abs = (i <= 0) ' -i. i,

Использование: el, е2

Сначала выполняется выражение е1, потом выражение е2. Значением всего выражения является значение выражения е2. Пример

for (i=A, J=B, i<i, i++, j—)

p[i] =pdl

sizeof Использование: sizeof(e)

Число байт, требуемых для размещения данных типа е. ' 'Если е описывает массив, то в этом случае е обозначает весь массив, а не только адрес первого элемента, как во всех остальных операциях. "^ sizeof Использование: sizeof(-nin)

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

п = sizeof(arnaine) / sizeof(mt),

Число элементов в массиве целых чисел, определяемое как число байт в массиве, поделенное на число байт, занимаемых одним элементом массива,

(тип) Использование: (тип)е

Значение е, преобразованное в тип данных тип. Пример

х= (float)n/3,

Целое значение переменной п преобразуется в число с

плавающей точкой перед делением на 3.

( ) Использование: fe(el, е2,..., eN)

Вызов функции fe с аргументами е1, е2,..., eN.

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

29

Значением выражения является значение, возвращаемое функцией. Обратите внимание, что порядок выполнения выражений е1,..., eN не гарантируется (см. с 39) При мер х = sqrt(y),

4.12. Приоритеты и порядок выполнения операций

Для каждой группы операций в нижеследующей таблице приоритеты одинаковы. Чем выше приоритет группы операций, тем выше она расположена в таблице. Порядок выполнения определяет группировку операций и операндов (слева направо или справа налево), если отсутствуют скобки и операции относятся к одной группе.

Пр им ёры

Выражение а * b / с эквивалентно выражению (а * b) / с, так как операции выполняются слева направо. Выражение а = b = с эквивалентно выражению а = (b = с), так как операция выполняется справа налево. '




( ) Вызов функции (с 28) Слева направо

[ ] Выделение элемента массива (с. 27)

Выделение элемента структуры или объеди нения (с.27)

—> Выделение элемента структуры (объедине ния) , адресуемой (го) указателем (с. 27)

! Логическое отрицание (с. 24) Справа налево

~ Побитовое отрицание (с. 25) - ,,+Изменение знака (с. 19) i

++ Увеличение на единицу (с. 19)

-- Уменьшение на единицу (с. 20) & Определение адреса (с. 26)

* Обращение по адресу (с. 26) (тип) Преобразование типа (с. 28) sizeof Определение размера в байтах (с. 28)

/ Деление (с. 19) % Деление по модулю (с. 19)
 

- Вычитание (с. 19)

< < Сдвиг влево (с. 26) Слева направо > > Сдвиг вправо (с. 25)
< Меньше, чем (с. 23) Слева направо <= Меньше или равно (с. 24) > Больше, чем (с. 24) >= Больше или равно (с. 24)
== Равно (с. 23) Слева направо != Неравно (с. 23)
& Побитовая операция И (с. 26) Слева направо
Побитовая операция исключающее ИЛИ (с. 26) Слева направо
 

&& Логическая операция Ц (с. 25) Слева направо
11 Логическая операция ИЛИ (с. 24) Слева направо
= Присваивание (с. 21 -22) Справа налево “= ”= &= *= 1=
, Операция запятая (с. 28) Слева направо

4.13. Порядок обработки операндов

Для четырех операций (&& II ?: ,) гарантируется, что левый операнд будет обрабатываться первым. Для остальных операций поря-) док обработки может быть разным на разных компиляторах. 'Это означает, что если программа, отлаженная на некоторой ЭВМ, зависит от негарантированного порядка обработки операндов, то на другой ЭВМ с другим компилятором она может выполняться неправильно. Пример

v = (х = 5) + (++х);

Если порядок обработки операндов в операции + слева направо, то переменная v получит значение 11 (5 + 6) и значение х будет равно 6.

Если порядок справа налево, значение v зависит от значения х, которое эта переменная имела до выполнения выражения; например, если значение х было равно 0, то значение v станет равным б (5 + 1) и значение х станет равным 5.




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

х=5;

v = х + (х + 1);

++х;

4.14. Арифметические преобразования в выражениях

Прежде всего каждый операнд типа char или short преобразуется в значение типа int и операнды типа unsigned char или unsigned short преобразуются в значение типа unsigned int1. Затем если один из операндов имеет тип double, то другой преобразуется в значение типа double и результат будет иметь тип double.

' Кроме того, операнды типа float до начала операции преобразуются в значение типа double [Дб]. - Прим. перев.

Иначе если один из операндов имеет тип unsigned long, то другой преобразуется в значение типа unsigned long и таким же будет тип результата.

Иначе если один из операндов имеет тип long, то другой преобразуется в значение типа long и таким же будет тип результата. Иначе если один из операндов имеет тип long, а другой - тип unsigned int, то оба операнда преобразуются в значение типа unsigned long и результат будет иметь тип unsigned long. Иначе если один из операндов имеет тип unsigned, то другой преобразуется в значение типа unsigned и результат будет иметь тип unsigned.

Иначе оба операнда должны быть типа int и таким же будет тип результата.

5. ОПЕРАТОРЫ

' 5.1. Формат и вложенность

Формат. Один оператор может занимать одну или более строк. Два или большее количество операторов могут быть расположены на одной строке.

Вложенность. Операторы, управляющие порядком выполнения (if, if-else, switch, while, do-while и for), могут быть вложены друг в друга.

5.2. Метка оператора

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

АВС2: х=3;

5.3. Составной оператор




Составной оператор (блок) состоит из одного или большего числа операторов любого типа, заключенных в фигурные скобки ( { } ) •

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

{х=1; у=2; z=3;}

5.4. Оператор-выражение

Любое выражение, заканчивающееся точкой с запятой (;), является оператором. Далее следуют примеры операторов-выражений.

Оператор присваивания

Идентификатор = выражение;

Пр имер х=3;

Оператор вызова функции

Имя_функции (аргумент!,..., аргумент^);

Пример

fclose (file);

Пустой оператор

Состоит только из точки с запятой (;). Используется для обозначения пустого тела управляющего оператора.

5.5. Оператор завершения break

break;

Прекращает выполнение ближайшего вложенного внешнего оператора switch, while, do или for. Управление передается оператору, следующему за заканчиваемым. Одно из назначений этого оператора -закончить выполнение цикла при присваивании некоторой переменной определенного значения.

Пример

for (i = 0, i < n; i++) if((a[i] =b[i])==0) break,

5.6. Оператор продолжения continue

continue,

Передает управление в начало ближайшего внешнего оператора цикла while, do или for, вызывая начало следующей итерации.

Этот оператор по действию противоположен оператору break. Пр им е р

for (i=0, К n, i++){ if(a[i] '=0)

continue, a[i]=b[i]. k++;

} "/ 5.7. Оператор возврата return

return,

Прекращает выполнение текущей функции и возвращает управление вызвавшей программе. return выражение;

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

return x+y;

5.8. Оператор перехода goto

goto метка;

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

goto ABC,

5.9. Условный оператор if-else

if (выражение) оператор

Если выражение истинно, то выполняется оператор. Если выражение ложно, то ничего не делается.




При м.е р

if (а == х) temp = 3;

temp = 5,

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

оператор} else

оператор2

Если выражение истинно, то выполняется оператор! и управление передается на оператор, следующий за оператором^ (т. е. оператор2 не выполняется). Если выражение ложно, то выполняется оператор2.

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

Примеры

Часть else относится ко второму оператору if:

if (х > 1) if (у==2)

z= 5, else

z=6,

Часть else относится к первому оператору if if(x>l){ if (У==2)

z= 5, \ else

z= б,

Вложенные операторы if if (х == 'а') У=1,

else if (x=='b'){

У=2,

z=3, у else if (x == 'c')

У =4, else

prmtf("ERROR"),

5.10. Оператор-переключатель switch

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

case константа: операторы case константа: операторы

default: операторы

}

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

Каждый вариант case может быть помечен целой или символьной константой, или константным выражением. Константное выражение не может включать переменные или вызовы функций'. Примеры

Правильно: case 3+4:

Неправильно: case X+Y:

Операторы, связанные с меткой default, выполняются, если ни одна из констант в операторах case не равна значению выражения. Вариант default не обязательно должен быть последним. Если ни одна константа не соответствует значению выражения и вариант default отсутствует, то не выполняется никаких действий. Ключевое слово case вместе с константой служат просто метками, и если будут выполняться операторы для некоторого варианта case, то далее будут выполняться операторы всех последующих вариан

' Константное выражение вычисляется в период компиляции. — Прим. перев.

тов до тех пор, пока не встретится оператор break/'Sio позволяет связывать одну последовательность операторов с несколькими вариантами,




Никакие две константы в одном операторе-переключателе не могут иметь одинаковые значения.

Пример

switch (x) { case 'A':

printf("CASE A\n");

break, case 'В':

case 'С':

printf("CASE В or C\n"), break;

default:

printf("NOT А, В or C\n");

break;

} Наиболее общая синтаксическая форма оператора switch:

switch (выражение) оператор Пр имер

switch (x) case 2:

case 4:

у=3;

5.11. Оператор цикла while

while (выражение) оператор

Если выражение истинно, то оператор выполняется до тех пор, пока выражение не станет ложным.

Если выражение ложно, то управление передается следующему оператору.

3 а м е ч а н-”Р'е. Значение выражения определяется до выполнения оператора. Следовательно, если выражение ложно с самого начала, то оператор вообще не выполняется.

Пр и м е р

while (k < n) { У=У*х, k++:

{

5.12. Оператор цикла do-while

do оператор while (выражение);

Оператор выполняется. Если выражение истинно, то оператор выполняется и вычисляется значение выражения; это повторяется до тех пор, пока выражение не станет ложным. Если выражение ложно, то управление передается следующему оператору.

Замечание. Значение выражения определяется после выполнения оператора. Поэтому оператор выполняется хотя бы один раз. Оператор do-while проверяет условие в конце цикла. Оператор while проверяет условие в начале цикла.

Прим ер х= 1:

do

printf("%d\n", power(x, 2));

whue (-н-х <= 7),

5.13. Оператор цикла for

for (выражение!;

выражение!;

выражениеЗ) оператор Выражение! описывает инициализацию цикла.

ВыражениеЗ — проверка условия завершения цикла. Если оно истинно, то выполняется оператор тела цикла for, выполняется выражениеЗ,

все повторяется, пока выражение2 не станет ложным. Если оно ложно, цикл заканчивается и управление передается следующему оператору.

ВыражениеЗ вычисляется после каждой итерации. Оператор for эквивалентен следующей последовательности операторов:

выражение!;

while (выражение2) { оператор • выражениеЗ;

Прим ер




for (х= 1, х<= 7, х++)

printf("%d\n", power(x, 2));

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

Если опущено выражение2, то считается, что оно постоянно истинно. Оператор for (;;) представляет собой бесконечный цикл, эквивалентный оператору while(l).

Каждое из выражений 1-3 может состоять из нескольких выражений, объединенных оператором запятая (,). Пр и м е р

for (i=0, j=n-l; i< n, i++, J--) a[i]=a[J];

6.ФУНКЦИИ

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

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

Пример

double тип результата linfunc (х, а, Ь) имя функции список параметров double x; описание параметров double a;

double b;

{ составной оператор return (a*x + b); возвращаемое значение

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

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

void

ernnesg(s)

char * s;

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

}

6.2. Вызов функции

Существуют два способа вызова функции:

имя_функцш (е1, е2,. .., eN)

(^указатель _ш_функцию) (е1, е2,..., eN) Указатель _на_функцию - это переменная, содержащая адрес функции. Адрес функции может быть присвоен указателю оператором

указатель_на_функцию = имя _функции;

Аргументы (фактические параметры) передаются по значению, т. е. каждое выражение el, . . . , eN вычисляется и значение передается функции, например, загрузкой в стек. Порядок вычисления выражений и порядок загрузки значений в стек не гарантируются.

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




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

extern double ' linfunc();

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

Пр им ёры

Правильно: extern double linfuncO;

float у;

у = linfunc (3.05, 4.0, le-3), Значение функции перед присваиванием переменной у преобразуется из типа double в тип float.

Неправильно: float х;

float у;

х=3.05;

у = linfunc (х, 4, le-3);

Тип аргументов не соответствует типу параметров, описанных в определении функции, а именно: константа 4 имеет тип int, а не double. В результате аргументы, загруженные в стек, имеют неправильные тип и формат, поэтому значения, выбираемые из стека, бессмысленны и значение, возвращаемое функцией, не определено. Кроме того, если тип функции не описан, то считается, что возвращаемое значение имеет тип int. Поэтому, даже если функция linfunc возвращает правильное значение типа double, выражение, представляющее вызов функции, получит бессмысленное значение типа int (например, старшая половина значения double).

6.3. Функция main

Каждая программа начинает работу с функции main(). Во время выполнения программы можно передавать аргументы через формальные параметры argc и argv функции main. Переменные среды языка оболочки shell передаются программе через параметр env. Пример

/*

* программа печатает значения фактических параметров,

* а затем переменных среды

*/

main (argc, aigv, envp)

int argc; /* число параметров*/

char **argv, /* вектор параметров-строк */

char "envp; /* вектор переменных среды */

{ register int i;

register char **p;

/* печать значений параметров */ for (i = 0, i < argc, i++)

printf("arg %i:%s\n", i, argv [i]);

/* печать значений переменных среды */ for (p = envp; *p != (char*)0; p++) printf ("%s\n", *p);




Замечание. Параметры argv и envp могут быть описаны также следующим образом:

char *argv[];

char *envp[];

7. ОПИСАНИЯ

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

ных на основе существующих типов. Описание не является оператором.

7.1. Основные типы

Примеры

char с,

int х;

Основными типами являются:

char - символ (один байт) ;

int - целое (обычно одно слово);

unsigned - неотрицательное целое (такого же размера, как целое);

short - короткое целое (слово или полуслово);

long — длинное целое (слово или двойное слово);

float - число с плавающей точкой (ординарной точности);

double - число с плавающей точкой (двойной точности);

void — отсутствие значения (используется для нейтрализации

значения, возвращаемого функцией). Символы (char) в зависимости от компилятора могут быть со знаком или без знака. Рассматриваемые как целые, символы со знаком имеют значения от -127 до 128, а символы без знака - от 0 до 256.

Некоторые реализации допускают явный тип unsigned char. Данные целого типа int могут иметь такой же диапазон, как данные типа long иди short.

Описание типа unsigned эквивалентно описанию типа unsigned int. Описание unsigned может сочетаться с описанием типа char, short или long, формируя описания типов unsigned char, unsigned short, unsigned long.

Описания типов short и long эквивалентны описаниям типов short int и long mt.

Диапазон данных типа long обычно в два раза больше диапазона данных типа short

6.3. Функция main

Каждая программа начинает работу с функции main(). Во время выполнения программы можно передавать аргументы через формальные параметры argc и argv функции main. Переменные среды языка оболочки shell передаются программе через параметр env. Пример

/*

* программа печатает значения фактических параметров,

* а затем переменных среды

*/

main (argc, aigv, envp)

int argc; /* число параметров*/

char **argv, /* вектор параметров-строк */




char "envp; /* вектор переменных среды */

{ register int i;

register char **p;

/* печать значений параметров */ for (i = 0, i < argc, i++)

printf("arg %i:%s\n", i, argv [i]);

/* печать значений переменных среды */ for (p = envp; *p != (char*)0; p++) printf ("%s\n", *p);

Замечание. Параметры argv и envp могут быть описаны также следующим образом:

char *argv[];

char *envp[];

7. ОПИСАНИЯ

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

ных на основе существующих типов. Описание не является оператором.

7.1. Основные типы

Примеры

char с,

int х;

Основными типами являются:

char - символ (один байт) ;

int - целое (обычно одно слово);

unsigned - неотрицательное целое (такого же размера, как целое);

short - короткое целое (слово или полуслово);

long — длинное целое (слово или двойное слово);

float - число с плавающей точкой (ординарной точности);

double - число с плавающей точкой (двойной точности);

void — отсутствие значения (используется для нейтрализации

значения, возвращаемого функцией). Символы (char) в зависимости от компилятора могут быть со знаком или без знака. Рассматриваемые как целые, символы со знаком имеют значения от -127 до 128, а символы без знака - от 0 до 256.

Некоторые реализации допускают явный тип unsigned char. Данные целого типа int могут иметь такой же диапазон, как данные типа long иди short.

Описание типа unsigned эквивалентно описанию типа unsigned int. Описание unsigned может сочетаться с описанием типа char, short или long, формируя описания типов unsigned char, unsigned short, unsigned long.

Описания типов short и long эквивалентны описаниям типов short int и long mt.

Диапазон данных типа long обычно в два раза больше диапазона данных типа short

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

Замечание. Допустимо бесконечно большое число различных типов указателей и массивов. Далее следуют типовые примеры.




Указатель на основной тип Пример

char *p,

Переменная р является указателем на символ, т. е. этой переменной должен присваиваться адрес символа. Указатель на указатель Пр им е р

char **t,

Переменная t - указатель на указатель символа. Одномерный массив Пример

int a [50];

Переменная а - массив из 50 целых чисел. Двухмерный массив Пр име р

char m[7][50];

Переменная m - массив из семи массивов, каждый из которых

состоит из 50 символов. Массив из семи указателей Пр им ер

char *г[7];

Массив г состоит из указателей на символы. Указатель на функцию Прим ер

int (*f)();

f - указатель на функцию, возвращающую целое значение.

7.3. Структуры

Структура' объединяет логически связанные данные разных типов. Структурный тип данных определяется следующим описанием:

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

описания_элементов

1 Иногда называют записью. - Прим. перев.

Пр и м е р

struct dinner {

char * place,

float cost,

struct dinner *next, };

Структурная переменная описывается с помощью структурного типа.

Примеры

struct dinner week_days [7], /* массив структур */ struct dinner best_one, /* одна структурная переменная */ struct dinner *p; /* указатель на структурную переменную */

7.4. Поля бит в структурах

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

struct bfeg {

unsigned int of_fldl 10;

unsigned int bf_fld2 : 6

^

Данная структура описывает 10-битовое поле, которое для вычислений преобразуется в значение типа unsigned int, и 6-битовое поле, которое обрабатывается как значение типа unsigned int.

7.5. Объединения

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

' Иногда называют смесью. — Прим. перев.

46

Определение объединенного типа данных аналогично определению структурного типа данных

union имя объединения {

описания_элемен тов }, Пр и ме р

union bigword {




long bg_long, char *bg_char[4],

}•

Данные типа union bigword занимают память, необходимую для размещения наибольшего из своих элементов, и выравниваются в памяти к границе, удовлетворяющей ограничениям по адресации как для типа long, так и для типа char * [4]

Описание переменной объединенного типа Пример

union bigword x,

union bigword *p,

union bigword a[l00],

7.6. Перечисления

Данные перечислимого типа относятся к некоторому ограниченно

му множеству данных

Определение перечислимого типа данных

enum имя _перечислимого типа { список значении

}>

Каждое значение данного перечислимого типа задается идентификатором Пример

enum color {

red green yellow

}•

Описания 47

Описание переменной перечислимого типа Пр и м е р

enum color chair,

enum color suite [40];

Использование переменной перечислимого типа в выражении Пример

char = red,

suite [5] '= yellow

7.7. Переименование типов Формат

typedef старый _тип новый_тип Примеры

typedef long large

/* определяется тип large эквивалентный типу long */

typedef char *stnng

/* определяется тип string эквивалентный типу char * */ Переименование типов используется для введения осмысленных или сокращенных имен типов, что повышает понятность программ, и для улучшения переносимости программ (имена одного типа данных могут различаться на разных ЭВМ)

7.8. Определение локальных переменных

Замечание 1 Постоянные переменные, сохраняемые в некоторой области памяти, инициализируются нулем, если явно не заданы начальные значения Временные переменные, значения которых сохраняются в стеке или регистре, не получают начального значения, если оно не описано явно

Замечание 2 Все описания в блоке должны предшествовать первому оператору Автоматические переменные Пример

mt x, /* х - это автоматическая переменная */ }

48 Описания

Автоматическая переменная является временной, так как ее значение теряется при выходе из блока. Областью определения является блок, в котором эта переменная определена. Переменные, определенные в блоке, имеют приоритет перед переменными, определенными в охватывающих блоках.




Регистровые переменные

Пр и м е р

{

register mt у;

}"

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

Формальные параметры Пр им ёры

mt func(x), int func(x) int x; register int x;

^ i

} }

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

Описания 49

Статические переменные Пример

{ static int flag,

}"

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

7.9. Определение глобальных переменных

Глобальные переменные При мер

int Global_flag;

Определяются на том же уровне, что и функции, т. е. не локальны ни в каком блоке. Постоянные. Инициализируются нулем, если явно не задано другое начальное значение. Областью действия является вся программа. Должны быть описаны во всех файлах программы, в которых к ним есть обращения. 3 амечание. Некоторые компиляторы требуют, чтобы глобальные переменные UHggonpenei^ лены только в одном файле и описаны как внешние вдругахфай-лах, где они используются (см. с. 51). Должны быть описаны в файле до первого использования. Статические переменные Пр и м е р

static mt File _ flag;

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




7.10. Инициализация переменных

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

50 Описания

Любая постоянная переменная инициализируется нулем (0) ', если явно не задано другое начальное значение. В качестве начального значения может использоваться любое константное выражение. Основные типы Пр имеры

mt 1 = 1,

float x= 3.145e - 2, Массивы Примеры

mta[] = {1,4,9,16,25,36},

chars [20] = {'a'.'b'.'c' },

Список значений элементов массива должен быть заключен в фигурные скобки.

Если задан размер массива, то значения, не заданные явно, равны 0.

Если размер массива опущен, то он определяется по числу начальных значений. Строки Пр имер

char s[] = "hello". Это описание эквивалентно описанию

chars[] = {'h'.'e','l',T,'o'.'\0'}, Структуры Пример

struct person {

int height, char gender,

};

struct person x = {70, Y \ ,

struct person family [ ] = { {73.'X'}. {68.'Y'}, {50.'X'}

1 Это значит, что если переменная целая, то ее начальное значение равно 0, если символьная, то '\0', если это число с плавающей точкой, то 0.0. - Прим. перев

Описания 51

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

Пр имер

struct person people [l0] = { {68}, {71},

{74},

Элементам height первых трех структурных переменных массива присваиваются явные значения; остальные переменные получают значение 0.

7.11. Описание внешних объектов

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




Примеры

extern int Global_var;

extern char *Name;

extern int func();

Можно опускать длину внешнего одномерного массива. Пример

extern float Num_array [ ];

Поскольку все функции определены на внешнем уровне, то для описания функции внутри блока прилагательное extern избыточно и часто опускается.

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

Пример

L int func();

}"

Функция, не возвращающая значения, должна описываться как имеющая тип void. Если тип функции явно не задан, считается, что она имеет тип int. Областью действия описания на внешнем уровне является остаток файла; внутри блока областью действия является данный блок. Обычно внешние описания располагаются в начале файла.

Некоторые компиляторы допускают описание переменных на внешнем уровне без прилагательного extern. Многократные описания внешних переменных компоновщик сводит к одному определению.

8. ПРЕПРОЦЕССОР

Если в качестве первого символа в строке программы используется символ #, то эта строка является командной строкой препроцессора (макропроцессора). Командная строка препроцессора заканчивается символом перевода на новую строку. Если непосредственно перед концом строки поставить символ обратной косой (\), то командная строка будет продолжена на следующую строку программы.

8.1. Замена идентификаторов

#define идентификатор строка Пример

#efine ABC 100

Заменяет каждое вхождение идентификатора ABC а тексте программы на 100.

#undef идентификатор Пример

#undef ABC Отменяет предыдущее определение для идентификатора ABC.

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

8.2. Макросы

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

#define идентификатор! (идентификатор'1,. .. ) строка

Пример

#define abs(A) (((A) > 0) ? (А) . -(А)) Каждое вхождение выражения abs(arg) в тексте программы заменяется на ((arg) > 0) ? (arg) : -(arg), причем параметр макроопределения А заменяется на arg.

Пример

#define nmem (P, N)\

(Р) -> p_mem [N] .u_long Символ \ продолжает макроопределение на вторую строку. Это макроопределение уменьшает сложность выражения, описывающего массив объединений внутри структуры.




8.3. Включение файлов

Замечание. Командная строка #include может встречаться в любом месте программы, но обычно все включения размешаются в начале файла исходного текста.

#include (имя _файла'1

Пример

#mclude<r.iath.h>

Препроцессор заменяет эту строку содержимым файла math.h. Угловые скобки обозначают, что файл math.h будет взят из некоторого стандартного каталога (обычно это /usr/mclude). Текущий каталог не просматривается. #include "имя_файла"

Пример

#mclude "ABC"

Препроцессор заменяет эту строку содержимым файла ABC. Так как имя файла заключено в кавычки, то поиск производится в текущем каталоге (в котором содержится основной файл исходного текста). Если в текущем каталоге данного

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

файла нет, то поиск производится в каталогах, определенных именем пути в опции -1 препроцессора Если и там файла нет, то просматривается стандартный каталог

8.4. Условная компиляция

Командные строки препроцессора используются для условной ком пиляции различных частей исходного текста в зависимости от внешних условий # if константное _аыражение Пример

“if АВС + 3

Истина, если конвтантное выражение АВС + 3 не равно нулю #fdef идентификатор Пример

“ifdef АВС

Истина, если идентификатор АВС определен ранее командой “define “ifndef идентификатор Пример

“ifndef АВС

Истина, если идентификатор АВС не определен в настоящий момент • “else

“endif

Если предшествующие проверки “if, #ifdef или #ifndef дают значение Истина, то строки от “else до “endif игнорируются при компиляции

Если эти проверки дают значение Ложь, то строки oi проверки до “else (а при отсутствии “else — до “endif) игнорируются Команда “endif обозначает конец условной компиляции

Пример

“ifdef DEBUG

iprmtf (stderr, "location x = %d\ n", x), “endif

Структура программы 55

8.5. Номер строки и имя файла

“line целая _константа "имя __файла" Пример

*hne 20 "АВС"

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




9 СТРУКТУРА ПРОГРАММЫ

Программа, описанная в следующем примере, вводит до MAXLINES строк со стандартного входа, сортирует строки в лексикографическом порядке, возрастающем или уменьшающемся в зависимости от признака, передаваемого функции mainQ через аргумент argv Затем программа записывает отсортированные строки на стандартный выход Стандартными входом и выходом (stdin и stdout) могут быть терминал, канал или некоторый файл

bblsort.h

А#define MAXLINES 100

“define LINLSIZE (132+1)

main.c

ю “include (stdio h> “include "bblsort h"

char Lme[MAXLINES]ELINESIZE], /* буфер строк */ С int Revfig , /* признак направления * сортировки */

/*

* сортировка строк текста в лексикографическом порядке

*/

56 Структура программы

niain(argc, argv)

D char **argv, /* аргументы вызова программы */

int argc, /* число аргументов */

{.

д- mt rdlinesO;

void bblsortO, wrlines();

F int numlines;

G Revflg = (argc > 1 && |argv[l][0] [ == '-');

numlines = rdlinesO;

bblsort(numlmes);

wrlines(numlines),

/*

* запись строк со стандартного выхода

*/

Н static int rdlinesO

chai^ *fgets(), / register int l;

/ foi(i=0,i< MAXLINES; i++)

if (fgets(Lme[i], LINESIZE, stdin)

/ == ] (char *)NULL ) break;

return (i);

} /*

* запись строк на стандартный выход

*/

Структура программы 57

К static void wrhnes(n) L register mt n; /* число строк */

{

register int l,

for (i = 0; i < n; i++) fputs(Lme [i], stdout);

bblsort.c

M # include "bblsort.h"

N extern char Line [] [LINESIZE]; [

/* ,* bubble sort

*/ 0 void

bblsort(n) register int n; /* число строк */

int lexcmp();

void swap();

regicterint i,j;

for (i = 1; i <= n - 1; i++) for(J=n- 1;j>= i;J—) if (lexcmp(j - 1, J)) swapO - l.j);

58 Структура программы

/*

* лексикографическое сравнение двух строк

*/

static int lexcmp(i,J)

Р register int i, j; /* элементы массива строк */ {

q mt strcmpO;

extern int Revflg;

register int Ic;

Ic = strcmp(Line [i], Line [j]);

d return ((Ic < 0 && Revflg)




11 (Ic > 0 && '.Revflg));

1

/*

* обмен строк

*/

static void swap(iJ) register int i, j; /* элементы массива строк */

char *strcpy();

char temp [LINESIZE];

strcpy(temp. Line [i]);

strcpy(Line [i], Lme [j]);

strepy(Line [j], temp);

Структура программы 59

Пояснения к программе

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

В Включаемые файлы обычно помещаются в начало некоторого файла программы. Файл stdio h содержит описания файлов stdm. stdout и константы NULL, необходимых для использования функций fgets() и fputsQ

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

D Если функция main() использует формальные параметры, то они должны быть описаны. Функция main() выполняется первой.

Е Типы функций, вызываемых в теле функции, обычно описываются в начале тела функции.

F Переменная numlmes описана как локальная в блоке автоматическая целая переменная

G По соглашению первый аргумент argv, передаваемый функции main(), является именем программы; argv [l] — это второй аргумент и argv[l] [О] - это первый символ второго аргумента. Обратите внимание, что перед обращением к argv [l ] [0] проверяется число аргументов argc, так как при отсутствии второго аргумента выражение argv [l] [О] не имеет смысла.

И Функция rdlmesQ возвращает целое число прочитанных строк, так как она определена как имеющая тип mt. Прилагательное static указывает, что функция используется только в данном файле.

/ Переменная i определена как локальная в блоке. Описание register — это попытка ускорить выполнение цикла for

J Поскольку функция fgetsO возвращает значение типа char *,то это значение должно сравниваться с указателем на символ. Поэтому нулевой указатель NULL преобразуется к типу (char *).




60 Библиотека ввода-вывода

К Функция wrhnesO не возвращает значения, поэтому она опреде лена как имеющая тип void.

L Формальный параметр п определен как register для ускорения цикла.

М Включение файла bblsort.h определяет поименованную константу LINESIZE.

N Это описание массива Line относится ко всему последующему файлу исходного кода. Здесь описывается тип Line, но сам массив определен в предыдущем файле.

О Функция bblsortO не описывается как static, потому что она вызывается функцией main(), которая определена в другом файле.

Р Формальные параметры i и j объявлены с помощью одного описания. Порядок параметров в таком описании несуществен.

Q Глобальный признак Revfig должен быть описан как extern, чтобы показать, что эта переменная определена в другом файле. Это описание может располагаться в начале текущего файла вместе с описанием массива Line

R Значением этого выражения является или Истина (1), или Ложь (0), кодируемые целыми значениями. Поэтому функция 1ехстр() определена как имеющая тип int.

10. БИБЛИОТЕКА ВВОДА-ВЫВОДА

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

#include (stdio h>

файр stdio .h содержит:

1. Определение типа данных FILE

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

stdm - стандартный файл ввода;

stdout - стандартный файл вывода;

Библиотека ввода-вывода 61

stderr - файл вывода сообщений об ошибках;

NULL -нулевой (0) указатель;

EOF - конец файла.

Замечание. По умолчанию файлы stdm, stdout и stderr связываются с терминалом.

3. Макроопределения. pute() ferrorO getcQ с1еагегг() putchar() feof() getcharO fileno()

Замечание. Поток ввода-вывода идентифицируется указателем на переменную типа FILE. Средства буферизации включаются в поток как часть стандартного пакета ввода-вывода.

10.1. Доступ к файлам

fopen — открыть поток ввода-вывода.

Определение. FILE *fopen (filename, type)

char *filename, *type, freopen - закрыть поток stream и открыть файл newfile, используя




описание этого потока.

Определение: FILE *freopen (newfile, type, stream) char * newfile, *type, FILE * stream,

fdopen — связать поток с дескриптором файла, открытым функцией open.

Определение: FILE * fdopen (tildes, type) int fildes, char *type,

fclose - закрыть открытый поток ввода-вывода stream Определение: int fclose (stream)

FILE *stream,

fflush — записать символы из буфера в выходной поток stream Определение: int fflush (stream) FILE * stream,

62 Библиотека ввода-вывода

fseek - изменить текущую позицию offset в файле stream. Определение: int fseek (stream, offset, ptrname) FILE * stream;

long offset;

int ptrname;

rewind - переставить указатель текущего байта в потоке на начало

файла.

Определение: void rewind (stream) FILE * stream,

setbuf - модифицировать буфер потока. Определение: void setbuf (stream, buf) FILE * stream;

char *buf;

setvbuf - модифицировать буфер потока.

Определение: int setvbuf (stream, buf, type, size) FILE *stream;

char *buf;

int type, size;

10.2. Доступ к каналам

pclose - закрыть поток, открытый функцией рореп. Определение: int pclose (stream) FILE * stream;

рореп - создать поток как канал обмена между процессами. Определение: FILE *popen (command, type) char "command, *type,

10.3. Состояние файла

clearerr - обнулить признаки ошибки потока. Определение: void clearerr (stream) FILE *stream;

feof - проверить состояние конца файла в потоке. Определение: int feof (stream) FILE *stream,

Библиотека ввода-вывода 63

ferror — проверить состояние ошибки в потоке. Определение: int terror (stream) FILE *stream;

fileno - связать дескриптор файла, открытого функцией open с

существующим потоком. Определение: int fileno (stream) FILE * stream;

10.4. Форматированный ввод-вывод

Функции prmtf, fprmtf и spnntf описаны в разд. 12. Функции scanf, fscanfn sscan'f описаны в разд. 13.

10.5. Ввод-вывод строк

fgets — прочитать строку из входного потока, включая символ новой строки.

Определение: char *fgets (s, n, stream) char *<;




int n, FILE *stream;

gets — прочитать строку из стандартного файла ввода stdm. Определение: char *gets(s) char *s;

fputs - записать строку в поток stream. Определение: int fputs (s, stream) char *s;

FILE *stream, puts — записать строку в стандартный файл вывода stdout. В конце

строки записывается символ новой строки. Определение: int puts (s) char *s;

10.6. Ввод символа

fgetc — прочитать следующий символ из входного потока stream Определение: int fgetc (stream) FILE •stream;

64 Библиотека ввода-вывода

Замечание. Функции getc(), getchar(), ungetc() являются макроопределениями.

getc - прочитать следующий символ из входного потока.

Определение: int getc (stream)

FILE *stream, getchar - прочитай, следующий символ из стандартного файла ввода.

Определение: int getchar ()

ungetc - вернуть символ во входной поток. Определение: int ungetc (с, stream) int с, FILE * stream;

10.7. Вывод символа

fputc — записать символ в поток. Определение: int fputc (с, stream) int с;

FILE * stream;

Замечание. Функции putc() и putchar() являются макроопределениями.

putc - записать символ в поток. Определение: int putc (с, stream) int с, FILE *stream,

putchar - записать символ в стандартный файл вьшода. Определение: int putchar (с) int с;

10.8. Блочный ввод-вывод

fread - прочитать из входного потока определенное число байт

(символов).

Определение: int fread (ptr, size, nitems, stream) char *ptr, int size, nitems, FILE *stream,

Другие библиотеки 65

fwrite - записать определенное число байт в выходной поток. Определение: int fwrite (ptr, size, nitems, stream) char *ptr;

int size, nitems;

FILE *stream,

11. ДРУГИЕ БИБЛИОТЕКИ

Ниже перечислены некоторые (не все) широко используемые библиотечные функции.

11.1. Выполнение команд языка shell

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

^include <stdio.h>

system - выполнить команду языка shell, описанную как строка string.

Определение: int system (string) char * string,




11.2. Временные файлы

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

tmpnam — создать временное имя файла. Определение: char *tmpnam (s) char *s;

tempnam - создать временное имя файла, используя каталог dir и

префикс файла pfx. Определение: char *tempnam (dir, pfx) char *dir, *pfx,

66 Другие библиотеки

mktemp - создать уникальное имя файла по шаблону, записанному

в строке template.

Определение: char *mktemp (template) char "template;

tmpfile - создать временный файл. Определение: FILE *tmpfile ()

11.3. Обработка строк

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

#mclude (string.h)

strcat - сцепить две строки. Определение: char *strcat (si, s2) char *sl, *s2;

strncat - сцепить две строки, причем из второй строки копировать

не более п символов. Определение: char *stmcat (si, s2, n) char *sl, *s2;

mt n;

strcmp - сравнить две строки в лексикографическом порядке. Определение: int strcmp (si, s2) char *sl, *s2;

stmcmp — сравнить первые n символов двух строк. Определение: mt strncmp (si, s2, n) char *sl, *s2;

mt n;

strcpy - копировать строку s2 в строку si. Определение: char * strcpy (si, s2) char *sl, s2;

stmcpy - копировать не более n символов строки s2. Определение: char *strncpy (si, s2, n) char *sl, *s2;

int n;

Другие библиотеки 67

strlen - определить длину строки (число символов без завершающего нулевого символа):

Определение: int strlen (s) char *s;

strchr - найти в строке первое вхождение символа с. Определение: char *strchr (s„c) char *s;

int c;

strrchr — найти в строке последнее вхождение символа с. Определение: char *strrchr (s, с) char *s;

int с;

strpbrk — найти в строке si любой из множества символов, входящих в строку s2. Определение: char *strpbrk (si, s2) char *sl, *s2;

strspn — определить длину отрезка строки si, содержащего символы

из множества символов, входящих в строку s2. Определение: int strspn (si, s2) char *sl, *s2;




strcspn - определить длину отрезка строки si, содержащего символы, не входящие в множество символов строки s2. Определение: int strcspn (si, s2)

char *sl, *s2, strtok — выделить из строки si лексемы, разделенные любым из

множества символов, входящих в строку s2. Определение: char *strtok (si, s2) char *sl, *s2;

11.4. Проверка символов

Замечание. Макроопределения, описанные в этом подразделе, определены в файле ctype.h, который должен быть включен в программы командой

#include <ctype.h>

Определение: int имя_макроса (с)

mt с,

68 Другие библиотеки

isalpha Символ с — буква

isupper Символ с - прописная буква (в верхнем регистре)

islower Символ с - строчная буква (в нижнем регистре)

isdigit Символ с - цифра (0 - 9).

isxdigit Символ с - шестнадцатеричная цифра (0 - 9), прописная (А - F) иди строчная (а - f) буква

isalnum Символ с — буква или цифра

isspace Символ с - символ пробела, табуляции, перевода строки, новой строки, вертикальной табуляции или перевода фор мата

ispunct Символ с - символ пунктуации, т е не управляющий сим вол, не буква, не цифра и не пробел

isprint Символ с - печатный, т е имеющий значение (в коде ASCII) от 040 (пробел) до 0176 (тильда)

isgraph Символ с - графический, т е печатный, за исключением пробела

iscntrl Символ с - управляющий (0 - 037) или символ удаления (0177)

isascii Символ с — символ кода ASCII, т е его значение лежит в диапазоне от 0 до 0200

11.5. Преобразование символов

Замечание. Макроопределения, описанные в этом разделе, определены в файле ctype h, который должен быть включен в программу, использующую эти макросы, командой #mclude <ctype h>

Определение int имя_макроса (с)

int с,

toascu - преобразование целого в символ кода ASCII tolower - преобразование буквы в нижний регистр toupper - преобразование буквы в верхний регистр _tolower - такая же, как tolower, но более быстрая и ограниченная

функция. _toupper - такая же, как toupper, но более быстрая и ограниченная

функция

Другие библиотеки 69

11.6. Преобразование строки в число




strtol — преобразование строки в длинное целое число Определение long strtol (sir, ptr, base) char *str, **ptr, int base,

atol — преобразование строки в длинное целое число (специальный

вариант функции strtol()) Определение long atol (str) char *str,

atoi — преобразование строки в целое Преобразование в тип int результата, возвращаемого функцией atol() Определение int atoi (str)

char *str,

atof — преобразование строки в число двойной точности с плавающей точкой Определение double atof (str) char *str,

strtod — преобразование строки в число двойной точности с плавающей точкой

Определение, double strtod (str, ptr) char *str, *ptr,

11.7. Доступ к аргументам

getenv - ввести строку, связанную с переменной оболочки Определение char *getenv (name) char *name,

getopt — ввести следующий символ опций из списка аргументов Определение int getopt (argc, argv, optstrmg) int argc, char **argv, *optstring,

Для записи текущих значений индекса и указателя аргументов используются следующие внешние переменные

extern char *optarg, extern int opind, opterr,

70 Форматированный вывод

11.8. Распределение памяти

malloc - выделение памяти размером size байт. Определение: char *malloc (size) unsigned size;

calloc - выделение памяти и обнуление ее. Определение: char *calloc (nelem, elsize) unsigned nelem, elsize;

realloc — изменение размера ранее выделенной памяти. Определение: char *realloc (ptr, size) char *ptr;

unsigned size;

free - освобождение ранее выделенной памяти. Определение: void free (ptr) char *ptr;

12. ФОРМАТИРОВАННЫЙ ВЫВОД

Для описания функций форматированного вывода printf, fprintf,

sprintf используются следующие метаобозначения:

^ Пробел (символ ^на самом деле не печатается!). \ f Используется только один из перечисленных элементов. [] Используется только один или не используется ни одного из перечисленных элементов.

Замечание. Для использования функций printf, fprintf, sprintf

в программу необходимо вставить команду препроцессора

^include <stdio.h>




Функции printf, fprintf и sprintf имеют переменное число аргументов. Число и типы аргументов должны соответствовать спецификациям преобразования в форматной (управляющей) строке. printf — записать аргументы в стандартный файл вывода stdout в соответствии с форматной строкой format. Определение: int printf (^rmat [,arg]...) char •format;

Форматированный вывод 71

fprintf - записать аргументы в поток stream в соответствии с форматной строкой format.

Определение: int fprintf (stream, format [,arg]...) FILE * stream;

char *format;

sprintf — записать аргументы в массив символов s в соответствии с

форматной строкой.

Определение: int sprintf (s, format [,arg]...) char *s, *format;

Пр им ep

printf ("error no. %d: %s", err, mesg);

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

error no. 13: cannot access file

12.1. Спецификация преобразования

%Гвыравнивание] ГширинЗ Гдополнительныв символ I I / ^ 1 1 признаки f преобразования

Выравнивание вправо: по умолчанию. Выравнивание влево: символ —.

Ширина определяет минимальное число выводимых символов. Она может задаваться целым числом; если значение соответствующей переменной превышает явно заданную ширину, то выводится столько символов, сколько необходимо. Символ * обозначает, что число выводимых символов будет определяться текущим значением переменной.

Пример

printf ("%*d", width, number);

72 Форматированный вывод

12.2. Спецификация вывода символа

% /—7 [ширина] с Примеры %с А

%3с WA %-3с AW

12.3. Спецификация вывода строки

%[-] [ширина] [.точность] s

Точность определяет число печатаемых символов. Если строка длиннее, чем заданная точность, то остаток строки отбрасывается. Пр и м е р

%10s abcdefghijklrnn

%-10.5s abcde^^

%10.5s )W^abcde

12.4. Спецификация вывода целого числа со знаком

% Hf^f [ширина] [l]d

Для отрицательных чисел автоматически выводится знак - (минус). Для положительных чисел знак + (плюс) выводится только в том случае, если задан признак +; если в спецификации задан пробел ^>, то в позиции знака выводится пробел. Символы преобразования




1 — необходим для данных типа long';

d - определяет вывод данных типа int в десятичном формате

со знаком. Пр им е р ы

%d 43

%+d +43

%d Ю

Символ 1 не является самостоятельным символом преобразования, он только модифицирует преобразование, выполняемое по символу d. - Прим. пере”.

Форматирован” ый в ывод 7 3

12.5. Спецификация вывода целого числа без знака

("I % [-][#] [ширина] [1]^0 \

Iх J

Символ # определяет вывод начального нуля в восьмеричном формате или вывод начальных Ох или ОХ в шестнадцатеричном формате. Символ 1 необходим для данных типа long. Символы преобразования

u — десятичное без знака;

о - восьмеричное без знака;

х — шестнадцатеричное без знака;

Х - шестнадцатеричное без знака с прописными буквами А - F. Примеры (для 32-разрядных чисел)

%и 777626577

%о 5626321721

%#о 05626321721

%х 2e59a3dl

%#Х OX2E59A3D1

12.6. Спецификация вывода числа с плавающей точкой

m

%[-] /+ [#] [ширина] [.точность] 1 Е

/ Р/ L- I GJ

Для отрицательных чисел автоматически выводится знак - (минус). Для положительных чисел выводится знак + (плюс), если задан признак +; если в спецификации задан пробел ^, то в позиции знака выводится пробел.

Завершающие нули не выводятся, если в спецификацию не включен признак #. Этот признак также обусловливает вывод десятичной точки даже при нулевой точности.

Точность определяет число цифр после десятичной точки для форматов f, е и Е или число значащих цифр для форматов g и G. Округление "делается отбрасыванием. По умолчанию принимается точность в шесть десятичных цифр.

74 Форматированный ввод

Символы преобразования и формат вывода по умолчанию

f /—/ddd.ddd (число с фиксированной точкой) .

е / —7 d ddddde {±} dd (число в экспоненциальном формате).

Е /-7ddddddE{i}dd

g Наиболее короткий формат из f или е.

G Наиболее короткий формат из f или Е. Типы аргументов float и double не различаются. Числа с плавающей точкой печатаются в десятичном формате. Пр имеры

%f 1234.567890

%.lf 1234.6

%Е 1.234568Е+03

%.3е 1.235е+03




%g 1234.57

Замечание. Чтобы вывести символ %, необходимо в форматной строке задать два символа %%. Пример

printf("%5.2f%%", 99.44);

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

99.44%

13. ФОРМАТИРОВАННЫЙ ВВОД

Для описания функций форматированного ввода scanf, fscanf, sscanf используются следующие метаобоэначения:

1t> Пробел (символ ^ на самом деле не печатается!) { \ Используется только один из перечисленных элементов. [] Используется только один или не используется ни одного

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

#mclude <stdio.h>

Функции scanf, fscanf и sscanf могут иметь переменное число аргументов. Число и типы аргументов должны соответствовать спецификациям преобразования в форматной строке.

Форматированный ввод 75

scanf - ввести данные из стандартного файла ввода stdin в соответствии с форматной строкой format, присваивая значения переменным, заданным указателями pointer. Определение: mt scanf (format [.pointer]...) char * format;

fscanf - ввести данные из потока stream в соответствии с форматной строкой format

Определение: mt fscanf (stream, format [.pointer]...) FILE •stream, char *format;

sscanf - читать данные из строки s в соответствии с форматной строкой format. Определение: int sscanf (s, format [.pointer]...)

char *s, *format, Пр имеры

Входной поток содержит символы:

12.45 1048.73 АЕ405271 438 Вызов функции.

float x; char id [8+Г]; int n;

scanf("%f%*f%8[A-ZO-9]%d", &x,id, &n);

Переменной х присваивается значение 12.45, символы 1048.73 пропускаются, переменной id присваивается строка символов "АЕ405271", переменной n - целое значение 438.

Входной поток содержит символы:

25 54.32Е-01- monday Вызов функции:

int 1; float x, char name [50];

scanf ("%d%f%s", &i, &x, name);

Переменной i присваивается значение 25, переменной х - значение 5.432, переменной name - строка "monday"




Входной поток содержит:

56 789 0123 56АВС

Вызов функции:

int i, float x; char name [50];

scanf ("%2d%f%*d,% [0-9]", &i, &x, name);

76 Форматированный ввод

Переменной i присваивается значение 56, переменной х — значение 789.0, символы 0123 пропускаются, строка "56" присваивается переменной name. Последующий ввод символа из этого потока функцией getchar дает значение 'А'.

13.1. Спецификация преобразования

% [*] [ширина] /дополнительные j символ

L признаки J преобразования

Символ * обозначает пропуск при вводе поля, определенного данной спецификацией; вводимое значение не присваивается никакой переменной.

Ширина определяет максимальное число символов, вводимых по данной спецификации.

13.2. Пустые символы

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

13.3. Литеральные символы

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

13.4. Спецификация ввода символа

% [*] [ширина] с

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

Форматированный ввод 77

13.5. Спецификация ввода строки

% [*] [ширина] s

Ширина описывает максимальную длину вводимой строки. Строки во входном потоке должны разделяться пустыми символами;

ведущие пустые символы игнорируются.

13.6. Спецификация ввода целого числа

н

%[*] [ширина] ^] ^

Буква 1 определяет тип вводимых данных как long, буква h - как short. По умолчанию принимается тип int. Символы преобразования

d - десятичное целое со знаком;

u — десятичное целое без знака;

о — восьмеричное целое без знака;

х - шестнадцатеричное целое без знака.




13.7. Спецификация ввода числа с плавающей точкой

N

%[*] [ширина][I] ^ е ^

Iе)

Буква 1 определяет тип вводимых данных как double, по умолчанию принимается тип float. Символы преобразования f, e, g являются синонимами.

13.8. Спецификация ввода по образцу

% [*] [ширина] образец

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

78 Мобильность программ на языке Си

Пр и м ёры

[abed]

[А321]

Непрерывный (в коде ASCII) диапазон символов образца описывается первым и последним символами диапазона. Пр и м ёры

Га-z]

[А - FO - 9]

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

ГО-9]

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

14. МОБИЛЬНОСТЬ ПРОГРАММ НА ЯЗЫКЕ СИ

Мобильность программ — это свойство, позволяющее выполнять программы на разных ЭВМ, работающих под управлением разных версий ОС UNIX, с минимальными изменениями.

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

14.1. Верификатор lint

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

Мобильность программ на языке Си 79

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




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

14.2. Зависимость от компилятора

Некоторые детали в языке Си не стандартизированы. Это может проявиться в небольших различиях при обработке программы разными компиляторами. Какими бы "малыми" эти различия не были, они могут породить серьезные проблемы при переносе программ с одной вычислительной системы на другую. Например, в описании языка Си не определен порядок вычисления операндов большинства бинарных операций, таких, как сложение и умножение (см. с 31). Следовательно, не определен порядок появления возможных побочных эффектов. Поэтому не делайте никаких предположений о реализации свойств языка, которые строго не определены.

14.3. Зависимость от ЭВМ

Не полагайтесь на определенный размер машинного слова.

Размер данных типа mt зависит от размера машинного слова, различающегося у разных ЭВМ. Если вы не уверены в результате операций над целыми числами, используйте тип long, чтобы избежать проблемы переполнения.

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

80 Мобильность программ на языке Си

int. который, в свою очередь, меньше или равен размеру данных типа long. Размер слова может сказаться на обработке двоичных масок. Пример

#defme MASK 0177770 /* неправильно*/

mt х,

х &= MASK;

В этом примере три правых бита целого х будут обнуляться только в том случае, если данные типа mt занимают 16 бит. Но если размер данных типа int больше 16 бит, то кроме этого будут обнуляться левые биты данного х. Чтобы избежать таких проблем, используйте следующее макроопределение:




#define MASK (~07) /* правильно */ int х;

х &=- MASK,

Этот пример корректен для всех ЭВМ независимо от размера

данных типа int

Тщательно проверяйте операции сдвига.

Максимальное число бит, которые могут быть сдвинуты вправо или влево, различно на разных ЭВМ. Если заданный в операции сдвиг превысит допустимый максимум, то результаты операции будут непредсказуемы.

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

Используйте поименованные константы.

Использование в программе числовых констант, особенно когда смысл их неочевиден, является плохим стилем программирования. Числовые константы лучше определять в программе символическими именами, связанными с числовыми константами командой препроцессора #defme. Такие определения легко находить и модифицировать, если они размещены в некотором стандартном месте. Обычно это начало программы или файл заголовка.

Мобильность программ на языке Си 81

Пр имер

#define SCREENWIDTH 80

Такое определение позволяет использовать поименованную

константу SCREENWIDTH вместо числа 80.

Определяйте размер объекта операцией sizeof

Для определения размера некоторого объекта часто используют константы, что снижает мобильность программ. Использование операции sizeof позволяет решить эту проблему. Пример

#defme NUMELEM(ARRAY)\

(sizeof(ARRAY) / sizeof(*(ARRAY))) Такое макроопределение обеспечивает мобильный способ определения числа элементов в массиве ARRAY.

Не используйте несколько символов в одной символьной константе.

Поскольку символьные константы представляются значениями типа int, определение языка Си позволяет в принципе задать символьную константу, состоящую из нескольких символов. Однако порядок размещения символов в машинном слове различен на разных ЭВМ.




Не полагайтесь на внутреннюю кодировку целых чисел.

Большинство ЭВМ представляет целые числа в дополнительном коде, но некоторые - в обратном коде. Поэтому не используйте возможности, которые предоставляет дополнительный код. Например, сдвиг на 1 бит влево отрицательного числа (чтобы уменьшить его значение в два раза) не приведет к желаемому результату на ЭВМ с обратным двоичным кодом. ' Формат чисел с плавающей точкой различен на разных ЭВМ.

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

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

Число байт и порядок их размещения а машинном слове различны у разных ЭВМ.

82 Мобильность программ на языке Си

Пр и м е р

Следующая функция определена неправильно - на выход буд-дет записан нулевой символ, если какой-то байт в слове имеет меньший адрес, чем младший байт;

#define STDOUT 1

putchar(c), /* неправильно */

mt с;

write(STDOUT, (char *) &c, 1);

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

Не полагайтесь на определенное число бит в байте.

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

/usr/include/values.h

Замечание. Все системные файлы-заголовки размещаются в каталоге /usr/include.

Будьте осторожны с символами, имеющими знак.

На некоторых ЭВМ символы представляются как целые значения со знаком, и при вычислении выражений данные типа char обрабатываются с учетом знака. Для повышения мобильности можно использовать явное описание типа unsigned char или преобразовывать символы перед обработкой к типу unsigned char. В других случаях надо использовать данные целого типа. Пр им е р




Если на данной ЭВМ допустимы символы со знаком, то существует опасность, что индексация символом некоторого массива приведет к выходу за его границы:

#define TABSIZE 256 char с;

extern char table [TABSIZE];

с = table [c]; /* неправильно */

Мобильность программ на языке Си 83

Чтобы избежать этого, опишите переменную с как имеющую тип unsigned char или индексируйте таблицу значением, преобразованным к типу unsigned char. Пр имер

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

#include <stdio.h> char с; /* неправильно */ if ((с = getcharQ) != EOF)

Если значение символа не может быть отрицательным, то переменная с никогда не станет равной поименованной константе EOF, которая равна -1. Библиотечная функция getchar (см. с. 64) возвращает значение типа int, поэтому с надо описать как переменную типа int. Не комбинируйте разные поля бит.

Не используйте поля бит для представления данных на внешних носителях.

Поля бит можно сделать мобильными, если не объединять разные поля. Максимальный размер поля бит зависит от размера машинного слова, и поля бит не могут пересекать границу слова. Кроме того, порядок размещения полей в слове (слева направо или справа налево) зависит от типа ЭВМ.

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

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

Учитывайте выравнивание при изменении значения указателя.

Если вы преобразуете указатели из одного типа в другой, то




84 Мобильность программ на языке Си

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

Следите за сравнением указателей, имеющих знак.

Некоторые ЭВМ выполняют сравнение указателей с учетом знака, другие делают беззнаковое сравнение Это различие несущественно, если сравнивать указатели, содержащие правильные адреса Если указателю будет присвоено значение -1, то в зависимости от ЭВМ оно бупет рассматриваться или как наибольшее допустимое значение, или как недопустимое значение (меньше минимально до пустимого)

Единственная константа, которую можно "безопасно" присваивать указателю, - это нуль, преобразованный к типу соответствующего указателя

Следите за переполнением значения указателей.

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

Пример

Этот фрагмент программы показывает возможность появления потери значимости

struct large x [SIZE] *p, /* неправильно */ for (p=x [SIZE l],p>=x,p -)

Если массив х расположен в начале памяти, то возможна ситуация, при которой х - 1 будет не меньше, а больше х вслед ствие перехода через нижнюю границу диапазона значений ука зателей (потери значимости)

Не полагайтесь на конкретную кодировку символов.

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

Мобильность программ на языке Си 85

Пример char с,

if (с >= а && с <= 'z ) /* неправильно */ Такая проверка символа с на принадлежность к строчным буквам не является мобильной Чтобы такая проверка правильно выполнялась на других ЭВМ, сделайте так char с,




if (islower(c)) /* правильно */

Библиотечная функция islower (см с 67) определена в библи отеке стандартных функций ввода-вывода и является машин но-зависимой Поскольку ее спецификация мобильна, то данная функция обеспечивает мобильную проверку символов Учтите, что разные символьные коды могут отличаться по числу входящих в них символов Не используйте разность значений двух букв для вычисления лексикографического расстояния между ними

Не используйте программные трюки, зависящие от аппаратуры.

Любое повышение эффективности выполнения программы, достигаемое за счет знания особенностей конкретной ЭВМ, обычно не оправдывает связанную с этим потерю мобильности

14.4. Хорошо организованные программы

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

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

Важнейшим средством разработки мобильных программ являются команды препроцессора #mclude и #define (см с 52) Помещайте все определения типов данных, поименованных констант, макроопределения, используемые более чем одной программой, в единый файл заголовка так, чтобы все возможные изменения были локализованы Пр им ер

#mclude <values h>

86 Мобильность программ на языке Си

С помощью этой команды в программу включается стандартный файл заголовка /usr/include/values.h, который содержит аппаратные константы.

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

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

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




Для локализации программных фрагментов, зависящих от ЭВМ, используйте функции, условную компиляцию и команду #define.

Функции, зависящие от конкретной ЭВМ, объедините в отдельный исходный файл. Если таких файлов несколько, то соберите их в отдельном каталоге.

Фрагменты исходного кода, зависящие от аппаратуры, заключайте в команды условной компиляции (см. с. 54).

Пр и м е р

Следующий фрагмент программы описывает стек, который может наращиваться в разных направлениях в зависимости от аппаратных особенностей ЭВМ. int *stackptr,

#ifdef MACHINEl

*--stackptr = datum; /* растет вниз */

#else

*++stackptr = datum; /* растет вверх */

#endif

Для локализации характеристик конкретной ЭВМ можно использовать макроопределения (см. с. 53).

Мобильность программ на языке Си 87

Пример

#defme BITSPERBYTE 8

#define BITS(TYPE)\

(sizeof(TYPE) * BITSPERBYTE)

Спецификация поименованной константы BITSPERBYTE мобильна, реализация — не мобильна. В макроопределении BITS мобильна как спецификация, так и реализация'.

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

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

В файле /usr/mclude/varargs.h описаны средства для мобильного определения функций с переменным числом аргументов. Например, библиотечная функция pnntf (см. с. 70) реализована с использованием этих средств.

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

Стандартные библиотечные функции ОС UNIX обеспечивают большой выбор универсальных процедур. Библиотеки ОС UNIX содержат стандартные функции, обеспечивающие доступ к таким средствам операционной системы, как ввод и вывод. Библиотечные функции обеспечивают повышение мобильности, изолируют вашу программу от возможных изменений в операционной системе (см. также с. 60 - 77) . Тщательно определяйте внешние имена.




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

' Спецификацией здесь называется определяемая лексема, а реализацией - определяющее константное выражение. - Прим. перев.

88 Мобильность программ на языке Си

ний extern (см с 49 и 51) При этом можно использовать разные методы Можно поместить эти внешние описания в начало исходных файлов программы (с помощью включения файлов) или описать эти внешние переменные в функциях, которые их используют

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

Используйте описание typedef для локализации определения типов данных, зависящих от ЭВМ.

Описание typedef (см с 47) обеспечивает локальные определения типов тех данных, которые зависят от конкретной ЭВМ Если вы измените определение типа, заданное описанием typedef, то соответственно изменятся все переменные, описанные с помощью этого производного типа Система обеспечивает набор стандартных определений в файле

/usr/mclude/sys/types h Пример

typedef unsigned short ino_t, /* индекс файла */

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

типа в файле /usr/mclude/sys/types h

14.5. Мобильность файлов данных

Для переноса файлов, содержащих двоичные данные, используйте символьный ввод-вывод.

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

Один из способов решения этой проблемы заключается в раз работке специальных программ преобразования для конкретных форматов данных Другой подход заключается в записи байт, составляющих объект данных, в некотором машинно-независимом порядке Для передачи символьных данных используйте библио течные функции prmtf и scanf (см. с 70 и 74), хотя это и непрактично



Содержание раздела