Содержание

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

Далее обсуждаются:

Декларации объектов

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

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

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

Обобщённая форма определения (создания) объекта:

спецификатор-класса-памяти спецификатор-типа декларатор;

или

спецификатор-класса-памяти спецификатор-типа декларатор = инициализатор;

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

Декларатор — имя определяемого объекта вместе с информацией о его типе. Возможны несколько деклараторов разделённых запятыми.

Инициализатор обсуждается в разделе Инициализация объектов.

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

int x;

Внутри функции
Объект x декларирован как целое, с автоматической продолжительностью хранения. Его значение доступно только внутри функции (или составной команды), в которой он определён. Это также определение.

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

extern int x;

Одновременно это — определение.

register void * memptr;

Внутри функции
Объект memptr декларируется как указатель на void (неконкретный тип объекта), часто используемый внутри функции. Одновременно это — определение.

Вне функции
Незаконно в силу класса памяти register.

auto long int x, y;

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

Вне функции
Незаконно в силу класса памяти auto.

static int nums[10];

Внутри функции
Объект nums декларирован как массив из 10 целых со статической длительностью хранения. Его значение доступно только внутри функции и будет неизменным от вызова до вызова функции. Одновременно это — определение.

Вне функции
Объект nums декларирован как массив из 10 целых со статической длительностью хранения. Его значение доступно только внутри модуля. (Разница заключается в области видимости объекта nums.) Одновременно это — определение.

extern int x;

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

Вне функции
Объект x декларирован как целое со статической длительностью хранения. Его значение доступно всем функциям внутри модуля. Объект определён в другом модуле или в другом месте данного модуля.

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

Область видимости имени

На идентификатор можно ссылаться только внутри его области видимости.

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

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

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

Метка (она может быть только внутри функции) имеет область видимости функции.

Спецификаторы типа

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

Есть несколько различных типов, определённых в языке C. Они предоставляют широкую свободу в выборе методов хранения и обработки данных, а также помогают улучшить читабельность программы.

Есть несколько «основных типов», которые теоретически могут встретиться в любой программе. Более продвинутые типы предоставляют методы для описания структур данных и обсуждаются в разделе Выводимые типы.

Спецификатор типа — один или более из:

И может также включать следующие квалификаторы типа:

Компиляторы Watcom C/16 и C/32 дополнительно предоставляют следующие квалификаторы типа:

__based    __fortran   __near     __segment
__cdecl    __huge      _Packed    __segname
__export   __interrupt            __pascal
__self
__far      __loadds    __saveregs __syscall

Компилятор Watcom C/32 дополнительно предоставляет следующие квалификаторы типа:

__far16    _Seg16      __stdcall

Ключевые слова __based, __segment, __segname, и __self описаны в разделе Относительные указатели Watcom C/16 и C/32.

ключевые слова __far, __huge и __near описаны в разделах Специальные типы указателей Watcom C/16 и Специальные типы указателей Watcom C/32.

Ключевые слова __far16 и __Seg16 описаны в разделе Специальные типы указателей Watcom C/32.

Ключевое слово _Packed описано в разделе Структуры.

Остальные ключевые слова см. Ключевые слова компилятора.

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

Основные типы: char, int, float и double. Ключевые слова short, long, signed, unsigned, const и volatile модифицируют эти типы.

Целочисленные типы

Наиболее часто используется целый тип. Целые используются для хранения чисел, которые не требуют десятичной точки — счётчики, размеры и индексы массивов. Диапазон целых ограничен машинной архитектурой и обычно определяется диапазоном значений, которые наиболее удобно хранить и обрабатывать. Большинство 16-битных машин могут обрабатывать целые в диапазоне от -32768 до 32767. Более мощные - от -2147483648 до 2147483647.

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

Указание типа int говорит о том, что количество памяти должно соответствовать наиболее просто обрабатываемому на данном оборудовании типу. Значение будет интерпретироваться как знаковое. В соответствии со стандартом языка C минимальный диапазон для int — от -32768 до 32767, однако компилятор может обеспечивать больший диапазон.

В Watcom C/16 int имеет диапазон от -32768 до 32767.

В Watcom C/32 int имеет диапазон от -2147483648 до 2147483647.

Указание типа char говорит о том, что количество памяти достаточно для того, чтобы хранить любой член символьного набора времени исполнения. Если член исходного набора символов (см. Наборы символов) записан в объекте типа char, то гарантируется, что его значение будет положительным. Будут ли другие символы положительными зависит от реализации. (Другими словами, знаковый ли char, зависит от реализации. Если необходимо, чтобы объект типа char был знаковым или беззнаковым, то объект нужно декларировать явно, как описано ниже.)

Компиляторы Watcom C/16 и C/32 определяют char как unsigned (беззнаковый), позволяя объектам этого типа хранить значения в диапазоне от 0 до 255. Может быть указан параметр командной строки, чтобы трактовать char как signed (знаковый). Этот параметр нужно указывать только при переносе программы на C с системы, где charзнаковый.

