- •Введение
- •Язык программирования Си Элементы языка программирования.
- •Множества символов
- •Буквы и цифры
- •Пробельные символы
- •Знаки пунктуации и специальные символы
- •Операции
- •Константы
- •Целые константы
- •Константы с плавающей точкой
- •Константа-символ
- •Строковые литералы
- •Идентификаторы
- •Ключевые слова
- •Комментарии
- •Лексемы
- •Структура программы
- •Исходная программа
- •Исходные файлы
- •Выполнение программ
- •Время жизни и видимость
- •Классы имен
- •Объявления
- •Спецификаторы типов
- •Область значений величин
- •Деклараторы
- •Деклараторы массивов, функций и указателей
- •Составные деклараторы
- •Объявления переменной
- •Объявление простой переменной
- •Объявление перечисления
- •Объявления структур
- •Объявление совмещений
- •Объявление массива
- •Объявление указателей
- •Объявление функций
- •Объявление классов
- •Классы памяти
- •Объявления переменной на внешнем уровне
- •Объявление переменной на внутреннем уровне
- •Объявление функции на внешнем и внутреннем уровнях
- •Инициализация
- •Базовые типы и типы указателей
- •Составные типы
- •Строковые инициализаторы
- •Объявления типов
- •Типы структур, совмещений и перечислений
- •Объявления typedef
- •Имена типов
- •Выражения и присваивания
- •Введение
- •Операнды
- •Константы
- •Идентификаторы
- •Вызовы функций
- •Индексные выражения
- •Выражение выбора структурного элемента
- •Выражения с операциями
- •Выражения в скобках
- •Type-cast выражения
- •Константные выражения
- •Операции
- •Обычные арифметические преобразования.
- •Операции дополнения
- •Операция адресации и разадресации
- •Операция sizeof
- •Мультипликативные операции
- •Аддитивные операции
- •Операции сдвига
- •Операции отношений
- •Побитовые операции
- •Логические операции
- •Операция последовательного вычисления
- •Условная операция
- •Операции присваивания
- •Lvalue-выражения
- •Унарные инкремент и декремент
- •Простое присваивание
- •Составное присваивание
- •Старшинство и порядок выполнения
- •Побочные эффекты
- •Преобразования типов
- •Преобразование типов при присваивании
- •Преобразования type-cast
- •Преобразования, выполняемые операциями
- •Преобразования при вызовах функций
- •Операторы
- •Введение
- •Оператор break
- •Составной оператор
- •Оператор continue
- •Оператор do
- •Оператор-выражение
- •Оператор for
- •Goto и помеченные операторы
- •Оператор if
- •Оператор null
- •Оператор return
- •Оператор switch
- •Оператор while
- •Функции
- •Введение
- •Определение функции
- •Класс памяти
- •Тип возврата
- •Формальные параметры
- •Тело функции
- •Объявления функции
- •Вызовы функций
- •Фактические аргументы
- •Вызовы с переменным числом аргументов
- •Рекурсивные вызовы
- •Директивы препроцессора и указания компилятору
- •Поименованные константы и макросы
- •Директива # define
- •Директива #undef
- •#Include файлы
- •Условная компиляция
- •Директивы #if, #elif, #else, #endif
- •Директивы #ifdef и #ifndef
- •Управление нумерацией строк
Побочные эффекты
Побочные эффекты - это изменения состояния машины, которые возникают в результате вычисления выражений. Они имеют место всякий раз, когда изменяется значение переменной. Любая операция присваивания вызывает побочный эффект, и любой вызов функции, который содержит операцию присваивания, имеет побочные эффекты.
Порядок получения побочных эффектов зависит от реализации, за исключением случаев, когда компилятор обеспечивает определенный порядок вычислений, как было описано в разделе 5.5.
Например, побочный эффект имеет место в следующем вызове функции:
add ( i + 1, i = j +2)
Аргументы вызова функции могут быть вычислены в любом порядке. Выражение i + 1 может быть вычислено перед i=j+2, или наоборот, с различным результатом в каждом случае.
Унарные операции инкремента и декремента включают присваивание и могут быть причиной побочных эффектов, как это показано в следующем примере:
d=0
a=b++=c++=d++;
Значение a непредсказуемо. Значение d (инициализируется нулем), могло быть присвоено c, затем b и затем a, прежде чем любая из переменных была бы инкрементирована. В этом случае a должно было бы быть эквивалентно нулю.
Второй способ вычисления этого выражения начинается вычислением операнда c++=d++. Значение d (инициализированное нулем) присваивается c, а затем d и c инкрементируются. Затем значение c, которое теперь равно 1, присваивается b и b инкрементируется. Наконец, инкрементированное значение b присваивается a. В этом случае окончательное значение a равно 2.
Т.к. язык Си не опеределяет порядок изменения состояний машины (побочных эффектов) при вычислениях, то оба эти метода вычисления корректны и могут быть выполнены. Операторы, которые зависят от частностей порядка вычисления побочных эфектов, выдают непереносимый и неясный код.
Преобразования типов
Преобразование типов имеет место, когда тип значения, которое присваивается переменной, отличается от типа переменной. Преобразование типов выполняется, когда операция перед вычислением преобразует тип своего операнда или операндов и когда преобразуется значение, посылаемое как аргумент функции. Правила управления в каждом случае проебразований рассматриваются ниже.
Преобразование типов при присваивании
В операциях присваивания тип значения, которое присваивается, преобразуется к типу переменной, получающей это значение. В Си допускаются преобразования при присваивании между целыми и плавающими типами, даже в случаях, когда преобразование влечет за собой потерю информации. Методы выполнения преобразований зависят от типов следующим образом.
Знаковое целое преобразуется к короткому знаковому целому (short signed int) посредством усечения старших битов.
Знаковое целое преобразуется к длинному знаковому целому (long signed int) путем размножения знака влево. Преобразование знаковых целых к плавающим величинам происходит без потери информации, за исключением потери некоторой точности, когда преобразуются величины long в float. При преобразовании знакового целого к беззнаковому целому (unsigned int), знаковое целое преобразуется к размеру беззнакового целого и результат интерпретируется как беззнаковая величина.
Преобразование знаковых целых типов представлено в Таблица 5-13. В таблице предполагается, что типы char являются знаковыми по умолчанию. Если во время компиляции используется опция, которая изменяет умолчание типа char на безнаковый, то преобразования типов unsigned char даны в Таблица 5-14.
Из |
В |
Метод |
char |
short |
дополнение знаком |
char |
long |
дополнение знаком |
char |
unsigned char |
сохранение битов; старший бит теряет функцию знакового бита |
char |
unsigned short |
дополнение знаком до short, преобразование short в unsigned short |
char |
usigned long |
дополнение знаком до long; преобразование long в unsigned long |
char |
float |
дополнение знаком до long; преобразование long в float |
char |
double |
дополнение знаком до long; преобразование long в double |
short |
char |
сохранение младшего байта |
short |
long |
размножение знака |
short |
unsigned short |
сохранение битов; старший бит теряет функцию знакового бита |
short |
unsigned long |
дополнение знаком до long; преобразование long в unsigned long |
short |
float |
дополнение знаком до long; преобразование long к float |
short |
double |
дополнение знаком до long; преобразование long в double |
long |
char |
сохранение младшего байта |
long |
short |
сохранение младшего слова |
long |
unsigned char |
сохранение младшего байта |
long |
unsigned short |
сохранение младшего слова |
long |
unsigned long |
сохранение всех битов; старший бит теряет функцию знакового бита |
long |
float |
представляется как float; если long не может быть представлено точно, то происходит некоторая потеря точности |
long |
double |
представляется как double; если long не может быть представлено точно как double происходит некоторая потеря точности |
Таблица 5‑13 Преобразование знаковых целых типов
Замечание: Тип int эквивалентен или short типу или типу long в зависимости от оборудования. Преобразование значений int производится как для short или long, в зависимости от того, что подходит.
Преобразование беззнаковых целых типов.
Беззнаковое целое преобразуется к короткому беззнаковому или знаковому целому путем усечения старших битов. Беззнаковое целое преобразуется к длинному беззнаковому или знаковому целому путем размножения нуля. Беззнаковые целые преобразуются к плавающим величинам путем преобразования к ближайшему знаковому целому того же самого размера, а затем преобразования этой знаковой величины к величине с плавающей точкой.
Когда беззнаковое целое преобразуется к знаковому целому того же размера, то состояние битов не меняется. Однако, значение этого представления изменится, если был установлен знаковый бит.
Преобразование беззнаковых целых типов представлено в Таблица 5-14.
Из |
В |
Метод |
unsigned char |
char |
сохраняются все биты; старший бит становится знаковым |
unsigned char |
short |
дополнение нулем |
unsigned char |
long |
дополнение нулем |
unsigned char |
unsigned short |
дополнение нулем |
unsigned char |
unsigned long |
дополнение нулем |
unsigned char |
float |
преобразование к long, преобразование long в float |
unsigned char |
double |
преобразование к long, преобразование long к double |
unsigned short |
char |
сохранение младшего байта |
unsigned short |
short |
сохранение всех битов, старший бит становится знаковым |
unsigned short |
long |
дополнение нулем |
unsigned short |
unsigned char |
сохранение младшего байта |
unsigned short |
unsigned long |
дополнение нулем |
unsigned short |
float |
преобразование к long, преобразование long в float |
unsigned short |
double |
преобразование к long, преобразование long к double |
unsigned long |
char |
сохранение младшего байта |
unsigned long |
short |
сохранение младшего слова |
unsigned long |
long |
сохранение всех битов; старший бит становится знаковым |
unsigned long |
unsigned char |
сохранение младшего байта |
unsigned long |
unigned short |
сохранение младшего слова |
unsigned long |
float |
преобразование к long, преобразование long к float |
unsigned long |
double |
преобразование к long, преобразование long к double |
Таблица 5‑14 Преобразование беззнаковых целых типов
Замечание: Тип unsigned int эквивалентен или unsigned short, или unsigned long типам в зависимости от оборудования. Преобразование из unsigned int производятся как для unsigned short или unsigned long в зависимости от того, что подходит.
Преобразование плавающих типов.
Величины float преобразуются к double, не меняясь в значении. Величины double, преобразованные к float, представляются точно, если возможно. Если значение слишком велико для float, то точность теряется.
Плавающие величины преобразуются к целым типа long.Преобразование к другим целым типам выполлняется как для long. Дробная часть плавающей величины отбрасывается при преобразовании к long; если результат слишком велик для long, то результат преобразования неопределен.
Преобразования плавающих типов сведены в Таблица 5-15 Преобразования плавающих типов.
Из |
В |
Метод |
Float |
char |
преобразуется к long, long преобразуется к char |
Float |
short |
преобразуется к long, long преобразуется в short |
Float |
long |
усечение дробной части; результат неопределен, если он слишком велик для представления в long |
Float |
unsigned short |
преобразуется к long, long преобразуется к unsigned short |
Float |
unsigned long |
преобразуется к long, long преобразуется к unsigned long |
Float |
double |
изменение внутреннего представления |
Double |
char |
преобразование к float, преобразование float к char |
Double |
short |
преобразование к float, преобразование float к short |
Double |
long |
усечение дробной части; результат неопределен, если он слишком велик для представления в long |
Double |
unsigned short |
преобразование к long, преобразование long к unsigned short |
Double |
unsigned long |
преобразование к long, преобразование long к unsigned long |
Double |
float |
представляется как float; если значение double не может быть точно представлено как float, то точность теряется; если значение слишком велико для представления как float, то результат неопределен |
|
|
|
Таблица 5‑15 Преобразования плавающих типов
Преобразование адресных типов.
Указатель на величину одного типа может быть преобразован к указателю на величину другого типа. Результат может быть, однако, неопределенным из-за отличия в требованиях к выравниванию и размерам памяти.
В некоторых реализациях имеются специальные ключевые слова near, far, huge, модифицирующие размер указателей в программах. Указатель может быть преобразован к указателю другого размера; путь преобразования зависит от реализации. Например, на процессоре 8086 компилятор должен использовать значение регистра сегмента, чтобы преобразовать 16-разрядный указатель в 32-х разрядный указатель. Смотрите системную документацию для получения информации о преобразовани указателя.
Значение указателя может быть преобразовано к целой величине. Путь преобразования зависит от размера указателя и размера целого типа следующим образом:
если указатель имеет тот же самый или меньший размер, чем целый тип, то указатель преобразуется точно так же как беззнаковое целое, за исключением того, что он не может быть преобразован к плавающей величине;
если указатель меньше чем размер целого типа, то указатель вначале преобразуется к указателю с тем же самым размером, что и целый тип, и затем преобразуется к целому типу. Метод преобразования указателя к более длинному указателю зависит от реализации; смотрите системную документацию чтобы получить информацию о таких преобразованиях.
Целый тип может быть преобразован к адресному типу. Если целый тип того же самого размера, что и адресный, то производится простое преобразование к виду указателя (беззнакового целого). Если размер целого типа отличен от размера адресного типа, то целый тип вначале преобразуется к размеру указателя, используя методы преобразования данных в Таблица 5-13 и Таблица 5-14. Затем полученное значение представляется как указатель.
Если поддерживаются специальные ключевые слова near, far, huge, то может быть сделано неявное преобразование адресных величин. В частности, компилятор может по умолчанию создавать указатели определенного размера и производить преобразования получаемых адресных величин, если в программе не появится forward обьявление, переопределяющее это умолчание. Смотрите системную документацию для получения информации об адресных преобразованиях.
Преобразования других типов.
Из определения типа enum следует, что величины enum являются величинами типа int. Поэтому преобразования в и из типа enum осуществляется как для int типов. Тип int эквивалентен типам short или long в зависимости от реализации.
Не допустимы преобразования объектов типа структур и совмещений.
Тип void не имеет значения по определению. Поэтому он не может быть преобразован к любому другому типу, но любая величина может быть преобразована к типу void путем присваивания. Тем не менее, величина может быть явно преобразована cast операцией к void, как это рассматривается в разделе 5.7.2.