Ключевое слово int может быть указано вместе со словами short (короткое) или long (длинное). Эти ключевые слова дают дополнительную информацию о диапазоне значений, которые может хранить объект этого типа. В соответствии со стандартом языка C signed short целое имеет минимальный диапазон от -32767 до 32767. А signed long — от -2147483647 до 2147483647.

В Watcom C/16 и C/32, short int имеет диапазон от -32768 до 32767, а long int - от -2147483647 до 2147483647 .

Типы char и int могут быть указаны с ключевыми словами signed (знаковый) или unsigned (беззнаковый). Эти ключевые слова явно указывают знаковые или беззнаковые (неотрицательные) значения представляет этот тип.

Ключевое слово int может быть опущено в декларации, если указано одно из (или несколько) ключевых слов signed, unsigned, short или long. Другими словами, просто short эквивалентно signed short int, а unsigned longunsigned long int.

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

В следующей таблице приведены все возможные целые типы и их диапазоны, как они реализованы в компиляторах Watcom C/16 и C/32. Таблица упорядочена по возрастанию размера, занимаемого переменной в памяти.

Тип Минимальное
значение
Максимальное
значение
signed char -128 127
unsigned char 0 255
char 0 255
short int -32768 32767
unsigned short int 0 65535
int (C/16) -32768 32767
int (C/32) -2147483648 2147483647
unsigned int (C/16) 0 65535
unsigned int (C/32) 0 4294967295
long int -2147483648 2147483647
unsigned long int 0 4294967295

В Watcom C/16 объект типа int имеет тот же диапазон, что и объект типа short int.

В Watcom C/32 объект типа int имеет тот же диапазон, что и объект типа long int.

Ниже приведены несколько примеров деклараций объектов целого типа:

short              a;
unsigned short int b;
int                c, d;
signed             e;
unsigned int       f;
long               g;
signed long        h;
unsigned long int  i;

Типы с плавающей точкой

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

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

Есть три типа чисел с плавающей точкой: float, double и long double.

В приложении Макро для численных пределов обсуждается набор макроопределений, описывающих диапазон и другие характеристики различных численных типов. Там же обсуждается файл <float.h>, в котором описаны типы с плавающей точкой.

В следующей таблице приводятся диапазоны доступные на компьютерах на базе процессора/сопроцессора 80x86/80x87 с использованием компиляторов Watcom C/16 и C/32. Формат плавающей точки - IEEE Standard for Binary Floating-Point Arithmetic (Стандарт для двоичной арифметики с плавающей точкой) (ANSI/IEEE Std 754-1985).

Тип Наименьшее
абсолютное
значение
Наибольшее
абсолютное
значение
Значащие
цифры
Имя типа
для 80x87
float 1.1E-38 3.4E+38 6 short real
double 2.2E-308 1.7E+308 15 long real
long double 2.2E-308 1.7E+308 15 long real

По умолчанию компиляторы Watcom C/16 и C/32 эмулируют арифметику с плавающей точкой. Если во время исполнения будет присутствовать сопроцессор 8087 или 80x87, то можно застваить компилятор генерировать инструкции с плавающей точкой при помощи опции командной строки, как это описано в «Руководстве пользователя». За исключением увеличения скорости, окончательный результат должен быть таким же, как если бы не было сопроцессора.

Ниже приведены несколько примеров деклараций объектов с плавающей точкой:

float       a;
double      b;
long double c;

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

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

Перечислимый тип это — набор идентификаторов, который соответствует константам типа int. Эти идентификаторы называются константами перечисления. Первый идентификатор в наборе имеет значение 0, а последующие больше предыдущего на 1. Везде, где допускается константа типа int, можно подставить константу перечисления.

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

enum actions { DISPLAY, EDIT, PURGE };

Константа перечисления DISPLAY эквивалентна целой константе 0, а EDIT и PURGE — 1 и 2 соответственно.

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

Пространство имени тега перечислимого типа не перекрывается с пространством имён объектов, меток и имён членов структур и объединений, поэтому имя тега может совпадать с их идентификаторами. Тег перечислимого типа не может совпадать с тегом структуры или объединения или другого перечислимого типа.

Константам перечисления можно задавать конкретные значения указав их после знака '='. Например, декларация,

enum colors { RED = 1, BLUE = 2, GREEN = 4 };

создаёт константы RED, BLUE, и GREEN со значениями 1, 2 и 4 соответственно.

enum fruits { GRAPE, ORANGE = 6, APPLE, PLUM };

создаёт константы со значениями 0, 6, 7 и 8.

enum fruits { GRAPE, PLUM, RAISIN = GRAPE, PRUNE = PLUM };

делает GRAPE и RAISIN равными 0, а PLUM и PRUNE равными 1.

Формальная спецификация перечислимого типа:

enum идентификатор

или

enum { список-констант-перечисления }

или

enum идентификатор { список-констант-перечисления }

список-констант-перечисления:

константа-перечисления

или

константа-перечисления, список-констант-перечисления

константа-перечисления:

идентификатор

или

идентификатор = константное-выражение

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

Из следующей таблицы компилятор Watcom C/16 выбирает наименьший тип, которого достаточно для представления всех констант конкретного перечисления:

Тип Наименьшее
значение
Наибольшее
значение
signed char -128 127
unsigned char 0 255
signed int -32768 32767
unsigned int 0 65535

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

Из следующей таблицы компилятор Watcom C/32 выбирает наименьший тип, которого достаточно для представления всех констант конкретного перечисления:

Тип Наименьшее
значение
Наибольшее
значение
signed char -128 127
unsigned char 0 255
signed short -32768 32767
unsigned short 0 65535
signed int -2147483648 2147483647
unsigned int 0 4294967295

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

Для создания объекта перечислимого типа можно использовать два способа. Первый способ — создать тип, как показано выше, и, затем, декларировать объект:

enum тег имя-объекта;

Например, декларация

enum fruits fruit;

декларирует объект fruit как перечисление типа fruits.

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

enum fruits { GRAPE, ORANGE, APPLE, PLUM } fruit;

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

enum { GRAPE, ORANGE, APPLE, PLUM } fruit;

Идентификатор — константа перечисления — может входить только в один перечислимый тип. Например константа ORANGE не может быть включена в другое перечисление, потому что компилятор при этом будет иметь два значения для ORANGE.

Массивы

Массив это — совокупность объектов одного и того же типа. Все элементы (объекты) в массиве хранятся в памяти последовательно друг за другом.

Обращение к элементу массива обычно производится при помощи индексирования. Для этого элементы массива нумеруются начиная с нуля. Следовательно, массив из n элементов индексируется с 0 до n-1.

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

Массив неопределённого размера имеет незавершенный тип.

Массив можно декларировать следующим образом:

тип идентификатор[ константное-выражение ];

или

тип идентификатор[] = { список-инициализаторов };

или

тип идентификатор[ константное-выражение ] = { список-инициализаторов };

или

тип идентификатор[];

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

Например,

int values[10];

декларирует values как массив из 10 целых с индексом от 0 до 9. Выражение values[5] ссылается на шестое целое в массиве.

char text[] = { "some stuff" };

декларирует text как массив из 11 символов, каждый из которых содержит последовательные буквы из "some stuff". Значение text[10] равно '\0' (нулевой символ), который закрывает стринг (см. Стринги).

extern NODES nodelist[];

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

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

int tbl[2][3];

определяет массив целых из 2-х строк и 3-х колонок. На самом деле это определяет массив двух массивов из трёх целых. Значения располагаются в памяти в следующем порядке:

tbl[0][0]
tbl[0][1]
tbl[0][2]
tbl[1][0]
tbl[1][1]
tbl[1][2]

Строки таблицы хранятся друг за другом. Эта форма расположения массива называется порядок со старшинством строк. Выражение tbl[1][2] отсылает к элементу в последней строке и последней колонке массива.

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

int   array[10];
int * aptr;

aptr = array;

присвоение переменной aptr равноценно

aptr = &array[0];

Так как многомерные массивы являются просто массивами массивов, то пропуск одного, но не всех, измерений эквивалентно взятию адреса первого элемента в подмассиве. Например,

int   array[9][5][2];
int * aptr;

aptr = array[7];

присвоение переменной aptr равноценно

aptr = &array[7][0][0];

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

Стринги

Стринг — специальная форма типа «массив символов», конкретнее, массив символов, оканчивающийся нулевым символом. Нулевой символ — символ со значением нуль, который представляется в виде \0 внутри строки, или как символьная константа '\0'. Так как работа со стрингами — частая задача в программировании, C предоставляет набор библиотечных функций для их обработки.

Доступ к стрингу осуществляется через адрес его первого символа. Длина стринга — число символов до, но не включая, нулевого символа.

Массив может быть инициализирован как стринг следующим образом:

тип идентификатор[] = { "стринговое-значение" };

(Фигурные скобки необязательны). Например,

char ident[] = "This is my program";

декларирует ident как массив из 19 символов, последний из которых — нуль. Стринг имеет 18 символов плюс нулевой символ.

В предыдущем примере, ident — массив, чьё значение — строка. Однако заключённое в кавычки значение, использованное для инициализации, называется стринговый литерал. Стринговые литералы описаны в разделе Константы.

Стринговые литералы могут использоваться везде, где можно использовать «указатель на char». Например, если есть декларация

char * ident;

то команда

ident = "This is my program";

присвоит переменной ident адрес стринга "This is my program".