Вы находитесь на странице: 1из 488

Оставьте отзыв о скачивании PDF-файла.

Документация по языку C
Узнайте, как использовать C и библиотеку времени выполнения C.

Начало работы с C в Visual Studio

a СКАЧАТЬ

Скачать Visual Studio для Windows

Установка поддержки C/C++ в Visual Studio

Скачать только средств сборки для командной строки

g УЧЕБНИК

Компиляция программы на языке C из командной строки

Использование компилятора и средств

i ССЫЛКА

Справочные сведения о сборке кода на C/C++

Проекты и системы сборки

Справочник по компилятору

Справочник по компоновщику

Дополнительные средства сборки

Ошибки и предупреждения

Язык C

i ССЫЛКА

Справочник по языку C

Элементы языка C

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

Объявления и типы
Объявления и типы

Выражения и присваивания

Операторы

Функции

Краткие сведения о синтаксисе языка C

Поведение, определяемое реализацией

Справочник по препроцессору в C/C++

Библиотека времени выполнения (CRT)

i ССЫЛКА

Особенности библиотеки CRT

Алфавитный указатель функций

Подпрограммы среды выполнения C по категориям

Глобальные переменные и стандартные типы

Глобальные константы

Глобальное состояние

Универсальные текстовые сопоставления


Справочник по языку C
Статья • 03.03.2023 • Чтение занимает 2 мин

Справочник по языку C описывает язык программирования C, реализованный в


Microsoft C. Руководство организовано на основе стандарта ANSI C (иногда
называется C89) и содержит дополнительные сведения о расширениях Майкрософт
для стандарта ANSI C.

Организация Справочника по языку C

Дополнительные справочные материалы по языку C++ и препроцессору см. в


следующих документах:

C++ Language Reference (Справочник по языку C++)

C/C++ Preprocessor Reference (Справочник по препроцессору C/C++)

Параметры компилятора и компоновщика приведены в Образец построения


C/C++.

См. также
Справочник по языку C++
Организация Справочника по языку C
Статья • 03.03.2023 • Чтение занимает 2 мин

Элементы языка C

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

Объявления и типы

Выражения и присваивания

Операторы

Функции

Краткие сведения о синтаксисе языка C

Поведение, определяемое реализацией

См. также
Справочник по языку C
Содержание данного руководства
Статья • 03.03.2023 • Чтение занимает 2 мин

C — гибкий язык, предоставляющий широкие возможности для


программирования. При этом C накладывает несколько ограничений на
преобразование типов. Хотя возможности языка упрощают программирование,
для понимания того, как будут работать программы, необходимо хорошо знать
язык. В данной книге приведены сведения о компонентах языка C и возможностях
реализации Microsoft. Синтаксис языка C соответствует стандарту ANSI X3.159-1989,
American National Standard for Information Systems - Programming Language- C
(Американский национальный стандарт для информационных систем — Язык
программирования — Язык C). Здесь и далее он называется стандартом ANSI C,
хотя не является частью этого стандарта. В статье Общие сведения о синтаксисе
языка C рассмотрен синтаксис и описаны правила чтения и использования
определений синтаксиса.

Программирование на языке C++ в данной книге рассматривается. Сведения о


языке C++ см. в справочнике по языку C++.

См. также
Организация Справочника по языку C
Соответствие ANSI
Статья • 03.03.2023 • Чтение занимает 2 мин

Microsoft C соответствует стандарту языка C, представленному в выпуске 9899:1990


стандарта ANSI C.

Расширения Microsoft для стандарта ANSI C указаны в тексте и синтаксисе данного


руководства, а также в электронной справочной документации. Поскольку
расширения не являются частью стандарта ANSI C, их применение может
ограничить переносимость программ между системами. По умолчанию
расширения Microsoft включены. Чтобы отключить расширения, используйте
параметр компилятора /Za. С помощью параметра /Za весь отличный от ANSI код
создает ошибки или предупреждения.

См. также
Организация Справочника по языку C
Элементы языка C
Статья • 03.03.2023 • Чтение занимает 2 мин

В этом разделе описываются элементы языка программирования C, включая


имена, числа и символы, используемые в нем для составления программ. В
синтаксисе ANSI C эти компоненты называются токенами.

В этом разделе объясняется, как определять токены и как компилятор вычисляет


их.

Рассматриваются следующие темы:

Лексемы

Комментарии

Ключевые слова

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

Константы

Строковые литералы

Знаки препинания и специальные символы

Раздел также включает справочные таблицы по триграфам, ограничениям на


константы с плавающей запятой, пределам целых чисел в C и C++ и escape-
последовательностям.

Операторы (как одиночные символы, так и сочетания символов) — это символы,


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

См. также
Справочник по языку C
Токены C
Статья • 03.03.2023 • Чтение занимает 2 мин

В исходной программе C основным элементом, распознаваемым компилятором,


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

Синтаксис
token :

  keyword

  identifier

  constant

  string-literal

  operator

  punctuator

7 Примечание

Описание соглашений о синтаксисе ANSI вы найдете во введении к статье


Общие сведения о синтаксисе языка C.

Ключевые слова, идентификаторы, константы, строковые литералы и операторы,


описанные в этом разделе, являются примерами токенов. Знаки препинания, такие
как квадратные скобки ( [ ] ), фигурные скобки ( { } ), круглые скобки ( ( ) ) и
запятые ( , ), также являются маркерами.

См. также
Элементы языка C
Пробелы
Статья • 26.09.2022 • Чтение занимает 2 мин

Символы пробела, табуляции, перевода строки (новой строки), возврата каретки,


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

См. также
Токены C
Комментарии в C
Статья • 03.03.2023 • Чтение занимает 2 мин

Комментарий — это последовательность символов, которая начинается с косой


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

Они могут располагаться в любом месте, где допускается использование


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

Комментарии используются для документирования кода. В следующем примере


компилятор принимает комментарий:

/* Comments can contain keywords such as

for and while without generating errors. */

Комментарии в коде могут находиться в той же строке, что и оператор:

printf( "Hello\n" ); /* Comments can go here */

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


комментариев с описаниями.

/* MATHERR.C illustrates writing an error routine

* for math functions.

*/

Так как комментарии не могут содержать вложенные комментарии, в этом


примере возникает ошибка:

C
/* Comment out this routine for testing

/* Open file */

fh = _open( "myfile.c", _O_RDONLY );

*/

Причина ошибки в том, что компилятор распознает первое сочетание символов,


*/ , расположенное после слов Open file , как конец комментария. Он пытается

обработать оставшийся текст, а обнаружив символы */ за пределами


комментария, выдает сообщение об ошибке.

Хотя с помощью комментариев можно скрывать часть кода для тестирования, для
этого есть полезная альтернатива: директивы препроцессора #if и #endif и
условная компиляция. Дополнительные сведения см. в статье Preprocessor
Directives (Директивы препроцессора) в справочника по препроцессору.

Блок, относящийся только к системам Microsoft

Компилятор Microsoft также поддерживает однострочные комментарии, перед


которыми ставятся две косые черты ( // ). Эти примечания не могут быть
расширены до второй строки.

// This is a valid comment

Комментарии, начинающиеся с двух косых черт ( // ), заканчиваются следующим


символом новой строки, которому не предшествует escape-символ. В следующем
примере символу новой строки предшествует обратная косая черта ( \ ), создается
escape-последовательность. Эта escape-последовательность заставляет компилятор
обрабатывать следующую строку как часть предыдущей строки. Дополнительные
сведения см. в статье Escape Sequences (Escape-последовательности).

// my comment \

i++;

Поэтому оператор i++; скрыт комментарием.


В Microsoft C расширения Microsoft по умолчанию включены. Отключить их можно
при помощи параметра /Za.

Завершение блока, относящегося только к системам Майкрософт

См. также
Токены C
Оценка токенов
Статья • 03.03.2023 • Чтение занимает 2 мин

Когда компилятор интерпретирует токены, он включает в один токен максимально


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

i+++j

В этом примере компилятор сначала извлекает максимально возможный оператор


( ++ ) из последовательности знаков "плюс", а затем обрабатывает последний знак
"плюс" как оператор сложения ( + ). Таким образом, выражение интерпретируется
как (i++) + (j) , а не как (i) + (++j) . Для того чтобы предотвратить
неоднозначность и гарантировать правильное вычисление выражений, в
подобных случаях рекомендуется использовать пробелы и скобки.

Блок, относящийся только к системам Microsoft

Компилятор C обрабатывает символ CTRL+Z как индикатор конца файла. Весь


текст, расположенный после символа CTRL+Z, игнорируется.

Завершение блока, относящегося только к системам Майкрософт

См. также
Токены C
Ключевые слова в C
Статья • 03.03.2023 • Чтение занимает 2 мин

Ключевыми словами называются слова, которые имеют особое значение для


компилятора C. На 7 и 8 этапах трансляции идентификатор не может иметь такое
же написание и регистр записи, что и ключевое слово C. Дополнительные
сведения см. в разделе Этапы преобразованияСправочника по препроцессору.
Дополнительные сведения об идентификаторах см. в этом разделе.

Ключевые слова стандартного языка C


В языке C используются следующие ключевые слова:

auto

break

case

char

const

continue

default

do

double

else

enum

extern

float

for

goto

if

inline 1, a

int

long

register

restrict 1, a

return

short

signed

sizeof

static

struct

switch

typedef

union

unsigned

void

volatile

while

_Alignas2, a

_Alignof2, a

_Atomic 2, b

_Bool 1, a

_Complex 1, b

_Generic2, a

_Imaginary 1, b

_Noreturn 2, a

_Static_assert 2, a

_Thread_local 2, b

1 Ключевые слова, представленные в стандарте ISO C99.

2
Ключевые слова, представленные в стандарте ISO C11.

a Начиная с Visual Studio 2019 версии 16.8 эти ключевые слова поддерживаются в


коде, скомпилированном как C, если указаны параметр компилятора /std:c11 или
/std:c17 .

b Начиная с Visual Studio 2019 версии 16.8 эти ключевые слова распознаются, но не


поддерживаются компилятором в коде, скомпилированном как C, если указаны
параметр компилятора /std:c11 или /std:c17 .

Переопределить ключевые слова невозможно. Но с помощью директив


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

Ключевые слова C для систем Microsoft


Стандарты ANSI и ISO C позволяют зарезервировать идентификаторы,
начинающиеся с двух символов подчеркивания, для реализаций компилятора.
Поэтому в системах Microsoft действует соглашение, что используемые в них
ключевые слова начинаются с двух символов подчеркивания. Эти слова
невозможно использовать как имена идентификаторов. Описание правил
именования идентификаторов, включая использование двойных знаков
подчеркивания, см. в статье об идентификаторах.

Компилятор Microsoft C распознает следующие ключевые слова и особые


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

__asm 5

__based 3, 5

__cdecl 5

__declspec 5

__except 5

__fastcall

__finally 5

__inline 5

__int16 5

__int32 5

__int64 5

__int8 5

__leave 5

__restrict

__stdcall 5

__try 5

dllexport 4

dllimport 4

naked 4

static_assert 6

thread 4

3
Ключевое слово __based имеет ограниченное применение: в компиляциях для
32- и 64-разрядных платформ.

4 Если эти ключевые слова используются с ключевым словом __declspec , они


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

5
Для совместимости с предыдущими версиями эти ключевые слова доступны как с
двумя символами подчеркивания в начале, так и с одним при включении
расширений Microsoft.

6
Если файл <assert.h> не включен, компилятор Microsoft Visual C сопоставляет
static_assert с ключевым словом C11 _Static_assert .

Расширения Microsoft по умолчанию включены. Чтобы помочь в создании


переносимого кода, расширения Microsoft можно отключить, указав во время
компиляции параметр /Za (Отключить расширения языка) . При этом некоторые
ключевые слова для систем Microsoft будут отключены.

Когда расширения Microsoft включены, в программах можно использовать


перечисленные выше ключевые слова. Чтобы соответствовать стандарту языка,
большинству этих ключевых слов предшествует двойное подчеркивание. Четыре
исключения, dllexport , dllimport , naked и thread , используются только с
ключевым словом __declspec и поэтому не требуют двойного знака
подчеркивания. Для всех остальных ключевых слов поддерживаются версии с
одним символом подчеркивания. Это сделано для обеспечения обратной
совместимости.

См. также
Элементы языка C
Идентификаторы C
Статья • 03.03.2023 • Чтение занимает 3 мин

"Идентификаторы" или "символы" — это имена, задаваемые в программе для


переменных, типов, функций и меток. Написание и регистр символов в именах
идентификаторов должны отличаться от всех ключевых слов. Вы не можете
использовать ключевые слова (C или Microsoft) в качестве идентификаторов; они
зарезервированы для специального использования. Идентификатор создается
путем его указания в объявлении переменной, типа или функции. В этом примере
result представляет собой идентификатор целой переменной, а main и printf  —

это имена идентификаторов для функций.

#include <stdio.h>

int main()

int result;

if ( result != 0 )

printf_s( "Bad file handle\n" );

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


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

В инструкциях можно использовать goto специальный набор идентификаторов,


называемый меткой инструкции. (Объявления рассматриваются в разделе
Объявления и типы. Метки операторов рассматриваются в разделе Оператор goto
и помеченные операторы.)

Синтаксис
identifier :

  nondigit

  identifier nondigit

  identifier digit

nondigit : один из следующих символов:

 _ a b c d e f g h i j k l m n o p q r s t u v w x y z

 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
digit : один из следующих символов:

 0 1 2 3 4 5 6 7 8 9

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


(т. е., первым символом должен быть знак подчеркивания или прописная либо
строчная буква). Стандарт ANSI допускает 6 значащих символов в имени внешнего
идентификатора и 31 символ для имен внутренних (внутри функции)
идентификаторов. Внешние идентификаторы (объявленные в глобальной области
или объявленные с классом extern хранения) могут подвергаться дополнительным
ограничениям именования, так как эти идентификаторы должны обрабатываться
другим программным обеспечением, например компоновщиками.

Блок, относящийся только к системам Microsoft

Хотя стандарт ANSI допускает 6 значащих символов в именах внешних


идентификаторов и 31 символ в именах внутренних (внутри функции)
идентификаторов, компилятор Microsoft C допускает 247 символов в именах
внутренних и внешних идентификаторов. Если вас не интересует совместимость с
ANSI, вы можете изменить это значение по умолчанию, чтобы использовать
меньшее или большее число, указав /H параметр (ограничить длину внешних
имен).

Завершение блока, относящегося только к системам Майкрософт

В компиляторе языка C прописные и строчные буквы считаются разными


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

add

ADD

Add

aDD

Блок, относящийся только к системам Microsoft

Не выбирайте имена для идентификаторов, которые начинаются с двух символов


подчеркивания или символа подчеркивания, за которым следует прописная буква.
Стандарт ANSI языка C разрешает резервировать имена идентификаторов,
начинающиеся с таких сочетаний символов, для использования компилятором.
Идентификаторы с областью действия на уровне файлов также не должны
начинаться со знака подчеркивания и последующей строчной буквы. Имена
идентификаторов, начинающиеся с таких символов, также зарезервированы. По
принятому соглашению в системах Microsoft символ подчеркивания и прописная
букву используются в начале имен макросов, а два символа подчеркивания
используются для специальных ключевых слова систем Microsoft. Чтобы избежать
конфликтов именования, всегда выбирайте имена идентификаторов, которые не
начинаются с одного или двух символов подчеркивания, или имена, которые
начинаются с символа подчеркивания, за которым следует прописная буква.

Завершение блока, относящегося только к системам Майкрософт

Ниже приведены примеры допустимых идентификаторов, которые соответствуют


ограничениям на имена, накладываемым стандартом ANSI или системами
Microsoft:

count

temp1

top_of_page

skip12

LastNum

Блок, относящийся только к системам Microsoft

Хотя идентификаторы в исходных файлах по умолчанию чувствительны к регистру,


символы в объектных файлах не являются. Microsoft C обрабатывает
идентификаторы в единице компиляции с учетом регистра.

Компоновщик Microsoft учитывает регистр. Необходимо указывать все


идентификаторы единообразно с учетом регистра.

"Исходная кодировка" — это набор допустимых символов, которые могут


использоваться в файлах исходного кода. Для Microsoft C исходной кодировкой
является стандартный набор символов ASCII. Исходная кодировка и кодировка
выполнения содержат символы ASCII, используемые в виде escape-
последовательностей. Сведения о кодировке выполнения см. в статье Константы
символов в C.

Завершение блока, относящегося только к системам Майкрософт

Идентификатор имеет "область", которая является областью программы, в которой


он известен. Он также имеет "компоновку", которая определяет, ссылается ли то же
имя в другой области на тот же идентификатор. Эти термины описаны в разделе
Время существования, Область, Видимость и Компоновка.

См. также
Элементы языка C
Многобайтовая кодировка и
расширенные символы
Статья • 03.03.2023 • Чтение занимает 2 мин

Многобайтовые символы — это символы, составленные последовательностями из


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

Расширенные символы — это коды многоязычных символов, которые всегда


имеют размер 16 бит. Символьные константы имеют тип char ; для расширенных
символов используется тип wchar_t . Поскольку расширенные символы всегда
имеют фиксированный размер, их использование упрощает программирование с
использованием международных кодировок.

Строковый литерал с расширенными символами L"hello" становится массивом из


шести целочисленных значений типа wchar_t .

{L'h', L'e', L'l', L'l', L'o', 0}

Расширенные символы определены в спецификации Юникода. Преобразование


между многобайтовыми и расширенными символами выполняется процедурами
библиотеки времени выполнения mbstowcs , mbtowc , wcstombs и wctomb .

См. также
Идентификаторы C
Триграфы
Статья • 03.03.2023 • Чтение занимает 2 мин

Кодировка исходного кода исходных программ на языке C содержится в 7-


разрядной кодировке ASCII, но в то же время является надмножеством
инвариантной кодировки ISO 646-1983. Последовательности триграфа позволяют
писать программы на языке C с использованием только инвариантной кодировки,
соответствующей стандартам ISO. Триграфы — это последовательности из трех
символов (которым предшествует два вопросительных знака), которые компилятор
заменяет соответствующими знаками пунктуации. Триграфы можно использовать в
файлах исходного кода на языке C с набором символов, который не содержит
удобных графических представлений некоторых знаков пунктуации.

C++17 удаляет триграфы из языка. Поддержка триграфов может сохраняться при


определяемом реализацией сопоставлении физического исходного файла с
основной кодировкой исходного кода, хотя обычно это не рекомендуется. C++14
обеспечивает такую же поддержку триграфов как в языке C.

Visual C++ по-прежнему поддерживает подстановку триграфов, но по умолчанию


она отключена. Сведения о том, как включить подстановку триграфов, см. в статье
/Zc:trigraphs (подстановка триграфов).

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


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

Последовательности триграфов

Триграф Знак препинания

??= #

??( [

??/ \

??) ]

??' ^

??< {

??! |
Триграф Знак препинания

??> }

??- ~

Триграф всегда отображается как один символ исходного кода. Преобразование


триграфов происходит на первом этапе перевода перед распознаванием escape-
символов в строковых литералах и символьных константах. Распознаются только
девять триграфов, отображаемых в таблице выше. Все другие последовательности
символов остаются непреобразованными.

Последовательность escape-символов, \? , не позволяет неверно


интерпретировать символьные последовательности, напоминающие триграфы.
(Сведения об escape-последовательностях см. в разделе Escape-
последовательности.) Например, при попытке напечатать строку What??! с данной
инструкцией printf

printf( "What??!\n" );

напечатанная строка имеет вид What| , так как ??!  — это триграфическая
последовательность, которая заменяется символом | . Напишите инструкцию
следующим образом, чтобы правильно напечатать строку:

printf( "What?\?!\n" );

В этой инструкции printf escape-символ обратной косой черты перед вторым


вопросительным знаком не позволяет неверно интерпретировать ??! как триграф.

См. также
/Zc:trigraphs (подстановка триграфов)

Идентификаторы C
Константы в C
Статья • 03.03.2023 • Чтение занимает 2 мин

Константа — это число, символ или символьная строка, которую можно


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

Синтаксис
constant :

  floating-point-constant

  integer-constant

  enumeration-constant

  character-constant

Константы имеют значение и тип. Константы с плавающей запятой, целочисленные


константы и символьные константы рассматриваются в следующих трех статьях.
Константы перечисления рассматриваются в статье Объявления перечислений.

См. также
Элементы языка C
Константы с плавающей запятой в C
Статья • 03.03.2023 • Чтение занимает 2 мин

"Константа с плавающей запятой — это десятичное число, которое представляет


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

Синтаксис
floating-point-constant :

  fractional-constant exponent-part opt floating-suffix opt

  digit-sequence exponent-part floating-suffix opt

fractional-constant :

  digit-sequence opt. digit-sequence

  digit-sequence .

exponent-part :

 e sign opt digit-sequence

 E sign opt digit-sequence

sign : один из следующих символов:

 + -

digit-sequence :

  digit

  digit-sequence digit

floating-suffix : один из следующих символов:

 f l F L

Можно опустить либо цифры перед десятичной запятой (целочисленная часть


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

В следующих примерах показаны некоторые формы констант и выражений с


плавающей запятой.
C

15.75

1.575E1 /* = 15.75 */

1575e-2 /* = 15.75 */

-2.5e-3 /* = -0.0025 */

25E-4 /* = 0.0025 */

Константы с плавающей запятой имеют положительное значение, если перед ними


не стоит знак "минус" ( - ). А этом случае знак "минус" интерпретируется как
унарный арифметический оператор изменения знака. Константы с плавающей
запятой относятся к типу float , double или long double .

Константы с плавающей запятой без суффикса f , F , l или L относятся к типу


double . Если суффикс представлен буквой f или F , константа относится к типу

float . Если суффикс представлен буквой l или L , константа относится к типу long

double . Пример:

10.0L /* Has type long double */

10.0 /* Has type double */

10.0F /* Has type float */

Компилятор Майкрософт для C внутренне представляет long double так же, как и
тип double . Но типы различаются. Дополнительные сведения о типах double , float
и long double см. в статье Хранилище базовых типов.

Целочисленную часть константы с плавающей запятой можно опустить, как


показано в следующих примерах. Число 0,75 можно выразить несколькими
способами, например:

.0075e2

0.075e1

.075e1

75e-2

См. также раздел


Константы в C
Ограничения на константы с
плавающей запятой
Статья • 03.03.2023 • Чтение занимает 2 мин

Блок, относящийся только к системам Microsoft

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


плавающей запятой. Эта информация содержится в файле заголовка FLOAT.H.

Ограничения на константы с плавающей запятой

Константа Значение Значение

FLT_DIG
Количество цифр q, при котором число с 6

DBL_DIG
плавающей запятой с q десятичными 15

LDBL_DIG цифрами можно округлить в 15


представление с плавающей запятой и
обратно без потери точности.

FLT_EPSILON
Наименьшее положительное число x, при 1,192092896e-07F

DBL_EPSILON
котором x + 1,0 не равно 1,0. 2,2204460492503131e-016

LDBL_EPSILON 2,2204460492503131e-016

FLT_GUARD 0

FLT_MANT_DIG
Количество цифр для основания, 24

DBL_MANT_DIG
заданного константой FLT_RADIX, в 53

LDBL_MANT_DIG значащей части числа с плавающей 53


запятой. Основание содержит два знака;
следовательно, эти значения определяют
разряды.

FLT_MAX
Максимальное представимое число с 3,402823466e+38F

DBL_MAX
плавающей запятой. 1,7976931348623158e+308

LDBL_MAX 1,7976931348623158e+308

FLT_MAX_10_EXP
Максимальное целое число, при котором 38

DBL_MAX_10_EXP
число 10, возведенное в степень этого 308

LDBL_MAX_10_EXP числа, является представимым числом с 308


плавающей запятой.

FLT_MAX_EXP
Максимальное целое число, при котором 128

DBL_MAX_EXP
значение FLT_RADIX, возведенное в 1024

LDBL_MAX_EXP степень этого числа, является 1024


представимым числом с плавающей
запятой.
Константа Значение Значение

FLT_MIN
Минимальное положительное значение. 1,175494351e-38F

DBL_MIN
2,2250738585072014e-308

LDBL_MIN 2,2250738585072014e-308

FLT_MIN_10_EXP
Минимальное отрицательное целое число, -37

DBL_MIN_10_EXP
при котором число 10, возведенное в -307

LDBL_MIN_10_EXP степень этого числа, является -307


представимым числом с плавающей
запятой.

FLT_MIN_EXP
Минимальное отрицательное целое число, -125

DBL_MIN_EXP
при котором значение FLT_RADIX, -1021

LDBL_MIN_EXP возведенное в степень этого числа, -1021


является представимым числом с
плавающей запятой.

FLT_NORMALIZE 0

FLT_RADIX
Основание экспоненциальной формы 2

_DBL_RADIX
представления. 2

_LDBL_RADIX 2

FLT_ROUNDS
Режим округления для сложения чисел с 1 (приблизительно)

_DBL_ROUNDS
плавающей запятой. 1 (приблизительно)

_LDBL_ROUNDS 1 (приблизительно)

Обратите внимание, что в будущих реализациях информация из приведенной


выше таблицы может отличаться.

Завершение блока, относящегося только к системам Майкрософт

См. также
Константы с плавающей запятой в C
Целочисленные константы в C
Статья • 03.03.2023 • Чтение занимает 2 мин

Целая константа является десятичным (основание 10), восьмеричным (основание


8) или шестнадцатеричным (основание 16) числом, представляющим целое
значение. Используйте целочисленные константы для представления
целочисленных значений, которые нельзя изменить.

Синтаксис
integer-constant :

  decimal-constant integer-suffix необ.

  octal-constant integer-suffix необ.

  hexadecimal-constant integer-suffix необ.

decimal-constant :

  nonzero-digit

  decimal-constant digit

octal-constant :

 0

  octal-constant octal-digit

hexadecimal-constant :
  hexadecimal-prefix hexadecimal-digit

  hexadecimal-constant hexadecimal-digit

hexadecimal-prefix : один из следующих символов:

  0x 0X

nonzero-digit : один из следующих символов:

 1 2 3 4 5 6 7 8 9

octal-digit : один из следующих символов:

 0 1 2 3 4 5 6 7

hexadecimal-digit : один из следующих символов:

 0 1 2 3 4 5 6 7 8 9

 a b c d e f

 A B C D E F
integer-suffix :

  unsigned-suffix long-suffix необ.

  unsigned-suffix long-long-suffix

  unsigned-suffix 64-bit-integer-suffix

  long-suffix unsigned-suffix opt

  long-long-suffix unsigned-suffix необ.

  64-bit-integer-suffix

unsigned-suffix : один из следующих символов:

 u U

long-suffix : один из следующих символов:

 l L

long-long-suffix : один из следующих символов:

  ll LL

64-bit-integer-suffix : один из следующих символов:

  i64 I64

Суффиксы i64 и I64 относятся к корпорации Майкрософт.

Целочисленные константы являются положительными, если им не предшествует


знак минус ( - ). Знак "минус" интерпретируется как унарный арифметический
оператор изменения знака. (Сведения об этом операторе см. в статье Unary
Arithmetic Operators (Унарные арифметические операторы).

Если целочисленная константа начинается с 0x или 0X , это шестнадцатеричное


значение. Если он начинается с цифры 0 , это восьмеричная. В противном случае
предполагается, что он является десятичным.

Следующие целочисленные константы эквивалентны:

28

0x1C /* = Hexadecimal representation for decimal 28 */

034 /* = Octal representation for decimal 28 */

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


следующих примерах показаны некоторые допустимые десятичные, восьмеричные
и шестнадцатеричные константы.
C

/* Decimal Constants */

int dec_int = 28;

unsigned dec_uint = 4000000024u;

long dec_long = 2000000022l;

unsigned long dec_ulong = 4000000000ul;

long long dec_llong = 9000000000LL;

unsigned long long dec_ullong = 900000000001ull;

__int64 dec_i64 = 9000000000002I64;

unsigned __int64 dec_ui64 = 90000000000004ui64;

/* Octal Constants */

int oct_int = 024;

unsigned oct_uint = 04000000024u;

long oct_long = 02000000022l;

unsigned long oct_ulong = 04000000000UL;

long long oct_llong = 044000000000000ll;

unsigned long long oct_ullong = 044400000000000001Ull;

__int64 oct_i64 = 04444000000000000002i64;

unsigned __int64 oct_ui64 = 04444000000000000004uI64;

/* Hexadecimal Constants */

int hex_int = 0x2a;

unsigned hex_uint = 0XA0000024u;

long hex_long = 0x20000022l;

unsigned long hex_ulong = 0XA0000021uL;

long long hex_llong = 0x8a000000000000ll;


unsigned long long hex_ullong = 0x8A40000000000010uLL;

__int64 hex_i64 = 0x4a44000000000020I64;

unsigned __int64 hex_ui64 = 0x8a44000000000040Ui64;

См. также
Константы в C

Целочисленные типы
Статья • 03.03.2023 • Чтение занимает 2 мин

Всем целым константам присваивается тип на основе их значения и способа


представления. Для любой целой константы можно принудительно задать тип long
, добавив букву l или L в конец константы; для принудительного задания типа
unsigned добавьте в конец значения букву u или U . Следует избегать

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


1. Ниже приведены некоторые формы целых констант long :

/* Long decimal constants */

10L

79L

/* Long octal constants */

012L

0115L

/* Long hexadecimal constants */

0xaL or 0xAL

0X4fL or 0x4FL

/* Unsigned long decimal constant */

776745UL

778866LU

Тип, присваиваемый константе, зависит от значения, которое она представляет.


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

Для десятичной константы без суффикса используется тип int , long int или
unsigned long int . Константе присваивается первый из этих 3 типов, в
котором возможно представление значения константы.

Восьмеричным или шестнадцатеричным константам без суффиксов


присваивается тип int , unsigned int , long int или unsigned long int в
зависимости от размера константы.

Константам с суффиксом u или U присваивается тип unsigned int или


unsigned long int в зависимости от размера.
Константам с суффиксом l или L присваивается тип long int или unsigned
long int в зависимости от размера.

Константам с суффиксом u или U и l или L присваивается тип unsigned long


int .

См. также
Целочисленные константы в C
Пределы целых чисел в C и C++
Статья • 03.03.2023 • Чтение занимает 2 мин

Блок, относящийся только к системам Microsoft

Ограничения для целочисленных типов в C и C++ представлены в следующей


таблице. Эти ограничения заданы в стандартном файле заголовка C <limits.h> .
Стандартный файл заголовка C++ <limits> содержит <climits> , который включает
в себя <limits.h> .

В Microsoft C также допускается объявление целочисленных переменных с


указанием размера, которые относятся к целочисленным типам с размером 8, 16,
32 или 64 бит. Дополнительные сведения о них см. в статье Целочисленные типы с
указанием размера.

Ограничения для целочисленных констант


Константа Значение Значение

CHAR_BIT Количество битов в наименьшей переменной, 8


которая не является битовым полем.

SCHAR_MIN Минимальное значение для переменной типа –128


signed char .

SCHAR_MAX Максимальное значение для переменной типа 127


signed char .

UCHAR_MAX Максимальное значение для переменной типа 255 (0xff)


unsigned char .

CHAR_MIN Минимальное значение для переменной типа –128 (или 0, если


char . используется параметр /J)

CHAR_MAX Максимальное значение для переменной типа –127 (или 255, если
char . используется параметр /J)

MB_LEN_MAX Максимальное количество байтов в 5


многосимвольной константе.

SHRT_MIN Минимальное значение для переменной типа -32768


short .

SHRT_MAX Максимальное значение для переменной типа 32767


short .
Константа Значение Значение

USHRT_MAX Максимальное значение для переменной типа 65 535 (0xffff)


unsigned short .

INT_MIN Минимальное значение для переменной типа -2147483647 - 1


int .

INT_MAX Максимальное значение для переменной типа 2147483647


int .

UINT_MAX Максимальное значение для переменной типа 4 294 967 295 (0xffffffff)


unsigned int .

LONG_MIN Минимальное значение для переменной типа -2147483647 - 1


long .

LONG_MAX Максимальное значение для переменной типа 2147483647


long .

ULONG_MAX Максимальное значение для переменной типа 4 294 967 295 (0xffffffff)


unsigned long .

LLONG_MIN Минимальное значение для переменной типа –


long long . 9 223 372 036 854 775 807 –
1

LLONG_MAX Максимальное значение для переменной типа 9 223 372 036 854 775 807


long long .

ULLONG_MAX Максимальное значение для переменной типа 18 446 744 073 709 551 615


unsigned long long . (0xffffffffffffffff)

Если значение превышает максимально возможное представление


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

Завершение блока, относящегося только к системам Майкрософт

См. также
Целочисленные константы в C
Константы символов в C
Статья • 03.03.2023 • Чтение занимает 2 мин

"Константа символа" создается путем заключения одного символа из набора


представимых символов в одинарные кавычки ( ' ' ). Константы символов
используются для представления символов в кодировке выполнения.

Синтаксис
character-constant :

  ' c-char-sequence '

  L' c-char-sequence '

c-char-sequence :

  c-char

  c-char-sequence c-char

c-char :

 Любой член исходной кодировки, кроме одинарной кавычки ( ' ), обратной


косой черты ( \ ) и символа новой строки

  escape-sequence

escape-sequence :

  simple-escape-sequence

  octal-escape-sequence

  hexadecimal-escape-sequence

simple-escape-sequence : один из следующих символов:

  \a \b \f \n \r \t \v

  \' \" \\ \?

octal-escape-sequence :

  \ octal-digit

  \ octal-digit octal-digit

  \ octal-digit octal-digit octal-digit

hexadecimal-escape-sequence :

  \x hexadecimal-digit

  hexadecimal-escape-sequence hexadecimal-digit
См. также
Константы в C
Символьные типы
Статья • 03.03.2023 • Чтение занимает 2 мин

Целочисленная символьная константа, в начале которой не указана буква L, имеет


тип int . Значением целой символьной константы, содержащей один символ,
является численное значение символа, интерпретированное как целое число.
Например, численное значение символа a равно 97 в десятичном представлении
и 61 в шестнадцатеричном представлении.

Синтаксически "расширенная символьная константа" представляет собой


символьную константу с префиксом в виде буквы L. Расширенная символьная
константа имеет тип wchar_t  — целочисленный тип, определенный в файле
заголовка STDDEF.H. Пример:

char schar = 'x'; /* A character constant */

wchar_t wchar = L'x'; /* A wide-character constant for

the same character */

Расширенные символьные константы имеют ширину 16 бит и указывают члены


расширенной кодировки выполнения. Они обеспечивают представление символов
в алфавитах, слишком больших для представления типом char . Дополнительные
сведения о расширенных символах см. в статье Многобайтовые и расширенные
символы.

См. также
Константы символов в C
Набор символов исполнения
Статья • 03.03.2023 • Чтение занимает 2 мин

Это содержимое часто называется набором символов исполнения. Набор


символов исполнения необязательно совпадает с исходным набором символов,
используемым для написания программ на языке C. Кодировка выполнения
содержит все символы исходной кодировки, а также нуль-символ, символ новой
строки, символ возврата, символ горизонтальной табуляции, символ вертикальной
табуляции, символ возврата каретки и escape-последовательности. В других
реализациях исходная кодировка и кодировка выполнения могут различаться.

См. также
Константы символов в C
Escape-последовательность
Статья • 03.03.2023 • Чтение занимает 2 мин

Сочетания символов, состоящие из обратной косой черты (\), за которой следует


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

Escape-последовательности обычно используются для указания действий,


например возврата каретки или табуляции, на терминалах и принтерах. Они также
используются для обозначения буквенных представлений непечатаемых символов,
а также символов, которые обычно имеют специальное значение, например
двойных кавычек ( " ). В следующей таблице перечислены escape-
последовательности ANSI и представляемые ими значения.

Обратите внимание, что вопросительный знак, перед которым стоит обратная


косая черта (\?), обозначает литерал вопросительного знака в случаях, когда эта
последовательность символов может быть ошибочно интерпретирована как
триграф. Дополнительные сведения см. в разделе Триграфы.

Escape-последовательность

Escape- Представляет
последовательность

\a Звонок (предупреждение)

\b Backspace

\f Перевод страницы

\n Новая строка

\r Возврат каретки

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

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

\' Одиночная кавычка

\" Двойная кавычка


Escape- Представляет
последовательность

\\ Обратная косая черта

\? Литерал вопросительного знака

\ooo Символ ASCII в восьмеричной нотации

\xhh Символ ASCII в шестнадцатеричной нотации

\xhhhh Символ юникода в шестнадцатеричном формате, если эта escape-


последовательность используется в многобайтовой знаковой
константе или строковом литерале юникода.

Например, WCHAR f = L'\x4e00' или WCHAR b[] = L"The Chinese


character for one is \x4e00" .

Блок, относящийся только к системам Microsoft

Если обратная косая черта предшествует символу, которого нет в таблице,


компилятор не обрабатывает неопределенный символ сам как символ. Например,
escape-последовательность \c обрабатывается как символ c .

Завершение блока, относящегося только к системам Майкрософт

Escape-последовательности позволяют отправлять неграфические управляющие


символы на устройство отображения. Например, символ ESC ( \033) часто
используется в качестве первого символа команды управления для терминала или
принтера. Некоторые escape-последовательности используются только на
конкретных устройствах. Например, escape-последовательности вертикальной
табуляции и перевода страницы ( \v и \f) не влияют на вывод данных на экране, но
отвечают за соответствующие операции на принтере.

Кроме того, обратную косую черту ( \ ) можно использовать как символ


продолжения. Если сразу за обратной косой чертой следует символ новой строки
(эквивалентен нажатию клавиши ВОЗВРАТ), компилятор игнорирует эту
последовательность и обрабатывает следующую строку как продолжение
предыдущей. Эта возможность используется в основном для определений
препроцессора, длина которых превышает одну строку. Пример:

#define assert(exp) \

( (exp) ? (void) 0:_assert( #exp, __FILE__, __LINE__ ) )

См. также
Константы символов в C
Спецификации восьмеричных и
шестнадцатеричных символов
Статья • 03.03.2023 • Чтение занимает 2 мин

Последовательность \ooo означает, что можно указать любой набор символов в


кодировке ASCII в качестве трехразрядного восьмеричного кода знака. Числовое
значение восьмеричного целого числа указывает значение требуемого символа
или расширенного символа.

Аналогичным образом последовательность \xhhh позволяет указать любой символ


в кодировке ASCII в качестве шестнадцатеричного кода знака. Например, символ
backspace (ASCII) можно представить как обычную escape-последовательность C (
\b), закодировать как восьмеричный код \010 или шестнадцатеричный код \x008.

В восьмеричной escape-последовательности можно использовать только цифры от


0 до 7. Длина восьмеричной escape-последовательности не может превышать три
цифры, и такие последовательности заканчиваются на первом символе, который
не является цифрой в восьмеричном формате. Хотя нет необходимости
использовать все три цифры, необходимо использовать по крайней мере одну.
Например, для символа backspace по таблице ASCII восьмеричное представление
имеет вид \10, а для буквы A — \101.

Аналогичным образом, необходимо использовать хотя бы одну цифру для


шестнадцатеричной escape-последовательности, но можно опустить вторую и
третью цифры. Следовательно, шестнадцатеричную escape-последовательность
для символа backspace можно указать как \x8, \x08 или \x008.

Значение восьмеричной или шестнадцатеричной escape-последовательности


должно находиться в диапазоне представимых значений для типа unsigned char
для символьной константы и типа wchar_t для расширенной символьной
константы. Дополнительные сведения о расширенных символьных константах см. в
статье Многобайтовая кодировка и расширенные символы.

В отличие от восьмеричных escape-констант количество шестнадцатеричных цифр


в escape-последовательности не ограничено. Шестнадцатеричная escape-
последовательность заканчивается на первом символе, который не является
цифрой в восьмеричном формате. Поскольку шестнадцатеричные цифры
включают буквы от a до f, убедитесь, что escape-последовательность заканчивается
на соответствующей цифре. Чтобы избежать путаницы, можно добавить
определения восьмеричных или шестнадцатеричных символов в определение
макроса.
#define Bell '\x07'

В случае шестнадцатеричных значений можно разделить строку для четкого


отображения правильного значения.

"\xabc" /* one character */

"\xab" "c" /* two characters */

См. также
Константы символов в C
Строковые литералы в C
Статья • 03.03.2023 • Чтение занимает 2 мин

Строковый литерал — это последовательность символов из исходной кодировки,


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

Синтаксис
string-literal :

  " s-char-sequence необ. "

  L" s-char-sequence необ. "

s-char-sequence :

  s-char

  s-char-sequence s-char

s-char :

 любой член исходной кодировки, кроме двойных кавычек ( " ), обратной косой
черты ( \ ) или символа новой строки

  escape-sequence

Комментарии
В этом примере показан простой строковый литерал:

char *amessage = "This is a string literal.";

В строковых литералах допустимо использовать все коды, перечисленные в


таблице escape-последовательностей. Для представления в строковом литерале
двойной кавычки следует использовать escape-последовательность \" . Одинарная
кавычка ( ' ) может быть представлена без escape-последовательности. Если в
строке имеется обратная косая черта ( \ ), после нее должна следовать вторая
такая черта ( \\ ). Если обратная косая черта появляется в конце строки, она всегда
интерпретируется как символ продолжения строки.
См. также
Элементы языка C
Тип для строковых литералов
Статья • 03.03.2023 • Чтение занимает 2 мин

Строковые литералы имеют тип массива char (т. е., char[ ] ). (Строки расширенных


символов имеют тип массива wchar_t (т. е., wchar_t []).) Это означает, что строка
является массивом с элементами типа char . Число элементов в массиве равно
числу символов в строке плюс один дополнительный элемент для завершающего
символа null.

См. также
Строковые литералы в C
Хранение строковых литералов
Статья • 03.03.2023 • Чтение занимает 2 мин

Символы строкового литерала хранятся по порядку в смежных адресах памяти.


Любая escape-последовательность (например, \\ или \") внутри строкового
литерала считается одним символом. В каждый строковый литерал автоматически
добавляется нуль-символ (представленный escape-последовательностью \0),
который обозначает его конец. (Это происходит на этапе преобразования 7.)
Обратите внимание, что компилятор не может сохранить две одинаковые строки
отдельно с разными адресами. Ключ /GF указывает, что компилятор должен
сохранять в исполняемом файле только одну копию одинаковых строк.

Примечания
Блок, относящийся только к системам Microsoft

Строки имеют статическую длительность хранения. Дополнительные сведения о


длительности хранения см. в разделе Классы хранения в C.

Завершение блока, относящегося только к системам Майкрософт

См. также
Строковые литералы в C
Объединение строковых литералов
Статья • 03.03.2023 • Чтение занимает 2 мин

Для создания строкового литерала, занимающего более одной строки, можно


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

"Long strings can be bro\

ken into two or more pieces."

идентичен строке

"Long strings can be broken into two or more pieces."

Вместо использования обратной косой черты с последующим символом новой


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

Чтобы принудительно создать новую строку внутри строкового литерала, следует


ввести escape-последовательность новой строки ( \n) в том месте строки, где
требуется сделать разрыв:

"Enter a number between 1 and 100\nOr press Return"

Поскольку строки могут начинаться в любом столбце исходного кода и длинные


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

printf_s ( "This is the first half of the string, "

"this is the second half ") ;

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

"This is the first half of the string, this is the second half"

Указатель на строку, инициализированный как два разных строковых литерала,


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

char *string = "This is the first half of the string, "

"this is the second half";

printf_s( "%s" , string ) ;

На шестом этапе преобразования последовательности многобайтовых символов,


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

См. также
Строковые литералы в C
Максимальная длина строки
Статья • 03.03.2023 • Чтение занимает 2 мин

Блок, относящийся только к системам Microsoft

В режиме совместимости с ANSI требуется, чтобы компилятор принимал до 509


символов в строковом литерале после объединения. Максимальная допустимая
длина строкового литерала в Microsoft C — приблизительно 2048 байтов. Однако
если строковый литерал состоит из двух частей, заключенных в двойные кавычки,
препроцессор объединяет эти части в одну строку, и для каждой объединенной
строки добавляет дополнительный байт к общему количеству байтов.

Например, предположим, что строка состоит из 40 строк с 50 символами в каждой


строке (2000 символов) и одной строки с 7 символами и что каждая строка
заключена в двойные кавычки. Это значит, что добавляется до 2007 байтов плюс
один байт для завершающего нуль-символа, то есть всего 2008 байтов. В
объединении дополнительный символ добавляется для каждой из первых 40 строк.
В результате мы получаем 2048 байтов. Обратите внимание, что если вместо
двойных кавычек используются продолжения строк (\), препроцессор не добавляет
дополнительный символ для каждой строки.

Хотя отдельные строки в кавычках не могут быть длиннее 2048 байтов, объединив
строки, можно создать строковый литерал, состоящий приблизительно из 65535
байтов.

Завершение блока, относящегося только к системам Майкрософт

См. также
Строковые литералы в C
Знаки препинания и специальные
символы
Статья • 03.03.2023 • Чтение занимает 2 мин

Знаки пунктуации и специальные символы в наборе символов языка C имеют


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

Синтаксис
punctuator : один из ( ) [ ] { } * , : = ; ... #

Эти символы имеют специальное значение в языке C. Их применение описывается


в настоящем руководстве. Знак решетки (#) может использоваться только в
директивах препроцессора.

См. также
Элементы языка C
Структура программы
Статья • 03.03.2023 • Чтение занимает 2 мин

В этом разделе приводится обзор программ на языке C и описание их выполнения.


Также здесь приводятся термины и функции, важные для понимания программ и
компонентов на языке C. В разделе рассматриваются следующие темы.

Файлы с исходным кодом и исходные программы

Функция main и выполнение программ

Анализ аргументов командной строки

Время существования, область, видимость и компоновка

Пространства имен

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


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

См. также
Справочник по языку C
Файлы с исходным кодом и исходные
программы
Статья • 03.03.2023 • Чтение занимает 2 мин

Исходную программу можно разделить на один или несколько "исходных файлов"


или "единиц преобразования". Входные данные для компилятора называются
"единицей преобразования".

Синтаксис
translation-unit :

  external-declaration

  translation-unit external-declaration

external-declaration :
  function-definition

  declaration

В статье Общие сведения об объявлениях описывается синтаксис нетерминала


declaration , а в справочнике по препроцессору поясняется, как происходит

обработка записи преобразования.

7 Примечание

Описание соглашений о синтаксисе ANSI вы найдете во введении к статье


Общие сведения о синтаксисе языка C.

Компонентами блока трансляции являются внешние объявления, которые


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

Исходная программа языка C представляет собой набор директив, директив


pragma, объявлений, определений, блоков операторов и функций. Для того чтобы
все они были допустимыми компонентами программы на языке Microsoft C, они
должны иметь синтаксис, описанный в настоящей книге. При этом они могут
находиться в программе в любом порядке (в пределах описанных здесь правил).
Однако от расположения этих компонентов в программе зависит то, каким
образом в программе будут использоваться переменные и функции.
Дополнительные сведения см. в разделах Время существования, Область,
Видимость и Компоновка.

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


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

См. также
Структура программы
Директивы препроцессору
Статья • 03.03.2023 • Чтение занимает 2 мин

Директива указывает препроцессору C выполнять определенное действие в тексте


программы перед компиляцией. Директивы препроцессора полностью описаны в
справочнике по препроцессору. В следующем примере используется директива
препроцессора #define .

#define MAX 100

Этот оператор указывает компилятору заменять каждое вхождение MAX на 100


перед компиляцией. Ниже перечислены директивы препроцессора компилятора C.

#define #endif #ifdef #line

#elif #error #ifndef #pragma

#else #if #include #undef

См. также
Файлы с исходным кодом и исходные программы
Прагмы C
Статья • 03.03.2023 • Чтение занимает 2 мин

Блок, относящийся только к системам Microsoft

Директива pragma указывает компилятору выполнить определенное действие во


время компиляции. Директивы pragma зависят от компилятора. Например, чтобы
задать оптимизации для выполнения в программе, можно использовать директиву
pragma optimize . Ниже перечислены директивы pragma Microsoft C.

alloc_text

auto_inline

bss_seg

check_stack

code_seg

comment

component

const_seg

data_seg

deprecated

detect_mismatch

fenv_access

float_control

fp_contract

function

hdrstop

include_alias

inline_depth

inline_recursion

intrinsic

make_public

managed

message

omp

once

optimize

pack

pop_macro
push_macro

region, endregion
runtime_checks

section

setlocale

strict_gs_check

system_header

unmanaged

warning

Описание директив pragma для компилятора Microsoft C см. в статье Директивы


Pragma и ключевое слово __Pragma.

Завершение блока, относящегося только к системам Майкрософт

См. также
Файлы с исходным кодом и исходные программы
Объявления и определения в C
Статья • 03.03.2023 • Чтение занимает 2 мин

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


типом и их атрибутами. В статье Общие сведения об объявлениях приводится
синтаксис ANSI для нетерминального элемента declaration . В объявлении также
определяется, где и когда возможен доступ к идентификатору ("компоновка"
идентификатора). Дополнительные сведения о компоновке см. в статье Время
существования, область, видимость и компоновка.

Определение переменной устанавливает те же связи, что и объявление, но также


приводит к выделению памяти для переменной.

Например, функции main , find и count и переменные var и val определяются в


одном исходном файле в следующем порядке:

int main() {}

int var = 0;

double val[MAXVAL];

char find( fileptr ) {}

int count( double f ) {}

Переменные var и val могут использоваться в функциях find и count ; никакие


дополнительные объявления не требуются. Однако в функции main эти имена не
видны (доступ к ним невозможен).

См. также
Файлы с исходным кодом и исходные программы
Объявления и определения функций
Статья • 03.03.2023 • Чтение занимает 2 мин

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


а также тип и число ее формальных параметров. Определение функции содержит
тело функции.

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

См. также
Файлы с исходным кодом и исходные программы
Blocks
Статья • 03.03.2023 • Чтение занимает 2 мин

Последовательность объявлений, определений и операторов, заключенных в


фигурные скобки ({ }), называется блоком. В C есть два типа блоков. Составной
оператор, состоящий из одного или нескольких операторов (см. Составной
оператор), является одним из таких блоков. Второй тип блоков — определение
функции — состоит из составного оператора (тело функции) и связанного
заголовка функции (имя функции, тип возвращаемого значения и формальные
параметры). Блок, находящийся в другом блоке, называется вложенным.

Обратите внимание, что хотя все составные операторы заключены в фигурные


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

См. также
Файлы с исходным кодом и исходные программы
Пример программы
Статья • 03.03.2023 • Чтение занимает 2 мин

Следующая исходная программа на языке C состоит из двух файлов исходного


кода. В ней показаны различные объявления и определения, возможные в
программе на языке C. В последующих разделах этого документа описывается, как
создавать такие объявления, определения и инициализации, и как использовать
ключевые слова C, например static и extern . Функция printf объявлена в файле
заголовка C STDIO.H.

Предполагается, что функции main и max находятся в разных файлах, а выполнение


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

/*****************************************************************

FILE1.C - main function

*****************************************************************/

#define ONE 1

#define TWO 2

#define THREE 3

#include <stdio.h>

int a = 1; // Defining declarations

int b = 2; // of external variables

extern int max( int a, int b ); // Function prototype

int main() // Function definition

{ // for main function

int c; // Definitions for

int d; // two uninitialized

// local variables

extern int u; // Referencing declaration

// of external variable

// defined elsewhere

static int v; // Definition of variable

// with continuous lifetime

int w = ONE, x = TWO, y = THREE;

int z = 0;

z = max( x, y ); // Executable statements

w = max( z, w );

printf_s( "%d %d\n", z, w );

return 0;

/****************************************************************

FILE2.C - definition of max function

****************************************************************/

int max( int a, int b ) // Note formal parameters are

// included in function header

if( a > b )

return( a );

else

return( b );

Файл FILE1.C содержит прототип для функции max . Объявления этого типа иногда
называются "опережающими", поскольку функция объявляется до ее
использования. Определение функции main содержит вызовы функции max .

Строки, начинающиеся с #define , представляют собой директивы препроцессора.


В соответствии с этими директивами препроцессор заменяет идентификаторы ONE ,
TWO и THREE в файле FILE1. C цифрами 1 , 2 и 3 , соответственно. Однако эти
директивы не применяются к файлу FILE2.C, который компилируется отдельно, а
затем компонуется с файлом FILE1.C. Строка, начинающаяся с #include , содержит
указание компилятору на включение файла STDIO.H, содержащего прототип для
функции printf . Директивы препроцессора рассматриваются в справочнике по
препроцессору.

В файле FILE1.C используются определяющие объявления для инициализации


глобальных переменных a и b . Локальные переменные c и d объявлены, но не
инициализированы. Для всех этих переменных выделена память. Статические и
внешние переменные, u и v , автоматически инициализируются значением 0.
Поэтому при объявлении значимые значения содержат только переменные a , b ,
u и v , которые явно или неявно инициализированы. Файл FILE2.C содержит

определение функции max . Это определение соответствует вызовам функции max


из файла FILE1.C.

Время существования и область видимости идентификаторов рассматриваются в


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

См. также
Файлы с исходным кодом и исходные программы
Функция main и выполнение
программ
Статья • 03.03.2023 • Чтение занимает 2 мин

Каждая программа C имеет основную функцию, которая должна быть названа


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

К функции, которая не применяется к другим функциям C, применяются main


несколько ограничений. Функция main :

Не удается объявить как inline .


Не удается объявить как static .
Не удается принять его адрес.
Не удается вызвать из программы.

Сигнатура main функции


Функция main не имеет объявления, так как она встроена в язык. Если это так,
синтаксис main объявления будет выглядеть следующим образом:

int main( void );

int main( int argc, char *argv[ ] );

int main( int argc, char *argv[ ], char *envp[ ] );

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


определении main функции можно использовать любую из этих сигнатур.
Компилятор Майкрософт также позволяет main иметь тип возвращаемого void
значения, если значение не возвращается. Параметры argv и envp параметры,
которые wmain также можно определить как тип char** . Дополнительные сведения
о аргументах см. в описании аргумента.

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

Можно объявить любую функцию, включая main параметры. Термин "параметр"


или "формальный параметр" относится к идентификатору, получающему значение,
передаваемое функции. Сведения о передаче аргументов в качестве параметров
вы найдете в статье Параметры. Когда одна функция вызывает другую, вызываемая
функция получает значения своих параметров от вызывающей функции. Эти
значения называются аргументами. Можно объявить формальные параметры
main таким образом, чтобы он смог получать аргументы из командной строки,

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

Если вы хотите передать сведения в функцию main , параметры обычно именуются


argc и argv , хотя компилятор C не требует этих имен. Традиционно, если третий

параметр передается main в , этот параметр называется envp . Типы для argc , argv и
envp определяются языком C. Вы также можете объявить argv как char** argv и

envp как char** envp . В приведенных ниже в данном разделе примерах

описывается использование этих трех параметров для доступа к аргументам


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

Если код соответствует модели программирования Юникода, вы можете


использовать версию расширенных символов main wmain Корпорации Майкрософт
в качестве точки входа в вашей программе. Дополнительные сведения об этой
версии расширенных символов main см. в разделе "Использование wmain".

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

См. также раздел


main аргументы функции и командной строки (C++)

Анализ аргументов командной строки C


Использование wmain
Статья • 03.03.2023 • Чтение занимает 2 мин

Блок, относящийся только к системам Microsoft

В модели программирования Юникода можно определить версию main функции с


широкими символами. Используйте wmain вместо main того, чтобы писать
переносимый код, который соответствует модели программирования Юникода.

Например main , к функции, которая не применяется к другим функциям C,


применяются wmain несколько ограничений. Функция wmain :

Не удается объявить как inline .


Не удается объявить как static .
Не удается принять его адрес.
Не удается вызвать из программы.

Сигнатура wmain функции


Функция wmain не имеет объявления, так как она встроена в язык. В противном
случае синтаксис wmain объявления будет выглядеть следующим образом:

int wmain( void );

int wmain( int argc, wchar_t *argv[ ] );

int wmain( int argc, wchar_t *argv[ ], wchar_t *envp[ ] );

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


определении wmain функции можно использовать любую из этих сигнатур. Затем
можно передать в качестве аргументов "широкие" символы и указатель среды
кодировки Юникод (необязательно) в программу. Компилятор Майкрософт также
позволяет wmain иметь тип возвращаемого значения, если void значение не
возвращается. Параметры argv и envp параметры, которые wmain также можно
определить как тип wchar_t** . Дополнительные сведения о аргументах см. в
описании аргумента.

Среда envp
Если программа использует main функцию, среда многобайтовых символов
создается библиотекой времени выполнения при запуске программы. Копия среды
для Юникода создается только при необходимости (например, для вызова функции
_wgetenv или _wputenv ). При первом вызове _wputenv или _wgetenv , если среда
многобайтовой кодировки (MBCS) уже существует, создается соответствующая
среда широкосимвольной строки, на которую затем указывает глобальная
переменная _wenviron , представляющая собой широкосимвольную версию
глобальной переменной _environ . В этот момент две копии среды (для
многобайтовой кодировки и Юникода) существуют одновременно и
поддерживаются операционной системой в течение всего срока жизни
программы.

Аналогичным образом, если программа использует wmain функцию, при запуске


программы создается среда расширенных символов и указывает на глобальную
_wenviron переменную. Среда многобайтовой кодировки MBCS (ASCII) создается
при первом вызове функции _putenv или getenv , и на нее указывает глобальная
переменная _environ .

Дополнительные сведения о среде MBCS см. в разделе "Интернационализация".

Завершение блока, относящегося только к системам Майкрософт

См. также
Функция main и выполнение программ
Описание аргумента
Статья • 03.03.2023 • Чтение занимает 2 мин

Параметр argc в и main wmain функциях — это целое число, указывающее, сколько
аргументов передаются программе из командной строки. Поскольку имя
программы считается аргументом, значение argc по крайней мере равно единице.

Примечания
Параметр argv является массивом указателей на строки, завершающиеся
значением NULL, который представляет аргументы программы. Каждый элемент
массива указывает на строковое представление аргумента, переданного main (или
wmain ). (Сведения о массивах см. в объявлениях массивов.) Параметр argv можно
объявить как массив указателей на тип char ( char *argv[] ) или как указатель на
указатели на тип char ( char **argv ). Для wmain параметра argv можно объявить как
массив указателей на тип wchar_t ( wchar_t *argv[] ) или в качестве указателя на тип
wchar_t ( wchar_t **argv ).

По соглашению — это команда, argv[0] с помощью которой вызывается


программа. Однако можно создать процесс, CreateProcess и если вы используете
оба первых и второго аргументов ( lpApplicationName и lpCommandLine ), argv[0]
может не быть исполняемым именем; используется GetModuleFileName для
получения имени исполняемого файла.

Последний указатель ( argv[argc] ) — NULL . (См getenv . в справочнике по


библиотеке времени выполнения альтернативный метод получения сведений о
переменной среды.)

Блок, относящийся только к системам Microsoft

Параметр envp является указателем на массив строк, завершающихся значением


NULL, которые представляют значения, заданные в переменных среды
пользователя. Параметр envp можно объявить как массив указателей на тип char
( char *envp[] ) или как указатель на указатели на тип char ( char **envp ). wmain В
функции envp параметр можно объявить как массив указателей wchar_t на
( wchar_t *envp[] ) или как указатель на указатели wchar_t на ( wchar_t **envp ). Конец
массива обозначается указателем NULL* . Блок среды, переданный main или wmain
является "замороженным" копией текущей среды. Если позже вы измените среду с
помощью вызова _putenv или _wputenv , текущая среда
(возвращенная/ _wgetenv getenv и _environ _wenviron переменная) изменится, но
блок, на который указывает, envp не изменится. Параметр envp совместим с
ANSI/ISO C89 в C, но является расширением Майкрософт в C++.

Завершение блока, относящегося только к системам Майкрософт

См. также
Функция main и выполнение программ
Расширение аргументов-
заполнителей
Статья • 03.03.2023 • Чтение занимает 2 мин

Раскрытие аргументов с подстановочными знаками используется только в системах


Майкрософт.

При выполнении программы на языке C можно использовать любой из двух


подстановочных знаков, знак вопроса ( ? ) или звездочку ( * ), для задания
аргументов имени файла и пути в командной строке.

По умолчанию подстановочные знаки в аргументах командной строки не


раскрываются. Вы можете заменить обычную подпрограмму загрузки вектора
аргументов argv на версию, которая раскрывает подстановочные знаки, используя
компоновку с файлом setargv.obj или wsetargv.obj . Если программа использует
функцию main , свяжите ее с файлом setargv.obj . Если программа использует
функцию wmain , свяжите ее с файлом wsetargv.obj . Они оба реализуют
эквивалентное поведение.

Чтобы создать связь с setargv.obj или wsetargv.obj , используйте параметр /link .


Пример:

cl example.c /link setargv.obj

Подстановочные знаки разворачиваются так же, как команды операционной


системы.

См. также
Параметры ссылок

Функция main и выполнение программ


Анализ аргументов командной строки
C
Статья • 03.03.2023 • Чтение занимает 2 мин

Блок, относящийся только к системам Microsoft

В коде запуска Microsoft C используются следующие правила при обработке


аргументов, вводимых в командной строке операционной системы.

Аргументы разделяются символами пробела. Это могут быть пробелы или


символы табуляции.

Первый аргумент ( argv[0] ) обрабатывается особым образом. Он


представляет имя программы. Это должен быть допустимый путь, поэтому
разрешены части, заключенные в двойные кавычки ( " ). Эти знаки двойных
кавычек не включаются в выходные данные argv[0] . Заключение частей в
двойные кавычки не позволяет интерпретировать пробел или символ
табуляции как конец аргумента. Последующие правила в этом списке не
применяются.

Строка, заключенная в двойные кавычки, обрабатывается как один аргумент


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

Символ двойной кавычки после обратной косой черты ( \" )


интерпретируется как литеральный символ двойной кавычки ( " ).

Символы обратной косой черты считаются литералами, если сразу за ними не


стоит двойная кавычка.

Если двойная кавычка стоит после четного числа символов обратной косой
черты, в массив argv помещается по одному символу обратной косой черты (
\ ) для каждой пары символов обратной косой черты ( \\ ), а сама двойная

кавычка ( " ) интерпретируется как разделитель строк.


Если двойная кавычка стоит после нечетного числа символов обратной косой
черты, в массив argv помещается по одному символу обратной косой черты (
\ ) для каждой пары символов обратной косой черты ( \\ ). Сам знак

двойной кавычки в этом случае интерпретируется как escape-


последовательность в сочетании с последним символом обратной косой
черты, то есть в argv помещается литерал двойной кавычки ( " ).

В следующем списке, иллюстрирующем указанные выше правила, показаны


результаты, передаваемые в массив argv для нескольких примеров аргументов
командной строки. Выходные данные, представленный во втором, третьем и
четвертом столбцах, получены из программы ARGS.C, приведенной после списка.

Входные данные в командной строке argv[1] argv[2] argv[3]

"a b c" d e a b c d e

"ab\"c" "\\" d ab"c \ d

a\\\b d"e f"g h a\\\b de fg h

a\\\"b c d a\"b c d

a\\\\"b c" d e a\\b c d e

a"b"" c d ab" c d

Пример

Код
C

// ARGS.C illustrates the following variables used for accessing

// command-line arguments and environment variables:

// argc argv envp

//

#include <stdio.h>

int main( int argc, // Number of strings in array argv

char *argv[], // Array of command-line argument strings

char **envp ) // Array of environment variable strings

int count;

// Display each command-line argument.

printf_s( "\nCommand-line arguments:\n" );

for( count = 0; count < argc; count++ )

printf_s( " argv[%d] %s\n", count, argv[count] );

// Display each environment variable.

printf_s( "\nEnvironment variables:\n" );

while( *envp != NULL )

printf_s( " %s\n", *(envp++) );

return;

Ниже приведен один пример данных, выводимых этой программой:

Command-line arguments:

argv[0] C:\MSC\ARGS.EXE

Environment variables:

COMSPEC=C:\NT\SYSTEM32\CMD.EXE

PATH=c:\nt;c:\binb;c:\binr;c:\nt\system32;c:\word;c:\help;c:\msc;c:\;

PROMPT=[$p]

TEMP=c:\tmp

TMP=c:\tmp

EDITORS=c:\binr

WINDIR=c:\nt

Завершение блока, относящегося только к системам Майкрософт

См. также
Функция main и выполнение программ
Настройка обработки командной
строки C
Статья • 03.03.2023 • Чтение занимает 2 мин

Если программа не принимает аргументы командной строки, можно сохранить


небольшой объем пространства, подавив подпрограмму обработки командной
строки. Для этого включите файл noarg.obj (для main и wmain ) в параметры
компилятора /link или командную строку LINK .

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


доступа к аргументу envp , можно подавить внутреннюю подпрограмму обработки
среды. Для этого включите файл noenv.obj (для main и wmain ) в параметры
компилятора /link или командную строку LINK .

Дополнительные сведения о параметрах компоновщика для запуска среды


выполнения см. в статье Параметры ссылок.

Программа может вызывать семейство подпрограмм spawn или exec в библиотеке


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

См. также раздел


Функция main и выполнение программ

Параметры ссылок
Время жизни, область, видимость и
компоновка
Статья • 03.03.2023 • Чтение занимает 2 мин

Чтобы понимать, как работает программа на языке C, необходимо знать правила,


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

Время существования

Scope and visibility (Область и видимость)

Компоновка

См. также
Структура программы
Время существования
Статья • 03.03.2023 • Чтение занимает 2 мин

"Время жизни" представляет собой период во время выполнения программы, в


течение которого существует переменная или функция. Время жизни
идентификатора определяется длительностью его хранения.

Идентификатор, объявленный описателем класса хранения static , имеет


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

Идентификатор, объявленный без описателя класса хранения типа static , имеет


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

Следующие правила определяют, имеет ли идентификатор глобальное


(статическое) или локальное (автоматическое) время жизни:

Все функции имеют статическое время жизни. Поэтому они существуют в


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

Если локальная переменная имеет инициализатор, она инициализируется при


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

Хотя идентификатор с глобальным временем существования существует в течение


всего времени выполнения исходной программы (например, внешне объявленная
переменная или локальная переменная, объявленная с ключевым словом static ),
он может быть доступен не во всех частях программы. Сведения о видимости
приводятся в статье Область и видимость, а нетерминальный описатель storage-
class-specifier рассматривается в статье Классы хранения в C.

Память может выделяться по мере необходимости (динамически), если создается с


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

См. также
Время жизни, область, видимость и компоновка
Область и видимость
Статья • 03.03.2023 • Чтение занимает 2 мин

Видимость идентификатора определяет части программы, на которые можно


ссылаться, — ее область действия. Идентификатор является видимым (т. е. его
можно использовать) только в частях программы, охватываемых ее областью,
которая может быть ограничена (для повышения ограниченности) файлом,
функцией, блоком или прототипом функции, в котором она отображается. Область
идентификатора — это часть программы, в которой можно использовать имя.
Иногда это называется лексической областью. Существует четыре типа областей:
функция, файл, блок и прототип функции.

Все идентификаторы за исключением меток имеют область, определенную


уровнем объявления. Видимость идентификаторов внутри программы
регулируется следующими правилами для каждого типа области.

Область видимости файла

Декларатор или описатель типа идентификатора с областью видимости файла


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

Область функции

Метка — это единственный тип идентификатора, который имеет область видимости


функции. Метка объявляется неявно путем использования в операторе. Имена
меток должны быть уникальными внутри функции. (Дополнительные сведения о
метках и именах меток см. в статье Оператор goto и помеченные операторы.)

Область действия блока

Декларатор или описатель типа идентификатора с областью видимости блока


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

Декларатор или описатель типа идентификатора с областью видимости прототипа


функции отображается в списке объявлений параметров в прототипе функции (не
является частью объявления функции). Эта область заканчивается в конце
декларатора функции.

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


переменных в других исходных файлах, см. в статье Классы хранения в C. Однако
переменные и функции, объявленные на внешнем уровне с описателем класса
хранения static , доступны только в том исходном файле, в котором они
определены. Все остальные функции видимы глобально.

См. также
Время жизни, область, видимость и компоновка
Сводка времени существования и
видимости
Статья • 03.03.2023 • Чтение занимает 2 мин

В следующей таблице приведена сводка характеристик времени существования и


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

Сводка времени существования и видимости

Атрибуты:
Элемент Спецификатор
Результат: ;
Видимость

Уровень класса Время


хранения существования

Область Определение static Global Оставшаяся часть


видимости переменной файла исходного
файла кода, в котором
встречается

Объявление extern Global Оставшаяся часть


переменной файла исходного
кода, в котором
встречается

Прототип или static Global Один файл


определение исходного кода
функции

Прототип extern Global Оставшаяся часть


функции файла исходного
кода

Область Объявление extern Global Block


действия переменной
блока

Определение static Global Block


переменной
Атрибуты:
Элемент Спецификатор
Результат: ;
Видимость

Уровень класса Время


хранения существования

Определение auto или Локальная Block


переменной register

Пример

Описание
В следующем примере показаны блоки, вложение и видимость переменных:

Код
C

// Lifetime_and_Visibility.c

#include <stdio.h>

int i = 1; // i defined at external level

int main() // main function defined at external level

printf_s( "%d\n", i ); // Prints 1 (value of external level i)

{ // Begin first nested block

int i = 2, j = 3; // i and j defined at internal level

printf_s( "%d %d\n", i, j ); // Prints 2, 3

{ // Begin second nested block


int i = 0; // i is redefined

printf_s( "%d %d\n", i, j ); // Prints 0, 3

} // End of second nested block

printf_s( "%d\n", i ); // Prints 2 (outer definition

// restored)

} // End of first nested block


printf_s( "%d\n", i ); // Prints 1 (external level

// definition restored)

return 0;

Комментарии
В этом примере имеется 4 уровня видимости: внешний уровень и 3 уровня блоков.
Значения выводятся на экран, как указано в комментариях после каждого
оператора.

См. также
Время жизни, область, видимость и компоновка
Компоновка
Статья • 03.03.2023 • Чтение занимает 2 мин

Имена идентификаторов могут относиться к разным идентификаторам в разных


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

См. также
Использование ключевого слова extern для задания компоновки
Внутренняя компоновка
Статья • 03.03.2023 • Чтение занимает 2 мин

Если объявление идентификатора области доступности файла для объекта или


функции содержит описатель класса хранения static , идентификатор имеет
внутреннюю компоновку. В противном случае идентификатор имеет внешнюю
компоновку. Использование нетерминального параметра storage-class-specifier
приводится в статье Классы хранения.

В пределах одной записи преобразования каждый экземпляр идентификатора с


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

См. также
Использование ключевого слова extern для задания компоновки
Внешняя компоновка
Статья • 03.03.2023 • Чтение занимает 2 мин

Если в первом объявлении на уровне области видимости файла для


идентификатора не используется описатель класса хранения static , объект имеет
внешнюю компоновку.

Если в объявлении идентификатора для функции отсутствует описатель storage-


class-specifier, его компоновка определяется так же, как если бы он был объявлен с
описателем storage-class-specifier extern . Если объявление идентификатора объекта
содержит область видимости файла и не содержит описатель storage-class-specifier,
его компоновка является внешней.

Имя идентификатора с внешней компоновкой обозначает ту же функцию или


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

См. также
Использование ключевого слова extern для задания компоновки
Без компоновки
Статья • 03.03.2023 • Чтение занимает 2 мин

Если объявление идентификатора в блоке не содержит спецификатор класса


хранения extern , этот идентификатор не имеет компоновки и уникален для
функции.

Следующие идентификаторы не имеют компоновки:

идентификатор, объявленный в качестве нечто отличного от объекта или


функции;

идентификатор, объявленный в качестве параметра функции;

идентификатор области видимости блока для объекта, объявленного без


спецификатора класса хранения extern .

Если идентификатор не имеет компоновки, повторное объявление того же имени


(в деклараторе или спецификаторе типа) на том же уровне области видимости
приводит к появлению ошибки переопределения символов.

См. также
Использование ключевого слова extern для задания компоновки
Пространства имен
Статья • 03.03.2023 • Чтение занимает 2 мин

Компилятор настраивает пространства имен, чтобы различать идентификаторы,


используемые для различных типов элементов. Имена в каждом пространстве
имен должны быть уникальными во избежание конфликтов, но одинаковые имена
могут использоваться в нескольких пространствах имен. Это означает, что можно
использовать один и тот же идентификатор для двух или более различных
элементов при условии, что элементы расположены в разных пространствах имен.
Компилятор может разрешить ссылки на основе синтаксического контекста
идентификатора в программе.

7 Примечание

Не следует путать ограниченную нотацию C пространства имен с


возможностью пространства имен C++. Дополнительные сведения см. в
разделе Пространства имен (C++) в справочнике по языку C++.

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

Метки операторов

Именованные метки операторов являются частью операторов. За определениями


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

Теги структур, объединений и перечислений

Эти теги являются частью описателей типа структуры, объединения и перечисления


и, если они имеются, всегда сразу следуют за зарезервированными словами
struct , union или enum . Имена тегов должны отличаться от всех других тегов
структур, перечислений или объединений с такой же видимостью.

Члены структур или объединений

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


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

Обычные идентификаторы

Все другие имена находятся в пространстве имен, которое содержит переменные,


функции (включая формальные параметры и локальные переменные) и константы
перечисления. Имена идентификаторов имеют вложенную видимость, поэтому их
можно переопределять в блоках.

Имена Typedef

Имена typedef невозможно использовать в качестве идентификаторов в одной и


той же области.

Например, поскольку теги структуры, члены структуры и имена переменных


находятся в трех разных пространствах имен, три элемента с именем student в
этом примере не конфликтуют. Контекст каждого элемента позволяет правильно
интерпретировать каждое вхождение student в программе. (Дополнительные
сведения о структурах см. в разделе Объявления структур.)

struct student {

char student[20];

int class;

int id;

} student;

Если student отображается после ключевого слова struct , компилятор распознает


его как тег структуры. Если student отображается после оператора выбора члена (-
> или .), имя ссылается на элемент структуры. В других контекстах student
ссылается на переменную структуры. Однако не рекомендуется перегружать
пространство имен тега, поскольку это искажает смысл.

См. также
Структура программы
Выравнивание (C11)
Статья • 03.03.2023 • Чтение занимает 3 мин

Одной из низкоуровневых особенностей C является возможность указать точное


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

Процессоры считывают и записывают память более эффективно, если данные


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

По умолчанию компилятор выравнивает данные по размеру: char по 1-байтовой


границе, short по 2-байтовой границе, int , long и float по 4-байтовой границе,
double по 8-байтовой границе и т. д.

Кроме того, выравнивание часто используемых данных в соответствии с размером


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

Как правило, вам не нужно беспокоиться о выравнивании. Компилятор обычно


выравнивает данные по естественным границам с учетом характеристик целевого
процессора и размера данных. Данные выравниваются по 4-байтовым границам
на 32-разрядных процессорах и по 8-байтовым границам на 64-разрядных
процессорах. Но в некоторых случаях можно повысить производительность или
обеспечить экономное расходование памяти, настроив настраиваемое
выравнивание для структур данных.

Используйте ключевое слово C11 _Alignof , чтобы получить предпочтительное


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

Удобные макросы alignof и alignas , определенные в <stdalign.h> , напрямую


сопоставляются с _Alignof и _Alignas соответственно. Эти макросы соответствуют
ключевым словам, используемым в C++. Поэтому использование макросов вместо
ключевых слов C может оказаться полезным для переноса кода, если в вашем коде
используется два языка.
alignas и _Alignas (C11)
Чтобы указать пользовательское выравнивание для переменной или
определяемого пользователем типа, используйте alignas или _Alignas . Их можно
применить к структуре, объединению, перечислению или переменной.

Синтаксис alignas
C

alignas(type)

alignas(constant-expression)

_Alignas(type)

_Alignas(constant-expression)

Remarks
_Alignas нельзя использовать в объявлении typedef, битового поля, функции,
параметра функции или объекта, объявленного с описателем register .

Укажите выравнивание, которое является степенью двух, например 1, 2, 4, 8, 16


и т. д. Не используйте значение меньше размера типа.

Типы struct и union имеют выравнивание, равное значению самого крупного


выравнивания любого элемента. В struct добавляются байты заполнения, чтобы
обеспечить соблюдение требований к выравниванию отдельных элементов.

Если в объявлении есть несколько описателей alignas (например, struct с


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

Пример alignas
В этом примере используется удобный макрос alignof , так как его можно
перенести в C++. Если вы используете _Alignof , поведение будет аналогичным.

// Compile with /std:c11

#include <stdio.h>

#include <stdalign.h>

typedef struct

int value; // aligns on a 4-byte boundary. There will be 28 bytes of


padding between value and alignas
alignas(32) char alignedMemory[32]; // assuming a 32 byte friendly cache
alignment

} cacheFriendly; // this struct will be 32-byte aligned because


alignedMemory is 32-byte aligned and is the largest alignment specified in
the struct

int main()

printf("sizeof(cacheFriendly): %d\n", sizeof(cacheFriendly)); // 4 bytes


for int value + 32 bytes for alignedMemory[] + padding to ensure alignment

printf("alignof(cacheFriendly): %d\n", alignof(cacheFriendly)); // 32


because alignedMemory[] is aligned on a 32-byte boundary

/* output

sizeof(cacheFriendly): 64
alignof(cacheFriendly): 32

*/

alignof и _Alignof (C11)


_Alignof и его псевдоним alignof возвращает выравнивание в байтах для

указанного типа. Возвращает значение типа size_t .

Синтаксис alignof
C++

alignof(type)

_Alignof(type)

Пример alignof
В этом примере используется удобный макрос alignof , так как его можно
перенести в C++. Если вы используете _Alignof , поведение будет аналогичным.

// Compile with /std:c11

#include <stdalign.h>

#include <stdio.h>

int main()

size_t alignment = alignof(short);

printf("alignof(short) = %d\n", alignment); // 2

printf("alignof(int) = %d\n", alignof(int)); // 4

printf("alignof(long) = %d\n", alignof(long)); // 4

printf("alignof(float) = %d\n", alignof(float)); // 4

printf("alignof(double) = %d\n", alignof(double)); // 8

typedef struct

int a;

double b;

} test;

printf("alignof(test) = %d\n", alignof(test)); // 8 because that is the


alignment of the largest element in the structure

/* output

alignof(short) = 2

alignof(int) = 4

alignof(long) = 4

alignof(float) = 4

alignof(double) = 8

alignof(test) = 8

*/

Требования
Выполните сборку с помощью команды /std:c11.

Пакет Windows SDK 10.0.20348.0 (версия 2104) или выше. Скачать последнюю


версию Windows SDK можно здесь. Инструкции по установке и использованию
пакета SDK для разработки C11 и C17 см. в статье Включение поддержки C11 и C17
в Visual Studio.

См. также раздел


/std (определение стандартной версии языка)

C++ alignof и alignas

Обработка выравнивания данных компилятором


Объявления и типы
Статья • 03.03.2023 • Чтение занимает 2 мин

В этом разделе описывается объявление и инициализация переменных, функций и


типов. Язык C содержит стандартный набор базовых типов данных. Имеется также
возможность добавлять собственные типы данных, называемые производными
типами, путем объявления новых типов на основе уже определенных.
Рассматриваются следующие темы:

Общие сведения об объявлениях

Классы хранения в C

Описатели типов С

Квалификаторы типов

Деклараторы и объявления переменных

Интерпретация сложных деклараторов

Инициализация

Хранение базовых типов

Неполные типы

Объявления Typedef

Расширенные атрибуты классов хранения в C

См. также
Справочник по языку C
Общие сведения об объявлениях
Статья • 03.03.2023 • Чтение занимает 3 мин

Объявление задает интерпретацию и атрибуты набора идентификаторов.


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

Синтаксис
declaration :

  declaration-specifiers attribute-seq opt init-declarator-list opt ;

/* attribute-seq необ. относится только к продуктам Майкрософт */

declaration-specifiers :

  storage-class-specifier declaration-specifiers необ.

  type-specifier declaration-specifiers необ.

  type-qualifier declaration-specifiers необ.

init-declarator-list :
  init-declarator

  init-declarator-list , init-declarator

init-declarator :

  declarator

  declarator = initializer

7 Примечание

Этот синтаксис для объявления ( declaration ) не будет повторяться в


следующих разделах. Синтаксис в следующих разделах обычно начинается с
нетерминального декларатора ( declarator ).

Объявления в init-declarator-list содержат именуемые идентификаторы, а


сокращение init означает инициализатор. init-declarator-list  — это
последовательность операторов объявления, разделенных запятыми. Каждый из
них может содержать дополнительную информацию о типе и (или) инициализатор.
В деклараторе ( declarator ) содержатся объявляемые идентификаторы (при
наличии). Нетерминальные описатели declaration-specifiers состоят из
последовательности спецификаторов типа и класса хранения, которые указывают
компоновку, время хранения и по меньшей мере часть типа сущностей,
обозначаемых операторами объявления. Объявления состоят из произвольного
сочетания описателей класса хранения, описателей типов, квалификаторов типов,
операторов объявлений и инициализаторов.

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


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

В общей форме объявления переменных в параметре type-specifier задается тип


данных для переменной. type-specifier может быть составным, например иметь
модификаторы const и volatile . В параметре declarator содержится имя
переменной, которое может быть изменено для объявления массива или типа
указателя. Например, примененная к объекту директива

int const *fp;

В приведенном выше примере переменная с именем fp объявляется как указатель


на неизменяемое ( const ) значение int . В объявлении можно определить
несколько переменных; для этого используется несколько деклараторов, которые
разделяются запятыми.

Объявление должно содержать по меньшей мере один декларатор, или его


спецификатор типа должен объявлять тег структуры, тег объединения или члены
перечисления. Деклараторы предоставляют всю остальную информацию об
идентификаторе. Оператор объявления — это идентификатор, который можно
изменить с помощью квадратных скобок ( [ ] ), звездочек ( * ) или круглых скобок (
( ) ) для объявления типов массива, указателя или функции, соответственно. При

объявлении простых переменных (например символьных, целочисленных или с


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

Все определения неявным образом являются объявлениями, но не все объявления


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

"определяющими" объявлениями. Если сослаться на внешнюю переменную


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

В объявлениях переменных должен быть указан класс хранения или тип (или и то, и
другое). За исключением ключевого слова __declspec в объявлениях допускается
только один спецификатор класса хранения. Некоторые спецификаторы класса
хранения не допускаются в некоторых контекстах. Класс хранения __declspec
может использоваться с другими описателями класса хранения; его можно
указывать более одного раза. Спецификатор класса хранения для объявления
влияет только на то, каким образом объявленный элемент хранится и
инициализируется, и какие части программы могут ссылаться на элемент.

В C определены терминальные описатели storage-class-specifier auto , extern ,


register , static и typedef . Кроме того, Microsoft C включает терминальный
описатель storage-class-specifier __declspec . Все терминалы storage-class-
specifier , за исключением typedef и __declspec , рассматриваются в статье Классы

хранения в C. Сведения о typedef см. в статье Декларации typedef. Сведения о


__declspec см. в статье Расширенные атрибуты классов хранения в C.

Местоположение объявления в исходном коде программы, а также наличие и


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

Спецификаторы типа предоставляют некоторые сведения о типах данных в


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

Есть два типа терминальных описателей type-qualifier : const и volatile . Эти


квалификаторы задают дополнительные свойства типов, которые актуальны только
при доступе к объектам этого типа через l-значения. Дополнительные сведения о
ключевых словах const и volatile см. в статье Квалификаторы типов. Определение
l-значений см. в разделе Выражения L-Value и R-Value.

См. также
Краткие сведения о синтаксисе языка C

Объявления и типы

Общие сведения об объявлениях


Классы хранения в C
Статья • 03.03.2023 • Чтение занимает 2 мин

"Класс хранения" переменной определяет время существования элемента:


глобальное или локальное. В C эти два времени существования называются
"статическое" и "автоматическое". Элемент с глобальным временем существования
существует и имеет значение на протяжении всего выполнения программы. Все
функции имеют глобальное время существования.

Автоматическим переменным или переменным с локальным временем


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

C предоставляет следующие описатели класса хранения:

Синтаксис
storage-class-specifier :

  auto

  register

  static

  extern

  typedef

  __declspec ( extended-decl-modifier-seq ) /* Только для систем Майкрософт */

__declspec За исключением , в объявлении declaration-specifier можно

использовать только один storage-class-specifier из них. Если нет спецификации


класса хранения, объявления в блоке создают автоматические объекты.

Элементы, объявленные с описателем auto или register , имеют локальное время


существования. Элементы, объявленные с описателем static или extern , имеют
глобальное время существования.

Поскольку typedef и __declspec семантически отличаются от остальных четырех


storage-class-specifier терминалов, они обсуждаются отдельно. Подробные

сведения о typedef см. в статье Объявления typedef. Подробные сведения о


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

Точное значение каждого определителя класса хранения зависит от двух факторов:

уровня отображения объявления (внешний или внутренний)

типа объявляемого элемента (переменная или функция)

Описатели класса хранения для объявлений External-Level и описатели класса


хранения для объявлений Internal-Level описывают storage-class-specifier
терминалы в каждом типе объявления и объясняют поведение по умолчанию,
когда storage-class-specifier объект опущен из переменной. Статья Описатели
классов хранения с объявлениями функций поможет разобраться с описателями
storage-class, используемыми с функциями.

См. также
Объявления и типы
Спецификаторы классов хранения
для объявлений внешнего уровня
Статья • 03.03.2023 • Чтение занимает 3 мин

Внешние переменные — это переменные в области видимости файла. Они


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

Объявления переменных на внешнем уровне являются определениями


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

Внешнее объявление переменной, которое также инициализирует переменную


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

Переменная, объявляемая с использованием описателя класса хранения


static . Вы можете явно инициализировать переменную static с

использованием константного выражения, как описано в статье


Инициализация. Если опустить инициализатор, переменная инициализируется
значением 0 по умолчанию. Например, следующие два оператора считаются
определениями переменной k .

static int k = 16;

static int k;

Переменная, которая явно инициализируется на внешнем уровне. Например,


int j = 3; — это определение переменной j .

В объявлениях переменных на внешнем уровне (т. е. вне любых функций) можно


либо использовать описатель класса хранения static или extern , либо полностью
опустить его. Вы не можете использовать терминалы auto и register storage-class-
specifier на внешнем уровне.
После определения переменной на внешнем уровне она становится видимой во
всей оставшейся части записи преобразования. Переменная невидима до ее
объявления в том же исходном файле. Кроме того, она будет невидима в других
исходных файлах программы, пока объявление не сделает ее видимой, как описано
ниже.

Ниже приводятся правила в отношении static .

Переменные, объявленные вне всех блоков без ключевого слова static ,


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

Указать переменную на внешнем уровне можно только один раз в рамках


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

Описатель класса хранения static также может применяться к функциям.


Если функция объявлена как static , ее имя невидимо вне файла, в котором
она объявлена.

Ниже приведены правила использования extern .

Описатель класса хранения extern объявляет ссылку на переменную,


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

Пример
В примере ниже показаны внешние объявления.

/******************************************************************

SOURCE FILE ONE

*******************************************************************/

#include <stdio.h>

extern int i; // Reference to i, defined below

void next( void ); // Function prototype

int main()

i++;

printf_s( "%d\n", i ); // i equals 4

next();

int i = 3; // Definition of i

void next( void )

i++;

printf_s( "%d\n", i ); // i equals 5

other();

/******************************************************************

SOURCE FILE TWO

*******************************************************************/

#include <stdio.h>

extern int i; // Reference to i in

// first source file

void other( void )

i++;

printf_s( "%d\n", i ); // i equals 6

Два исходных файла в этом примере содержат три внешних объявления i . Только
одно объявление является определяющим. Это объявление

int i = 3;

определяет глобальную переменную i и инициализирует ее начальным


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

extern int x;

и что определяющая ссылка

int x = 0;

отображается в другой записи преобразования программы.

Все три функции ( main , next и other ) выполняют одну и ту же задачу: они
расширяют переменную i и отображают ее значение. Отображаются значения 4,
5 и 6.

Если бы переменная i не была инициализирована, ей автоматически было бы


присвоено значение 0. В этом случае были бы отображены значения 1, 2 и 3.
Дополнительные сведения об инициализации переменных см. в разделе
Инициализация.

См. также
Классы хранения в C
Спецификаторы классов хранения
для объявлений внутреннего уровня
Статья • 03.03.2023 • Чтение занимает 2 мин

Любой из четырех терминальных описателей storage-class-specifier можно


использовать для объявления переменных на внутреннем уровне. Если в таком
объявлении не указан storage-class-specifier , по умолчанию используется класс
хранения auto . Поэтому ключевое слово auto редко встречается в программах на
языке C.

См. также
Классы хранения в C
Описатель класса хранения auto
Статья • 03.03.2023 • Чтение занимает 2 мин

Описатель класса хранения auto объявляет автоматическую переменную, то есть


переменную с локальным временем существования. Переменная auto доступна
только в том блоке, в котором она объявлена. Объявления переменных auto могут
содержать инициализаторы, как описано в статье Инициализация. Поскольку
переменные с классом хранения auto не инициализируются автоматически,
необходимо явно инициализировать их при объявлении или присвоить им
начальные значения в блоке. Для неинициализированных переменных auto
значения не определены. (Локальная переменная с классом хранения auto или
register инициализируется заново каждый раз, когда она попадает в область
доступности, если для нее указан инициализатор.)

Внутреннюю переменную static (статическую переменную с локальной областью


доступности или областью доступности в блоке) можно инициализировать адресом
любого внешнего элемента или элемента static , но не адресом элемента auto ,
поскольку адрес элемента auto не является константой.

См. также
Ключевое слово auto
register описатель класса хранения
Статья • 03.03.2023 • Чтение занимает 2 мин

Блок, относящийся только к системам Microsoft

Компилятор Microsoft C/C++ не учитывает пользовательские запросы на


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

Завершение блока, относящегося только к системам Майкрософт

См. также
Описатели класса хранения для объявлений внутреннего уровня
Спецификатор класса хранения static
Статья • 03.03.2023 • Чтение занимает 2 мин

Переменная, объявленная на внутреннем уровне с помощью описателя класса


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

Комментарии
Если переменная с типом static не инициализирована явно, она
инициализируется со значением 0 по умолчанию. Внутри функции static выделяет
хранилище и служит определением. Внутренние статические переменные
представляют закрытое постоянное хранилище, видимое только одной функции.

См. также
Классы хранения в C

Storage classes (C++) (Классы хранения (C++))


Спецификатор класса хранения extern
Статья • 03.03.2023 • Чтение занимает 2 мин

Переменная, объявленная с использованием описателя для класса хранения extern


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

Пример
Этот пример иллюстрирует объявления на внутреннем и внешнем уровнях.

// Source1.c

int i = 1;

// Source2. c

#include <stdio.h>

// Refers to the i that is defined in Source1.c:

extern int i;

void func(void);

int main()

// Prints 1:

printf_s("%d\n", i);

func();

return;

void func(void)

// Address of global i assigned to pointer variable:

static int *external_i = &i;

// This definition of i hides the global i in Source.c:

int i = 16;

// Prints 16, 1:

printf_s("%d\n%d\n", i, *external_i);

В этом примере переменная i определяется в Source1.c с исходным значением 1.


Объявление extern в Source2.c делает переменную i доступной в этом файле.

В функции func адрес глобальной переменной i используется для инициализации


переменной указателя external_i с описателем static . Это возможно, поскольку
глобальная переменная имеет время существования типа static , то есть ее адрес
не меняется во время исполнения программы. Кроме того, переменная i
определяется в пределах области func как локальная переменная с исходным
значением 16. Это определение не влияет на значение переменной i внешнего
уровня, которое скрыто благодаря использованию соответствующего имени для
локальной переменной. Значение глобальной переменной i теперь доступно
только через указатель external_i .

См. также
Спецификаторы классов хранения для объявлений внутреннего уровня
Спецификаторы классов хранения с
объявлениями функций
Статья • 03.03.2023 • Чтение занимает 2 мин

В объявлениях функций можно использовать описатель класса хранения static


или extern . Функции всегда имеют глобальное время существования.

Блок, относящийся только к системам Microsoft

Объявления функций на внутреннем уровне имеют то же значение, что и


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

Завершение блока, относящегося только к системам Майкрософт

Правила видимости для функций слегка отличаются от правил для переменных


следующим.

Функция, объявленная как static , видна только в пределах исходного файла,


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

Функции, объявленные как extern , видны во всех исходных файлах


программы (если впоследствии такая функция не будет повторно объявлена
как static ). Любая функция может вызывать функцию extern .

Объявления функций, опускающие описатель класса хранения, по умолчанию


являются extern .

Блок, относящийся только к системам Microsoft

В системах Майкрософт можно повторно определять идентификатор extern как


static .

Завершение блока, относящегося только к системам Майкрософт

См. также
Классы хранения в C
Спецификаторы типов C
Статья • 03.03.2023 • Чтение занимает 2 мин

Спецификаторы типа в объявлениях определяют тип объявления переменной или


функции.

Синтаксис
type-specifier : void char short int long float double signed unsigned struct-or-

union-specifier enum-specifier typedef-name

Типы signed char , signed int , signed short int и signed long int вместе с
аналогичными типами unsigned и enum совокупно именуются целочисленными
типами. Описатели типов float , double и long double называются типами с
плавающей точкой или плавающей запятой. Любой спецификатор
целочисленного типа или типа с плавающей запятой можно использовать в
объявлении переменной или функции. Первоначально, если type-specifier объект
не был указан в объявлении, он считался int . Компилятор Майкрософт больше не
принимает объявления по умолчанию int .

Необязательные ключевые слова signed и unsigned могут предшествовать или


следовать за любым целочисленным типом, кроме enum , и также могут
использоваться только в качестве описателей типов. В этом случае они
понимаются как signed int и unsigned int соответственно. Если ключевое слово
int используется отдельно, оно обрабатывается как signed . Если ключевые слова
long и short используются отдельно, они трактуются как long int и short int .

Типы перечисления считаются базовыми типами. Описатели типов для типов


перечисления рассматриваются в статье Объявления перечислений C.

Ключевое слово void используется для трех целей: задание типа возвращаемого
значения функции, задание списка типов аргументов для функции, не
принимающей аргументов, и задание указателя на неуказанный тип. Тип void
можно использовать для объявления функций, не возвращающих никаких
значений, или для объявления указателя на неуказанный тип. В статье Аргументы
описано, как расценивается void , если это единственное ключевое слово в скобках
после имени функции.

Блок, относящийся только к системам Microsoft


Проверка типов теперь соответствует требованиям ANSI, то есть типы short и int
считаются разными типами. Например, в этом примере показано переопределение
в компиляторе Microsoft C, принятое предыдущими версиями компилятора.

int myfunc();

short myfunc();

Следующий пример также создает предупреждение о косвенном обращении к


разным типам:

int *pi;

short *ps;

ps = pi; /* Now generates warning */

Компилятор Microsoft C также выдает предупреждения в случае разного


использования знака (типы со знаком и без знака). Пример:

signed int *pi;

unsigned int *pu

pi = pu; /* Now generates warning */

Выражения типа void вычисляются для учета побочных эффектов. Вы не можете


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

любой тип, кроме void . При использовании в контексте, в котором требуется


выражение void , выражения любого другого типа его значение игнорируется.

Для соответствия спецификации void** ANSI нельзя использовать в качестве


int** . В качестве указателя на неуказанный тип можно использовать только void* .

Завершение блока, относящегося только к системам Майкрософт

Вы можете создать дополнительные описатели типов с typedef объявлениями, как


описано в разделе Объявления typedef. Сведения о размерах для каждого типа см.
в статье Хранение базовых типов.
См. также
Объявления и типы
Описатели и эквиваленты типов
данных
Статья • 26.09.2022 • Чтение занимает 2 мин

В рамках этой документации в большинстве случаев вместо полных форм


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

Описатели типов и их эквиваленты


Спецификаторы типов Эквиваленты

1
signed char char

signed int signed , int

signed short int short , signed short

signed long int long , signed long

unsigned char —

unsigned int unsigned

unsigned short int unsigned short

unsigned long int unsigned long

float —

2
long double —

1
 Если вы указали, что тип char по умолчанию является беззнаковым (указав
параметр компилятора /J ), вы не сможете сократить signed char до char .

2 В 32- и 64-разрядных операционных системах компилятор Microsoft С


устанавливает соответствие между типами long double и double .

Только для систем Майкрософт

При помощи параметра компилятора /J можно указать, что тип char по


умолчанию является не signed char , а unsigned char . При использовании этого
параметра описатель char означает то же, что и unsigned char . Для объявления
знакового символьного значения необходимо использовать ключевое слово
signed . Если значение char явным образом объявлено как signed , то параметр
/J на него не влияет и его расширение до типа int выполняется с расширением

знака. Расширение типа char до типа int выполняется с дополнением нулями.

ОКОНЧАНИЕ Только для систем Майкрософт

См. также
Спецификаторы типов C
Квалификаторы типов
Статья • 03.03.2023 • Чтение занимает 3 мин

Квалификаторы типов предоставляют идентификатору одно из двух свойств.


Квалификатор типа const объявляет объект как неизменяемый. Квалификатор типа
volatile объявляет элемент, значение которого можно изменить допустимым
образом с помощью средств, недоступных программе, в которой он находится,
таких как выполняемый в данный момент поток.

Квалификаторы типов const , restrict и volatile могут использоваться в


объявлении только один раз. Квалификаторы типов могут использоваться с любым
описателем типа; они не могут находиться после первой запятой в объявлении
нескольких элементов. Например, следующие объявления допустимы.

typedef volatile int VI;

const int ci;

Следующие объявления недопустимы.

typedef int *i, volatile *vi;

float f, const cf;

Квалификаторы типов имеют смысл только при обращении к идентификаторам как


к l-значениям в выражениях. Дополнительные сведения о левосторонних
значениях и выражениях см. в статье Выражения L-Value и R-Value.

Синтаксис
type-qualifier :

  const

  restrict

  volatile

const и volatile
Ниже представлены допустимые объявления const и volatile .
C

int const *p_ci; // Pointer to constant int

int const (*p_ci); // Pointer to constant int

int *const cp_i; // Constant pointer to int

int (*const cp_i); // Constant pointer to int

int volatile vint; // Volatile integer

Если спецификация типа массива включает квалификаторы типов, определяется


элемент, а не тип массива. Если спецификация типа функции включает
квалификаторы, поведение не определено. volatile и const не влияют на
диапазон значений или арифметические свойства объекта.

Ключевое слово const можно использовать для изменения любого


фундаментального или агрегатного типа, указателя на объект любого типа или
typedef . Если элемент объявлен только с квалификатором типа const , его тип

принимается равным const int. Переменная const может быть


инициализирована или помещена в доступную только для чтения область
памяти. Ключевое слово const полезно при объявлении указателей на
значения const . Так вы сообщите функции, что этот указатель нельзя
изменять.

Компилятор предполагает, что в любом месте программы к переменной


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

Если volatile используется отдельно, предполагается int . Описатель типа


volatile можно использовать для предоставления надежного доступа к

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


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

Элемент может одновременно быть const и volatile , и тогда его


невозможно изменить допустимым образом в той же программе, но можно
изменить в некотором асинхронном процессе.
restrict
Квалификатор типа restrict , появившийся в C99 и доступный в режиме /std:c11
или /std:c17, можно применять к объявлениям указателей. Он определяет сам
указатель, а не то, на что он указывает.

restrict является для компилятора указанием оптимизации и обозначает то, что


ни один другой указатель в текущей области не ссылается на то же расположение в
памяти. Таким образом, для получения доступа к объекту в течение времени
существования такого указателя используется только этот указатель или
производное от него значение (например, "указатель + 1"). Это помогает
компилятору создавать более оптимизированный код. C++ имеет эквивалентный
механизм: __restrict.

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


Если вы создадите псевдоним для указателя с пометкой restrict , результат будет
неопределенным.

Вот пример с использованием restrict :

void test(int* restrict first, int* restrict second, int* val)

*first += *val;

*second += *val;

int main()

int i = 1, j = 2, k = 3;

test(&i, &j, &k);

return 0;

// Marking union members restrict tells the compiler that

// only z.x or z.y will be accessed in any scope, which allows

// the compiler to optimize access to the members.

union z

int* restrict x;

double* restrict y;
};

См. также:
/std (определение стандартной версии языка)

Объявления и типы
Деклараторы и объявления
переменных
Статья • 03.03.2023 • Чтение занимает 2 мин

В остальной части данного раздела описывается форма и значение объявлений для


переменных типов, включенных в этот список. В частности, в остальных разделах
объясняется, как объявить следующие объекты.

Тип Описание
переменной

Простые Однозначные переменные целочисленного типа или типа с плавающей


переменные запятой

Массивы Переменные, состоящие из коллекции элементов того же типа

Указатели Переменные, указывающие на другие переменные и содержащие


расположения переменных (в виде адресов) вместо значений

Переменные Простые переменные с целочисленным типом, содержащие одно


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

Структуры Переменные, состоящие из коллекции значений, которые могут иметь


разные типы

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


занимающих одинаковое пространство для хранения

Оператор объявления — это часть объявления, задающая имя, которое нужно


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

Блок, относящийся только к системам Microsoft

В этом операторе объявления

__declspec(thread) char *var;

char  — это описатель типа, __declspec(thread) и *  — это модификаторы, а var  —


это имя идентификатора.

Завершение блока, относящегося только к системам Майкрософт


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

Синтаксис
declarator :

  pointer необ. direct-declarator

direct-declarator :

  identifier

  ( declarator )

  direct-declarator [ constant-expression opt ]

  direct-declarator ( parameter-type-list )

  direct-declarator ( identifier-list opt )

pointer :

  * type-qualifier-list необ.

  * type-qualifier-list необ. pointer

type-qualifier-list :

  type-qualifier

  type-qualifier-list type-qualifier

7 Примечание

См. соответствующий синтаксис для declaration в статьях Общие сведения об


объявлениях или Краткие сведения о синтаксисе языка C для получения
сведений о синтаксисе, который ссылается на declarator .

Если декларатор состоит из неизмененного идентификатора, объявляемый элемент


имеет базовый тип. Если звездочка ( * ) отображается слева от идентификатора, тип
изменяется на тип указателя. Если после идентификатора следуют квадратные
скобки ( [ ] ), тип изменяется на тип массива. Если после идентификатора
указываются скобки, тип меняется на тип функции. Дополнительные сведения об
интерпретации приоритетности в пределах объявлений см. в статье Интерпретация
сложных операторов объявлений.
Каждый декларатор объявляет по крайней мере один идентификатор. Декларатор
должен включать описатель типа, чтобы называться полным объявлением.
Описатель типа предоставляет тип элементов типа массива, тип объекта, к
которому относится тип указателя, или тип возвращаемого значения функции.

Объявления массивов и указателей более подробно обсуждаются далее в этом


разделе. В следующем примере проиллюстрировано несколько простых форм
деклараторов.

int list[20]; // Declares an array of 20 int values named list

char *cp; // Declares a pointer to a char value

double func( void ); // Declares a function named func, with no

// arguments, that returns a double value

int *aptr[10] // Declares an array of 10 pointers

Блок, относящийся только к системам Microsoft

Компилятор Microsoft для C не ограничивает число операторов объявления,


которые могут изменять арифметические, структурные типы или типы
объединений. Это число ограничивается только объемом доступной памяти.

Завершение блока, относящегося только к системам Майкрософт

См. также
Объявления и типы
Простые объявления переменных
Статья • 03.03.2023 • Чтение занимает 2 мин

Объявление обычной переменной, простейшей формы прямого декларатора,


указывает имя и тип переменной. Оно также указывает класс хранения и тип
данных переменной.

Классы хранения или типы (или и то, и другое) требуются в объявлениях


переменных. Нетипизированные переменные (например, var; ) создают
предупреждения.

Синтаксис
declarator :

  pointer необ. direct-declarator

direct-declarator :

  identifier

identifier :

  nondigit

  identifier nondigit

  identifier digit

В случае арифметического типа, типа структуры, типа объединения, типа void и


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

Чтобы указать несколько переменных в одном объявлении, можно использовать


список идентификаторов, разделенных запятыми ( , ). Все переменные,
определенные в объявлении, имеют один и тот же базовый тип. Пример:

int x, y; /* Declares two simple variables of type int */

int const z = 1; /* Declares a constant value of type int */

Переменные x и y могут содержать любое значение в наборе, определенном


типом int для конкретной реализации. Простой объект z инициализируется
значением 1 и не может изменяться.

Если бы объявление z было выполнено для неинициализированной статической


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

unsigned long reply, flag; /* Declares two variables

named reply and flag */

В этом примере обе переменные reply и flag имеют тип unsigned long и
содержат целочисленные значения без знака.

См. также
Деклараторы и объявления переменных
Объявления перечислений C
Статья • 03.03.2023 • Чтение занимает 3 мин

Перечисление состоит из набора именованных целочисленных констант.


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

Переменные типа enum можно использовать в выражениях индексации и в


качестве операндов всех арифметических операторов и операторов отношения.
Перечисления являются альтернативой директиве препроцессора #define с тем
преимуществом, что можно создать значения, подчиняющиеся обычным правилам
области.

В ANSI C выражения, определяющие значение константы перечислителя, всегда


имеют тип int . Это значит, что хранилище, связанное с переменной
перечисления, — это хранилище, необходимое для одного значения int .
Константу перечисления или значение перечисляемого типа можно использовать
как целочисленное выражение в любом месте, допустимом в языке C.

Синтаксис
enum-specifier :

  enum identifier opt { enumerator-list }

  enum identifier

enumerator-list :

  enumerator

  enumerator-list , enumerator

enumerator :

  enumeration-constant

  enumeration-constant = constant-expression

enumeration-constant :

  identifier
Необязательный параметр identifier задает имя типа перечисления, который
определен с помощью enumerator-list . Этот идентификатор часто называется
тегом перечисления, определенным списком. Описатель типа объявляет, что
identifier является тегом для перечисления, определенного нетерминальным
параметром enumerator-list , как показано ниже.

enum identifier

// enumerator-list

enumerator-list определяет элементы набора перечисления.

Если объявление тега является видимым, все последующие объявления, в которых


используется этот тег, но отсутствует enumerator-list , обозначают ранее
объявленный перечисляемый тип. Тег должен ссылаться на определенный тип
перечисления, и этот тип перечисления должен находиться в текущей области.
Поскольку тип перечисления определен в другом месте, enumerator-list не
отображается в этом объявлении. В объявлениях типов, производных от
перечислений, и объявлениях typedef для типов перечислений тег перечисления
может использоваться до определения типа перечисления.

Каждый параметр enumeration-constant в enumerator-list присваивает имя


значению набора перечисления. По умолчанию первый параметр enumeration-
constant связан со значением 0. Следующий параметр enumeration-constant в

списке связывается со значением ( constant-expression  + 1), если явно не указано


другое значение. Имя параметра enumeration-constant эквивалентно его значению.

С помощью параметра enumeration-constant = constant-expression можно


переопределить установленную по умолчанию последовательность значений.
Таким образом, если enumeration-constant = constant-expression отображается в
enumerator-list , параметр enumeration-constant связывается со значением,

заданным constant-expression . Параметр constant-expression должен иметь тип


int и может быть отрицательным.

К членам набора перечисления применяются следующие правила.

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


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

К тегам перечисления применяются обычные правила области. Они должны


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

Примеры
В следующих примерах показаны объявления перечисления.

enum DAY /* Defines an enumeration type */

saturday, /* Names day and declares a */

sunday = 0, /* variable named workday with */

monday, /* that type */

tuesday,

wednesday, /* wednesday is associated with 3 */

thursday,

friday

} workday;

Значение 0 связано с saturday по умолчанию. Для идентификатора sunday явно


задано значение 0. Оставшимся идентификаторам по умолчанию присваиваются
значения от 1 до 5.

В этом примере значение из набора DAY присваивается переменной today .

enum DAY today = wednesday;

Имя константы перечисления используется для присвоения значения. Поскольку


тип перечисления DAY был объявлен ранее, необходим только тег перечисления
DAY .

Чтобы явно присвоить целочисленное значение переменной перечисляемого типа


данных, используйте следующее приведение типа.

C
workday = ( enum DAY ) ( day_value - 1 );

Это приведение рекомендовано к использованию в С, но не является


обязательным.

enum BOOLEAN /* Declares an enumeration data type called BOOLEAN */

false, /* false = 0, true = 1 */

true

};

enum BOOLEAN end_flag, match_flag; /* Two variables of type BOOLEAN */

Это объявление также можно указать как

enum BOOLEAN { false, true } end_flag, match_flag;\

или как

enum BOOLEAN { false, true } end_flag;

enum BOOLEAN match_flag;

Пример, в котором используются эти переменные, может выглядеть следующим


образом.

if ( match_flag == false )

. /* statement */

end_flag = true;

Также можно объявить неименованные типы данных перечислителя. Имя типа


данных опускается, но можно объявлять переменные. Переменная response
является переменной определенного типа.
C

enum { yes, no } response;

См. также
Перечисления
Объявления структур
Статья • 03.03.2023 • Чтение занимает 4 мин

"Объявление структуры" именует тип и задает последовательность переменных


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

Синтаксис
struct-or-union-specifier :

  struct-or-union identifier opt { struct-declaration-list }

  struct-or-union identifier

struct-or-union :

  struct

  union

struct-declaration-list :

  struct-declaration

  struct-declaration-list struct-declaration

struct-declaration :

  specifier-qualifier-list struct-declarator-list ;

specifier-qualifier-list :

  type-specifier specifier-qualifier-list необ.

  type-qualifier specifier-qualifier-list необ.

struct-declarator-list :

  struct-declarator struct-declarator-list , struct-declarator

struct-declarator :

  declarator

  type-specifier declarator Выбрать : constant-expression

Объявление типа структуры не выделяет место для структуры. Это только шаблон
для последующих объявлений переменных структуры.
Ранее определенный identifier (тег) можно использовать для ссылки на тип
структуры, определенный в другом месте. В этом случае нельзя повторять, struct-
declaration-list если определение является видимым. Объявления указателей на

структуры и объекты typedef для типов структуры могут использовать тег структуры
до определения типа структуры. Однако определение структуры необходимо
получить до выполнения каких-либо фактических действий с размером полей. Это
использование является неполным определением типа и тега типа. Для того чтобы
это определение стало полным, определение типа должно отображаться позже в
той же области.

Задает struct-declaration-list типы и имена элементов структуры. Аргумент


struct-declaration-list содержит одно или несколько объявлений переменных

или битовых полей.

Каждая переменная, объявленная в , struct-declaration-list определяется как


член типа структуры. Объявления переменных в имеют struct-declaration-list ту
же форму, что и другие объявления переменных, рассматриваемые в этом разделе,
за исключением того, что объявления не могут содержать описатели класса
хранения или инициализаторы. Элементы структуры могут содержать любые типы
переменной, кроме типа void , неполного типа или типа функции.

Элемент не может быть объявлен как имеющий тип структуры, в которой он


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

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


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

Каждый элемент struct-declaration в struct-declaration-list должен быть


уникальным в пределах списка. Однако имена идентификаторов в struct-
declaration-list не обязательно должны отличаться от обычных имен переменных

или идентификаторов в других списках объявлений структуры.

Доступ к вложенным структурам можно осуществлять так же, как если бы они были
объявлены на уровне области файлов. Например, с данным объявлением

struct a

int x;

struct b

int y;

} var2;

} var1;

оба объявления являются допустимыми:

struct a var3;

struct b var4;

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

struct employee /* Defines a structure variable named temp */

char name[20];

int id;

long class;

} temp;

Структура employee содержит три члена: name , id и class . Член name  — это 20-
элементный массив, а id и class  — простые элементы с типом int и long
соответственно. Идентификатор employee является идентификатором структуры.

struct employee student, faculty, staff;

В этом примере определяются три переменных структуры: student , faculty и


staff . Каждая структура имеет такой же список из трех элементов. Эти элементы
объявлены как имеющие структурный тип employee , определенный в предыдущем
примере.

struct /* Defines an anonymous struct and a */

{ /* structure variable named complex */

float x, y;

} complex;

Структура complex содержит два элемента с типом float  — x и y . Тип структуры


не имеет тегов и, следовательно, является безымянным или анонимным.

struct sample /* Defines a structure named x */

char c;

float *pf;

struct sample *next;

} x;

Первые два элемента структуры — это переменная char и указатель на значение


float . Третий элемент, next , объявляется как указатель на определяемый
структурный тип ( sample ).

Анонимные структуры могут быть полезны, если имя тега не требуется, например,
если одно объявление определяет все экземпляры структуры. Пример:

struct

int x;

int y;

} mystruct;

Встроенные структуры часто являются анонимными.

struct somestruct

struct /* Anonymous structure */

int x, y;

} point;

int type;

} w;

Блок, относящийся только к системам Microsoft


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

struct identifier { set-of-declarations type array-name []; };

Безразмерные массивы могут отображаться только в качестве последнего члена


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

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

struct s

float y;

struct

int a, b, c;

};

char str[10];

} *p_s;

p_s->b = 100; /* A reference to a field in the s structure */

Дополнительные сведения о ссылках на структуры см. в разделе Структура и члены


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

Завершение блока, относящегося только к системам Майкрософт

См. также
Деклараторы и объявления переменных
Битовые поля в C
Статья • 03.03.2023 • Чтение занимает 2 мин

Помимо операторов объявления для членов структуры или объединения, оператор


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

Синтаксис
struct-declarator :

  declarator

  type-specifier declarator Выбрать : constant-expression

Задает constant-expression ширину поля в битах. Для type-specifier declarator


должен быть unsigned int , signed int или int , а constant-expression должен быть
неотрицательным целочисленным значением. Если указано значение 0, то
объявление не содержит declarator . Массивы битовых полей, указатели на
битовые поля и функции, возвращающие битовые поля, не допускаются.
Необязательный параметр declarator задает имя битового поля. Битовые поля
могут объявляться только в рамках структуры. Оператор address-of ( & ) не может
применяться к компонентам битового поля.

На неименованные битовые поля нельзя ссылаться, и их содержимое во время


выполнения является непредсказуемым. Их можно использовать в качестве
фиктивных полей в целях выравнивания. Неименованное битовое поле, для
которого задана ширина 0, гарантирует, что область хранения для члена, который
следует за ним в struct-declaration-list, начнется на границе int .

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

short a:17; /* Illegal! */

int long y:33; /* Illegal! */

В этом примере определен двумерный массив структур с именем screen .

C
struct

unsigned short icon : 8;

unsigned short color : 4;

unsigned short underline : 1;

unsigned short blink : 1;

} screen[25][80];

Массив содержит 2000 элементов. Каждый элемент представляет собой отдельную


структуру с четырьмя членами, каждый из которых представляет собой битовое
поле: icon , color , underline и blink . Размер каждой структуры составляет 2 байта.

Битовые поля имеют одну и ту же семантику, что и целочисленный тип. Битовое


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

Блок, относящийся только к системам Microsoft

Битовые поля, определенные как int , обрабатываются как signed . Расширение


Microsoft к стандарту ANSI C допускает битовые поля типов char и long (как signed
, так и unsigned ). Неименованные битовые поля с базовым типом long , short или
char ( signed или unsigned ) принудительно устанавливают выравнивание по
границе, соответствующей этому типу.

Битовые поля в целом числе назначаются в направлении от младшего разряда к


старшему. В приведенном ниже коде

struct mybitfields

unsigned short a : 4;

unsigned short b : 5;

unsigned short c : 7;

} test;

int main( void )

test.a = 2;

test.b = 31;

test.c = 0;

return 0;

биты test размещаются следующим образом:


00000001 11110010

cccccccb bbbbaaaa

Так как в семействе процессоров 8086 хранится низкий байт целочисленных


значений до высокого байта, целое число 0x01F2 будет храниться в физической
памяти, за 0xF2 которым следует 0x01 .

Стандарт ISO C99 позволяет реализации выбирать, может ли битовое поле


охватывать два экземпляра хранилища. Рассмотрим эту структуру, в которой
хранятся битовые поля с общим объемом 64 бита:

struct

unsigned int first : 9;

unsigned int second : 7;

unsigned int may_straddle : 30;

unsigned int last : 18;

} tricky_bits;

Стандартная реализация C может упаковать эти битовые поля в два 32-разрядных


целых числа. Она может хранить tricky_bits.may_straddle как 16 бит в одном 32-
разрядном целом числе и 14 бит в следующем 32-разрядном целом числе.
Соглашение Windows ABI подразумевает упаковку битовых полей в одиночные
целые числа хранилища и не объединяет единицы хранения. Компилятор
Майкрософт хранит каждое битовое поле в приведенном выше примере таким
образом, чтобы оно полностью помещалось в одно 32-разрядное целое число. В
этом случае first и second хранятся в одном целом числе, may_straddle хранится
во втором целом числе, а last  — в третьем. Оператор sizeof возвращает
значение 12 для экземпляра tricky_bits . Дополнительные сведения см. в разделе
Заполнение и выравнивание членов структуры.

Завершение блока, относящегося только к системам Майкрософт

См. также
Объявления структур
Хранение и выравнивание структур
Статья • 03.03.2023 • Чтение занимает 2 мин

Блок, относящийся только к системам Microsoft

Структурные элементы сохраняются последовательно, в порядке объявления:


первый элемент имеет самый низкий адрес памяти, а последний — наивысший.

У каждого объекта есть элемент alignment-requirement. Для структур таким


требованием является крупнейший из элементов таких структур. Каждому объекту
задается элемент offset, чтобы было верно следующее выражение:

offset % alignment-requirement == 0

Смежные битовые поля упакованы в тот же одно-, двух- или четырехбайтовый


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

Для экономии места или соответствия существующим структурам данных может


потребоваться сохранить структуры более или менее компактно. Параметр
компилятора /Zp[n] и #pragma pack управляют упаковкой данных структуры в
память. При использовании параметра /Zp[n], где n — 1, 2, 4, 8 или 16, каждый
элемент структуры после первого хранится в байтовом диапазоне, который
представляет собой требование к выравниванию поля или размер пакета (n) в
зависимости от того, что меньше. В виде формулы байтовые границы можно
выразить следующим образом:

min( n, sizeof( item ) )

где n — это размер пакета, выраженный с параметром /Zp[n], а item — это элемент
структуры. Размер пакета по умолчанию — /Zp8.

Чтобы использовать директиву pragma pack для указания упаковки, отличной от


заданной в командной строке для определенной структуры, разместите директиву
pragma pack , где размер пакета — 1, 2, 4, 8 или 16, перед структурой. Для
возобновления компоновки в соответствии с инструкциями с командной строки
задайте директиву pragma pack без аргументов.
Битовые поля по умолчанию имеют размер long для компилятора Microsoft C.
Элементы структуры выравниваются по размеру типа или размеру /Zp [n] в
зависимости от того что меньше. Размер по умолчанию — 4.

Завершение блока, относящегося только к системам Майкрософт

См. также
Объявления структур
union Объявления
Статья • 03.03.2023 • Чтение занимает 2 мин

Объявление объединения определяет набор значений переменных и при


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

Синтаксис
struct-or-union-specifier :

  struct-or-union identifier opt { struct-declaration-list }

  struct-or-union identifier

struct-or-union :

  struct

  union

struct-declaration-list :

  struct-declaration

  struct-declaration-list struct-declaration

Содержимое объединения определено как

struct-declaration :

  specifier-qualifier-list struct-declarator-list ;

specifier-qualifier-list :

  type-specifier specifier-qualifier-list необ.

  type-qualifier specifier-qualifier-list необ.

struct-declarator-list :

  struct-declarator

  struct-declarator-list , struct-declarator

Переменная с типом union хранит одно из значений, определенных в этом типе.


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

Члены объединений не могут иметь неполный тип, тип void или тип функции.
Поэтому члены не могут быть экземпляром объединения, но могут быть
указателями на объявляемый тип объединения.

Объявление типа объединения — это лишь шаблон. Память не резервируется до


объявления переменной.

7 Примечание

Если объявляется объединение двух типов и сохраняется одно значение, но


для доступа к объединению используется другой тип, результаты будут
ненадежными. Например, объявлено объединение float и int . Сохраняется
значение float , но программа позднее использует это значение как int . В
таком случае полученное значение будет зависеть от внутренних хранимых
данных для значений float . Целочисленное значение будет ненадежным.

Примеры
Ниже представлены примеры объединений.

union sign /* A definition and a declaration */

int svar;

unsigned uvar;

} number;

В этом примере определяется переменная объединения с типом sign и


объявляется переменная number с двумя членами: целым числом со знаком svar и
целым числом без знака uvar . В результате этого объявления можно сохранить
текущее значение number как целое число со знаком или целое число без знака. С
данным типом объединения связан тег sign .

union /* Defines a two-dimensional */

{ /* array named screen */

struct

unsigned int icon : 8;

unsigned color : 4;

} window1;

int screenval;

} screen[25][80];

Массив screen содержит 2000 элементов. Каждый элемент массива представляет


собой отдельное объединение с двумя членами: window1 и screenval . Член window1
является структурой с двумя членами-битовыми полями: icon и color . Член
screenval представляет собой int . В любой момент времени каждый элемент
объединения содержит значение int , представленное screenval , либо структуру,
представленную значением window1 .

Блок, относящийся только к системам Microsoft

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


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

struct str

int a, b;

union / * Unnamed union */

char c[4];

long l;

float f;

};

char c_array[10];

} my_str;

my_str.l == 0L; /* A reference to a field in the my_str union */

Объединения часто являются вложенными в структуре, содержащей поле, которое


представляет тип данных, содержащихся в объединении в определенный момент
времени. Ниже приведен пример объявления для такого объединения:

struct x

int type_tag;

union

int x;

float y;

Дополнительные сведения о ссылках на объединения см. в разделе Члены структур


и объединений.

Завершение блока, относящегося только к системам Майкрософт

См. также
Деклараторы и объявления переменных
Хранение объединений
Статья • 03.03.2023 • Чтение занимает 2 мин

Хранилище, связанное с переменной объединения, — это хранилище,


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

union /* Defines a union named x */

char *a, b;

float f[20];

} x;

Элементы объединения x являются (в порядке их объявления) указателями на


значение типа char , значение типа char и массив значений типа float .
Хранилище, выделенное для x , — это хранилище, необходимое для массива f ,
состоящего из 20 элементов, поскольку f является самым длинным членом
объединения. Так как ни один тег не связан с объединением, его тип является
безымянным или анонимным.

См. также
Объявления объединений
Объявления массивов
Статья • 03.03.2023 • Чтение занимает 2 мин

"Объявление массива" именует массив и задает тип его элементов. Он также может
определять число элементов в массиве. Переменная с типом массива считается
указателем на тип элементов массива.

Синтаксис
declaration :

  declaration-specifiers init-declarator-list необ. ;

init-declarator-list :

  init-declarator

  init-declarator-list , init-declarator

init-declarator :

  declarator

  declarator = initializer

declarator :

  pointer необ. direct-declarator

direct-declarator : /* Оператор объявления функции */

  direct-declarator [ constant-expression opt ]

Так как constant-expression является необязательным, синтаксис имеет две формы:

Первая форма определяет переменную массива. Аргумент constant-


expression в квадратных скобках указывает количество элементов в массиве.

Значение constant-expression , если оно присутствует, должно иметь


целочисленный тип и значение больше нуля. Каждый элемент имеет тип,
заданный параметром , который может быть любым типом type-specifier ,
кроме void . Элемент массива не может быть типом функции.

Вторая форма объявляет переменную, которая определена в другом месте.


Аргумент пропускается constant-expression в квадратных скобках, но не в
квадратных скобках. Эту форму можно использовать только в том случае, если
вы ранее инициализировали массив, объявили его в качестве параметра или
объявили как ссылку на массив, явно определенный в другом месте
программы.

В обеих формах direct-declarator присваивает переменной имя и может изменять


ее тип. Квадратные скобки ( [ ] ) после direct-declarator изменения декларатора
на тип массива.

Квалификаторы типов могут появиться в объявлении объекта типа массивов, но


квалификаторы применяются к элементам, а не самому массиву.

Можно объявить массив массивов (многомерный массив), поместив после


декларатора массива список константных выражений в квадратных скобках в
следующем виде:

type-specifier declarator [ constant-expression ] [ constant-expression ] ...

Каждый constant-expression в квадратных скобках определяет количество


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

Можно определить массивы указателей на различные типы объектов с помощью


сложных деклараторов, как описано в разделе Interpreting More Complex
Declarators (Интерпретация более сложных деклараторов).

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


строк по три столбца каждая.

char A[2][3];

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

Для обращения к одиночному элементу массива используется выражение индекса,


как описано в разделе Постфиксные операторы.

Примеры
В следующих примерах показаны объявления массивов.

float matrix[10][15];

Двумерный массив с именем matrix включает 150 элементов, каждый из которых


относится к типу float .

struct {

float x, y;

} complex[100];

Этот пример является объявлением массива структур. Этот массив содержит 100
элементов, каждый элемент — это структура, содержащая два члена.

extern char *name[];

Эта инструкция объявляет тип и имя массива указателей на char . Фактическое


определение name происходит в другом месте.

Блок, относящийся только к системам Microsoft

Тип целого числа, необходимый для хранения максимального размера массива,


равен размеру size_t .

Завершение блока, относящегося только к системам Майкрософт

См. также
Деклараторы и объявления переменных
Хранение массивов
Статья • 03.03.2023 • Чтение занимает 2 мин

Хранилище, связанное с типом массива, является хранилищем, необходимым для


всех его элементов. Элементы массива хранятся в смежных адресах памяти в
порядке увеличения от первого элемента к последнему.

См. также
Объявления массивов
Объявления указателей
Статья • 03.03.2023 • Чтение занимает 3 мин

Объявление указателя именует переменную указателя и задает тип объекта, на


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

Синтаксис
declarator :

  pointer необ. direct-declarator

direct-declarator :

  identifier

  ( declarator )

  direct-declarator [ constant-expression opt ]

  direct-declarator ( parameter-type-list )

  direct-declarator ( identifier-list opt )

pointer :

  * type-qualifier-list необ.

  * type-qualifier-list необ. pointer

type-qualifier-list :

  type-qualifier

  type-qualifier-list type-qualifier

задает type-specifier тип объекта , который может быть любым базовым типом,
типом структуры или объединения. Переменные указателя также могут указывать
на функции, массивы и другие указателям. (Дополнительные сведения об
объявлении и интерпретации более сложных типов указателей см. в разделе
Интерпретация более сложных деклараторов.)

Создавая type-specifier void , можно отложить спецификацию типа, на который


ссылается указатель. Этот элемент называется "указатель на void " и записывается
как void * . Переменная, объявленная как указатель на void , может
использоваться для указания на объект любого типа. Однако для того чтобы
выполнять большинство операций с указателем или объектом, на который он
указывает, тип, на который он указывает, должен быть явно задан для каждой
операции. (Переменные типа char * и типа void * совместимы с присваиваниями
без приведения типов.) Такое преобразование можно выполнить с помощью
приведения типа. Дополнительные сведения см. в разделе Преобразования
приведения типов.

Может type-qualifier быть либо const , либо volatile , либо и то, и другое. Эти
ключевые слова указывают, соответственно, что указатель не может быть изменен
самой программой ( const ), или что указатель может быть изменен каким-либо
процессом, не зависящим от программы ( volatile ). Дополнительные сведения о
ключевых словах const и volatile см. в статье Квалификаторы типов.

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


declarator представляет массив, тип указателя изменяется так, чтобы он был

указателем на массив.

Можно объявить указатель на структуру, объединение или тип перечисления,


прежде чем определять тип структуры, объединения или перечисления. Указатель
объявляется с помощью структуры или тега union, как показано в примерах. Такие
объявления разрешены, так как компилятору не нужно знать размер структуры или
объединения, чтобы выделить пространство для переменной указателя.

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

char *message; /* Declares a pointer variable named message */

Указатель message указывает на переменную с char типом .

int *pointers[10]; /* Declares an array of pointers */

Массив pointers содержит 10 элементов; каждый элемент является указателем на


переменную с int типом .

int (*pointer)[10]; /* Declares a pointer to an array of 10 elements */

Переменная pointer указывает на массив с 10 элементами. Каждый элемент в этом


массиве имеет тип int .

int const *x; /* Declares a pointer variable, x,

to a constant value */

Указатель x можно изменить, чтобы он указывал на другое int значение, но не


может быть изменено значение, на которое он указывает.

const int some_object = 5 ;

int other_object = 37;

int *const y = &fixed_object;

int volatile *const z = &some_object;

int *const volatile w = &some_object;

Переменная y в этих объявлениях объявляется как постоянный указатель на int


значение. Значение, на которое он указывает, можно изменить, но сам указатель
всегда должен указывать на одно и то же расположение: адрес fixed_object .
Аналогичным образом является z указателем константы, но он также объявляется
для указания на int объект , значение которого не может быть изменено
программой. Описатель volatile указывает, что, хотя значение const int объекта ,
на которое указывает z , не может быть изменено программой, оно может быть
изменено процессом, выполняющимся параллельно с программой. Объявление w
указывает, что программа не может изменить значение, на которое указывает, и
что программа не может изменить указатель.

struct list *next, *previous; /* Uses the tag for list */

В этом примере объявляют две переменные указателя ( next и previous ),


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

struct list

char *token;

int count;

struct list *next;

} line;

Переменная line имеет тип структуры с именем list . Тип list структуры состоит
из трех элементов: первый элемент является указателем на char значение, второй
— int значением, а третий — указателем на другую list структуру.

struct id

unsigned int id_no;


struct name *pname;

} record;

Переменная record имеет тип id структуры . pname объявляется как указатель на


другой тип структуры с именем name . Это объявление может появиться перед
определением name типа.

См. также
Деклараторы и объявления переменных
Хранение адресов
Статья • 03.03.2023 • Чтение занимает 2 мин

Объем хранилища, необходимый для адреса, и значение адреса зависят от


реализации компилятора. Одинаковая длина указателей на разные типы не
гарантируется. Поэтому значение sizeof(char *) необязательно равно значение
sizeof(int *).

Блок, относящийся только к системам Microsoft

Для компилятора Microsoft C значение sizeof(char *) равно значению sizeof(int *).

Завершение блока, относящегося только к системам Майкрософт

См. также
Объявления указателей
Основанные указатели (C)
Статья • 03.03.2023 • Чтение занимает 2 мин

Блок, относящийся только к системам Microsoft

__based (Справочник по C ++)

В 32- и 64-разрядных компиляторах Microsoft базовый указатель является 32- или


64-разрядным смещением от 32- или 64-разрядной базы указателя. Базовая
адресация полезна для управления разделами, в которых размещены объекты,
поскольку уменьшается размер исполняемого файла и увеличивается скорость
выполнения. Как правило, используется следующая форма определения
относительного указателя.

type__based(base)declarator

Вариант базовой адресации, основанный на указателе, позволяет использовать


спецификацию указателя в качестве базы. В свою очередь, относительный
указатель является смещением в раздел памяти, который начинается в начале
указателя, на котором он основан. Указатели, основанные на адресах указателей,
являются единственной формой ключевого слова __based , допустимой в 32- и 64-
разрядных компиляциях. В таких компиляциях они представляют собой 32- или 64-
разрядные смещения от 32- или 64-разрядной базы.

Указатели на основе указателей, в частности, используются для постоянных


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

В следующем примере показан указатель на основе указателя.

void *vpBuffer;

struct llist_t

void __based( vpBuffer ) *vpData;

struct llist_t __based( vpBuffer ) *llNext;

};

Указателю vpBuffer назначается адрес в памяти, который выделяется на более


позднем этапе программы. Связанный список перемещается относительно
значения vpBuffer .

Завершение блока, относящегося только к системам Майкрософт

См. также
Деклараторы и объявления переменных
Абстрактные деклараторы в C
Статья • 03.03.2023 • Чтение занимает 2 мин

Абстрактный декларатор — это декларатор без идентификатора, состоящий из


одного или нескольких указателей, массивов или модификаторов функций.
Модификатор указателя ( * ) всегда предшествует идентификатору в деклараторе, а
за идентификатором следуют модификаторы массива ([ ]) и функций (( )). Зная это,
можно определить, появится ли идентификатор в абстрактном деклараторе, и
интерпретировать декларатор соответствующим образом. В статье Интерпретация
сложных деклараторов содержатся дополнительные сведения и примеры сложных
деклараторов. Обычно для упрощения деклараторов можно использовать typedef .
См. статью Объявления Typedef.

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


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

В следующих примерах показаны абстрактные деклараторы.

int * // The type name for a pointer to type int:

int *[3] // An array of three pointers to int

int (*) [5] // A pointer to an array of five int

int *() // A function with no parameter specification

// returning a pointer to int

// A pointer to a function taking no arguments and

// returning an int

int (*) ( void )

// An array of an unspecified number of constant pointers to

// functions each with one parameter that has type unsigned int

// and an unspecified number of other parameters returning an int

int (*const []) ( unsigned int, ... )

7 Примечание

Абстрактный декларатор не может состоять из набора пустых скобок, ( ) ,


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

См. также
Деклараторы и объявления переменных
Интерпретация сложных
деклараторов
Статья • 03.03.2023 • Чтение занимает 4 мин

Можно заключить любой оператор объявления в круглые скобки, чтобы указать


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

При интерпретации сложных деклараторов квадратные и круглые скобки (т. е.,


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

Простым способом интерпретации сложных деклараторов является чтение


"изнутри наружу", с помощью следующих 4 шагов:

1. Начните с идентификатора и найдите ближайшие квадратные или круглые


скобки справа от него (если есть).

2. Интерпретируйте эти квадратные или круглые скобки, затем найдите


звездочку слева.

3. Если на каком-либо шаге обнаружится правая круглая скобка, перейдите


назад и примените правила 1 и 2 ко всему выражению в круглых скобках.

4. Примените спецификатор типа.

char *( *(*var)() )[10];

^ ^ ^ ^ ^ ^ ^

7 6 4 2 1 3 5

В этом примере шаги пронумерованы по порядку и интерпретируются следующим


образом:

1. Идентификатор var объявлен как

2. указатель на

3. функцию, возвращающую

4. указатель на

5. массив из 10 элементов, которые являются

6. указателями на

7. значения типа char .

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

int *var[5]; /* Array of pointers to int values */

Модификатор массива имеет более высокий приоритет, чем модификатор


указателя, поэтому переменная var объявляется как массив. Модификатор
указателя применяется к типу элементов массива; следовательно, элементы
массива являются указателями на значения int .

int (*var)[5]; /* Pointer to array of int values */

В этом объявлении переменной var скобки обеспечивают модификатору


указателя более высокий приоритет, чем у модификатора массива, и переменная
var объявляется как указатель на массив из 5 значений int .

long *var( long, long ); /* Function returning pointer to long */

Модификаторы функции также имеют более высокий приоритет, чем


модификаторы указателя, поэтому в этом объявлении var переменная var
объявляется как функция, возвращающая указатель на значение типа long . В
соответствии с объявлением функция принимает два значения типа long в
качестве аргументов.

long (*var)( long, long ); /* Pointer to function returning long */

Этот пример аналогичен предыдущему. Скобки отдают модификатору указателя


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

struct both /* Array of pointers to functions */

{ /* returning structures */

int a;

char b;

} ( *var[5] )( struct both, struct both );

Элементы массива не могут быть функциями, но это объявление показывает, как


объявить массив указателей на функции. В данном примере переменная var
объявляется как массив из 5 указателей на функции, возвращающие структуры с
двумя членами. В качестве аргументов для функций объявлены две структуры с тем
же типом структуры, both . Обратите внимание, что скобки вокруг *var[5]
обязательны. Без них объявление было бы недопустимой попыткой объявить
массив функций, как показано ниже:

/* ILLEGAL */

struct both *var[5](struct both, struct both);

Следующий оператор объявляет массив указателей.

unsigned int *(* const *name[5][10] ) ( void );

Массив name содержит 50 элементов, организованных в многомерный массив.


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

В следующем примере показана функция, возвращающая указатель на массив из


трех значений типа double .

double ( *var( double (*)[3] ) )[3];

В этом объявлении функция возвращает указатель на массив, поскольку функции


не могут возвращать массивы. Здесь переменная var объявлена как функция,
возвращающая указатель на массив из трех значений типа double . Функция var
принимает один аргумент. Аргумент, как и возвращаемое значение, является
указателем на массив из трех значений типа double . Тип аргумента задан сложным
абстрактным декларатором abstract-declarator. Скобки вокруг звездочки в типе
аргумента обязательны. Без них аргумент будет иметь тип массива из трех
указателей на значения типа double . Обсуждение и примеры абстрактных
деклараторов см. в статье Абстрактные деклараторы.

union sign /* Array of arrays of pointers */

{ /* to pointers to unions */

int x;

unsigned y;

} **var[5][5];

Как показано в приведенном выше примере, указатель может указывать на другой


указатель, и массив может содержать элементы, являющиеся массивами. Здесь
переменная var представляет собой массив из 5 элементов. Каждый элемент
представляет собой 5-элементный массив указателей на объединения с двумя
членами.

union sign *(*var[5])[5]; /* Array of pointers to arrays

of pointers to unions */

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


этом примере переменная var является 5-элементным массивом указателей на 5-
элементные массивы указателей на объединения. Примеры применения typedef
для исключения сложных объявлений см. в статье Объявления Typedef.

См. также
Объявления и типы
Инициализация
Статья • 03.03.2023 • Чтение занимает 2 мин

"Инициализатор" представляет собой значение или последовательность значений,


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

В следующих разделах рассматривается инициализация переменных скалярных,


агрегатных и строковых типов. К "скалярным типам" относятся все арифметические
типы и указатели. К "агрегатным типам" относятся массивы, структуры и
объединения.

См. также
Объявления и типы
Инициализация скалярных типов
Статья • 03.03.2023 • Чтение занимает 2 мин

При инициализации скалярных assignment-expression типов значение


присваивается переменной . Применяются правила преобразования для
присваивания. (См. сведения о правилах преобразования в статье Преобразования
типов (C).)

Синтаксис
declaration :

  declaration-specifiers init-declarator-list необ. ;

declaration-specifiers :

  storage-class-specifier declaration-specifiers необ.

  type-specifier declaration-specifiers необ.

  type-qualifier declaration-specifiers необ.

init-declarator-list :

  init-declarator

  init-declarator-list , init-declarator

init-declarator :

  declarator

  declarator = initializer /* Для инициализации скалярных типов */

initializer :

  assignment-expression

Переменные любого типа можно инициализировать при условии, что


соблюдаются следующие правила:

Переменные, объявленные на уровне области файлов, можно


инициализировать. Если вы явно не инициализируете переменную на
внешнем уровне, она по умолчанию инициализируется значением 0.

Константное выражение можно использовать для инициализации любой


глобальной переменной, объявленной с использованием описателя
static storage-class-specifier . Переменные, объявленные как static ,

инициализируются в начале исполнения программы. Если не


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

Переменные, объявленные с auto помощью описателя класса хранения или


register , инициализируются каждый раз, когда управление выполнением

передается блоку, в котором они объявлены. Если не включать


инициализатор в объявление переменной auto или register , начальное
значение переменной будет неопределенным. Для автоматических и
регистрирующих значений инициализатор не ограничен константой; Это
может быть любое выражение, включающее ранее определенные значения,
даже вызовы функций.

Начальные значения для объявлений внешних переменных и для всех


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

Если объявление идентификатора имеет область блока, а идентификатор


имеет внешнюю компоновку, объявление не может иметь инициализацию.

Примеры
Инициализация иллюстрируется следующими примерами.

int x = 10;

Целочисленная переменная x инициализируется с константным выражением 10 .

register int *px = 0;

Указатель px инициализируется значением 0, создавая указатель null.

const int c = (3 * 1024);

В этом примере константное выражение (3 * 1024) используется для


инициализации c константного значения, которое невозможно изменить из-за
ключевого const слова .

int *b = &x;

Этот оператор инициализирует указатель b адресом другой переменной, x .

int *const a = &z;

Указатель a инициализируется с адресом переменной с именем z . Однако, так как


она указана как const , переменная a может быть только инициализирована,
никогда не изменяя. Она всегда указывает на одно и то же расположение.

int GLOBAL ;

int function( void )

int LOCAL ;

static int *lp = &LOCAL; /* Illegal initialization */

static int *gp = &GLOBAL; /* Legal initialization */

register int *rp = &LOCAL; /* Legal initialization */

Глобальная переменная GLOBAL объявляется на внешнем уровне и поэтому имеет


глобальное время существования. Локальная переменная LOCAL имеет auto класс
хранения и имеет адрес только во время выполнения функции, в которой она
объявлена. Поэтому попытка инициализировать static переменную lp указателя
с адресом LOCAL не допускается. Переменную указателя static gp можно
инициализировать с использованием адреса GLOBAL , так как он всегда остается
неизменным. Аналогично, *rp можно инициализировать, так как переменная rp
является локальной и может иметь неконстантный инициализатор. Всякий раз при
входе в блок LOCAL имеет новый адрес, который затем присваивается rp .

См. также
Инициализация
Инициализация агрегатных типов
Статья • 03.03.2023 • Чтение занимает 4 мин

Агрегатный тип — это тип структуры, объединения или массива. Если агрегатный
тип содержит члены агрегатных типов, правила инициализации применяются
рекурсивно.

Синтаксис
initializer :

  { initializer-list } /* Для статистической инициализации */

  { initializer-list , }

initializer-list :

  initializer

  initializer-list , initializer

— initializer-list это список инициализаторов, разделенных запятыми. Каждый


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

В каждом списке инициализаторов значения константных выражений


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

Если initializer-list имеет меньше значений, чем агрегатный тип, остальные


элементы или элементы агрегатного типа инициализируются 0. Начальное
значение автоматического идентификатора, которое не инициализировано явно,
не определено. Если initializer-list имеет больше значений, чем агрегатный тип,
возникает ошибка. Эти правила применяются к каждому внедренном списку
инициализаторов и к агрегату в целом.

Инициализатор структуры — это либо выражение того же типа, либо список


инициализаторов для его членов, заключенный в фигурные скобки ( { } ).
Неименованные члены битового поля не инициализируются.
При инициализации initializer-list объединения должно быть одним
константным выражением. Значение константного выражения присваивается
первому члену объединения.

Если размер массива неизвестен, количество инициализаторов определяет размер


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

Количество инициализаторов может задать размер массива:

int x[ ] = { 0, 1, 2 }

Если указать размер и присвоить неверное количество инициализаторов,


компилятор выдаст ошибку.

Блок, относящийся только к системам Microsoft

Максимальный размер массива определяется параметром size_t .

Завершение блока, относящегося только к системам Майкрософт

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

int P[4][3] =
{

{ 1, 1, 1 },

{ 2, 2, 2 },

{ 3, 3, 3,},

{ 4, 4, 4,},

};

Этот оператор объявляется P как массив "четыре на три" и инициализирует


элементы своей первой строки значением 1, элементы второй строки 2 и т. д. через
четвертую строку. Список инициализаторов для третьей и четвертой строк
содержит запятые после последнего константного выражения. За последним
списком инициализаторов ( {4, 4, 4,}, ) также следует запятая. Эти
дополнительные запятые разрешены, но не являются обязательными. Требуются
только запятые, отделяющие константные выражения друг от друга, и запятые,
отделяющие один список инициализаторов от другого.

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


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

int P[4][3] =
{

1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4

};

Фигурные скобки также могут отображаться вокруг отдельных инициализаторов в


списке, что поможет прояснить пример.

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


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

typedef struct

int n1, n2, n3;

} triplet;

triplet nlist[2][3] =

{ { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } }, /* Row 1 */

{ { 10,11,12 }, { 13,14,15 }, { 16,17,18 } } /* Row 2 */

};

В этом примере параметр nlist объявлен как массив структур, состоящий из двух
строк и трех столбцов, где каждая структура имеет три члена. Строка 1
инициализации присваивает значения первой строке nlist следующим образом.

1. Первая открывающая фигурная скобка в строке 1 сообщает компилятору, что


начинается инициализация первого агрегатного члена nlist (то есть
nlist[0] ).

2. Вторая открывающая фигурная скобка указывает, что начинается


инициализация первого агрегатного члена nlist[0] (то есть структуры
nlist[0][0] ).

3. Первая закрывающая фигурная скобка завершает инициализацию структуры


nlist[0][0] . Следующая открывающая фигурная скобка начинает

инициализацию nlist[0][1] .

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


фигурная скобка завершит инициализацию nlist[0] .

Строка 2 присваивает значения второй строке nlist аналогичным образом.


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

triplet nlist[2][3] = /* THIS CAUSES AN ERROR */

{ 1, 2, 3 },{ 4, 5, 6 },{ 7, 8, 9 }, /* Line 1 */

{ 10,11,12 },{ 13,14,15 },{ 16,17,18 } /* Line 2 */

};

В этой конструкции первая открывающая фигурная скобка в строке 1 запускает


инициализацию nlist[0] , который является массивом из трех структур. Значения 1,
2 и 3 присваиваются трем членам первой структуры. При обнаружении следующей
закрывающей фигурной скобки (после значения 3) инициализация nlist[0]
завершается, и две оставшиеся структуры в массиве из трех структур
автоматически инициализируются значением 0. Аналогичным образом { 4,5,6 }
инициализирует первую структуру во второй строке nlist . Оставшимся двум
структурам nlist[1] присваивается значение 0. Когда компилятор обнаруживает
следующий список инициализаторов ( { 7,8,9 } ), он выполняет попытку
инициализировать nlist[2] . Поскольку nlist имеет только две строки, эта
попытка завершится с ошибкой.

В следующем примере три члена int x инициализируются значениями 1, 2 и 3


соответственно.

struct list

int i, j, k;

float m[2][3];

} x = {

1,

2,

3,

{4.0, 4.0, 4.0}

};

list В структуре три элемента в первой строке инициализируются m значением

4.0, а элементы оставшейся строки по умолчанию инициализируются m 0,0.

union

char x[2][3];

int i, j, k;

} y = { {

{'1'},

{'4'}

};

В данном примере инициализируется переменная объединения y . Первый


элемент объединения — это массив, поэтому инициализатор является агрегатным
инициализатором. Список инициализаторов {'1'} присваивает значения первой
строке массива. Поскольку только одно значение отображается в списке, элемент в
первом столбце инициализируется символом 1 , а оставшиеся два элемента в
строке инициализируются значением 0 по умолчанию. Аналогичным образом,
первый элемент второй строки x инициализируется символом 4 , а оставшиеся два
элемента в строке инициализируются значением 0.

См. также
Инициализация
Инициализация строк
Статья • 03.03.2023 • Чтение занимает 2 мин

Массив символов (или расширенных символов) можно инициализировать со


строковым литералом (или расширенным строковым литералом). Пример:

char code[ ] = "abc";

Этот код инициализирует символьный массив code с четырьмя элементами.


Четвертым элементом является символ null, которым завершаются все строковые
литералы.

Длина списка идентификаторов может быть равна количеству инициализируемых


идентификаторов. Если указанный размер массива меньше длины строки, то
лишние символы игнорируются. Например, следующее объявление
инициализирует символьный массив code с тремя элементами:

char code[3] = "abcd";

Массиву code присваиваются только первые три символа инициализатора. Символ


d и символ null, завершающий строки, отбрасываются. Обратите внимание, что
при этом будет создана незавершенная строка (т. е. строка без значения 0, которым
обозначается ее конец) и сгенерировано диагностическое сообщение,
указывающее на это состояние.

Объявление

char s[] = "abc", t[3] = "abc";

идентично объявлению

char s[] = {'a', 'b', 'c', '\0'},

t[3] = {'a', 'b', 'c' };

Если строка короче указанного размера массива, то остальные элементы массива


инициализируются как равные 0.

Блок, относящийся только к системам Microsoft

В Microsoft C строковые литералы могут иметь длину до 2048 байт.

Завершение блока, относящегося только к системам Майкрософт

См. также
Инициализация
Хранилище базовых типов
Статья • 03.03.2023 • Чтение занимает 2 мин

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


типом.

Размеры основных типов


Type Хранилище

char , unsigned char , signed char 1 байт

short , unsigned short 2 байта

int , unsigned int 4 байта

long , unsigned long 4 байта

long long , unsigned long long 8 байт

float 4 байта

double 8 байт

long double 8 байт

Типы данных C разделяются на общие категории. Целочисленные типы включают


в себя int , char , short , long и long long . Такие типы могут иметь уточнение
signed или unsigned , а unsigned можно использовать как краткую форму unsigned
int . Типы перечислений ( enum ) также в большинстве случаев обрабатываются как

целочисленные типы. К типам с плавающей запятой относятся float , double и


long double . Арифметические типы включают все целочисленные типы и типы с

плавающей запятой.

См. также
Объявления и типы
Тип char
Статья • 03.03.2023 • Чтение занимает 2 мин

Тип char используется для хранения целочисленного значения члена


представительной кодировки. Целочисленное значение — это код ASCII,
соответствующий указанному символу.

Блок, относящийся только к системам Microsoft

Значения символов типа unsigned char находятся в диапазоне от 0 до 0xFF по


шестнадцатеричной системе счисления. signed char имеет диапазон от 0x80 до
0x7F. Эти диапазоны преобразуются в диапазоны от 0 до 255 и от -128 до +127 по
десятичной системе счисления соответственно. Параметр компилятора /J
указывает, что вместо signed по умолчанию будет использоваться тип unsigned .

Завершение блока, относящегося только к системам Майкрософт

См. также
Хранение базовых типов
Тип int
Статья • 03.03.2023 • Чтение занимает 2 мин

Размер знакового или беззнакового элемента signed int или unsigned int
соответствует стандартному размеру целочисленного значения на конкретном
компьютере. Например, в 16-разрядных операционных системах тип int обычно
имеет размер 16 бит, или 2 байта. В 32-разрядных операционных системах тип int
обычно имеет размер 32 бита, или 4 байта. Таким образом в зависимости от
целевой среды тип int эквивалентен типу short int или long int , а тип unsigned
int  — типу unsigned short или unsigned long . Все типы int представляют

знаковые значения, если не указано иное.

Спецификаторы типа int и unsigned int (или просто unsigned ) определяют


некоторые функции языка C (например, тип enum ). В таких случаях определения
int и unsigned int для конкретной реализации определяют фактическую область
хранения.

Блок, относящийся только к системам Microsoft

Знаковые целочисленные типы представлены в форме дополнительного кода. В


старшем бите содержится следующий знак: 1 означает отрицательные числа, 0 —
положительные числа и ноль. Диапазон значений описывается в статье Пределы
целых чисел в C и C++. Пределы определены в файле заголовка LIMITS.H.

Завершение блока, относящегося только к системам Майкрософт

7 Примечание

Описатели типов int и unsigned int широко используются в программах на


языке C, поскольку они позволяют обрабатывать целочисленные значения
наиболее эффективным на конкретном компьютере способом. Однако
поскольку размеры типов int и unsigned int различаются, программы,
которые зависят от конкретного размера int , не могут переноситься на
другие компьютеры. Чтобы программы лучше переносились, вместо жестко
заданных размеров данных можно использовать выражения с оператором
sizeof (см. статью Оператор sizeof).

См. также
Хранение базовых типов
Целочисленные типы размеров C
Статья • 03.03.2023 • Чтение занимает 2 мин

Блок, относящийся только к системам Microsoft

В Microsoft C поддерживаются целочисленные типы с указанием размера. Это


позволяет объявлять 8-, 16-, 32- и 64-разрядные целочисленные переменные при
помощи описателя типа __intN (где N означает размер целочисленной
переменной в битах). Таким образом, n может иметь значение 8, 16, 32 или 64. В
следующем примере объявляется по одной переменной каждого из
4 целочисленных типов с указанием размера:

__int8 nSmall; // Declares 8-bit integer

__int16 nMedium; // Declares 16-bit integer

__int32 nLarge; // Declares 32-bit integer

__int64 nHuge; // Declares 64-bit integer

Первые три целочисленных типа с указанием размера являются аналогами типов


ANSI, имеющих такой же размер. Они полезны для написания переносимого кода,
который одинаково выполняется на различных платформах. Тип данных __int8
аналогичен типу char , __int16  — типу short , __int32  — типу int , а __int64  —
типу long long .

Завершение блока, относящегося только к системам Майкрософт

См. также
Хранение базовых типов
Тип float
Статья • 03.03.2023 • Чтение занимает 3 мин

Числа с плавающей запятой используют формат IEEE (Института инженеров по


электротехнике и электронике). Значения с одиночной точностью и типом float
имеют 4 байта, состоят из бита знака, 8-разрядной двоичной экспоненты excess-
127 и 23-битной мантиссы. Мантисса представляет число от 1,0 до 2,0. Поскольку
бит высокого порядка мантиссы всегда равен 1, он не сохраняется в числе. Это
представление обеспечивает для типа float диапазон примерно от 3,4E–38 до
3,4E+38.

Можно объявить переменные в качестве типа float или double в зависимости от


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

Типы с плавающей запятой

Type Значимые цифры Число байтов

float 6–7 4

double 15–16 8

Переменные с плавающей запятой представлены мантиссой, которая содержит


значение числа, и экспонентой, которая содержит порядок возрастания числа.

В следующей таблице показано количество битов, выделенных мантиссе и


экспоненте для каждого типа с плавающей запятой. Наиболее значимый бит
любого типа float или double — всегда бит знака. Если он равен 1, число считается
отрицательным; в противном случае — положительным.

Длина экспонент и мантисс

Type Длина экспоненты Длина мантиссы

float 8 бит 23 бита

double 11 бит 52 бита


Поскольку экспоненты хранятся в форме без знака, экспоненты смещены на
половину своего возможного значения. Для типа float смещение составляет 127;
для типа double это 1023. Можно вычислить фактическое значение экспоненты,
вычтя значение смещения из значения экспоненты.

Мантисса хранится в виде бинарной доли, которая больше или равна 1 и меньше 2.
Для типов float и double в мантиссе подразумевается наличие начального 1 в
наиболее значимой битовой позиции, поэтому фактически длина мантисс
составляет 24 и 53 бит соответственно, даже если наиболее значимый бит никогда
не хранится в памяти.

Вместо только что описанного метода хранения пакет значений с плавающей


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

В следующей таблице показаны минимальное и максимальное значения, которое


можно сохранить в переменных каждого типа с плавающей запятой. Значения,
указанные в этой таблице, применяются только к нормализованным числам с
плавающей запятой; денормализованные числа с плавающей запятой имеют
меньшее минимальное значение. Обратите внимание, что номера, сохраненные в
регистрах 80x87, всегда представлены в 80-разрядной нормализованной форме;
при сохранении в 32- или 64-разрядных переменных с плавающей запятой числа
могут быть представлены только в ненормализованной форме (переменные типов
float и long).

Диапазон типов с плавающей запятой

Type Минимальное значение Максимальное значение

плавающее 1,175494351 E – 38 3,402823466 E + 38

double 2,2250738585072014 E – 308 1,7976931348623158 E + 308


Если точность менее важна, чем размер хранимых данных, имеет смысл
использовать тип float для переменных с плавающей запятой. И наоборот, если
точность — наиболее важный критерий, используйте тип double.

Уровень переменных с плавающей запятой можно повысить до типа большей


значимости (преобразование типа float в тип double). Повышение уровня часто
происходит при выполнении арифметических действий с переменными
плавающего типа. Это арифметическое действие всегда выполняется на том же
уровне точности, что и переменная с наивысшим уровнем точности. Например,
проанализируйте объявления следующих типов.

float f_short;

double f_long;

long double f_longer;

f_short = f_short * f_long;

В предыдущем примере уровень переменной f_short повышается до типа double,


а затем переменная умножается на f_long ; затем результат округляется до типа
float и присваивается объекту f_short .

В следующем примере (с использованием объявлений из предыдущего примера)


арифметическая операция выполняется на уровне точности переменной типа float
(32-разрядной). Уровень результата затем повышается до уровня double.

f_longer = f_short * f_short;

См. также
Хранение базовых типов
Тип double
Статья • 03.03.2023 • Чтение занимает 2 мин

Значения типа double с двойной точностью имеют размер 8 байт. Формат


напоминает формат чисел с плавающей запятой, за исключением того, что он
имеет 11-разрядную экспоненту (ее значение может превышать 1023) и 52-
разрядную мантиссу, а также еще один старший разряд. Значения типа double в
этом формате могут находиться в диапазоне примерно от 1,7E–308 до 1,7E+308.

Блок, относящийся только к системам Microsoft

Тип double представлен 64 битами: 1 для знака, 11 для экспоненты и 52 для


мантиссы. Он имеет диапазон +/–1,7E308 с точностью не менее 15 знаков.

Завершение блока, относящегося только к системам Майкрософт

См. также
Хранение базовых типов
Тип long double
Статья • 03.03.2023 • Чтение занимает 2 мин

Тип long double идентичен типу double.

См. также
Хранение базовых типов
Неполные типы
Статья • 03.03.2023 • Чтение занимает 2 мин

Неполный тип — это тип, который описывает идентификатор, но не содержит


информацию, необходимую для определения размера идентификатора. Неполным
типом может быть:

Тип структуры, для которой еще не были определены члены.

Тип объединения для которого еще не были определены члены.

Тип массива, для которого еще не были определены размерности.

Тип void является неполным типом, который невозможно сделать полным. Чтобы
дополнить неполный тип, укажите отсутствующие данные. В следующих примерах
показано, как создать и дополнить неполные типы.

Чтобы создать неполный тип структуры, объявите тип структуры, не указывая


ее члены. В этом примере указатель ps указывает на неполный тип структуры
с именем student .

struct student *ps;

Чтобы дополнить неполный тип структуры, объявите тот же самый тип


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

struct student

int num;

} /* student structure now completed */

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

char a[]; /* a has incomplete type */

Чтобы дополнить неполный тип массива, объявите то же самое имя ниже в


той же самой области видимости и задайте его число повторений, как в
следующем примере:

char a[25]; /* a now has complete type */

См. также
Объявления и типы
Объявления Typedef
Статья • 03.03.2023 • Чтение занимает 2 мин

Объявление typedef — это объявление с typedef в качестве класса хранения.


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

Объявление typedef интерпретируется точно так же, как и объявление переменной


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

Синтаксис
declaration :

  declaration-specifiers init-declarator-list необ. ;

declaration-specifiers :

  storage-class-specifier declaration-specifiers необ.

  type-specifier declaration-specifiers необ.

  type-qualifier declaration-specifiers необ.

storage-class-specifier :

  typedef

type-specifier :

  void

  char

  short

  int

  long

  float

  double

  signed

  unsigned

  struct-or-union-specifier

  enum-specifier

  typedef-name
typedef-name :

  identifier

Объявление typedef не создает новые типы. Оно создает синонимы для


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

Имена typedef используют то же пространство имен, что и обычные


идентификаторы. (Дополнительные сведения см. в разделе Пространства имен.)
Таким образом, программа может иметь имя typedef и идентификатор локальной
области с одинаковым именем. Например:

typedef char FlagType;

int main()

int myproc( int )

int FlagType;

При объявлении идентификатора локальной области с тем же именем, что и


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

typedef char FlagType;

const FlagType x;

Чтобы повторно использовать имя FlagType для идентификатора, члена структуры


или члена объединения, необходимо указать тип:

const int FlagType; /* Type specifier required */

Недостаточно сказать

const FlagType; /* Incomplete specification */

поскольку FlagType воспринимается как часть типа, а не как заново объявляемый


идентификатор. Это объявление недопустимо, как и

int; /* Illegal declaration */

С помощью можно объявить любой тип typedef , включая типы указателей,


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

Имена typedef можно использовать, чтобы сделать код более понятным. Все три
следующих объявления signal задают один и тот же тип, причем в первом
объявлении имена typedef не используются.

typedef void fv( int ), (*pfv)( int ); /* typedef declarations */

void ( *signal( int, void (*) (int)) ) ( int );

fv *signal( int, fv * ); /* Uses typedef type */

pfv signal( int, pfv ); /* Uses typedef type */

Примеры
В следующих примерах показаны объявления typedef:

typedef int WHOLE; /* Declares WHOLE to be a synonym for int */

Например, WHOLE теперь можно использовать в объявлении переменной, такой как


WHOLE i; или const WHOLE i; . Однако объявление long WHOLE i; недопустимо.

C
typedef struct club

char name[30];

int size, year;

} GROUP;

Этот оператор объявляет имя GROUP как тип структуры с тремя членами. Поскольку
также указан тег структуры, club , в объявлениях можно использовать как имя
typedef ( GROUP ), так и тег структуры. Необходимо использовать ключевое struct
слово с тегом , а ключевое struct слово нельзя использовать с именем typedef.

typedef GROUP *PG; /* Uses the previous typedef name

to declare a pointer */

Тип PG объявлен как указатель на тип GROUP , который, в свою очередь, определен
как тип структуры.

typedef void DRAWF( int, int );

В этом примере задан тип DRAWF для функции, не возвращающей никакого


значения и принимающей два аргумента int. Это означает, например, что
объявление

DRAWF box;

эквивалентно объявлению

void box( int, int );

См. также
Объявления и типы
Расширенные атрибуты классов
хранения в C
Статья • 26.09.2022 • Чтение занимает 2 мин

Блок, относящийся только к системам Microsoft

Более актуальные сведения об атрибутах класса хранения см. в справочнике по


__declspec на C++.

Расширенный синтаксис атрибутов упрощает и стандартизирует расширения для


систем Microsoft в соответствии с правилами языка C. К атрибутам класса хранения,
в которых используется расширенный синтаксис атрибутов, относятся: thread ,
naked , dllimport и dllexport .

Расширенный синтаксис атрибутов для указания данных класса хранения


использует ключевое слово __declspec , которое указывает, что экземпляр
заданного типа должен храниться с атрибутом класса хранения для систем
Майкрософт ( thread , naked , dllimport или dllexport ). Примеры других
модификаторов класса хранения включают ключевые слова static и extern . Но
эти ключевые слова входят в стандарт ISO C, и как таковые они не используются с
расширенным синтаксисом атрибутов.

Синтаксис
storage-class-specifier :

  __declspec ( extended-decl-modifier-seq ) /* Только для систем Майкрософт */

extended-decl-modifier-seq : /* Только для систем Майкрософт */

  extended-decl-modifier необ.

  extended-decl-modifier-seq extended-decl-modifier

extended-decl-modifier : /* Только для систем Майкрософт */

  thread

  naked

  dllimport

  dllexport

Модификаторы объявления разделяются пробелами. Обратите внимание, что


extended-decl-modifier-seq может быть пустым, в этом случае ключевое слово
__declspec не оказывает влияния.

Атрибуты класса хранения thread , naked , dllimport и dllexport  — это свойства


исключительно объявления объекта или функции, к которым они применяются.
Они не переопределяют атрибуты типа самой функции. Атрибут thread влияет
только на данные. Атрибут naked влияет только на функции. Атрибуты dllimport и
dllexport влияют только на функции и данные.

Завершение блока, относящегося только к системам Майкрософт

См. также
Объявления и типы
Импорт и экспорт DLL
Статья • 03.03.2023 • Чтение занимает 2 мин

Блок, относящийся только к системам Microsoft

Модификаторы класса хранения dllimport и dllexport — это расширения языка С,


характерные для систем Microsoft. Эти модификаторы определяют интерфейс DLL
клиента (исполняемый файл или другая библиотека DLL). Дополнительные
сведения об использовании этих модификаторов см. в статье dllexport, dllimport.

Завершение блока, относящегося только к системам Майкрософт

См. также
Расширенные атрибуты классов хранения в C
Naked (C)
Статья • 03.03.2023 • Чтение занимает 2 мин

Блок, относящийся только к системам Microsoft

Атрибут класса хранения naked является расширением языка C для систем


Microsoft. Код функций, объявленных с этим атрибутом, создается компилятором
без кода пролога и эпилога. Благодаря этому вы можете вставить в качестве
пролога и эпилога свой собственный код на языке ассемблера. Функции с
атрибутом naked полезны для написания драйверов виртуальных устройств.

Дополнительные сведения см. в статье Функции Naked.

Завершение блока, относящегося только к системам Майкрософт

См. также
Расширенные атрибуты классов хранения в C
локальное хранилище потока
Статья • 03.03.2023 • Чтение занимает 2 мин

Блок, относящийся только к системам Microsoft

Локальное хранилище потока (TLS) — это механизм, с помощью которого в


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

Язык Microsoft C включает расширенный атрибут класса хранения, поток, который


используется вместе с ключевым словом __declspec для объявления локальной
переменной потока. В следующем примере кода показано, как объявлять
целочисленную локальную переменную потока и инициализировать её некоторым
значением:

__declspec( thread ) int tls_i = 1;

При объявлении статистически связанных локальных переменных потока


необходимо соблюдать следующие рекомендации.

Локальные переменные потока с динамической инициализацией


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

Атрибут thread можно применять только к объявлениям и определениям


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

#define Thread __declspec( thread )

Thread void func(); /* Error */

Атрибут thread можно задать только для элементов данных со статической


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

#define Thread __declspec( thread )

void func1()

Thread int tls_i; /* Error */

int func2( Thread int tls_i ) /* Error */

return tls_i;

Атрибут потока следует использовать для объявления и определения


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

#define Thread __declspec( thread )

extern int tls_i; /* This generates an error, because the */

int Thread tls_i; /* declaration and the definition differ. */

Атрибут thread невозможно использовать в качестве модификатора типа.


Например, следующий код вызовет ошибку компиляции:

char *ch __declspec( thread ); /* Error */

Адрес локальной переменной потока не является постоянным, и любое


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

#define Thread __declspec( thread )

Thread int tls_i;

int *p = &tls_i; /* Error */

В языке C разрешена инициализация переменной с помощью выражения со


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

#define Thread __declspec( thread )

Thread int tls_i = tls_i; /* Error */

int j = j; /* Error */

Thread int tls_i = sizeof( tls_i ) /* Okay */

Обратите внимание, что выражение sizeof, включающее инициализируемую


переменную, не является ссылкой на себя и является допустимым.

Использование __declspec(thread) может повлиять на отложенную загрузку


импортов DLL.

Дополнительные сведения об использовании атрибута потока см. в статье о


поддержке многопоточности.

Завершение блока, относящегося только к системам Майкрософт

См. также
Расширенные атрибуты классов хранения в C
Выражения и присваивания
Статья • 03.03.2023 • Чтение занимает 2 мин

В этом разделе описывается, как создать выражения и присвоить значения в языке


C. Константы, идентификаторы, строки и вызовы функций являются операндами,
управляемыми в выражениях. В языке C имеются все простые операторы языка. В
этом разделе описываются эти простые операторы и операторы, которые являются
уникальными для C или Microsoft C. Рассматриваются следующие темы:

Выражения L-Value и R-Value

Константные выражения

Побочные эффекты

Точки последовательности

Инструкции

Приоритет операторов

Преобразования типов

Приведение типов

См. также
Справочник по языку C
Операнды и выражения
Статья • 03.03.2023 • Чтение занимает 2 мин

Операнд — это сущность, с которой оператор выполняет какие-либо действия.


Выражение — это последовательность операторов и операндов, выполняющая
действия ниже в любой комбинации.

Вычисление значения

Назначение объекта или функции

Создание побочных эффектов

Операнды в C включают константы, идентификаторы, строки, вызовы функций,


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

См. также
Выражения и присваивания
Первичные выражения в C
Статья • 03.03.2023 • Чтение занимает 2 мин

Основные выражения являются стандартными блоками более сложных


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

Синтаксис
primary-expression :

  identifier

  constant

  string-literal

  ( expression )

  generic-selection

expression :

  assignment-expression

  expression , assignment-expression

См. также
Выделенный фрагмент _GenericОперанды и выражения
Идентификаторы в первичных
выражениях
Статья • 03.03.2023 • Чтение занимает 2 мин

Идентификаторы могут иметь целочисленный тип, float , enum , struct , union , тип
массива, тип указателя или тип функции. Идентификатор представляет собой
основное выражение, его он был объявлен как обозначение объекта (тогда это l-
значение) или как функция (тогда это обозначение функции). Определение
левостороннего значения приводится в статье Выражения L-Value и R-Value.

Значение указателя, представленное идентификатором массива, не является


переменной, поэтому идентификатор массива не может быть левым операндом
операции присваивания и, следовательно, не является изменяемым l-значением.

Идентификатор, объявленный как функция, представляет собой указатель,


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

См. также
Первичные выражения в C
Константы в первичных выражениях
Статья • 03.03.2023 • Чтение занимает 2 мин

Константный операнд имеет значение и тип того константного значения, которое


он представляет. Символьная константа имеет тип int . Целочисленная константа
имеет тип int , long , unsigned int или unsigned long (в зависимости от размера
целого числа и от способа указания значения). Дополнительные сведения см. в
статье Константы.

См. также
Первичные выражения в C
Строковые литералы в первичных
выражениях
Статья • 03.03.2023 • Чтение занимает 2 мин

"Строковый литерал" — это символ, расширенный символ или последовательность


соседних символов, заключенных в двойные кавычки. Поскольку эти символы не
являются переменными, ни строковые литералы, ни любой из их элементов не
могут использоваться в качестве левого операнда в операции присваивания. Тип
строкового литерала — это массив char (или массив wchar_t для двухбайтовых
строковых литералов). Массивы в выражениях преобразуются в указатели.
Дополнительные сведения о строках см. в статье Строковые литералы.

См. также
Первичные выражения в C
Выражения в скобках
Статья • 03.03.2023 • Чтение занимает 2 мин

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

( 10 + 5 ) / 5

скобки вокруг 10 + 5 означают, что значение 10 + 5 вычисляется первым и


становится левым операндом для оператора деления ( / ). Результат выражения (
10 + 5 ) / 5 — 3. Без скобок результатом выражения 10 + 5 / 5 было бы значение

11.

Хотя круглые скобки влияют на группировку операндов в выражении, они не могут


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

( i++ +1 ) * ( 2 + i )

Компилятор может вычислить две части умножения в любом порядке. Если


начальное значение i равно нулю, то все выражение может быть вычислено как
один из двух следующих операторов.

( 0 + 1 + 1 ) * ( 2 + 1 )

( 0 + 1 + 1 ) * ( 2 + 0 )

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


Побочные эффекты.

См. также
Первичные выражения в C
Выбор с использованием _Generic
(C11)
Статья • 03.03.2023 • Чтение занимает 2 мин

Ключевое слово _Generic используется для написания кода, который выбирает


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

Например, выражение _Generic(42, int: "integer", char: "character", default:


"unknown"); оценивает тип 42 и ищет соответствующий тип int в списке. При
обнаружении типа возвращается "integer" .

Синтаксис
generic-selection :

  _Generic ( assignment-expression , assoc-list )

assoc-list :

  association

  assoc-list , association

association :

  type-name : assignment-expression

  default : assignment-expression

Первое выражение assignment-expression называется управляющим. Тип


управляющего выражения определяется во время компиляции и сопоставляется с
assoc-list , чтобы найти выражение для вычисления и возврата. Само

управляющее выражение не вычисляется. Например, при наличии


_Generic(intFunc(), int: "integer", default: "error"); intFunc не вызывается во
время выполнения.

Когда определяется тип управляющего выражения, const , volatile и restrict


удаляются перед сопоставлением с assoc-list .

Записи в assoc-list , которые не были выбраны, не вычисляются.

Ограничения
assoc-list не может указывать один и тот же тип более одного раза.

assoc-list не может указывать типы, совместимые друг с другом, например


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

Пример
Как один из вариантов, _Generic можно использовать в макросе. Файл заголовка
<tgmath.h> использует _Generic для вызова правильной математической функции
в зависимости от типа аргумента. Например, макрос для cos сопоставляет вызов,
содержащий число с плавающей точкой, с cosf , одновременно сопоставляя вызов,
содержащий сложное число двойной точности, с ccos .

В приведенном ниже примере показано, как написать макрос, который определяет


тип переданного в него аргумента. Если в assoc-list нет записей, совпадающих с
управляющим выражением, создается "unknown" :

// Compile with /std:c11

#include <stdio.h>

/* Get a type name string for the argument x */

#define TYPE_NAME(X) _Generic((X), \

int: "int", \

char: "char", \

double: "double", \

default: "unknown")

int main()

printf("Type name: %s\n", TYPE_NAME(42.42));

// The following would result in a compile error because

// 42.4 is a double, doesn't match anything in the list,

// and there is no default.

// _Generic(42.4, int: "integer", char: "character"));

/* Output:

Type name: double

*/

Требования
Выполните сборку с помощью команды /std:c11.

Пакет Windows SDK 10.0.20348.0 (версия 2104) или выше. Скачать последнюю


версию Windows SDK можно здесь. Инструкции по установке и использованию
пакета SDK для разработки C11 и C17 см. в статье Включение поддержки C11 и C17
в Visual Studio.

См. также
/std (определение стандартной версии языка)

Математические символы универсального типа


Выражения L-Value и R-Value
Статья • 03.03.2023 • Чтение занимает 2 мин

Выражения, которые ссылаются на адреса памяти, называются выражениями l-


значения. L-значение представляет значение locator или left области хранения, что
означает, что оно может отображаться слева от знака равенства ( = ). L-значения
часто являются идентификаторами.

Выражения, ссылающиеся на изменяемые расположения, называются


"изменяемыми l-значениями". Изменяемое l-значение не может иметь тип
массива, неполный тип или тип с атрибутом const . Чтобы структуры и
объединения были изменяемыми l-значениями, они не должны содержать
элементы с атрибутом const . Имя идентификатора обозначает место хранения, а
значение переменной является значением, хранящимся в этом месте.

Идентификатор является изменяемым l-значением, если он ссылается на адрес


памяти и если он принадлежит арифметическому типу, типу структуры,
объединения или указателя. Например, если ptr — указатель на область хранения,
то *ptr будет изменяемым l-значением, которое обозначает область хранения, на
которую указывает ptr .

Любое из следующих выражений C может быть выражением l-значения.

Идентификатор целочисленного типа, типа указателя, структуры или


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

Подстрочного выражения ( [ ] ), которое не вычисляет массив

Выражение выбора элемента ( -> или . )

Унарное выражение ( * ), не ссылающееся на массив

Выражение l-значения в скобках.

Объект const (неизменяемое l-значение).

Термин "r-значение" иногда используется, чтобы описать значение выражения и


отличить его от l-значения. Все l-значения являются r-значениями, но не все r-
значения являются l-значениями.

Блок, относящийся только к системам Microsoft


Microsoft C включает расширение стандарта ANSI C, которое позволяет
использовать приведения l-значений в качестве l-значений, если размер объекта
не удлиняется до приведения. (Дополнительные сведения см. в разделе
Преобразования приведения типов.) В следующем примере показана эта функция:

char *p ;

short i;

long l;

(long *) p = &l ; /* Legal cast */

(long) i = l ; /* Illegal cast */

В Microsoft C расширения Microsoft по умолчанию включены. Используйте


параметр компилятора /Za для отключения этих расширений.

Завершение блока, относящегося только к системам Майкрософт

См. также
Операнды и выражения
Постоянные выражения в C
Статья • 03.03.2023 • Чтение занимает 2 мин

Константное выражение вычисляется во время компиляции, а не во время


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

Синтаксис
constant-expression :

  conditional-expression

conditional-expression :

  logical-OR-expression

  logical-OR-expression ? expression : conditional-expression

expression :

  assignment-expression

  expression , assignment-expression

assignment-expression :

  conditional-expression

  unary-expression assignment-operator assignment-expression

assignment-operator : один из следующих символов:

  = *= /= %= += -= <<= >>= &= ^= |=

Нетерминальные выражения для оператора объявления структуры, перечислителя,


прямого оператора объявления, прямого абстрактного оператора объявления и
оператора с меткой содержат нетерминальное выражение constant-expression .

Для определения размера члена битового поля структуры, значения константы


перечисления, размера массива или значения константы case нужно использовать
целочисленное константное выражение.
На константные выражения, используемые в директивах препроцессора,
накладываются некоторые ограничения. Они называются ограниченными
константными выражениями. Ограниченное константное выражение не может
содержать выражения sizeof , константы перечисления, приведения типов к
любому типу или константы с плавающей запятой. Но оно может содержать
специальное константное выражение defined ( identifier ) .

См. также
Операнды и выражения
Вычисление выражений (C)
Статья • 03.03.2023 • Чтение занимает 2 мин

Выражения, включающие назначение, унарный инкремент, унарный декремент


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

Побочные эффекты — это изменения, вызванные вычислением выражения.


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

См. также
Операнды и выражения
Побочные эффекты
Статья • 03.03.2023 • Чтение занимает 2 мин

Порядок вычисления выражений определяется конкретной реализацией, за


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

add( i + 1, i = j + 2 );

myproc( getc(), getc() );

Аргументы вызова функции могут вычисляться в любом порядке. Выражение i + 1


может вычисляться до выражения i = j + 2 или выражение i = j + 2 может
вычисляться до выражения i + 1 . В каждом случае результат будет различным.
Аналогично, невозможно гарантировать, какие символы фактически будут
переданы в myproc . Поскольку унарные операции инкремента и декремента
связаны с присваиваниями, такие операции могут вызывать побочные эффекты,
как показано в следующем примере:

x[i] = i++;

В этом примере значение изменяемой переменной x непредсказуемо. Значение


индекса может быть как новым, так и старым значением переменной i . Результат
может отличаться в различных компиляторах или при разных уровнях
оптимизации.

Поскольку язык C не определяет порядок вычисления побочных эффектов, оба


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

См. также
Вычисление выражений
Точки следования C
Статья • 03.03.2023 • Чтение занимает 2 мин

Между последовательными «точками последовательности» значение объекта


может быть изменено только один раз с помощью выражения. В языке C
определены следующие точки последовательности.

Левый операнд оператора логического И ( && ). Перед продолжением левый


операнд оператора логического AND полностью вычисляется и учитываются
все побочные эффекты. Если левый операнд имеет значение False (0),
значение второго операнда не вычисляется.

Левый операнд оператора логического OR ( || ). Перед продолжением левый


операнд оператора логического OR полностью вычисляется и учитываются
все побочные эффекты. Если левый операнд имеет значение True (не равен
нулю), значение второго операнда не вычисляется.

Левый операнд оператора запятой. Перед продолжением левый операнд


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

Оператор вызова функции. Перед переходом в функцию все ее аргументы


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

Первый операнд условного оператора. Перед продолжением первый


операнд условного оператора полностью вычисляется и учитываются все
побочные эффекты.

Конец выражения полной инициализации (т. е. выражения, которое не


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

Выражение в операторе выражения. Операторы-выражения состоят из


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

Управляющее выражение в операторе выбора ( if или switch ). Перед


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

Управляющее выражение оператора while или do . Перед выполнением


любых операторов в следующей итерации цикла while или do это выражение
полностью вычисляется и вступают в силу все побочные эффекты.

Каждое из трех выражений оператора for . Перед выполнением любых


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

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


функцию это выражение полностью вычисляется и учитываются все
побочные эффекты.

См. также
Вычисление выражений
Операторы в C
Статья • 03.03.2023 • Чтение занимает 2 мин

Операторы C являются подмножеством встроенных операторов C++.

Существуют три типа операторов. Унарное выражение состоит либо из унарного


оператора, за которым следует операнд, либо из ключевого слова sizeof или
_Alignof , за которым следует выражение. Выражением может быть либо имя

переменной, либо выражение приведения типа. В последнем случае выражение


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

В языке C имеются следующие унарные операторы:

Символ name

- ~ ! Операторы отрицания и дополнения

* & Операторы косвенного обращения и взятия адреса

_Alignof Оператор выравнивания (начиная с выпуска C11)

sizeof Оператор определения размера

+ Оператор унарного сложения

++ -- Унарные операторы инкремента и декремента

Бинарные операторы имеют левую ассоциативность, т. е. выполняются слева


направо. В языке C имеются следующие бинарные операторы:

Символ name

* / % Мультипликативные операторы

+ - Аддитивные операторы

<< >> Операторы сдвига

< > <= >= == != Реляционные операторы

& | ^ битовые операторы;

&& || Логические операторы


Символ name

, Оператор последовательного вычисления

Базовый оператор ( :> ), который поддерживается в предыдущих версиях


компилятора Microsoft C для 16-разрядных систем, описывается в кратком обзоре
синтаксиса языка C.

Оператор условного выражения имеет более низкий приоритет, чем бинарные


выражения, и отличается от них тем, что имеет правую ассоциативность.

К выражениям с операторами также относятся выражения присваивания, в


которых используются унарные или бинарные операторы присваивания. Унарные
операторы присваивания — это операторы инкремента и декремента ( ++ и --
соответственно). Бинарные операторы присваивания — это оператор простого
присваивания ( = ) и составные операторы присваивания. Все составные
операторы присваивания состоят из другого бинарного оператора и оператора
простого присваивания.

См. также
Выражения и присваивания
Приоритет и порядок оценки
Статья • 03.03.2023 • Чтение занимает 4 мин

Приоритет и ассоциативность операторов C влияют на группировку и вычисление


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

В следующей таблице указывается приоритет и ассоциативность (порядок


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

Приоритет и ассоциативность операторов C


Символ 1 Тип операции Ассоциативность

[ ] ( ) . ->
Выражение Слева направо
++ -- (постфикс)

sizeof & * + - ~ !
Унарный Справа налево
++ -- (префикс)

typecasts Унарный Справа налево

* / % Мультипликативный Слева направо

+ - Аддитивный Слева направо

<< >> Побитовый сдвиг Слева направо

< > <= >= Реляционный Слева направо

== != Равенство Слева направо

& Побитовое И Слева направо

^ Побитовое исключающее ИЛИ Слева направо


Символ 1 Тип операции Ассоциативность

| Побитовое включающее ИЛИ Слева направо

&& Логическое И Слева направо

|| Логическое ИЛИ Слева направо

? : Условное выражение Справа налево

= *= /= %=
Простое и составное присваивание 2 Справа налево
+= -= <<= >>= &=

^= |=

, Последовательное вычисление Слева направо

1
 Операторы перечислены в порядке убывания приоритета. Если в одной строке
или группе находится несколько операторов, они имеют равный приоритет.

2
 Все операторы простого и составного присваивания имеют равный приоритет.

Выражение может содержать несколько операторов с равным приоритетом. Если


несколько таких операторов находятся в выражении на одном уровне, вычисление
выполняется согласно их ассоциативности (либо справа налево, либо слева
направо). Направление вычисления не влияет на результаты выражений, в которых
на одном и том же уровне находится более одного оператора умножения ( * ) или
сложения ( + ) либо бинарного побитового оператора ( & , | или ^ ). Порядок
операций не определен языком. Если компилятор может гарантировать
согласованный результат, то он может вычислять такие выражения в любом
порядке.

Только операторы последовательного вычисления ( , ), логический оператор AND


( && ), логический оператор OR ( || ), операторы условных выражений ( ? : ) и
операторы вызова функции создают точки следования и тем самым гарантируют
определенный порядок вычисления своих операндов. Оператор вызова функции
представляет собой пару скобок, следующих за идентификатором функции.
Оператор последовательного вычисления ( , ) гарантирует, что его операнды будут
вычисляться слева направо (оператор-запятая в вызове функции не является
оператором последовательного вычисления и не предоставляет таких гарантий).
Дополнительные сведения см. в статье Точки следования C.

Логические операторы также гарантируют вычисление своих операндов слева


направо. Однако они вычисляют наименьшее количество операндов, необходимое
для определения результата выражения. Это называется "сокращенным
вычислением". Таким образом, некоторые операнды в выражении могут не
вычисляться. Например, в выражении:

x && y++

второй операнд, y++ , вычисляется, только если x имеет значение true (не равно
нулю). Таким образом, если y дает значение false (0), то x не увеличивается.

Примеры
Ниже приводится несколько примеров автоматической привязки выражений
компилятором:

Выражение Автоматическая привязка

a & b || c (a & b) || c

a = b || c a = (b || c)

q && r || s-- (q && r) || s--

В первом выражении оператор побитового И ( & ) имеет более высокий приоритет,


чем оператор логического ИЛИ ( || ), поэтому a & b формирует первый операнд
операции логического ИЛИ.

Во втором выражении оператор логического ИЛИ ( || ) имеет более высокий


приоритет, чем оператор простого присваивания ( = ), поэтому b || c группируется
как правый операнд присваивания. Обратите внимание, что операнду a
присваивается значение 0 или 1.

В третьем примере приводится правильно сформированное выражение, которое


может дать непредвиденный результат. Оператор логического И ( && ) имеет более
высокий приоритет, чем оператор логического ИЛИ ( || ), поэтому q && r
становится одним операндом. Так как логические операторы гарантируют
вычисление операндов слева направо, то операция q && r вычисляется раньше,
чем s-- . Но если выражение q && r имеет ненулевое значение, то s-- не
вычисляется и s не уменьшается. Если то, что значение s не будет уменьшено,
может вызвать проблемы в вашей программе, вы можете либо поставить s--
первым операндом в выражении, либо выполнить декремент s в отдельной
операции.

Следующее выражение недопустимо и приводит к созданию диагностического


сообщения во время компиляции.
Недопустимое выражение Группировка по умолчанию

p == 0 ? p += 1: p += 2 ( p == 0 ? p += 1 : p ) += 2

В этом выражении оператор равенства ( == ) имеет наибольший приоритет, поэтому


выражение p == 0 становится одним операндом. Далее по приоритету следует
оператор условного выражения ( ? : ). Его первым операндом является p == 0 , а
вторым — p += 1 . Однако последним операндом оператора условного выражения
считается не p , а p += 2 , поскольку в этом случае p имеет более тесную привязку к
оператору условного выражения, чем к оператору составного присваивания.
Синтаксическая ошибка возникает потому, что операция += 2 не имеет левого
операнда. Для того чтобы избежать ошибок такого рода и сделать код более
читаемым, необходимо использовать скобки. Так, предыдущий пример можно
исправить при помощи круглых скобок, как показано ниже:

( p == 0 ) ? ( p += 1 ) : ( p += 2 )

См. также
Операторы в C
Обычные арифметические
преобразования
Статья • 03.03.2023 • Чтение занимает 2 мин

Большинство операторов C выполняют преобразования типов для приведения


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

Арифметические преобразования, представленные ниже, называются обычными


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

1. Если какой-либо из операндов имеет тип long double , то другой операнд


преобразуется в тип long double .

2. Если вышеуказанное условие не выполняется, а один из операндов имеет тип


double , то второй операнд преобразуется в тип double .

3. Если два вышеуказанных условия не выполняются, а один из операндов имеет


тип float , то второй операнд преобразуется в тип float .

4. Если три вышеуказанных условия не выполняются (ни один из операндов не


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

Если какой-либо из операндов имеет тип unsigned long , то другой


операнд преобразуется в тип unsigned long .

Если вышеуказанное условие не выполняется, при этом один из


операндов имеет тип long , а второй — тип unsigned int , то оба
операнда преобразуются в тип unsigned long .
Если два вышеуказанных условия не выполняются, а один из операндов
имеет тип long , то второй операнд преобразуется в тип long .

Если три вышеуказанных условия не выполняются и какой-либо из


операндов имеет тип unsigned int , то другой операнд преобразуется в
тип unsigned int .

Если ни одно из вышеуказанных условий не соблюдается, то оба


операнда преобразуются в тип int .

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

float fVal;

double dVal;

int iVal;

unsigned long ulVal;

dVal = iVal * ulVal; /* iVal converted to unsigned long

* Uses step 4.

* Result of multiplication converted to double

*/

dVal = ulVal + fVal; /* ulVal converted to float

* Uses step 3.

* Result of addition converted to double

*/

См. также
Операторы в C
Постфиксные операторы
Статья • 03.03.2023 • Чтение занимает 2 мин

Постфиксные операторы имеют самый высокий приоритет (наиболее тесную


привязки) в вычислении выражений.

Синтаксис
postfix-expression :

  primary-expression

  postfix-expression [ expression ]

  postfix-expression ( argument-expression-list opt )

  postfix-expression . identifier

  postfix-expression -> identifier

  postfix-expression ++

  postfix-expression --

Операторы на этом уровне приоритета являются индексами массива,


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

См. также
Операторы в C
Одномерные массивы
Статья • 03.03.2023 • Чтение занимает 2 мин

Постфиксное выражение, за которым следует выражение в квадратных скобках ( [


] ), представляет собой подстрочный элемент объекта массива. Подстрочного

выражения представляет значение по адресу, которое находится expression за


пределами postfix-expression при выражении как

postfix-expression [ expression ]

Как правило, значение, представленное , postfix-expression является значением


указателя, например идентификатором массива, и expression является
целочисленным значением. Однако все, что требуется синтаксически, это то, что
одно из выражений имеет тип указателя, а другое — целочисленный тип.
Целочисленное значение может находиться в postfix-expression позиции , а
значение указателя — в квадратных скобках в expression позиции , или
"подстрочный". Например, такой код является допустимым:

// one_dimensional_arrays.c

int sum, *ptr, a[10];

int main() {

ptr = a;

sum = 4[ptr];

Подстрочные выражения часто используются для ссылки на элементы массива, но


можно применить подстрочный индекс к любому указателю. Независимо от
порядка значений, expression их необходимо заключать в квадратные скобки ( [
] ).

Выражение индекса вычисляется путем добавления целочисленного значения к


значению указателя, а результат передается в оператор косвенного обращения ( * ).
(Этот механизм обсуждается в статье Операторы косвенного обращения и адреса
операнда.) В конечном итоге в случае одномерного массива следующие 4
выражения эквивалентны, при условии что a является указателем, а b  — целым
числом:

C
a[b]

*(a + b)

*(b + a)

b[a]

Правила преобразования для оператора сложения приведены в разделе


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

Например, предположим, что идентификатор line ссылается на массив значений


int . Для вычисления выражения индекса line[ i ] используется следующая

процедура:

1. Целочисленное значение i умножается на количество байт, определенное


как длина элемента int . Преобразованное значение i представляет позиции
i   int .

2. Это преобразованное значение добавляется к исходному значению указателя


( line ) для получения адреса, представляющего позиции, смещенные на
i   int относительно line .

3. Оператор косвенного обращения применяется к новому адресу. Результат


представляет собой значение элемента массива в этой позиции (интуитивно,
line [ i ] ).

Выражение индекса line[0] представляет значение первого элемента массива line,


поскольку смещение от адреса, представляемого line , равно 0. Аналогично,
выражение line[5] ссылается на элемент, смещенный на 5 позиций относительно
line, или на шестой элемент массива.

См. также
Оператор индекса:
Многомерные массивы (C)
Статья • 03.03.2023 • Чтение занимает 2 мин

Индексное выражение также может иметь несколько индексов, как показано ниже:

expression1 [ expression2 ] [ expression3 ] /*...*/ ;

Индексные выражения связываются в направлении слева направо. Сначала


вычисляется крайнее левое подстрочный выражение expression1[ expression2 ] , .
Адрес, полученный в результате добавления expression1 и expression2
формирования выражения указателя; затем expression3 добавляется в это
выражение указателя, чтобы сформировать новое выражение указателя и т. д.,
пока не будет добавлено последнее выражение подстрока. Оператор косвенного
обращения ( * ) применяется после вычисления последнего подстрочного
выражения, если конечное значение указателя не соответствует типу массива.

Выражения с несколькими индексами ссылаются на элементы "многомерных


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

Примеры
В следующих примерах массив с именем prop объявляется с тремя элементами,
каждый из которых представляет собой массив 4x6, содержащий значения типа
int .

int prop[3][4][6];

int i, *ip, (*ipp)[6];

Ссылка на массив prop выглядит следующим образом:

i = prop[0][0][1];

В примере показано, как ссылаться на второй отдельный int элемент . prop


Массивы хранятся по строкам, поэтому последний индекс изменяется быстрее;
выражение prop[0][0][2] ссылается на следующий (третий) элемент массива и т. д.

i = prop[2][1][3];

Этот оператор представляет собой более сложную ссылку на отдельный элемент


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

1. Первый индекс, 2 , умножается на размер массива int 4x6 и добавляется к


значению указателя prop . Результат указывает на третий массив 4x6 массива
prop .

2. Второй индекс, 1 , умножается на размер 6-элементного массива int и


добавляется к адресу, представленному значением prop[2] .

3. Каждый элемент массива из 6 элементов является значением int , поэтому


окончательный индекс , 3 умножается на размер int перед добавлением в
prop[2][1] . Результирующий указатель указывает на четвертый элемент 6-

элементного массива.

4. Оператор косвенного обращения применяется к значению указателя.


Результат — элемент int , расположенный по этому адресу.

В следующих двух примерах показаны случаи, когда оператор косвенного


обращения не применяется.

ip = prop[2][1];

ipp = prop[2];

В первом из этих операторов выражение prop[2][1] является допустимой ссылкой


на трехмерный массив prop ; оно ссылается на массив из 6 элементов
(объявленный ранее). Так как значение указателя обращается к массиву, оператор
косвенного обращения не применяется.

Аналогично результат выражения prop[2] во втором операторе ipp = prop[2];


представляет значение указателя, указывающее на двумерный массив.
См. также
Оператор индекса:
Вызов функций (C)
Статья • 03.03.2023 • Чтение занимает 2 мин

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

Синтаксис
postfix-expression :

  postfix-expression ( argument-expression-list opt )

argument-expression-list :

  assignment-expression

  argument-expression-list , assignment-expression

Должен postfix-expression вычисляться по адресу функции (например,


идентификатору функции или значению указателя функции) и argument-expression-
list представляет собой список выражений (разделенных запятыми), значения

которых (аргументы) передаются в функцию. Аргумент argument-expression-list


может быть пустым.

Выражение вызова функции имеет значение и тип возвращаемого значения


функции. Функция не может вернуть объект типа массива. Если тип возвращаемого
значения функции — void (то есть объявлено, что функция никогда не возвращает
значение), выражение вызова функции также имеет тип void . Дополнительные
сведения см. в разделе Вызовы функций.

См. также раздел


Оператор вызова функции: ()
Члены структур и объединений
Статья • 03.03.2023 • Чтение занимает 2 мин

Выражение выбора члена ссылается на члены структур и объединений. Такое


выражение имеет значение и тип выбранного члена.

postfix-expression . identifier

postfix-expression -> identifier

В следующем списке приводится описание двух форм выражений выбора члена.

1. В первой форме postfix-expression представляет значение struct типа или


union и identifier присваивает имя члену указанной структуры или

объединения. Значение операции равно identifier значению и является l-


значением, если postfix-expression является l-значением. Дополнительные
сведения см. в разделах L-Value и R-Value Expressions.

2. Во второй форме postfix-expression представляет указатель на структуру или


объединение и identifier присваивает имя члену указанной структуры или
объединения. Значение равно значению , identifier а является l-значением.

Две формы выражений выбора члена оказывают аналогичное влияние.

Фактически выражение, включающее оператор выбора члена ( -> ), является


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

expression->identifier

эквивалентно

(*expression).identifier

когда expression — значение указателя.


Примеры
Данное объявление структуры представлено в следующих примерах.
Дополнительные сведения об операторе косвенного обращения ( * ),
используемом в этих примерах, см. в статье Операторы косвенного обращения и
определения адреса.

struct pair

int a;

int b;

struct pair *sp;

} item, list[10];

Выражение выбора члена для структуры item выглядит следующим образом.

item.sp = &item;

В этом примере адрес item структуры назначается члену sp структуры. Это


означает, что item содержит указатель на себя.

(item.sp)->a = 24;

В этом примере выражение item.sp указателя используется с оператором выбора


элемента ( -> ) для присвоения значения члену a .

list[8].b = 12;

На примере этого оператора показано, как выбрать отдельный член структуры из


массива структур.

См. также
Операторы доступа к членам: . и ->
Постфиксные операторы увеличения
и уменьшения в C
Статья • 03.03.2023 • Чтение занимает 2 мин

Операнды операторов постфиксных инкремента и декремента являются


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

Синтаксис
postfix-expression :

  postfix-expression ++

  postfix-expression --

Результатом операции постфиксного инкремента или декремента является


значение операнда. После получения результата значение операнда
инкрементируется (или декрементируется). В следующем примере кода
демонстрируется оператор постфиксного инкремента.

if ( var++ > 0 )

*p++ = *q++;

В этом примере переменная var сравнивается с нулем и затем инкрементируется.


Если значение var до инкрементирования было положительным, выполняется
следующий оператор. Сначала значение объекта, на который указывает q ,
присваивается объекту, на который указывает p . Затем q и p инкрементируются.

См. также
Постфиксные операторы увеличения и уменьшения: ++ и --
Унарные операторы C
Статья • 03.03.2023 • Чтение занимает 2 мин

Унарные операторы располагаются до операнда, и связь устанавливается справа


налево.

Синтаксис
unary-expression :

  postfix-expression

  ++ unary-expression

  -- unary-expression

  unary-operator cast-expression

  sizeof unary-expression

  sizeof ( type-name )

unary-operator : один из следующих символов:

 & * + - ~ !

См. также
Операторы в C
Префиксные операторы увеличения и
уменьшения
Статья • 03.03.2023 • Чтение занимает 2 мин

Унарные операторы ( ++ и -- ) называются операторами префиксного инкремента


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

Если оператор отображается перед операндом, операнд увеличивается или


уменьшается, и результат выражения будет его новым значением.

Операнд целочисленного типа или типа с плавающей запятой инкрементируется


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

Пример
В следующем примере показан унарный оператор префиксного декремента.

if( line[--i] != '\n' )

return;

В этом примере переменная i уменьшена до использования в качестве нижнего


индекса line .

См. также
Унарные операторы C
Операторы косвенного обращения и
взятия адреса
Статья • 03.03.2023 • Чтение занимает 2 мин

Унарный оператор косвенного обращения ( * ) обращается к значению не


напрямую, а через указатель. Операнд должен иметь тип указателя. Результатом
операции является значение в том адресе, на который указывает операнд. Тип
результата совпадает с типом значения в адресе операнда.

Результат выполнения оператора косвенного обращения представляет собой тип,


если тип операнда — указатель на тип. Если операнд указывает на функцию,
результатом является указатель функции. Если он указывает на объект, результатом
является адресующее объект значение (lvalue).

Если значение указателя недопустимо, результат оператора косвенного обращения


не будет определен. Некоторые типичные условия, при которых значение
указателя становится недопустимым.

Указатель является пустым указателем.

Указатель указывает адрес объекта после окончания его жизни во время


ссылки. (Например, объект, который вышел из области или освобожден.)

Указатель указывает адрес, который неправильно выровнен для типа объекта,


на который он указывает.

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


программой.

Унарный оператор взятия адреса ( & ) предоставляет адрес своего операнда.


Операнд должен представлять:

Значение lvalue, которое обозначает объект, который не объявлен register и


не является битовым полем.

Результат унарного разыменования ( * ) или оператора разыменования


массива ( [] ).

Указатель функций.

Результат является указателем на operand_type, если тип операнда — operand_type.


Если операнд является результатом унарного оператора * , ни один из операторов
не вычисляется, как если бы они оба были пропущены. Результат не является lvalue,
и к операторам по-прежнему применяются ограничения. Если операнд является
результатом оператора [] , оператор & и унарный оператор * , подразумеваемый
оператором [] , не вычисляются. Результат будет таким же, как при удалении
оператора & и смене оператора [] на + . В противном случае будет получен
указатель на объект или функцию, назначаемую операндом.

Примеры
Далее в примерах используются следующие типичные объявления.

int *pa, x;

int a[20];

Эта инструкция использует оператор взятия адреса ( & ) для получения адреса
шестого элемента массива a . Результат сохраняется в переменной указателя pa :

pa = &a[5];

Оператор косвенного обращения ( * ) в этом примере обращается к значению int ,


которое находится по адресу, сохраненному в переменной pa . Значение
присваивается целочисленной переменной x :

x = *pa;

В этом примере показано, что косвенное обращение к адресу x дает результат,


равный значению x .

assert( x == *&x );

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


функцию.
C

int roundup( void ); /* Function declaration */

int *proundup = roundup;

int *pround = &roundup;

assert( pround == proundup );

После объявления функции roundup объявляются и инициализируются два


указателя на roundup . При инициализации первого из них, proundup , указывается
только имя функции, а для второго, pround , используется оператор взятия адреса.
Обе инициализации эквивалентны.

См. также
Оператор косвенного обращения: *

Оператор address-of: &


Унарные арифметические операторы
Статья • 03.03.2023 • Чтение занимает 2 мин

Оператор унарного плюса C, арифметического отрицания, дополнения и


логического отрицания обсуждаются в следующем списке.

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

+ Оператор унарного плюса, предшествующий выражению в скобках, вызывает


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

- Оператор арифметического отрицания создает отрицательную форму своего


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

~ Оператор побитового дополнения (или bitwise-NOT) создает побитовое


дополнение своего операнда. Операнд должен иметь целочисленный тип. Этот
оператор выполняет обычные арифметические преобразования; результат
имеет тип операнда после преобразования.

! Оператор логического отрицания (logical-NOT) создает значение 0, если


операнд равен true (ненулевой), и значение 1, если операнд равен false (0).
Результат имеет тип int . Этот операнд должен представлять собой
целочисленное значение, значение с плавающей запятой или значение
указателя.

Унарные арифметические операции с указателями недопустимы.

Примеры
В следующих примерах показаны унарные арифметические операторы:

short x = 987;

x = -x;

В приведенном выше примере новое значение x является отрицательной формой


987 или –987.
unsigned short y = 0xAAAA;

y = ~y;

В этом примере новое значение, присвоенное переменной y , является


дополнением до единицы значения 0xAAAA или 0x5555 без знака.

if( !(x < y) )

Если x больше или равно y , результат выражения равен 1 (true). Если x меньше y ,
результат равен 0 (false).

См. также
Выражения с унарными операторами
Оператор sizeof (C)
Статья • 03.03.2023 • Чтение занимает 2 мин

Оператор sizeof предоставляет объем хранения (в байтах), необходимого для


хранения объекта типа "операнд". Этот оператор позволяет избежать задания
зависимых от компьютера размера данных в программах.

Синтаксис

sizeof unary-expression

sizeof ( type-name )

Примечания
Операнд является либо любым идентификатором unary-expression, либо
выражением type-cas (то есть описателем типа, заключенным в скобки). unary-
expression не может представлять объект битового поля, неполный тип или
указатель функции. Результатом является целочисленная константа без знака.
Стандартный заголовок STDDEF.H определяет этот тип как size_t.

При применении оператора sizeof к идентификатору массива результатом


является размер целого массива, а не размер указателя, представленного
идентификатором массива.

При применении оператора sizeof к имени структуры или типа объединения,


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

Если безразмерный массив является последним элементом структуры, оператор


sizeof возвращает размер структуры без массива.

buffer = calloc(100, sizeof (int) );

В этом примере для передачи размера объекта sizeof , который меняется в


зависимости от компьютеров, как аргумент функции времени выполнения с
именем int , используется оператор calloc . Значение, возвращаемое функцией,
хранится в buffer .

static char *strings[] = {

"this is string one",

"this is string two",

"this is string three",

};

const int string_no = ( sizeof strings ) / ( sizeof strings[0] );

В этом примере strings — это массив указателей на char . Число указателей — это
число элементов в массиве, но оно не определено. Легко определить количество
указателей с помощью оператора sizeof , вычислив число элементов в массиве.
Значение целого числа const string_no инициализируется до этого числа.
Поскольку это значение const , string_no невозможно изменить.

См. также
Операторы в C

Операторы C++, приоритет и ассоциативность


Операторы приведения
Статья • 03.03.2023 • Чтение занимает 2 мин

Приведение типа позволяет выполнить явное преобразование типа объекта в


конкретной ситуации.

Синтаксис
cast-expression :

  unary-expression

  ( type-name ) cast-expression

Компилятор обрабатывает как cast-expression тип type-name после приведения


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

См. также
Оператор приведения: ()
Операторы умножения в C
Статья • 03.03.2023 • Чтение занимает 2 мин

Мультипликативные операторы выполняют операции умножения ( * ), деления ( / )


и вычисления остатка ( % ).

Синтаксис
multiplicative-expression : cast-expression multiplicative-expression * cast-

expression multiplicative-expression / cast-expression multiplicative-

expression % cast-expression

Операнды оператора вычисления остатка ( % ) должны быть целочисленными.


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

Мультипликативные операторы выполняют обычные арифметические


преобразования над операндами. После преобразования тип результата совпадает
с типом операндов.

7 Примечание

Поскольку преобразования, выполняемые мультипликативными операторами,


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

Мультипликативные операторы C описаны в следующей таблице:

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

* Оператор умножения выполняет умножение двух операндов.


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

/ Оператор деления выполняет деление первого операнда на второй. Если два


целочисленных операнда разделены и результат не является целым числом, он
усекается в соответствии со следующими правилами:

— Согласно стандарту ANSI C результат деления на 0 не определен. Во время


компиляции или выполнения компилятор Microsoft C выдает ошибку.

— Если оба операнда положительные или не имеют знака, дробная часть


результата отбрасывается.

— Если операнд является отрицательным, определяется ли результат операции


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

% Результат выполнения оператора вычисления остатка — остаток от деления


первого операнда на второй. При неточном делении результат определяется
следующими правилами.

— Если правый операнд равен нулю, результат будет неопределенным.

— Если оба операнда положительные или не имеют знака, результат будет


положительным.

— Если один из операндов отрицательный и результат неточный, результат


определяется реализацией. (См. раздел, посвященный корпорации
Майкрософт.)

Специально для систем Майкрософт


Если при делении один из операндов отрицательный, дробная часть результата
отбрасывается.

Если при делении с использованием оператора вычисления остатка один из


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

Примеры
Приведенные здесь объявления используются в следующих примерах:

C
int i = 10, j = 3, n;

double x = 2.0, y;

В следующей инструкции используется оператор умножения.

y = x * i;

В этом примере x умножается на i и получается значение 20.0. Результат имеет


тип double .

n = i / j;

В этом примере 10 делится на 3. Дробная часть результата отбрасывается и


получается целое значение 3.

n = i % j;

В этой инструкции переменной n присваивается значение целочисленного остатка


(1) от деления 10 на 3.

Блок, относящийся только к системам Microsoft

Знак остатка совпадает со знаком делимого. Пример:

50 % -6 = 2

-50 % 6 = -2

В каждом случае значения 50 и 2 имеют один и тот же знак.

Завершение блока, относящегося только к системам Майкрософт

См. также
Операторы умножения и оператор модуля
Аддитивные операторы в C
Статья • 03.03.2023 • Чтение занимает 2 мин

Аддитивные операторы выполняют сложение ( + ) и вычитание ( - ).

Синтаксис
additive-expression :

  multiplicative-expression

  additive-expression + multiplicative-expression

  additive-expression - multiplicative-expression

7 Примечание

Хотя синтаксис для additive-expression включает multiplicative-expression ,


это не означает, что выражения, использующие умножение, являются
обязательными. См. синтаксис в разделе Сводка по синтаксису языка C для
multiplicative-expression , cast-expression и unary-expression.

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


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

Аддитивные операторы выполняют обычные арифметические преобразования с


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

См. также раздел


Аддитивные операторы: + и -
Сложение (+)
Статья • 03.03.2023 • Чтение занимает 2 мин

Оператор сложения ( + ) выполняет сложение двух операндов. Оба операнда могут


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

Если целое число добавить к указателю, целое значение целого числа (i) будет
преобразовано с помощью его умножения на размер значения, к которому
обращается указатель. После преобразования значение целого числа представляет
позиции памяти i, где каждая позиция имеет длину, заданную типом указателя.
Если преобразованное значение целого числа добавить ко значению указателя,
будет получено новое значение указателя, представляющее позиции i адреса из
исходного адреса. Новое значение указателя обращается ко значению того же
типа, что и исходное значение указателя. Следовательно, эта операция аналогична
индексации массива (см. статьи Одномерные массивы и Многомерные массивы
(C)). Если указатель суммы указывает за пределы массива, за исключением первого
расположения, находящегося за верхним пределом, результат не определен.
Дополнительные сведения см. в статье Расчеты с указателями.

См. также
Аддитивные операторы в C
Вычитание (-)
Статья • 03.03.2023 • Чтение занимает 2 мин

Оператор вычитания ( - ) вычитает второй операнд из первого. Оба операнда


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

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


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

Если целочисленное значение вычитается из значения указателя, оператор


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

См. также
Аддитивные операторы в C
Использование операторов сложения
Статья • 03.03.2023 • Чтение занимает 2 мин

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


используются следующие объявления.

int i = 4, j;

float x[10];

float *px;

Эти операторы эквивалентны.

px = &x[4 + i];

px = &x[4] + i;

Значение i умножается на длину типа float и добавляется к &x[4] .


Результирующее значение указателя является адресом x[8] .

j = &x[i] - &x[i-2];

В этом примере адрес третьего элемента x (заданного x[i-2] ) вычитается из


адреса пятого элемента x (заданного x[i] ). Разница делится на длину типа float ;
результатом является целочисленное значение 2.

См. также
Аддитивные операторы в C
Расчеты с указателями
Статья • 03.03.2023 • Чтение занимает 2 мин

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


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

Это предположение верно для элементов массива. По определению, массив


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

Аналогично, когда производится вычитание значений двух указателей, при


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

См. также
Аддитивные операторы в C
Операторы побитового сдвига
Статья • 03.03.2023 • Чтение занимает 2 мин

Операторы сдвига сдвигают свой первый операнд влево ( << ) или вправо ( >> ) на
число позиций, задаваемое вторым операндом.

Синтаксис
shift-expression :

  additive-expression

  shift-expression << additive-expression

  shift-expression >> additive-expression

Оба операнда должны быть целыми значениями. Эти операторы выполняют


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

В случае сдвига влево для освобождаемых правых битов задается значение 0. В


случае сдвига вправо освобождающиеся левые биты заполняются в зависимости
от типа первого операнда после преобразования. Если тип имеет значение
unsigned , для них задано значение 0. В противном случае они заполняются

копиями бита знака. Для операторов сдвига влево без переполнения оператор

expr1 << expr2

эквивалентен умножению на 2expr2. Для операторов сдвига вправо операция

expr1 >> expr2

эквивалентна делению на 2expr2, если операнд expr1 не имеет знака или имеет
неотрицательное значение.

Результат операции сдвига не определен, если второй операнд имеет


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

unsigned int x, y, z;

x = 0x00AA;

y = 0x5500;

z = ( x << 8 ) + ( y >> 8 );

В этом примере значение x сдвигается влево на 8 позиций, а значение y


сдвигается вправо на 8 позиций. Значения после сдвига складываются, а
получившееся в результате число 0xAA55 присваивается переменной z .

Сдвиг вправо отрицательного значения соответствует делению исходного


значения на два с округлением вниз. Например, сдвиг вправо значения –253
(11111111 00000011 в двоичном представлении) дает значение –127 (11111111
10000001 в двоичном представлении). Сдвиг вправо положительного числа 253
дает +126.

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

См. также
Операторы сдвигов влево и вправо (>> и <<)
Операторы отношения и равенства C
Статья • 03.03.2023 • Чтение занимает 2 мин

Операторы отношения и равенства сравнивают свои первые и вторые операнды


для проверки истинности указанного отношения. Результатом реляционного
выражения является 1, если проверенная связь имеет значение true, и 0, если
значение false. Результат имеет тип int .

Синтаксис
relational-expression :

  shift-expression

  relational-expression < shift-expression

  relational-expression > shift-expression

  relational-expression <= shift-expression

  relational-expression >= shift-expression

equality-expression :

  relational-expression

  equality-expression == relational-expression

  equality-expression != relational-expression

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

Оператор Проверяемое отношение

< Первый операнд меньше второго операнда

> Первый операнд больше второго операнда

<= Первый операнд меньше или равен второму операнду

>= Первый операнд больше или равен второму операнду

== Первый операнд равен второму операнду

!= Первый операнд не равен второму операнду

Первые четыре оператора в списке имеют более высокий приоритет, чем


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

Оба операнда любого оператора отношения или равенства могут быть


указателями на один и тот же тип. Для операторов равенства ( == ) и
неравенства ( != ) результат сравнения показывает, указывают ли оба
указателя на один и тот же адрес в памяти. Для других реляционных
операторов ( < , > , <= и >= ) результат сравнения указывает относительное
положение двух адресов памяти объектов, на которые указывает . Операторы
отношения сравнивают только смещения.

Сравнение указателей определено только для частей одного и того же


объекта. Если указатели относятся к элементам массива, сравнение
аналогично сравнению соответствующих индексов. Адрес первого элемента
массива "меньше" адреса последнего элемента. Для структур указатели на
элементы структуры, объявленные позже, являются указателями "больше" на
элементы, объявленные ранее в структуре. Указатели на члены одного
объединения эквивалентны.

Значение указателя можно сравнивать с постоянным значением 0 на


равенство ( == ) или неравенство ( != ). Указатель со значением 0 называется
указателем null; то есть он не указывает на допустимое расположение в
памяти.

Операторы равенства следуют тем же правилам, что и реляционные


операторы, но имеют больше возможностей: указатель можно сравнить с
константным целочисленным выражением со значением 0 или с указателем
на void . Два указателя "null" при сравнении считаются равными. Операторы
равенства сравнивают как сегмент, так и смещение.

Примеры
В этих примерах показаны реляционные операторы и операторы равенства.

int x = 0, y = 0;

if ( x < y )

Поскольку x и y равны, выражение в этом примере возвращает значение 0.

char array[10];

char *p;

for ( p = array; p < &array[10]; p++ )

*p = '\0';

Фрагмент в этом примере задает для каждого элемента массива array постоянное
символьное значение null.

enum color { red, white, green } col;

if ( col == red )

Эти операторы объявляют переменную перечисления с именем col и тегом color .


В любой момент переменная может содержать целочисленное значение 0, 1 или 2,
представляющее один из элементов набора color перечисления: цвет red ,
white или green соответственно. Если переменная col содержит значение 0 при

выполнении оператора if , будут выполнены все операторы, зависящие от этого


оператора if .

См. также
Реляционные операторы: <, >, <=и >=

Операторы равенства: == и !=
Побитовые операторы в C
Статья • 03.03.2023 • Чтение занимает 2 мин

Побитовые операторы выполняют операции побитового И ( & ), побитового


исключающего ИЛИ ( ^ ) и побитового включающего ИЛИ ( | ).

Синтаксис
AND-expression :

  equality-expression

  AND-expression & equality-expression

exclusive-OR-expression :

  AND-expression

  exclusive-OR-expression ^ AND-expression

inclusive-OR-expression :

  exclusive-OR-expression
  inclusive-OR-expression | exclusive-OR-expression

Операнды побитовых операторов должны иметь целочисленные типы, но их типы


могут отличаться. Эти операторы выполняют обычные арифметические
преобразования; типом результата является тип операнда после преобразования.

Ниже описываются побитовые операторы C.

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

& Оператор побитового И сравнивает каждый бит первого операнда с


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

^ Оператор побитового исключающего ИЛИ сравнивает каждый бит первого


операнда с соответствующим битом второго операнда. Если один бит равен 0, а
другой равен 1, соответствующий бит результата устанавливается равным 1. в
противном случае — нулю.

| Оператор побитового включающего ИЛИ сравнивает каждый бит первого


операнда с соответствующим битом второго операнда. Если любой из битов
равен единице, соответствующий бит результата устанавливается равным
единице, а в противном случае — нулю.
Примеры
Эти объявления используются для следующих трех примеров:

short i = 0xAB00;

short j = 0xABCD;

short n;

n = i & j;

Результат, присваиваемый переменной n в первом примере, совпадает с


результатом i (шестнадцатеричное значение 0xAB00).

n = i | j;

n = i ^ j;

Побитовое включающее ИЛИ во втором примере имеет своим результатом


шестнадцатеричное значение 0xABCD, в то время как побитовое исключающее
ИЛИ в третьем примере — шестнадцатеричное значение 0xCD.

Блок, относящийся только к системам Microsoft

Результат битовой операции с целыми числами со знаками зависит от реализации,


согласно стандарту C. Для компилятора Microsoft C побитовые операции со
знаковыми целочисленными значениями работают так же, как побитовые
операции с беззнаковыми целочисленными значениями. Например, значение -16
& 99 можно представить в двоичном виде следующим образом:

Expression

11111111 11110000

& 00000000 01100011

_________________

00000000 01100000

Выполнение побитовой операции И даст десятичное значение 96.

Завершение блока, относящегося только к системам Майкрософт


См. также
Побитовый оператор И: &

Битовый оператор исключающего ИЛИ: ^

Битовый оператор включающего ИЛИ: |


Логические операторы в C
Статья • 03.03.2023 • Чтение занимает 2 мин

Логические операторы выполняют операции логического И ( && ) и логического


ИЛИ ( || ).

Синтаксис
logical-AND-expression :

  inclusive-OR-expression

  logical-AND-expression && inclusive-OR-expression

logical-OR-expression :

  logical-AND-expression

  logical-OR-expression || logical-AND-expression

Комментарии
Логические операторы не выполняют обычных арифметических преобразований.
Вместо этого они оценивают каждый операнд с точки зрения его эквивалентности
нулю. Результатом логической операции является либо 0, либо 1. Результат имеет
тип int .

Логические операторы C описываются ниже.

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

&& Логический оператор И создает значение 1, если оба операнда имеют


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

|| Оператор логического ИЛИ выполняет над своими операндами операцию


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

Операнды выражений логического И и логического ИЛИ вычисляются слева


направо. Если значения первого операнда достаточно, чтобы определить результат
операции, второй операнд не вычисляется. Это называется сокращенным
вычислением. После первого операнда в выражении есть точка
последовательности. Дополнительные сведения см. в статье Точки следования C.

Примеры
В следующем примере показано использование логических операторов.

int w, x, y, z;

if ( x < y && y < z )

printf( "x is less than z\n" );

В этом примере функция printf вызывается для печати сообщения, если x меньше
y , а y меньше z . Если x больше y , то второй операнд ( y < z ) не вычисляется и
сообщение не печатается. Это еще один пример сокращенных вычислений,
который нужно учитывать во время написания логических сравнений. В противном
случае может возникнуть ошибка, если код использует побочные эффекты
вычисления второго операнда.

printf( "%d" , (x == w || x == y || x == z) );

В этом примере, если x равно w , y или z , то второй аргумент функции printf


имеет значение true и код выводит значение 1. В противном случае он возвращает
значение false и код выводит значение 0. Как только обнаруживается, что одно из
значений равно true, вычисление прекращается.

См. также
Логический оператор И: &&
Логический оператор ИЛИ: ||
Оператор Conditional-Expression
Статья • 03.03.2023 • Чтение занимает 2 мин

В C есть один тернарный оператор: оператор условного выражения ( ? : ).

Синтаксис
conditional-expression :

  logical-OR-expression

  logical-OR-expression ? expression : conditional-expression

Должен logical-OR-expression иметь целочисленный, плавающий тип или тип


указателя. Он оценивается с точки зрения его эквивалентности 0. За точкой
последовательности следует logical-OR-expression . Вычисление операндов
продолжается следующим образом.

Если logical-OR-expression значение не равно 0, expression вычисляется .


Результат вычисления выражения задается нетерминальным expression . (Это
означает, что expression вычисляется только в том случае, если logical-OR-
expression имеет значение true.)

Если logical-OR-expression равно 0, conditional-expression вычисляется .


Результатом выражения является значение . conditional-expression (Это
означает, что conditional-expression вычисляется только в том случае, если
logical-OR-expression имеет значение false.)

Результат: expression или conditional-expression вычисляется, но не оба.

Тип результата условной операции зависит от типа операнда expression или


conditional-expression следующим образом:

Если expression или conditional-expression имеет целочисленный или


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

Если оба expression типа и conditional-expression имеют одинаковую


структуру, объединение или тип указателя, тип результата будет таким же
типом структуры, объединения или указателя.

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

Если или expression conditional-expression является указателем, а другой


операнд является константным выражением со значением 0, типом
результата является тип указателя.

При сравнении типов для указателей не учитываются квалификаторы типов


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

Примеры
В следующих примерах показаны варианты использования условного оператора.

j = ( i < 0 ) ? ( -i ) : ( i );

В этом примере абсолютное значение i присваивается j . Если значение i


меньше 0, значение -i присваивается j . Если значение i больше или равно 0,
значение i присваивается j .

void f1( void );

void f2( void );

int x;

int y;

( x == y ) ? ( f1() ) : ( f2() );

В этом примере объявляется две функции f1 и f2 и две переменные x и y . Далее


в программе, если две переменные имеют одинаковое значение, вызывается
функция f1 . В противном случае вызывается функция f2 .

См. также
Условный оператор: ? :
Операторы назначения в C
Статья • 03.03.2023 • Чтение занимает 2 мин

Операция присваивания заключается в том, что значение правого операнда


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

Синтаксис
assignment-expression :

  conditional-expression

  unary-expression assignment-operator assignment-expression

assignment-operator : один из следующих символов:

  = *= /= %= += -= <<= >>= &= ^= |=

Операторы присваивания в языке C позволяют в ходе одной операции выполнить


и преобразование, и присваивание значения. В языке C имеются следующие
операторы присваивания:

Оператор Выполняемая операция

= Простое присваивание

*= Присваивание умножения

/= Присваивание деления

%= Присваивание остатка

+= Присваивание сложения

-= Присваивание вычитания

<<= Присваивание сдвига влево

>>= Присваивание сдвига вправо

&= Присваивание побитового И

^= Присваивание побитового исключающего ИЛИ

| = Присваивание побитового включающего ИЛИ


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

См. также
Операторы присваивания
Простое назначение (C)
Статья • 03.03.2023 • Чтение занимает 2 мин

Оператор простого присваивания присваивает значение правого операнда левому


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

double x;

int y;

x = y;

В этом примере значение переменной y преобразуется в тип double и


присваивается переменной x .

См. также
Операторы присваивания C
Составное назначение C
Статья • 03.03.2023 • Чтение занимает 2 мин

Составные операторы присваивания объединяют оператор простого присваивания


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

expression1+=expression2

можно понимать как

expression1=expression1+expression2

Но составной оператор присваивания не эквивалентен своей развернутой версии,


так как в составном операторе присваивания выражение выражение1 вычисляется
только один раз, а в развернутой версии выражение выражение1 вычисляется
дважды: в операции сложения и в операции присваивания.

Операнды составного оператора присваивания должны быть целочисленного типа


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

#define MASK 0xff00

n &= MASK;

В этом примере операция побитового включающего AND выполняется для n и


MASK , а результат присваивается переменной n . Константа манифеста MASK

определяется с помощью директивы препроцессора #define.

См. также
Операторы присваивания C
Оператор последовательного
вычисления
Статья • 03.03.2023 • Чтение занимает 2 мин

Оператор последовательного вычисления, называемый также оператором


"запятая", вычисляет свои два операнда последовательно в направлении слева
направо.

Синтаксис
expression :

  assignment-expression

  expression , assignment-expression

Левый операнд оператора последовательной оценки ( , ) вычисляется как void


выражение. Результат операции имеет те же значение и тип, что и правый
операнд. Каждый операнд может быть любого типа. Оператор последовательной
оценки не выполняет преобразования типов между операндами и не возвращает l-
значение. После первого операнда есть точка последовательности. Это означает,
что все побочные эффекты от оценки левого операнда завершаются до начала
оценки правого операнда. Дополнительные сведения см. в разделе Точки
последовательности.

Оператор последовательного вычисления обычно используется для вычисления


двух и более выражений в контекстах, где допустимо только одно выражение.

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


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

Пример
В следующем примере демонстрируется оператор последовательного вычисления.

for ( i = j = 1; i + j < 20; i += i, j-- );

В этом примере каждый операнд третьего выражения оператора for вычисляется


независимо. Сначала вычисляется левый операнд i += i , а затем — правый, j-- .

func_one( x, y + 2, z );

func_two( (x--, y + 2), z );

В вызове функции func_one передаются три аргумента, разделенные запятыми: x ,


y + 2 и z . В вызове функции func_two скобки заставляют компилятор
интерпретировать первую запятую в качестве оператора последовательного
вычисления. В этом вызове функции в функцию func_two передаются два
аргумента. Первым аргументом является результат операции последовательного
вычисления (x--, y + 2) , который имеет значение и тип выражения y + 2 ; второй
аргумент — z .

См. также
Оператор "запятая": ,
Преобразования типов (C)
Статья • 03.03.2023 • Чтение занимает 2 мин

Преобразования типов зависят от указанного оператора и типа операнда или


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

Когда значение одного типа присваивается переменной другого типа или


оператор преобразует тип своего операнда или операндов до выполнения
операции

Когда значение одного типа явно приводится к другому типу

Когда значение передается в качестве аргумента в функцию или когда тип


возвращается из функции

Символ, короткое целое число или целое битовое поле, со знаком или без, а также
объект типа перечисления можно использовать в выражении везде, где можно
использовать целое число. Если int может представлять все значения исходного
типа, значение преобразуется в int ; в противном случае оно преобразуется в
unsigned int . Этот процесс называется "целочисленным повышением уровня".
Целочисленные повышения сохраняют значение. То есть гарантируется, что
значение после приведения будет таким же, как до него. Дополнительные
сведения есть в статье Обычные арифметические преобразования.

См. также
Выражения и присваивания
Преобразования присваиваний
Статья • 03.03.2023 • Чтение занимает 2 мин

В операциях присваивания тип присваиваемого значения преобразуется в тип


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

Преобразования из целочисленных типов со знаком

Преобразования из целочисленных типов без знака

Преобразования типов с плавающей запятой

Преобразования в типы указателей и из типов указателей

Преобразования из других типов

Квалификаторы типа не влияют на допустимость преобразования, но в левой части


операции присваивания нельзя использовать l-значение const .

См. также
Преобразования типов
Преобразования из целочисленных
типов со знаком
Статья • 03.03.2023 • Чтение занимает 3 мин

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

При преобразовании целого числа со знаком в целое число большего размера


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

int i = -3;

unsigned short u;

u = i;

printf_s( "%hu\n", u ); // Prints 65533

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


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

Сведения о размерах целочисленных типов и типов с плавающей запятой см. в


статье Хранение базовых типов.

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


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

Блок, относящийся только к системам Microsoft

В компиляторе Майкрософт int и long являются отдельными, но эквивалентными


типами. Преобразование значения int выполняется так же, как и преобразование
long .
Завершение блока, относящегося только к системам Майкрософт

Таблица преобразований из целочисленных


типов со знаком
Исходный Кому Метод
тип

1
char short Расширение знака

char long Расширение знака

char long Расширение знака


long

char unsigned Сохранение шаблона; бит высокого порядка перестает


char функционировать как бит знака

char unsigned Расширение знака до short ; преобразование short в unsigned


short short

char unsigned Расширение знака до long ; преобразование long в unsigned long


long

char unsigned Расширение знака до long long ; преобразование long long в


long unsigned long long
long

char float Представление в точности как float

char double Представление в точности как double

char long Представление в точности как long double


double

short char Сохранение байта низкого порядка

short long Расширение знака

short long Расширение знака


long

short unsigned Сохранение байта низкого порядка


char

short unsigned Сохранение битового шаблона; бит высокого порядка перестает


short функционировать как бит знака
Исходный Кому Метод
тип

short unsigned Расширение знака до long ; преобразование long в unsigned long


long

short unsigned Расширение знака до long long ; преобразование long long в


long unsigned long long
long

short float Представление в точности как float

short double Представление в точности как double

short long Представление в точности как long double


double

long char Сохранение байта низкого порядка

long short Сохранение слова низкого порядка

long long Расширение знака


long

long unsigned Сохранение байта низкого порядка


char

long unsigned Сохранение слова низкого порядка


short

long unsigned Сохранение битового шаблона; бит высокого порядка перестает


long функционировать как бит знака

long unsigned Расширение знака до long long ; преобразование long long в


long unsigned long long
long

long float Представление в виде float . Если long невозможно представить


точно, некоторая точность теряется.

long double Представление в точности как double

long long Представление в точности как long double


double

long long char Сохранение байта низкого порядка

long long short Сохранение слова низкого порядка

long long long Сохранение младшего dword


Исходный Кому Метод
тип

long long unsigned Сохранение байта низкого порядка


char

long long unsigned Сохранение слова низкого порядка


short

long long unsigned Сохранение младшего dword


long

long long unsigned Сохранение битового шаблона; бит высокого порядка перестает
long функционировать как бит знака
long

long long float Представление в виде float . Если long long невозможно
представить точно, некоторая точность теряется.

long long double Представление в виде double . Если long long невозможно
представить точно в виде double , некоторая точность теряется.

long long long Представление в виде double . Если long long невозможно
double представить точно в виде double , некоторая точность теряется.

1
Все записи char основаны на том, что тип char является типом со знаком по
умолчанию.

См. также
Преобразования присваиваний
Преобразования из целочисленных
типов без знака
Статья • 03.03.2023 • Чтение занимает 2 мин

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

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

unsigned k = 65533;

short j;

j = k;

printf_s( "%hd\n", j ); // Prints -3

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

Сведения о размерах целочисленных типов и типов с плавающей запятой см. в


статье Хранение базовых типов.

Блок, относящийся только к системам Microsoft

В компиляторе Майкрософт unsigned (или unsigned int ) и unsigned long являются


отдельными, но эквивалентными типами. Преобразование значения unsigned int
выполняется так же, как и преобразование unsigned long .

Завершение блока, относящегося только к системам Майкрософт

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


знака.
Таблица преобразований из целочисленных
типов без знака
Исходный Кому Метод
тип

unsigned char char Сохранение битового шаблона; бит высокого порядка


становится битом знака

unsigned char short Нулевое расширение

unsigned char long Нулевое расширение

unsigned char long long Нулевое расширение

unsigned char unsigned Нулевое расширение


short

unsigned char unsigned long Нулевое расширение

unsigned char unsigned long Нулевое расширение


long

unsigned char float Преобразование точно в float

unsigned char double Преобразование точно в double

unsigned char long double Преобразование точно в long double

unsigned char Сохранение байта низкого порядка


short

unsigned short Сохранение битового шаблона; бит высокого порядка


short становится битом знака

unsigned long Нулевое расширение


short

unsigned long long Нулевое расширение


short

unsigned unsigned char Сохранение байта низкого порядка


short

unsigned unsigned long Нулевое расширение


short

unsigned unsigned long Нулевое расширение


short long
Исходный Кому Метод
тип

unsigned float Преобразование точно в float


short

unsigned double Преобразование точно в double


short

unsigned long double Преобразование точно в long double


short

unsigned long char Сохранение байта низкого порядка

unsigned long short Сохранение слова низкого порядка

unsigned long long Сохранение битового шаблона; бит высокого порядка


становится битом знака

unsigned long long long Нулевое расширение

unsigned long unsigned char Сохранение байта низкого порядка

unsigned long unsigned Сохранение слова низкого порядка


short

unsigned long unsigned long Нулевое расширение


long

unsigned long float Преобразование в ближайшее представление float

unsigned long double Преобразование точно в double

unsigned long long double Преобразование точно в long double

unsigned long char Сохранение байта низкого порядка


long

unsigned long short Сохранение слова низкого порядка


long

unsigned long long Сохранение младшего dword


long

unsigned long long long Сохранение битового шаблона; бит высокого порядка
long становится битом знака

unsigned long unsigned char Сохранение байта низкого порядка


long
Исходный Кому Метод
тип

unsigned long unsigned Сохранение слова низкого порядка


long short

unsigned long unsigned long Сохранение младшего dword


long

unsigned long float Преобразование в ближайшее представление float


long

unsigned long double Преобразование в ближайшее представление double


long

unsigned long long double Преобразование в ближайшее представление long double


long

См. также
Преобразования присваиваний
Преобразования типов с плавающей
запятой
Статья • 03.03.2023 • Чтение занимает 4 мин

Если значение с плавающей запятой преобразуется в другой тип с плавающей


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

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


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

Блок, относящийся только к системам Microsoft

Компиляторы Майкрософт используют представление binary32 согласно IEEE-754


для значений типа float и представление binary64 для long double и double . Так
как для long double и double используется одно и то же представление, они имеют
одинаковый диапазон и точность.

Когда компилятор преобразует число типа double или long double с плавающей
запятой в значение float , он округляет результат в соответствии с элементами
управления среды с плавающей запятой, значение по умолчанию которых —
"округлить до ближайшего числа с привязкой к четному". Если числовое значение
слишком велико или слишком мало для представления в виде числового значения
float , результат преобразования будет положительной или отрицательной
бесконечностью в соответствии со знаком исходного значения, и возникает
исключение переполнения, если оно включено.

Для преобразования в целочисленный тип меньше long сначала значение


преобразуется в тип long , а затем в результирующий тип.

При преобразовании в целочисленные типы не меньше long для значения,


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

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


максимально удаленное от нуля. Для типов со знаком это наименьшее
представимое значение (0x800...0). Для типов без знака это наибольшее
представимое значение (0xFF...F).

Результат может дополняться, то есть значение, которое слишком велико для


представления, преобразуется в наибольшее представимое значение, а
значение, которое слишком мало для представления, преобразуется в
наименьшее представимое значение. Одно из этих двух значений также
используется в качестве значения-метки.

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

Завершение блока, относящегося только к системам Майкрософт

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


запятой.

Таблица преобразования типов с


плавающей запятой
Исходный Кому Метод
тип

float char Преобразование в long , затем преобразование long в char

float short Преобразование в long , затем преобразование long в short

float int Усечение до десятичной запятой. Если результат слишком велик для
представления в качестве значения int , результат не определен.

float long Усечение до десятичной запятой. Если результат слишком велик для
представления в качестве значения long , результат не определен.

float long Усечение до десятичной запятой. Если результат слишком велик для
long представления в качестве значения long long , результат не
определен.

float unsigned Преобразование в long , затем преобразование long в unsigned


char char
Исходный Кому Метод
тип

float unsigned Преобразование в long , затем преобразование long в unsigned


short short

float unsigned Усечение до десятичной запятой. Если результат слишком велик для
представления в качестве значения unsigned , результат не
определен.

float unsigned Усечение до десятичной запятой. Если результат слишком велик для
long представления в качестве значения unsigned long , результат не
определен.

float unsigned Усечение до десятичной запятой. Если результат слишком велик для
long представления в качестве значения unsigned long long , результат
long не определен.

float double Представление в качестве значения double .

float long Представление в качестве значения long double . В MSVC long


double double и double имеют одно и то же представление.

double char Преобразование в float , затем преобразование float в char

double short Преобразование в float , затем преобразование float в short

double int Усечение до десятичной запятой. Если результат слишком велик для
представления в качестве значения int , результат не определен.

double long Усечение до десятичной запятой. Если результат слишком велик для
представления в качестве значения long , результат не определен.

double unsigned Преобразование в long , затем преобразование long в unsigned


char char

double unsigned Преобразование в long , затем преобразование long в unsigned


short short

double unsigned Усечение до десятичной запятой. Если результат слишком велик для
представления в качестве значения unsigned , результат не
определен.

double unsigned Усечение до десятичной запятой. Если результат слишком велик для
long представления в качестве значения unsigned long , результат не
определен.

double unsigned Усечение до десятичной запятой. Если результат слишком велик для
long представления в качестве значения unsigned long long , результат
long не определен.
Исходный Кому Метод
тип

double float Представление в качестве значения float . Если значение double


невозможно точно представить с типом float , происходит потеря
точности.

double long Значение long double рассматривается как double .


double

Преобразования из типа long double производятся так же, как из типа double .

См. также
Преобразования присваиваний
Преобразования в типы указателей и
из типов указателей
Статья • 03.03.2023 • Чтение занимает 2 мин

Указатель на один тип значения можно преобразовать в указатель на другой тип.


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

Указатель на тип void можно преобразовать в указатель на любой тип и из


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

Если указатель преобразуется в другой указатель с тем же типом, но с другими или


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

Значение указателя можно также преобразовать в целочисленное значение. Путь


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

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


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

Если размер указателя меньше размера целочисленного типа, он сначала


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

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


следующим правилам.

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


преобразование просто приводит к обработке целочисленного значения как
указателя (целого числа без знака).

Если размер целочисленного типа отличается от размера типа указателя,


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

Константное целочисленное выражение со значением 0 или аналогичное


выражение, приведенное к типу void* , можно преобразовать в указатель любого
типа путем приведения типов, присваивания или сравнения. Эта операция создает
пустой указатель, который равен другому пустому указателю того же типа, но не
равен ни одному указателю на функцию или объект. Целые числа, отличные от
константы 0, можно преобразовать в тип указателя, но результат не является
переносимым.

См. также
Преобразования назначений
Преобразования из других типов
Статья • 03.03.2023 • Чтение занимает 2 мин

Поскольку значение enum по определению имеет тип int , преобразования в тип


enum и обратно выполняются так же, как и для типа int . В компиляторе Microsoft C

тип integer обрабатывается так же, как и long .

Блок, относящийся только к системам Microsoft

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

Любое значение можно преобразовать в тип void , но результат такого


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

Тип void по определению не имеет значения. Поэтому его невозможно


преобразовать ни в какой другой тип, а другие типы невозможно преобразовать в
void путем присваивания. Однако значение можно явным образом преобразовать
в тип void , как описано в статье Преобразования с приведением типов.

Завершение блока, относящегося только к системам Майкрософт

См. также
Преобразования назначений
Преобразования приведений типов
Статья • 03.03.2023 • Чтение занимает 2 мин

Приведения типов можно использовать для явного преобразования типов.

Синтаксис
cast-expression :

  unary-expression

  ( type-name ) cast-expression

type-name :

  specifier-qualifier-list abstract-declarator необ.

является type-name типом и cast-expression значением, которое необходимо


преобразовать в этот тип. Выражение с приведением типа не является l-значением.
cast-expression Преобразуется так, как если бы он был назначен переменной типа

type-name . Правила преобразования при операциях присваивания (которые


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

Юридические типы приведения

Целевые типы Потенциальные источники

Целочисленные типы Любой целочисленный тип, тип с плавающей запятой или


указатель на объект

С плавающей запятой Любой арифметический тип

Указатель на объект или Любой целочисленный тип, void * указатель на объект или
void * указатель функции

Указатель на функцию Любой целочисленный тип, указатель на объект или


указатель функции

Структура, объединение Отсутствуют


или массив

Тип void Любой тип


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

Выражение void * имеет указатель void на тип , а не тип void . Если объект
приведен к типу void , результирующее выражение не может быть назначено ни
одному элементу. Аналогичным образом, объект приведения типа не является
допустимым l-значением, поэтому нельзя присваивать объекту приведения типа.

Блок, относящийся только к системам Microsoft

Приведение типа может быть выражением l-значения, если размер


идентификатора не изменяется. Дополнительные сведения о левосторонних
значениях см. в статье Выражения L-Value и R-Value.

Завершение блока, относящегося только к системам Майкрософт

Выражение можно преобразовать в тип void с приведением, но результирующее


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

См. также
Преобразования типов
Преобразования вызова функции
Статья • 03.03.2023 • Чтение занимает 2 мин

Тип преобразования, выполняемого над аргументами в вызове функции, зависит


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

Если существует прототип функции и в нем объявлены типы аргументов, то


компилятор выполняет проверку типа (см. статью Функции).

Если прототипа нет, то над аргументами в вызове функции выполняются только


обычные арифметические преобразования. Для каждого аргумента в вызове они
выполняются отдельно. Иными словами, значение типа float преобразуется в
double ; значение типа char или short  — в int ; а unsigned char или unsigned

short  — в unsigned int .

См. также
Преобразования типов
Операторы (C)
Статья • 03.03.2023 • Чтение занимает 2 мин

Операторы программы на языке C++ управляют потоком выполнения программы.


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

Оператор break

Составной оператор
Оператор continue

Оператор do-while

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

Оператор for

goto и помеченные операторы

Оператор if

Оператор NULL

Оператор return

Оператор switch

Оператор try-except

Оператор try-finally

Оператор while

См. также
Справочник по языку C
Общие сведения об операторах в C
Статья • 03.03.2023 • Чтение занимает 2 мин

Операторы языка C состоят из токенов, выражений и других операторов.


Оператор, который формирует компонент или другой оператор, называется телом
внешнего оператора. В этом разделе рассматриваются все типы операторов,
которые задаются следующим синтаксисом:

Синтаксис
statement :

 labeled-statement

 compound-statement

 expression-statement

 selection-statement

 iteration-statement

 jump-statement

 try-except-statement /* Только для систем Майкрософт */

 try-finally-statement /* Только для систем Майкрософт */

Часто тело оператора является "составным оператором". Составной оператор


состоит из других операторов, которые могут содержать ключевые слова.
Составной оператор разделен фигурными скобками ( { } ). Все остальные
операторы C заканчиваются точкой с запятой ( ; ). Точка с запятой является
признаком конца оператора.

Оператор выражения содержит выражение на языке C, которое может содержать


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

Любой оператор C может начинаться с метки-идентификатора, которая состоит из


имени и двоеточия. Поскольку метки операторов распознает только оператор
goto , они рассматриваются в разделе, посвященном оператору goto .
Дополнительные сведения см. в разделах Операторы goto и Помеченные
операторы.

См. также
Операторы
Оператор break (C)
Статья • 03.03.2023 • Чтение занимает 2 мин

Оператор break завершает выполнение ближайшего внешнего оператора do , for ,


switch или while , в котором он находится. Управление передается оператору,

который расположен после завершенного оператора.

Синтаксис
jump-statement :

  break ;

Оператор break часто используется для завершения обработки блока case,


относящегося к оператору switch . Если внешний оператор итерации или оператор
switch отсутствует, создается ошибка.

Если используются вложенные операторы, то break завершает выполнение только


того оператора do , for , switch или while , который непосредственно его окружает.
Передать управление за пределы вложенной структуры можно при помощи
оператора return или goto .

В следующем примере показано использование оператора break :

#include <stdio.h>

int main() {

char c;

for(;;) {

printf_s( "\nPress any key, Q to quit: " );

// Convert to character value

scanf_s("%c", &c);

if (c == 'Q')

break;

} // Loop exits only when 'Q' is pressed

См. также
Оператор break
Составной оператор (C)
Статья • 03.03.2023 • Чтение занимает 2 мин

Составной оператор (так называемый "блок") обычно используется как тело


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

Синтаксис
compound-statement :

  { declaration-list opt statement-list opt }

declaration-list :

  declaration

  declaration-list declaration

statement-list :

  statement

  statement-list statement

Если используются объявления, они должны задаваться до всех операторов.


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

Предполагается, что идентификаторы в составном операторе имеют модификатор


auto если иное явно не объявлено при помощи ключевых слов register , static
или extern . Это не относится к функциям, которые могут быть только extern . В
объявлении функции можно опустить спецификатор extern , и функция все равно
будет иметь тип extern .

Хранилище не выделяется, а инициализация запрещена, если переменная или


функция объявлены в составной инструкции с классом extern хранения . Такое
объявление относится к внешней переменной или функции, которая определена в
другом месте.

При каждом входе в составной оператор выполняется повторное выделение


памяти, а если нужно, то и повторная инициализация, для переменных,
объявленных в блоке с ключевым словом auto или register . Эти переменные
больше не определяются после выхода из составной инструкции. Если внутри
блока объявлена переменная с атрибутом static , то она инициализируется один
раз в начале выполнения программы и сохраняет значение на протяжении всего
выполнения программы. Дополнительные сведения о ключевом слове static
можно найти в статье Классы хранения.

Ниже показан пример составного оператора:

if ( i > 0 )

line[i] = x;

x++;

i--;

Если i в этом примере больше 0, то все инструкции внутри составного оператора


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

См. также
Операторы
Оператор continue (C)
Статья • 03.03.2023 • Чтение занимает 2 мин

Оператор continue передает элемент управления в следующую итерацию


ближайшего внешнего оператора do , for или while , в которой она отображается,
минуя все оставшиеся операторы в теле оператора do , for или while .

Синтаксис
jump-statement :

  continue ;

Следующая итерация оператора do , for или while определяется следующим


образом.

В операторе do или while следующая итерация начинается с повторного


вычисления выражения оператора do или while .

Оператор continue в операторе for приводит к вычислению выражения


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

Вот пример оператора continue :

while ( i-- > 0 )

x = f( i );

if ( x == 1 )

continue;

y += x * x;

В этом примере тело оператора выполняется при i больше 0. Для первой


переменной f(i) присваивается значение x ; затем, если x равно 1, выполняется
оператор continue . Остальные операторы в теле игнорируются. Выполнение
возобновляется в начале цикла с вычисления теста цикла.
См. также раздел
Оператор continue (C++)
Оператор do-while (C)
Статья • 03.03.2023 • Чтение занимает 2 мин

Оператор do-while позволяет повторять выполнение оператора или составного


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

Синтаксис
iteration-statement : do statement while ( expression ) ;

В операторе expression do-while вычисляется после выполнения основного текста


цикла. Поэтому тело цикла всегда выполняется по крайней мере один раз.

Объект expression должен иметь арифметический тип или тип указателя.


Выполнение происходит следующим образом:

1. Выполняется тело оператора.

2. expression Далее вычисляется. Если expression имеет значение false, do-while


оператор завершается и управление передается следующему оператору в
программе. Если expression имеет значение true (ненулевое), процесс
повторяется, начиная с шага 1.

Оператор do-while также может завершиться при выполнении break оператора ,


goto или return в теле инструкции .

Вот пример оператора do-while :

do

y = f( x );

x--;

} while ( x > 0 );

В этом операторе do-while два оператора y = f( x ); и x--; выполняются


независимо от начального значения переменной x . Затем вычисляется выражение
x > 0 . Если x значение больше 0, тело инструкции выполняется снова и x > 0
вычисляется повторно. Тело оператора многократно выполняется, пока значение
переменной x остается больше 0. Выполнение оператора do-while завершается,
когда значение переменной x становится равным 0 или отрицательным. Тело
цикла выполняется по крайней мере один раз.

См. также
Оператор do-while (C++)
Оператор выражений (C)
Статья • 03.03.2023 • Чтение занимает 2 мин

Когда выполняется оператор-выражение, соответствующее выражение


оценивается по правилам, описанным в статье Выражения и присваивания.

Синтаксис
expression-statement :

  expression необ. ;

Все побочные эффекты от вычисления выражений выполняются до перехода к


следующему оператору. Пустой оператор выражения называется неопределенным
оператором. Дополнительные сведения см. в разделе Оператор NULL.

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

x = ( y + 3 ); /* x is assigned the value of y + 3 */

x++; /* x is incremented */

x = y = 0; /* Both x and y are initialized to 0 */

proc( arg1, arg2 ); /* Function call returning void */

y = z = ( f( x ) + 3 ); /* A function-call expression */

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


этого выражения (т. е. значение, возвращаемое функцией) увеличивается на 3, а
затем присваивается обеим переменным: y и z .

См. также
Операторы
Оператор for (C)
Статья • 03.03.2023 • Чтение занимает 2 мин

Оператор for позволяет повторить выполнение того или иного оператора или
составного оператора заданное число раз. Тело оператора for выполняется ноль
или более раз, пока необязательное условие не примет значение false. Внутри
оператора for можно использовать необязательные выражения для
инициализации и изменения значений во время выполнения этого оператора
( for ).

Синтаксис
iteration-statement :

  for ( init-expression Выбрать ; cond-expression Выбрать ; loop-


expression Выбрать ) statement

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

1. Вычисляется выражение init-expression (при его наличии). Он задает


инициализацию для цикла. Нет ограничений на тип init-expression .

2. Вычисляется выражение cond-expression (при его наличии). Это выражение


должно иметь арифметический тип или тип указателя. Он вычисляется перед
каждой итерацией. Возможны три результата.

Если cond-expression имеет значение true (ненулевое), statement


выполняется; затем loop-expression вычисляется , если имеется. После
каждой итерации вычисляется выражение loop-expression . Нет
ограничений на его тип. Побочные эффекты выполняются по порядку.
Затем процесс начинается снова с вычисления выражения cond-
expression .

Если выражение cond-expression опущено, оно ( cond-expression )


считается истинным, и выполнение продолжается согласно описанию в
предыдущем абзаце. Выполнение оператора for без аргумента cond-
expression завершается только при выполнении в его теле оператора
break или return либо при выполнении оператора перехода goto к

оператору с меткой вне тела оператора for .


Если выражение cond-expression имеет значение false (0), выполнение
оператора for завершается и управление передается следующему
оператору программы.

Выполнение оператора for также завершается при выполнении в его теле


оператора break , goto или return . Оператор continue в цикле for приводит к
вычислению выражения loop-expression . При выполнении инструкции break
внутри for цикла loop-expression не вычисляется и не выполняется. Оператор

for( ; ; )

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


выполнить только с помощью break оператора , goto или return .

Пример
В следующем примере показано использование оператора for :

// c_for.c

int main()

char* line = "H e \tl\tlo World\0";

int space = 0;

int tab = 0;

int i;

int max = strlen(line);

for (i = 0; i < max; i++ )

if ( line[i] == ' ' )

space++;

if ( line[i] == '\t' )

tab++;

printf("Number of spaces: %i\n", space);

printf("Number of tabs: %i\n", tab);

return 0;

Вывод
Output

Number of spaces: 4

Number of tabs: 2

См. также
Операторы
goto и операторы с метками (C)
Статья • 03.03.2023 • Чтение занимает 2 мин

Оператор goto передает управление метке. Данная метка должна находиться в той
же функции и может присутствовать только перед одним ее оператором.

Синтаксис
statement :

  labeled-statement

  jump-statement

jump-statement :

  goto identifier ;

labeled-statement :

  identifier : statement

Метка оператора значима только для оператора goto ; в любом другом контексте
оператор с меткой выполняется независимо от нее.

Объект jump-statement должен находиться в одной и той же функции и может


отображаться только перед одним оператором в той же функции. Набор
identifier имен после имеет goto собственное пространство имен, поэтому имена

не влияют на другие идентификаторы. Метки не могут быть повторно объявлены.


Дополнительные сведения см. в разделе Пространства имен.

По возможности рекомендуется использовать break оператор goto , continue и


return . Поскольку оператор break выполняет выход только из одного уровня
вложенности, для глубоко вложенных циклов может потребоваться оператор goto
.

В следующем примере показано использование оператора goto .

// goto.c

#include <stdio.h>

int main()

int i, j;

for ( i = 0; i < 10; i++ )

printf_s( "Outer loop executing. i = %d\n", i );

for ( j = 0; j < 3; j++ )

printf_s( " Inner loop executing. j = %d\n", j );

if ( i == 5 )

goto stop;

/* This message does not print: */

printf_s( "Loop exited. i = %d\n", i );

stop: printf_s( "Jumped to stop. i = %d\n", i );

В этом примере оператор goto передает управление в точку с меткой stop , когда
значение переменной i равно 5.

См. также
Операторы
Оператор if (C)
Статья • 03.03.2023 • Чтение занимает 2 мин

Оператор if управляет условным ветвлением. Тело оператора if выполняется,


если значение выражения отлично от нуля. Существует две формы синтаксиса для
оператора if .

Синтаксис
selection-statement :

  if ( expression ) statement

  if ( expression ) statement else statement

В обоих формах оператора if выражение может иметь любое значение, кроме


структуры, и его вычисление влечет за собой все соответствующие побочные
эффекты.

В первой форме синтаксиса выполняется, если expression имеет значение true


(ненулевое). statement Если expression имеет значение false, statement параметр
игнорируется. Во второй форме синтаксиса, которая использует else , второй
statement выполняется, если expression имеет значение false. После этого
управление передается (в обеих формах) из оператора if в следующий по порядку
оператор программы, если выполняемый оператор не содержит операторов
break , continue или goto .

Ниже приведены примеры операторов if :

if ( i > 0 )

y = x / i;

else

x = i;

y = f( x );

В этом примере оператор y = x/i; выполняется, если i больше 0. Если i


значение меньше или равно 0, i присваивается параметру x , а f( x ) — . y
Оператор, образующий предложение, if заканчивается точкой с запятой.
Если нужно использовать вложение операторов if и предложений else ,
используйте фигурные скобки для группирования операторов и предложений в
составные операторы в соответствии с логикой. При отсутствии фигурных скобок
компилятор устраняет неоднозначности путем сопоставления каждого
предложения else с ближайшим оператором if , для которого еще не
обнаружено else .

if ( i > 0 ) /* Without braces */

if ( j > i )

x = j;

else

x = i;

В этом примере предложение else сопоставляется с внутренним оператором if .


Если значение i меньше или равно 0, переменной x не присваивается никакого
значения.

if ( i > 0 )

{ /* With braces */

if ( j > i )

x = j;

else

x = i;

В этом примере благодаря фигурным скобкам, окружающим внутренний оператор


if , предложение else относится к внешнему оператору if . Если значение i
меньше или равно 0, значение переменной i присваивается переменной x .

См. также
Оператор if-else (C++)
Оператор NULL (C)
Статья • 03.03.2023 • Чтение занимает 2 мин

Оператор null — это оператор, содержащий только точку с запятой. Он может


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

Синтаксис
;

Примечания
Некоторые операторы, например do , for , if и while , требуют, чтобы
исполняемые операторы были представлены в теле оператора. Оператор null
соответствует синтаксическим требованиям в случаях, когда существенная
основная часть оператора не требуется.

Как и с любым другим оператором С, можно включить метку перед оператором


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

В следующем примере показан оператор null.

for ( i = 0; i < 10; line[i++] = 0 )

Здесь выражение цикла в операторе for line[i++] = 0 инициализирует первые 10


элементов line значением 0. Основная часть оператора представляет собой
оператор null, поскольку остальные операторы не требуются.

См. также
Операторы
Оператор return (C)
Статья • 03.03.2023 • Чтение занимает 4 мин

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


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

Синтаксис
jump-statement :

  return expression необ. ;

Значение expression , если оно имеется, возвращается вызывающей функции. Если


expression параметр опущен, возвращаемое значение функции не определено.
Параметр "выражение", если он присутствует, вычисляется и преобразуется к типу,
возвращаемому функцией. Если оператор return содержит выражение в функциях,
имеющих тип возвращаемого значения void , то компилятор выдает
предупреждение, а выражение не вычисляется.

Если в определении функции оператор return не указан, то после выполнения


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

В качестве лучшей методики разработки рекомендуется всегда указывать тип


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

Многие программисты используют круглые скобки для заключения expression


аргумента return оператора . Однако использовать эти скобки в языке C
необязательно.

Если компилятор обнаруживает операторы, размещенные после return , он может


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

В функции main оператор return и выражение являются необязательными. То, что


происходит с указанным возвращаемым значением, зависит от реализации. Только
для Майкрософт:реализация C от Майкрософт возвращает значение выражения
процессу, вызвавшему программу, например cmd.exe . Если выражение return не
указано, среда выполнения C от Майкрософт возвращает значение,
соответствующее успешному (0) или неудачному (ненулевое значение)
выполнению.

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

// C_return_statement.c

// Compile using: cl /W4 C_return_statement.c

#include <limits.h> // for INT_MAX

#include <stdio.h> // for printf

long long square( int value )

// Cast one operand to long long to force the

// expression to be evaluated as type long long.

// Note that parentheses around the return expression

// are allowed, but not required here.

return ( value * (long long) value );

Функция square возвращает квадрат своего аргумента, используя более широкий


тип для избежания арифметической ошибки. Только для систем Майкрософт: в
реализации C от Майкрософт тип long long достаточно велик, чтобы вмещать
произведение двух значений int без переполнения.

Скобки вокруг выражения return в функции square вычисляются как часть


выражения, и использовать их в операторе return не требуется.
C

double ratio( int numerator, int denominator )


{

// Cast one operand to double to force floating-point

// division. Otherwise, integer division is used,

// then the result is converted to the return type.

return numerator / (double) denominator;

Функция ratio возвращает частное двух int аргументов в виде значения double с
плавающей запятой. Выражение return принудительно использует операцию с
плавающей запятой путем приведения одного из операндов к типу double . В
противном случае будет использоваться оператор целочисленного деления, а
дробная часть будет потеряна.

void report_square( void )

int value = INT_MAX;

long long squared = 0LL;

squared = square( value );

printf( "value = %d, squared = %lld\n", value, squared );

return; // Use an empty expression to return void.

Функция report_square вызывает square со значением параметра INT_MAX  —


самым большим целым числом со знаком, которое помещается в int . Результат
типа long long сохраняется в squared , а затем выдается в выводе. Функция
report_square имеет тип возвращаемого значения void , поэтому она не содержит

выражения в операторе return .

void report_ratio( int top, int bottom )

double fraction = ratio( top, bottom );

printf( "%d / %d = %.16f\n", top, bottom, fraction );

// It's okay to have no return statement for functions

// that have void return types.

Функция report_ratio вызывает ratio со значениями параметров 1 и INT_MAX .


Результат типа double сохраняется в fraction , а затем выдается в выводе. Функция
report_ratio имеет тип возвращаемого значения void , поэтому явно возвращать

значение не требуется. Выполнение report_ratio не дает результата и не


возвращает вызывающей функции никакого значения.

int main()

int n = 1;

int x = INT_MAX;

report_square();

report_ratio( n, x );

return 0;

Функция main вызывает две функции: report_square и report_ratio . Поскольку


report_square не принимает параметров и возвращает void , результат не

присваивается переменной. Аналогичным образом функция report_ratio


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

Чтобы скомпилировать пример, создайте файл исходного кода с именем


C_return_statement.c . Затем скопируйте весь пример кода в показанном здесь

порядке. Сохраните файл и скомпилируйте его в окне Командной строки


разработчика с помощью следующей команды:

cl /W4 C_return_statement.c

После этого, чтобы запустить пример кода, введите C_return_statement.exe в


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

Output

value = 2147483647, squared = 4611686014132420609

1 / 2147483647 = 0.0000000004656613

См. также
Операторы
Ключевое слово _Static_assert и
макрос static_assert (C11)
Статья • 03.03.2023 • Чтение занимает 2 мин

Проверяет утверждение во время компиляции. Если заданное константное


выражение имеет значение false , то компилятор выводит указанное сообщение и
компиляция завершается с ошибкой C2338; в противном случае не имеет силы.
Нововведение в стандарте C11.

Ключевое слово _Static_assert было представлено в стандарте C11. Макрос


static_assert также был представлен в стандарте C11 и сопоставляется с
ключевым словом _Static_assert .

Синтаксис
C

_Static_assert(constant-expression, string-literal);

static_assert(constant-expression, string-literal);

Параметры
constant-expression

Целочисленное константное выражение, которое можно полностью вычислить во


время компиляции. Если выражение равно нулю (false), отображается параметр
string-literal и компиляция завершается с ошибкой. Если выражение не равно
нулю (true), то не имеет силы.

string-literal

Сообщение, отображаемое, если значение constant-expression равно нулю (false).


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

Remarks
Ключевое слово _Static_assert и макрос static_assert позволяют проверить
программное утверждение во время компиляции. Их можно использовать как на
глобальном уровне, так и в области действия функции.
В отличие от этого макрос assert а также функции _assert и _wassert используются
для проверки программного утверждения во время выполнения, что предполагает
дополнительные затраты ресурсов времени выполнения.

Поведение в системах Майкрософт

В C при отсутствии <assert.h> компилятор Майкрософт обрабатывает


static_assert как ключевое слово, сопоставляемое с _Static_assert .

Рекомендуется использовать static_assert , так как в этом случае один и тот же


код будет работать как в C, так и в C++.

Пример утверждения времени компиляции


В следующем примере static_assert и _Static_assert используются для проверки
количества элементов перечисления и того, имеют ли целые числа ширину 32 бит.

// requires /std:c11 or higher

#include <assert.h>

enum Items

A,

B,

C,

LENGTH

};

int main()

// _Static_assert is a C11 keyword

_Static_assert(LENGTH == 3, "Expected Items enum to have three


elements");

// Preferred: static_assert maps to _Static_assert and is compatible


with C++

static_assert(sizeof(int) == 4, "Expecting 32 bit integers");

return 0;

Requirements (Требования)
Макрос Обязательный заголовок
Макрос Обязательный заголовок

static_assert <assert.h>

Выполните сборку с помощью команды /std:c11.

Пакет Windows SDK 10.0.20348.0 (версия 2104) или выше. Дополнительные


сведения об установке пакета Windows SDK для разработки с использованием C11
и C17 см. в статье Установка поддержки C11 и C17 в Visual Studio.

См. также
_STATIC_ASSERTМакрос

Макрос assert, а также функции _assert и _wassert/std (стандартная версия языка)


Оператор switch (C)
Статья • 03.03.2023 • Чтение занимает 3 мин

Операторы switch и case помогают управлять сложными условными операциями


и операциями ветвления. Оператор switch передает управление в оператор
внутри своего тела.

Синтаксис
selection-statement :

  switch (   expression   )   statement

labeled-statement :

  case   constant-expression   :   statement

  default   :   statement

Комментарии
Оператор switch передает управление одному из labeled-statement в своем теле в
зависимости от значения expression .

Значения expression и значение каждого constant-expression должны иметь


целочисленный тип. Выражение constant-expression должно иметь однозначное
константное целочисленное значение во время компиляции.

Управление передается оператору case , значение constant-expression которого


соответствует значению выражения expression . Оператор switch может
содержать неограниченное число экземпляров case . Однако значения ни одной
из пар выражений constant-expression в одном операторе switch не должны
совпадать. Выполнение тела оператора switch начинается с первого
соответствующего оператора labeled-statement или после него. Выполнение
продолжается до конца тела оператора или пока оператор break не передаст
управление за пределы тела.

Оператор switch обычно используется следующим образом:

switch ( expression )

// declarations

// . . .

case constant_expression:

// statements executed if the expression equals the

// value of this constant_expression

break;

default:

// statements executed if expression does not equal

// any case constant_expression

Оператор break можно использовать для завершения обработки определенного


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

Оператор default выполняется, если ни одно из значений case constant-expression


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

Выражения switch expression и case constant-expression должны быть


целочисленного типа. Значение каждого case constant-expression в теле оператора
должно быть уникальным.

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

7 Примечание

Объявления могут располагаться в начале составного оператора,


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

switch( c )

case 'A':

capital_a++;

case 'a':

letter_a++;

default :

total++;

В этом примере, если переменная c равна 'A' , выполняются все три оператора
тела switch , так как оператор break перед следующим оператором case
отсутствует. Управления передается первому оператору ( capital_a++; ) и
продолжается по-порядку во всей остальной части тела. Если переменная c равна
'a' , увеличиваются значения переменных letter_a и total . Если переменная c не
равна 'A' или 'a' , увеличивается только значение переменной total .

switch( i )

case -1:

n++;

break;

case 0 :

z++;

break;

case 1 :

p++;

break;

В этом примере оператор break указан после каждого оператора тела switch .
Оператор break вызывает принудительный выход из тела оператора после
выполнения одного оператора. Если переменная i равна –1, увеличивается только
значение переменной n . Оператор break после оператора n++; передает
управление за пределы тела оператора, минуя оставшиеся операторы. Аналогично,
если переменная i равна 0, увеличивается только значение переменной z ; если
переменная i равна 1, увеличивается только значение переменной p .
Заключительный оператор break , строго говоря, не требуется, так как управление
передается из тела в конце составного оператора. Он добавлен для единообразия.

Один оператор может содержать несколько меток case , как показано в


следующем примере:

switch( c )

case 'a' :

case 'b' :

case 'c' :

case 'd' :

case 'e' :

case 'f' : convert_hex(c);

В этом примере, если constant-expression значение равно любой букве между 'a'
и 'f' , convert_hex вызывается функция .

Специально для систем Майкрософт


Microsoft C не ограничивает количество значений case в операторе switch . Это
число ограничивается только объемом доступной памяти. ANSI C требует, чтобы в
операторе switch можно было использовать не менее 257 меток case .

В Microsoft C расширения Майкрософт по умолчанию (default) включены.


Используйте параметр компилятора /Za для отключения этих расширений.

См. также
Оператор switch (C++)
Оператор try-except (C)
Статья • 03.03.2023 • Чтение занимает 2 мин

Только для систем Майкрософт

Оператор try-except является расширением Майкрософт для языка C, которое


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

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


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

Синтаксис
try-except-statement :

  __try compound-statement __except ( expression ) compound-statement

Составной оператор после предложения __try  — это защищенный раздел.


Составной оператор после предложения __except  — это обработчик исключений.
Обработчик определяет набор действий, предпринимаемых при возникновении
исключения во время выполнения защищенного раздела. Выполнение происходит
следующим образом:

1. Сначала выполняется защищенный раздел.

2. Если исключение при этом не возникает, выполнение переходит в


инструкцию, стоящую после предложения __except .

3. Если исключение возникло во время выполнения защищенного раздела или в


любой процедуре, вызываемой защищенным разделом, вычисляется
выражение __except . Способ обработки исключения определяется
возвращенным значением. Возможны три значения.
EXCEPTION_CONTINUE_SEARCH . Исключение не распознано. Программа

переходит к поиску обработчика в стеке (сначала находятся выражения с


оператором try-except , а затем обработчики со следующим наивысшим
приоритетом).

EXCEPTION_CONTINUE_EXECUTION . Исключение распознано, но отклонено.

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

EXCEPTION_EXECUTE_HANDLER Исключение распознано. Управление


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

Так как выражение __except вычисляется как выражение C, оно ограничивается


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

7 Примечание

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


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

Каждая процедура в приложении может иметь свой собственный обработчик


исключений. Выражение __except выполняется в области тела __try . Оно имеет
доступ ко всем локальным переменным, объявленным в этой области.

В блоке оператора __leave можно использовать ключевое слово try-except .


Результат использования __leave  — переход в конец блока try-except .
Выполнение продолжается после окончания обработчика исключений. Хотя для
получения того же результата можно использовать оператор goto , он (оператор
goto ) приводит к освобождению стека. Оператор __leave более эффективен, так
как он не вызывает раскрутку стека.
Выход из оператора try-except с помощью функции времени выполнения longjmp
считается ненормальным завершением. Переход к оператору __try недопустим,
но выход из него допускается. Обработчик исключений не вызывается, если
процесс удаляется во время выполнения оператора try-except .

Пример
Ниже приведен пример обработчика исключений и обработчика завершения.
Дополнительные сведения об обработчиках завершения см. в разделе Оператор
try-finally (С).

puts("hello");

__try {

puts("in try");

__try {

puts("in try");

RAISE_AN_EXCEPTION();

} __finally {

puts("in finally");

} __except( puts("in filter"), EXCEPTION_EXECUTE_HANDLER ) {

puts("in except");

puts("world");

Ниже показаны выходные данные для этого примера с комментариями справа:

Output

hello

in try /* fall into try */

in try /* fall into nested try */

in filter /* execute filter; returns 1 so accept */

in finally /* unwind nested finally */

in except /* transfer control to selected handler */

world /* flow out of handler */

КОНЕЦ Только для систем Майкрософт

См. также раздел


Оператор try-except (C++)
Оператор try-finally (C)
Статья • 03.03.2023 • Чтение занимает 2 мин

Только для систем Майкрософт

Оператор try-finally является расширением Microsoft для языка C и позволяет


приложениям гарантировать выполнение кода очистки при прерывании
выполнения блока кода. Очистка включает такие задачи, как отмена
распределения памяти, закрытие файлов и освобождение их дескрипторов.
Оператор try-finally особенно полезен для подпрограмм, в которых в нескольких
местах выполняется проверка на наличие ошибок, способных вызвать
преждевременное возвращение из подпрограммы.

try-finally-statement :

  __try compound-statement __finally compound-statement

Составной оператор после предложения __try представляет собой защищенный


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

Управление достигает оператора __try в процессе обычного последовательного


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

1. Сначала выполняется защищенный раздел.

2. Вызывается обработчик завершения.

3. По окончании работы обработчика завершения выполнение продолжается


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

В блоке оператора __leave можно использовать ключевое слово try-finally .


Результат использования __leave  — переход в конец блока try-finally . Сразу же
выполняется обработчик завершения. Хотя для получения того же результата
можно использовать оператор goto , он (оператор goto ) приводит к
освобождению стека. Оператор __leave более эффективен, так как он не вызывает
раскрутку стека.

Выход из оператора try-finally с помощью оператора return или функции


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

Если при выполнении оператора try-finally процесс удаляется, обработчик


завершения не вызывается.

7 Примечание

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


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

Чтобы увидеть, как работает оператор try-finally , см. пример для оператора try-
except.

КОНЕЦ Только для систем Майкрософт

См. также раздел


Оператор try-finally (C++)
Оператор while (C)
Статья • 03.03.2023 • Чтение занимает 2 мин

Оператор while позволяет повторять выполнение оператора до тех пор, пока


указанное выражение не станет ложным.

Синтаксис
iteration-statement :

  while ( expression ) statement

Объект expression должен иметь арифметический тип или тип указателя.


Выполнение происходит следующим образом:

1. Вычисляется expression .

2. Если expression изначально имеет значение false, тело while оператора


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

Если expression имеет значение true (ненулевое), выполняется текст


инструкции и процесс повторяется, начиная с шага 1.

Выполнение оператора while прерывается, если в теле оператора выполняется


оператор break , goto или return . Для прерывания итерации без выхода из цикла
while используйте оператор continue . Оператор continue передает управление в
следующую итерацию оператора while .

Вот пример оператора while :

while ( i >= 0 )

string1[i] = string2[i];

i--;

В этом примере производится копирование символов из string2 в string1 . Если i


значение больше или равно 0, то string2[i] присваивается string1[i] и i
уменьшается. Когда значение переменной i становится равным 0 (или меньше 0),
выполнение оператора while прекращается.

См. также
Оператор while (C++)
Функции (C)
Статья • 03.03.2023 • Чтение занимает 2 мин

Функция — это базовая модульная единица языка C. Обычно функции создаются


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

Overview of Functions (Общие сведения о функциях)

Function Attributes (Атрибуты функций)

Specifying Calling Conventions (Указание соглашений о вызовах)

Inline Functions (Встраиваемые функции)

DLL Import and Export Functions (Функции экспорта и импорта библиотек DLL)

Naked Functions (Функции naked)

Storage Class (Класс хранения)

Return Type (Возвращаемый тип)

Аргументы

Параметры

См. также
Справочник по языку C
Общие сведения о функциях
Статья • 26.09.2022 • Чтение занимает 2 мин

Функции должны иметь определение и объявление, хотя определение может


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

Объявление функции указывает имя, возвращаемый тип и атрибуты функции,


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

Компилятор использует прототип для сравнения типов аргументов в последующих


вызовах функции с параметрами функции и для преобразования типов аргументов
в типы параметров, если это необходимо.

В вызове функции управление выполнением передается из вызывающей функции


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

См. также
Функции
Устаревшие формы объявлений и
определений функций
Статья • 03.03.2023 • Чтение занимает 2 мин

В объявлениях и определениях функций по старому стилю используют несколько


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

double old_style(); /* Obsolete function declaration */

double alt_style( a , real ) /* Obsolete function definition */

double *real;

int a;

return ( *real + a ) ;

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

В целях соответствия стандарту ANSI C объявления функций по старому стилю,


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

C++

void funct1( a, ... ) /* Generates a warning under /Ze or */

int a; /* an error when compiling with /Za */

Необходимо переписать это объявление в качестве прототипа.

C++
void funct1( int a, ... )

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


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

В следующей статье (Определения функций на языке C) описан синтаксис для


определения функций, в том числе устаревший синтаксис. Нетерминал identifier-list
обозначает список параметров для синтаксиса устаревшего стиля.

См. также
Общие сведения о функциях
Определения функций в C
Статья • 03.03.2023 • Чтение занимает 2 мин

Определение функции задает имя функции, типы и число ожидаемых параметров,


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

Синтаксис
translation-unit :

  external-declaration

  translation-unit external-declaration

external-declaration : /* Разрешено только во внешней (файловой) области */

  function-definition

  declaration

function-definition :

  declaration-specifiers opt attribute-seq opt declarator declaration-list opt compound-


statement

/* attribute-seq используется только в системах Майкрософт */

Параметры прототипа:

declaration-specifiers :

  storage-class-specifier declaration-specifiers необ.

  type-specifier declaration-specifiers opt

  type-qualifier declaration-specifiers необ.

declaration-list :

  declaration

  declaration-list declaration

declarator :

  pointer необ. direct-declarator

direct-declarator : /* Оператор объявления функции */

  direct-declarator ( parameter-type-list ) /* Оператор объявления нового стиля


*/

  direct-declarator ( identifier-list opt ) /* Оператор объявления старого стиля */

Для списка параметров в определении используется следующий синтаксис:

parameter-type-list : /* Список параметров */

  parameter-list

  parameter-list , ...

parameter-list :

  parameter-declaration

  parameter-list , parameter-declaration

parameter-declaration :

  declaration-specifiers declarator

  declaration-specifiers abstract-declarator необ.

В определении функции устаревшего стиле для списка параметров используется


следующий синтаксис:

identifier-list : /* Используется в определениях и объявлениях функций

устаревшего стиля */

  identifier

  identifier-list , identifier

Синтаксис тела функции:

compound-statement :

  { declaration-list opt statement-list opt }

Изменять объявления функций могут только описатели класса хранения extern и


static . Спецификатор extern означает, что на функцию можно ссылаться из

других файлов; то есть, имя функции экспортируется в компоновщик.


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

Необязательные declaration-specifiers и обязательные declarator вместе


указывают тип и имя возвращаемого значения функции. Декларатор declarator
представляет собой сочетание идентификатора, задающего имя функции, и
круглых скобок после имени функции. Необязательный attribute-seq нетерминал
— это функция, определяемая корпорацией Майкрософт в разделе Атрибуты
функции.

( direct-declarator в синтаксисе declarator ) указывает имя определяемой


функции и идентификаторы ее параметров. direct-declarator Если содержит
parameter-type-list , в списке указываются типы всех параметров. Такой

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

Объект declaration в определениях функции не может содержать объект ,


отличный от storage-class-specifier register . declaration-list В синтаксисе type-
specifier declaration-specifiers можно опустить только в том случае, если
register класс хранения указан для значения int типа .

— compound-statement это тело функции, содержащее объявления локальных


переменных, ссылки на объявленные извне элементы и инструкции.

Компоненты определения функции подробно рассматриваются в статьях Атрибуты


функций, Класс хранения, Тип возвращаемого значения, Параметры и Текст
функции.

См. также
Функции
Атрибуты функций
Статья • 03.03.2023 • Чтение занимает 2 мин

Блок, относящийся только к системам Microsoft

Необязательная нетерминальная attribute-seq позволяет выбирать соглашения о


вызовах на уровне функций. Можно также задать функцию как __fastcall или
__inline .

Завершение блока, относящегося только к системам Майкрософт

См. также
Определения функций в C
Указание соглашений о вызовах
Статья • 03.03.2023 • Чтение занимает 2 мин

Блок, относящийся только к системам Microsoft

Дополнительные сведения по соглашениям о вызовах см. в разделе Разделы


соглашений о вызовах.

Завершение блока, относящегося только к системам Майкрософт

См. также
Атрибуты функций
Встраиваемые функции
Статья • 03.03.2023 • Чтение занимает 2 мин

Ключевое слово inline  — это описатель функции, который указывает компилятору


заменить код в определении функции для каждого экземпляра вызова функции.

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

Чтобы функция рассматривалась в качестве кандидата на подстановку, она должна


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

Используйте следующую форму для определения встраиваемой функции:

inline function-definition

Встроенные функции создают более быстрый и часто более компактный код, чем
эквивалентные вызовы функций.

Встроенные функции избавляют от затрат времени на подготовку стека для


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

Даже если встроенная функция повторяется несколько раз, при небольшом


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

Компилятор может применять к встроенным функциям некоторые


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

Не путайте функции, которые используют inline , со встроенным кодом на


ассемблере. Дополнительные сведения о встроенном коде на ассемблере можно
получить в статье Встроенный ассемблер.

Только для систем Майкрософт

Кроме того, Майкрософт поддерживает ключевые слова __inline и __forceinline ,


позволяющие указать компилятору заменить код в определении функции для
каждого экземпляра вызова функции. Ключевое слово __inline является
синонимом для inline . Ключевое слово __forceinline указывает компилятору
ослабить ограничения эвристических данных о том, следует ли встраивать
функцию. Но это не гарантирует того, что функция будет встроена.

Для совместимости с предыдущими версиями _inline и _forceinline являются


синонимами __inline и __forceinline , соответственно, если не указан параметр
компилятора /Za (отключение расширений языка).

ОКОНЧАНИЕ Только для систем Майкрософт

См. также
inline, __inline, __forceinline
Встроенный ассемблер (C)
Статья • 26.09.2022 • Чтение занимает 2 мин

Блок, относящийся только к системам Microsoft

Встроенный ассемблер позволяет внедрять инструкции языка ассемблера


непосредственно в исходные программы С без дополнительных шагов по сборке и
компоновке. Встроенный код на ассемблере встроен в компилятор, поэтому вам не
требуется отдельный сборщик для языка ассемблера, такой как Microsoft Macro
Assembler (MASM).

Так как встроенный ассемблер не требует отдельных действий по сборке и


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

Ключевое слово __asm вызывает встроенный ассемблер и может использоваться в


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

Следующий код — это простой блок __asm , заключенный в фигурные скобки. (Этот
код является последовательностью пролога пользовательской функции.)

__asm

push ebp

mov ebp, esp

sub esp, __LOCAL_SIZE

Кроме того, можно поставить __asm перед каждой инструкцией по сборке.


__asm push ebp

__asm mov ebp, esp

__asm sub esp, __LOCAL_SIZE

Поскольку ключевое слово __asm является разделителем операторов, можно также


помещать инструкции ассемблера на одной строке.

__asm push ebp __asm mov ebp, esp __asm sub esp, __LOCAL_SIZE

Завершение блока, относящегося только к системам Майкрософт

См. также
Атрибуты функций
Ключевое слово _Noreturn и макрос
noreturn (C11)
Статья • 03.03.2023 • Чтение занимает 2 мин

Ключевое слово _Noreturn было представлено в стандарте C11. Оно указывает


компилятору, что функция, к которой оно применяется, не возвращается в
вызывающий объект. Таким образом, компилятор знает, что код после вызова
функции _Noreturn недостижим. Примером функции, которая не выполняет
возврат, является abort. Если поток управления может вернуться в вызывающий
объект, функция не должна иметь атрибут _Noreturn .

Это ключевое слово обычно используется в удобном макросе noreturn , который


находится в файле <stdnoreturn.h> и сопоставляется с ключевым словом _Noreturn .

Ключевое слово _Noreturn (или эквивалентный ему макрос noreturn ) позволяет


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

Функция, помеченная noreturn , не должна включать тип возвращаемого значения,


так как она не возвращает значение вызывающему объекту. Оно должно иметь
значение void .

Пример использования макроса noreturn и


ключевого слова _Noreturn
В следующем примере иллюстрируется использование ключевого слова _Noreturn
и эквивалентного ему макроса noreturn .

Если используется макрос noreturn , который можно игнорировать, функция


IntelliSense может возвращать ложную ошибку E0065 . Ее появление не мешает
запустить пример.

// Compile with Warning Level4 (/W4) and /std:c11

#include <stdio.h>

#include <stdlib.h>

#include <stdnoreturn.h>

noreturn void fatal_error(void)

exit(3);

_Noreturn void not_coming_back(void)

puts("There's no coming back");

fatal_error();

return; // warning C4645 - function declared with noreturn has a return


statement

void done(void)

puts("We'll never get here");

int main(void)

not_coming_back();

done(); // warning c4702 - unreachable code

return 0;

Requirements (Требования)
Макрос Обязательный заголовок

noreturn <stdnoreturn.h>

См. также
/std (определение стандартной версии языка)

/W4 (выбор уровня предупреждения)

Предупреждение C4702

__declspec(noreturn)
Функции импорта и экспорта DLL
Статья • 03.03.2023 • Чтение занимает 2 мин

Блок, относящийся только к системам Microsoft

Наиболее полную и актуальную информацию по этой теме можно найти в статье


dllexport, dllimport.

Модификаторы класса хранения dllimport и dllexport  — это расширения языка С


для систем Microsoft. Эти модификаторы явным образом определяют интерфейс
библиотеки DLL к ее клиенту (исполняемому файлу или другой библиотеке DLL).
Объявление функции в качестве dllexport позволяет обойтись без файла
определения модуля (.DEF). Кроме того, модификаторы dllimport и dllexport
можно использовать с данными и объектами.

Модификаторы класса хранения dllimport и dllexport должны использоваться с


ключевым словом расширенного синтаксиса атрибутов __declspec , как показано в
следующем примере:

#define DllImport __declspec( dllimport )

#define DllExport __declspec( dllexport )

DllExport void func();

DllExport int i = 10;

DllExport int j;

DllExport int n;

Дополнительные сведения о синтаксисе расширенных модификаторов класса


хранения см. в статье C Extended Storage-Class Attributes (Расширенные атрибуты
классов хранения C).

Завершение блока, относящегося только к системам Майкрософт

См. также
Определения функций в C
Определения и объявления (C)
Статья • 03.03.2023 • Чтение занимает 2 мин

Блок, относящийся только к системам Microsoft

Интерфейс DLL относится ко всем элементам (функциям и данным), про которые


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

#define DllImport __declspec( dllimport )

#define DllExport __declspec( dllexport )

DllImport int func() /* Error; dllimport prohibited in */

/* definition. */

return 1;

Показанный ниже код также вызовет ошибку.

#define DllImport __declspec( dllimport )

#define DllExport __declspec( dllexport )

DllImport int i = 10; /* Error; this is a definition. */

Однако следующий синтаксис правильный.

#define DllImport __declspec( dllimport )

#define DllExport __declspec( dllexport )

DllExport int i = 10; /* Okay: this is an export definition. */

Использование атрибута dllexport подразумевает определение, а dllimport  —


объявление. Для обеспечения объявления ключевое слово extern следует
использовать с аргументом dllexport ; в противном случае подразумевается
определение.

#define DllImport __declspec( dllimport )

#define DllExport __declspec( dllexport )

extern DllImport int k; /* These are correct and imply */

Dllimport int j; /* a declaration. */

Завершение блока, относящегося только к системам Майкрософт

См. также
Функции импорта и экспорта DLL
Определение подставляемых
функций C с использованием
dllexport и dllimport
Статья • 03.03.2023 • Чтение занимает 2 мин

Блок, относящийся только к системам Microsoft

Функцию с атрибутом dllexport можно определить как встроенную. В этом случае


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

В качестве встроенной можно также определить функцию, объявленную с


атрибутом dllimport . В этом случае функцию можно расширить (согласно
спецификации параметров компилятора /Ob (inline)), но ее экземпляр никогда не
создается. В частности, если принимается адрес встроенной импортированной
функции, возвращается адрес функции, находящейся в библиотеке DLL. Это
поведение аналогично получению адреса невстроенной импортированной
функции.

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


одинаковые идентификаторы между библиотекой DLL и клиентом так, как это было
бы в одной программе (т. е. исполняемый файл без интерфейса DLL).

При предоставлении импортированных встроенных функций соблюдайте


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

Завершение блока, относящегося только к системам Майкрософт

См. также
Функции импорта и экспорта DLL
Правила и ограничения
dllimport/dllexport
Статья • 03.03.2023 • Чтение занимает 2 мин

Блок, относящийся только к системам Microsoft

Если функция объявлена без атрибута dllimport или dllexport , то она не


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

Если в одном и том же модуле вашей программы содержатся объявления


одной и той же функции с атрибутами dllimport и dllexport , то атрибут
dllexport имеет приоритет над dllimport . Однако компилятор создает

предупреждение. Пример:

#define DllImport __declspec( dllimport )

#define DllExport __declspec( dllexport )

DllImport void func1( void );

DllExport void func1( void ); /* Warning; dllexport */

/* takes precedence. */

Вы не можете инициализировать указатель на статические данные с адресом


объекта данных, объявленного с атрибутом dllimport . Например, следующий
код вызывает ошибки:

#define DllImport __declspec( dllimport )

#define DllExport __declspec( dllexport )

DllImport int i;

int *pi = &i; /* Error */

void func2()

static int *pi = &i; /* Error */

Если при инициализации указателя на статическую функцию вы присвоите


ему адрес функции, объявленной с атрибутом dllimport , то указатель будет
ссылаться не на адрес функции, а на адрес преобразователя импорта DLL (это
заглушка, которая передает управление функции). Следующее присваивание
не порождает сообщения об ошибке:

#define DllImport __declspec( dllimport )

#define DllExport __declspec( dllexport )

DllImport void func1( void

static void ( *pf )( void ) = &func1; /* No Error */

void func2()

static void ( *pf )( void ) = &func1; /* No Error */

Поскольку программа с атрибутом dllexport в объявлении объекта должна


содержать определение этого объекта, то указатель на глобальную или
локальную статическую функцию можно инициализировать с адресом
функции с атрибутом dllexport . Аналогичным образом указатель на
глобальные или локальные статические данные можно инициализировать с
адресом объекта данных с атрибутом dllexport . Пример:

#define DllImport __declspec( dllimport )

#define DllExport __declspec( dllexport )

DllImport void func1( void );

DllImport int i;

DllExport void func1( void );

DllExport int i;

int *pi = &i; /* Okay */

static void ( *pf )( void ) = &func1; /* Okay */

void func2()

static int *pi = i; /* Okay */

static void ( *pf )( void ) = &func1; /* Okay */

Завершение блока, относящегося только к системам Майкрософт

См. также
Функции импорта и экспорта DLL
Функции Naked
Статья • 03.03.2023 • Чтение занимает 2 мин

Блок, относящийся только к системам Microsoft

Атрибут класса хранения naked является расширением языка C для систем


Microsoft. Код функций, объявленных с атрибутом naked , создается компилятором
без кода пролога и эпилога. Эту возможность можно использовать, чтобы
создавать свой собственный код на языке ассемблера и вставлять его в качестве
пролога и эпилога. Функции с атрибутом naked особенно полезны для написания
драйверов виртуальных устройств.

Поскольку атрибут naked относится только к определению функции и не является


модификатором типа, в функциях с этим атрибутом используется расширенный
синтаксис атрибутов, который описывается в статье Расширенные атрибуты
классов хранения в C.

В следующем примере определяется функция с атрибутом naked .

__declspec( naked ) int func( formal_parameters )

/* Function body */

Другой пример:

#define Naked __declspec( naked )

Naked int func( formal_parameters )

/* Function body */

Атрибут naked влияет только на создание кода компилятора для


последовательностей пролога и эпилога функции. Код, который создается для
вызова таких функций, не зависит от этого атрибута. Таким образом, атрибут naked
не входит в тип функции, а указатели на функции не могут иметь атрибут naked .
Кроме того, атрибут naked не может применяться к определениям данных.
Например, следующий код вызывает ошибки:
__declspec( naked ) int i; /* Error--naked attribute not */

/* permitted on data declarations. */

Атрибут naked относится только к определению функции и не может быть


определен в прототипе функции. Следующее объявление создает ошибку
компилятора:

__declspec( naked ) int func(); /* Error--naked attribute not */

/* permitted on function declarations. */ \

Завершение блока, относящегося только к системам Майкрософт

См. также
Определения функций в C
Правила и ограничения при
использовании функций Naked
Статья • 03.03.2023 • Чтение занимает 2 мин

Дополнительные сведения о правилах и ограничениях при использовании


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

См. также
Функции naked
Вопросы, связанные с написанием
кода пролога и эпилога
Статья • 03.03.2023 • Чтение занимает 2 мин

Блок, относящийся только к системам Microsoft

Перед написанием собственных последовательностей кодов пролога и эпилога


важно понимать, как размещается кадр стека. Полезно также знать, как можно
использовать предопределенную константу __LOCAL_SIZE.

Схема кадра CStack


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

push ebp ; Save ebp

mov ebp, esp ; Set stack frame pointer

sub esp, localbytes ; Allocate space for locals

push <registers> ; Save registers

Переменная localbytes представляет число байтов, которые требуются в стеке для


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

pop <registers> ; Restore registers

mov esp, ebp ; Restore stack pointer

pop ebp ; Restore ebp

ret ; Return from function

Стек всегда расширяется в направлении вниз (от старших адресов памяти к


младшим). Указатель базы ( ebp ) указывает на помещенное в стек значение ebp .
Область локальных переменных начинается с ebp-2 . Для доступа к локальным
переменным необходимо вычислить смещение от ebp путем вычитания
соответствующего значения из ebp .
Константа __LOCAL_SIZE
Компилятор предоставляет константу __LOCAL_SIZE, которую можно использовать
использования во встроенном ассемблерном блоке кода пролога функции. Она
служит для выделения пространства локальным переменным в кадре стека
пользовательского кода пролога.

Значение константы __LOCAL_SIZE определяется компилятором. Это значение


представляет общее количество байтов всех определяемых пользователем
локальных переменных и временных переменных, создаваемых компилятором.
Константу __LOCAL_SIZE можно использовать только в качестве непосредственного
операнда, но не в выражениях. Значение этой константы не следует изменять и
переопределять. Пример:

mov eax, __LOCAL_SIZE ;Immediate operand--Okay

mov eax, [ebp - __LOCAL_SIZE] ;Error

В следующем примере функции naked с пользовательскими последовательностями


пролога и эпилога константа __LOCAL_SIZE используется в последовательности
пролога.

__declspec ( naked ) func()

int i;

int j;

__asm /* prolog */

push ebp

mov ebp, esp

sub esp, __LOCAL_SIZE

/* Function body */

__asm /* epilog */

mov esp, ebp

pop ebp

ret

Завершение блока, относящегося только к системам Майкрософт

См. также
Функции naked
Класс хранения
Статья • 03.03.2023 • Чтение занимает 2 мин

Описатель класса хранения в определении функции предоставляет ей класс


хранения extern или static .

Синтаксис
function-definition :

  declaration-specifiers opt attribute-seq opt declarator declaration-list opt compound-


statement

/* attribute-seq используется только в системах Майкрософт */

declaration-specifiers :

  storage-class-specifier declaration-specifiers необ.

  type-specifier declaration-specifiers необ.

  type-qualifier declaration-specifiers необ.

storage-class-specifier : /* Для определений функций */

  extern

  static

Если определение функции не содержит storage-class-specifier , класс хранения


по умолчанию имеет значение extern . Функцию можно явно объявить как extern ,
но она не является обязательной.

Если объявление функции содержит , идентификатор имеет ту же компоновку


storage-class-specifier extern , что и любое видимое объявление идентификатора

с областью действия файла. Если нет видимого объявления с областью файла,


идентификатор имеет внешнюю компоновку. Если идентификатор имеет область
файла, а нет storage-class-specifier , идентификатор имеет внешнюю компоновку.
Внешняя компоновка означает, что каждый экземпляр идентификатора определяет
один и тот же объект или функцию. Дополнительные сведения о компоновке и
области файлов см. в разделах Время существования, Область, Видимость и
Компоновка.

Объявления функций в области видимости блока со спецификатором класса


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

Блок, относящийся только к системам Microsoft

При включенных расширениях Майкрософт функции, первоначально объявленной


без класса хранения (или с классом хранения extern ), предоставляется класс
хранения static , если определение этой функции находится в том же исходном
файле и в нем явно указан класс хранения static .

При компиляции с параметром компилятора /Ze функции, объявленные в блоке с


помощью ключевого extern слова , имеют глобальную видимость, что не
соответствует действительности при компиляции с /Za. Не следует полагаться на
эту функцию, если необходимо учитывать переносимость исходного кода.

Завершение блока, относящегося только к системам Майкрософт

См. также
Определения функций в C
Возвращаемый тип
Статья • 03.03.2023 • Чтение занимает 2 мин

Тип возвращаемого значения функции определяет размер и тип значения,


возвращаемого функцией. Он соответствует в type-specifier разделе Синтаксис:

Синтаксис
function-definition :

  declaration-specifiers opt attribute-seq opt declarator declaration-list opt compound-


statement

/* attribute-seq используется только в системах Майкрософт */

declaration-specifiers :

  storage-class-specifier declaration-specifiers необ.

  type-specifier declaration-specifiers необ.

  type-qualifier declaration-specifiers необ.

type-specifier :

  void

  char

  short

  int

  __int8 /* Только для систем Майкрософт */

  __int16 /* Только для систем Майкрософт */

  __int32 /* Только для систем Майкрософт */

  __int64 /* Только для систем Майкрософт */

  long

  long long

  float

  double

  long double

  signed

  unsigned

  struct-or-union-specifier

  enum-specifier

  typedef-name
может указать любой type-specifier основной тип, тип структуры или
объединения.

Тип возвращаемого значения, указанный в определении функции, должен


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

возвращаемого значения (при необходимости) и осуществляется возврат в точку,


где была вызвана функция. Если функция объявлена с типом возвращаемого
значения void , оператор return, содержащий выражение, создает предупреждение,
и выражение не вычисляется.

В приведенных ниже примерах показываются возвращаемые значения функции.

typedef struct

char name[20];

int id;

long class;

} STUDENT;

/* Return type is STUDENT: */

STUDENT sortstu( STUDENT a, STUDENT b )

return ( (a.id < b.id) ? a : b );

В этом примере определяются тип STUDENT с помощью объявления typedef , а


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

7 Примечание

Эффективность можно увеличить путем передачи указателей на конкретную


структуру, а не на всю структуру.

char *smallstr( char s1[], char s2[] )

int i;

i = 0;

while ( s1[i] != '\0' && s2[i] != '\0' )

i++;

if ( s1[i] == '\0' )

return ( s1 );

else

return ( s2 );

В этом примере определяется функция, возвращающая указатель на массив


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

Объявлять функции с возвращаемым типом int перед их вызовом не требуется,


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

См. также
Определения функций в C
Параметры
Статья • 03.03.2023 • Чтение занимает 2 мин

Аргументы — это имена значений, переданных в функцию в результате вызова


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

Синтаксис
function-definition :

  declaration-specifiers opt attribute-seq opt declarator declaration-list opt compound-


statement

/* attribute-seq используется только в системах Майкрософт */

declarator :

  pointer необ. direct-declarator

direct-declarator : /* Оператор объявления функции */

  direct-declarator ( parameter-type-list ) /* Оператор объявления нового стиля


*/

  direct-declarator ( identifier-list opt ) /* Оператор объявления старого стиля */

parameter-type-list : /* Список параметров */

  parameter-list

  parameter-list , ...

parameter-list :

  parameter-declaration

  parameter-list , parameter-declaration

parameter-declaration :

  declaration-specifiers declarator

  declaration-specifiers abstract-declarator необ.

parameter-type-list содержит последовательность объявлений параметров,

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


следующим образом.
register opt type-specifier declarator opt

Параметры функции, объявленные с атрибутом auto , создают ошибки.


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

Каждому идентификатору в parameter-type-list должен предшествовать


соответствующий описатель типа, как показано в следующем примере:

void new( double x, double y, double z )

/* Function body here */

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


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

Если аргументы не требуется передавать в функцию, список параметров


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

Порядок и тип параметров, включая любое использование нотации с


многоточием, должны быть одинаковыми во всех объявлениях функций (если есть)
и в определении функции. Типы аргументов после обычных арифметических
преобразований должны быть совместимы по назначению с типами
соответствующих параметров. (Дополнительные сведения об арифметических
преобразованиях см. в статье Обычные арифметические преобразования.)
Аргументы после многоточия не проверяются. Параметр может иметь любой
основной тип, тип структуры, объединения, указателя или массива.
Компилятор выполняет обычные арифметические преобразования отдельно для
каждого параметра и для каждого аргумента при необходимости. После
преобразования параметры имеют длину не менее int и никогда не имеют типа
float , если для конкретного параметра в прототипе явно не задан тип float . Это
означает, например, что объявление параметра как char будет иметь тот же
эффект, что и его объявление как int .

См. также
Определения функций в C
Текст функции
Статья • 03.03.2023 • Чтение занимает 2 мин

Тело функции — это составной оператор, содержащий операторы, которые


определяют выполняемые функцией действия.

Синтаксис
function-definition :

  declaration-specifiers opt attribute-seq opt declarator declaration-list opt compound-


statement

/* attribute-seq используется только в системах Майкрософт */

compound-statement : /* Текст функции */

  { declaration-list opt statement-list opt }

Если не указано иное, переменные, объявленные в теле функции (локальные


переменные), имеют класс хранения auto . При вызове функции создается
хранилище для локальных переменных и выполняются локальные инициализации.
Управление выполнением передается первому оператору в compound-statement и
продолжается до тех пор, пока не будет выполнена инструкция или не return
будет обнаружен конец тела функции. Затем управление возвращается в точку, из
которой вызвана функция.

Если функция должна возвращать значение, должен быть выполнен оператор


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

См. также
Определения функций в C
Прототипы функций
Статья • 03.03.2023 • Чтение занимает 2 мин

Объявление функции предшествует ее определению и указывает имя, тип


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

Синтаксис
declaration :

  declaration-specifiers attribute-seq opt init-declarator-list opt ;

/* attribute-seq необ. относится только к продуктам Майкрософт */

declaration-specifiers :

  storage-class-specifier declaration-specifiers необ.

  type-specifier declaration-specifiers необ.

  type-qualifier declaration-specifiers необ.

init-declarator-list :
  init-declarator

  init-declarator-list , init-declarator

init-declarator :

  declarator

  declarator = initializer

declarator :

  pointer необ. direct-declarator

direct-declarator : /* Оператор объявления функции */

  direct-declarator ( parameter-type-list ) /* Оператор объявления нового стиля


*/

  direct-declarator ( identifier-list opt ) /* Оператор объявления старого стиля */

Прототип имеет ту же форму, что и определение функции, за исключением того,


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

Они определяют тип возвращаемого значения для функции, возвращающих


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

Без полных прототипов выполняются стандартные преобразования, но не


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

Прототипы используются для инициализации указателей на функции до


определения этих функций.

Список параметров используется для проверки соответствия аргументов в


вызове функции и параметров в ее определении.

Преобразованный тип каждого параметра определяет интерпретацию аргументов,


помещаемых в стек при вызове функции. Несоответствие типов аргументов и
параметров может приводить к неправильной интерпретации аргументов в стеке.
Например, если на 16-разрядном компьютере в качестве аргумента передается 16-
разрядный указатель, объявленный как параметр long , первые 32 бита в стеке
интерпретируются как параметр типа long . Эта ошибка создает проблемы не
только с параметром long , но и со всеми последующими параметрами. Ошибки
такого типа можно обнаруживать, объявляя полные прототипы для всех функций.

Прототип устанавливает атрибуты функции. После этого вызовы функций,


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

Полные объявления параметров ( int a ) могут использоваться совместно с


абстрактными деклараторами ( int ) в одном объявлении. Например, следующее
объявление является допустимым:

int add( int a, int );

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

Теперь область прототипа в компиляторе Microsoft C соответствует стандарту ANSI


при компиляции с параметром /Za . Если вы объявите в прототипе тег struct или
union , этот тег добавляется именно в этой области, а не в глобальной области.
Например, если выполнять компиляцию с параметром /Za в соответствии со
стандартом ANSI, эту функцию невозможно будет вызвать без получения ошибки
несоответствия типов:

void func1( struct S * );

Чтобы исправить код, определите или объявите struct или union в глобальной
области перед прототипом функции:

struct S;

void func1( struct S * );

В /Ze параметре тег по-прежнему вводится в глобальной области.

См. также
Функции
Вызовы функций
Статья • 03.03.2023 • Чтение занимает 2 мин

Вызовом функции называется выражение, которое передает функции управление и


аргументы (если они есть). Такие выражения имеют следующую форму:

expression (expression-listopt)

где expression — это имя функции или выражение, результатом которого является
адрес функции, а expression-list содержит список выражений, разделенных
запятыми. Значения этих выражений являются аргументами, которые передаются
функции. Если функция не возвращает значения, то при ее объявлении
необходимо указать, что она возвращает тип void .

Если объявление указывается до вызова функции, однако информация о ее


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

7 Примечание

Выражения в списке аргументов функции могут оцениваться в любом порядке,


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

Единственное требование к любому вызову функции заключается в том, что


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

Пример
В следующем примере представлен вызов функций, совершаемый из оператора
switch :
int main()

/* Function prototypes */

long lift( int ), step( int ), drop( int );

void work( int number, long (*function)(int i) );

int select, count;

select = 1;

switch( select )

case 1: work( count, lift );

break;

case 2: work( count, step );

break;

case 3: work( count, drop );

/* Fall through to next case */

default:

break;

/* Function definition */

void work( int number, long (*function)(int i) )

int i;

long j;

for ( i = j = 0; i < number; i++ )

j += ( *function )( i );

В этом примере вызов функции располагается в функции main :

work( count, lift );

Он передает функции count целочисленную переменную lift , а также адрес


функции work . Обратите внимание, что адрес функции передается просто в виде
идентификатора функции, поскольку он равен выражению указателя. Чтобы
идентификатор функции можно было использовать таким образом, функцию
необходимо объявить и определить до того, как будет использован идентификатор;
в противном случае идентификатор не будет распознан. В этом случае прототип
функции work задан в начале функции main .

Параметр function в функции work объявлен как указатель на функцию,


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

Функция work вызывает выбранную функцию из цикла for , используя следующий


вызов:

( *function )( i );

Вызываемой функции передается один аргумент, i :

См. также
Функции
Аргументы
Статья • 03.03.2023 • Чтение занимает 2 мин

Аргументы в вызове функции могут иметь следующую форму:

expression(expression-listopt) /* Вызов функции */

При вызове функции параметр expression-list содержит список выражений,


разделенных запятыми. Значения этих выражений являются аргументами, которые
передаются функции. Если функция не принимает аргументы, то список expression-
list должен содержать ключевое слово void .

Аргументом может быть любое значение фундаментального типа или типа


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

Хотя массивы и функции не могут передаваться в качестве аргументов, вы можете


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

Порядок вычисления аргументов может различаться в зависимости от конкретного


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

При вызове функции вычисляются все выражения из списка expression-list, и для


каждого аргумента выполняются обычные арифметические преобразования. Если
доступен прототип, то результат вычисления аргументов сравнивается по типу с
соответствующим параметром прототипа. Если они не совпадают, то либо
выполняется преобразование, либо выводится диагностическое сообщение. Для
параметров также проводятся обычные арифметические преобразования.

Количество выражений в expression-list должно совпадать с количеством


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

Если список параметров прототипа содержит только ключевое слово void , то


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

Пример
В следующем примере в качестве аргументов используются указатели:

int main()

/* Function prototype */

void swap( int *num1, int *num2 );

int x, y;

swap( &x, &y ); /* Function call */

/* Function definition */

void swap( int *num1, int *num2 )

int t;

t = *num1;

*num1 = *num2;

*num2 = t;

В этом примере функция swap объявлена внутри функции main . Она имеет два
аргумента, num1 и num2 , которые являются указателями на значения типа int .
Параметры num1 и num2 в определении прототипа также объявляются как
указатели на значения типа int .

Функция вызывается следующей инструкцией:

swap( &x, &y )

Адрес x , который здесь используется, сохраняется в аргументе num1 , а адрес y  — в


аргументе num2 . Теперь для одного и того же местоположения имеется два имени,
или псевдонима. Ссылки на значения *num1 и *num2 в функции swap одновременно
представляют собой ссылки на значения x и y в функции main . Присваивания
внутри функции swap , по сути, меняют содержимое переменных x и y . Поэтому
оператор return не требуется.

Компилятор выполняет проверку типов для аргументов функции swap , поскольку в


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

См. также
Вызовы функций
Вызовы с переменным количеством
аргументов
Статья • 03.03.2023 • Чтение занимает 2 мин

Частичный список параметров может завершаться многоточием (запятая и три


точки за ней: , ... ). Это означает возможность передать функции еще несколько
аргументов, о которых не предоставляется дополнительной информации.
Проверка типов в таких аргументах не выполняется. Хотя бы один параметр
должен предшествовать блоку многоточия, и этот блок должен являться
последним токеном в списке параметров. Без блока многоточия поведение
функции является неопределенным, если она получает параметры в добавление к
объявленным в списке параметров.

Для вызова функции с переменным количеством аргументов достаточно указать


любое количество аргументов в вызове функции. В качестве примера можно
привести функцию printf из библиотеки времени выполнения C. Вызов функции
должен включать один аргумент для каждого объявленного имени типа в списке
параметров или списке типов аргументов.

Все аргументы, заданные в вызове функции, помещаются в стек. Исключение


составляют случаи, когда задано соглашение о вызовах __fastcall . Число
параметров, объявленных для функции, определяет, сколько аргументов взяты из
стека и присвоены параметрам. Ответственность за извлечение любых
дополнительных аргументов из стека и за определение числа присутствующих
аргументов лежит на пользователе. Файл STDARG.H содержит макросы в стиле
ANSI для доступа к аргументам функций, принимающих переменное число
аргументов. Кроме того, до сих пор поддерживаются макросы в стиле XENIX из
файла VARARGS.H.

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


аргументов.

int average( int first, ...);

См. также
Вызовы функций
Рекурсивные функции
Статья • 03.03.2023 • Чтение занимает 2 мин

Любая функция в программе на языке C может вызываться рекурсивно, т. е. может


вызывать сама себя. Число рекурсивных вызовов ограничено размером стека.
Сведения о параметрах компоновщика, определяющих размер стека, см. в
описании параметра компоновщика /STACK (распределения стека). При каждом
вызове функции для параметров и переменных auto и register выделяется новая
память, чтобы не перезаписывались их значения в предыдущих (незаконченных)
вызовах. Параметры доступны непосредственно только экземпляру функции, в
котором они были созданы. Последующим экземплярам функции предыдущие
параметры непосредственно недоступны.

Обратите внимание, что для переменных, объявленных с описателем static ,


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

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

int factorial( int num ); /* Function prototype */

int main()

int result, number;

result = factorial( number );


}

int factorial( int num ) /* Function definition */

if ( ( num > 0 ) || ( num <= 10 ) )

return( num * factorial( num - 1 ) );

См. также
Вызовы функций
Краткие сведения о синтаксисе языка
C
Статья • 03.03.2023 • Чтение занимает 2 мин

В этом разделе приводится общее описание языка C и его особенностей,


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

7 Примечание

Приведенная здесь краткая информация о синтаксисе не относятся к стандарту


ANSI C; она приводится исключительно в информационных целях. Синтаксис
для систем Microsoft описывается в комментариях, которые приводятся после
синтаксиса.

См. также
Справочник по языку C
Определения и соглашения
Статья • 03.03.2023 • Чтение занимает 2 мин

Терминальные слова — это конечные точки в определении синтаксиса. Никакое


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

Нетерминальные символы представляют собой местозаполнители в синтаксисе и


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

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


Например, примененная к объекту директива

{ expression необ. }

указывает необязательное выражение, заключенное в фигурные скобки.

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


компонентов синтаксиса. Ниже перечислены символы и шрифты.

Атрибут Описание

nonterminal Курсивом выделяются нетерминальные символы.

const Терминалы полужирного типа monospace — это литеральные


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

opt Нетерминальные символы, за которыми следует атрибут opt, всегда являются


необязательными.

Шрифт по Символы в наборе, описанные или перечисленные в этом шрифте, можно


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

Двоеточие ( : ) после нетерминального элемента обозначает начало его


определения. Альтернативные определения приводятся в отдельных строках, если
не указаны слова "один из".

См. также
Краткие сведения о синтаксисе языка C
Лексическая грамматика C
Статья • 03.03.2023 • Чтение занимает 3 мин

Токены
token :

  keyword

  identifier

  constant

  string-literal

  punctuator

preprocessing-token :

  header-name

  identifier

  pp-number

  character-constant

  string-literal

  punctuator

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


перечисленных выше элементов

Ключевые слова
keyword : один из следующих символов:

  auto break case char const continue

  default do double else enum extern

  float for goto if inline int long

  register restrict return short signed

  sizeof static struct switch typedef union

  unsigned void volatile while _Alignas

  _Alignof _Atomic _Bool _Complex _Generic

  _Imaginary _Noreturn _Static_assert

  _Thread_local

Список дополнительных ключевых слов для систем Майкрософт см. в статье


Ключевые слова в C.
Идентификаторы
identifier :

  identifier-nondigit

  identifier identifier-nondigit

  identifier digit

identifier-nondigit :

  nondigit

  universal-character-name

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

nondigit : один из следующих символов:

 _ a b c d e f g h i j k l m

 n o p q r s t u v w x y z

 A B C D E F G H I J K L M

 N O P Q R S T U V W X Y Z

digit : один из следующих символов:

 0 1 2 3 4 5 6 7 8 9

universal-character-name :

  \u hex-quad

  \U hex-quad hex-quad

hex-quad :

  hexadecimal-digit hexadecimal-digit hexadecimal-digit hexadecimal-digit

Константы
constant :

  integer-constant

  floating-constant

  enumeration-constant

  character-constant

integer-constant :

  decimal-constant integer-suffix необ.

  binary-constant 1 integer-suffix opt

  octal-constant integer-suffix opt

  hexadecimal-constant integer-suffix необ.

decimal-constant :

  nonzero-digit

  decimal-constant digit

binary-constant :1

  binary-prefix binary-digit

  binary-constant binary-digit

binary-prefix 1: один из

  0b 0B

binary-digit 1: один из

 0 1

octal-constant :

 0

  octal-constant octal-digit

hexadecimal-constant :

  hexadecimal-prefix hexadecimal-digit

  hexadecimal-constant hexadecimal-digit

hexadecimal-prefix : один из следующих символов:

  0x 0X

nonzero-digit : один из следующих символов:

 1 2 3 4 5 6 7 8 9

octal-digit : один из следующих символов:

 0 1 2 3 4 5 6 7

hexadecimal-digit : один из следующих символов:

 0 1 2 3 4 5 6 7 8 9

 a b c d e f

 A B C D E F

integer-suffix :

  unsigned-suffix long-suffix необ.

  unsigned-suffix long-long-suffix необ.

  long-suffix unsigned-suffix необ.

  long-long-suffix unsigned-suffix необ.

unsigned-suffix : один из следующих символов:

 u U

long-suffix : один из следующих символов:

 l L

long-long-suffix : один из следующих символов:

  ll LL

floating-constant :

  decimal-floating-constant

  hexadecimal-floating-constant

decimal-floating-constant :

  fractional-constant exponent-part opt floating-suffix opt

  digit-sequence exponent-part floating-suffix opt

hexadecimal-floating-constant :

  hexadecimal-prefix hexadecimal-fractional-constant binary-exponent-


part opt floating-suffix opt

  hexadecimal-prefix hexadecimal-digit-sequence binary-exponent-part floating-


suffix opt

fractional-constant :

  digit-sequence opt . digit-sequence

  digit-sequence .

exponent-part :

  e sign необ. digit-sequence

  E sign необ. digit-sequence

sign : один из следующих символов:

 + -

digit-sequence :

  digit

  digit-sequence digit
hexadecimal-fractional-constant :

  hexadecimal-digit-sequence opt . hexadecimal-digit-sequence

  hexadecimal-digit-sequence .

binary-exponent-part :
  p sign необ. digit-sequence

  P sign необ. digit-sequence

hexadecimal-digit-sequence :

  hexadecimal-digit

  hexadecimal-digit-sequence hexadecimal-digit

floating-suffix : один из следующих символов:

 f l F L

enumeration-constant :
  identifier

character-constant :

  ' c-char-sequence '

  L' c-char-sequence '

c-char-sequence :

  c-char

  c-char-sequence c-char

c-char :

 Любой элемент исходной кодировки, кроме одинарной кавычки ( ' ), обратной


косой черты ( \ ) и символа новой строки

  escape-sequence

escape-sequence :

  simple-escape-sequence

  octal-escape-sequence

  hexadecimal-escape-sequence

  universal-character-name

simple-escape-sequence : один из следующих символов:

  \a \b \f \n \r \t \v

  \' \" \\ \?
octal-escape-sequence :

  \ octal-digit

  \ octal-digit octal-digit

  \ octal-digit octal-digit octal-digit

hexadecimal-escape-sequence :

  \x hexadecimal-digit

  hexadecimal-escape-sequence hexadecimal-digit

Строковые литералы
string-literal :

  encoding-prefix " s-char-sequence opt "

encoding-prefix :

  u8

 u

 U

 L

s-char-sequence :

  s-char

  s-char-sequence s-char

s-char :

 Любой элемент исходной кодировки, кроме двойной кавычки ( " ), обратной


косой черты ( \ ) и символа новой строки

  escape-sequence

Символы пунктуации
punctuator : один из следующих символов:

  [ ] ( ) { } . ->

  ++ -- & * + - ~ !

  / % << >> < > <= >= ==

  != ^ | && || ? : ; ...

  = *= /= %= += -= <<= >>=

  &= ^= |= , # ##

  <: :> <% %> %: %:%:


Имена заголовков
header-name :

  < h-char-sequence >

  " q-char-sequence "

h-char-sequence :

  h-char

  h-char-sequence h-char

h-char :

 любой член исходной кодировки, кроме символа новой строки и >

q-char-sequence :

  q-char

  q-char-sequence q-char

q-char :

 любой член исходной кодировки, кроме символа новой строки и "

Предварительная обработка чисел


pp-number :

  digit

  . digit

  pp-number digit

  pp-number identifier-nondigit

  pp-number e sign

  pp-number E sign

  pp-number p sign

  pp-number P sign

  pp-number .

1
binary-constant , binary-prefix и binary-digit  — это расширения для систем
Майкрософт.

См. также
Краткие сведения о синтаксисе языка C
Грамматика структуры фразы
Статья • 03.03.2023 • Чтение занимает 2 мин

Выражения

Объявления

Операторы

Внешние определения

См. также
Краткие сведения о синтаксисе языка C
Общие сведения о выражениях
Статья • 03.03.2023 • Чтение занимает 2 мин

primary-expression :

  identifier

  constant

  string-literal

  ( expression )

  generic-selection

generic-selection :

  _Generic ( assignment-expression , generic-assoc-list )

generic-assoc-list :

  generic-association

  generic-assoc-list , generic-association

generic-association :

  type-name : assignment-expression

  default : assignment-expression

postfix-expression :

  primary-expression

  postfix-expression [ expression ]

  postfix-expression ( argument-expression-list opt )

  postfix-expression . identifier

  postfix-expression -> identifier

  postfix-expression ++

  postfix-expression --

  ( type-name ) { initializer-list }

  ( type-name ) { initializer-list , }

argument-expression-list :

  assignment-expression

  argument-expression-list , assignment-expression

unary-expression :

  postfix-expression

  ++ unary-expression

  -- unary-expression

  unary-operator cast-expression

  sizeof unary-expression

  sizeof ( type-name )
  _Alignof ( type-name )

unary-operator : один из следующих символов:

 & * + - ~ !

cast-expression :

  unary-expression

  ( type-name ) cast-expression

multiplicative-expression :

  cast-expression

  multiplicative-expression * cast-expression

  multiplicative-expression / cast-expression

  multiplicative-expression % cast-expression

additive-expression :

  multiplicative-expression

  additive-expression + multiplicative-expression

  additive-expression - multiplicative-expression

shift-expression :

  additive-expression

  shift-expression << additive-expression

  shift-expression >> additive-expression

relational-expression :

  shift-expression

  relational-expression < shift-expression

  relational-expression > shift-expression

  relational-expression <= shift-expression

  relational-expression >= shift-expression

equality-expression :

  relational-expression

  equality-expression == relational-expression

  equality-expression != relational-expression
AND-expression :

  equality-expression

  AND-expression & equality-expression

exclusive-OR-expression :

  AND-expression

  exclusive-OR-expression ^ AND-expression

inclusive-OR-expression :

  exclusive-OR-expression

  inclusive-OR-expression | exclusive-OR-expression

logical-AND-expression :

  inclusive-OR-expression

  logical-AND-expression && inclusive-OR-expression

logical-OR-expression :

  logical-AND-expression

  logical-OR-expression || logical-AND-expression

conditional-expression :

  logical-OR-expression

  logical-OR-expression ? expression : conditional-expression

assignment-expression :

  conditional-expression

  unary-expression assignment-operator assignment-expression

assignment-operator : один из следующих символов:

  = *= /= %= += -= <<= >>= &= ^= |=

expression :

  assignment-expression

  expression , assignment-expression

constant-expression :

  conditional-expression

См. также
Грамматика структуры фразы
Общие сведения об объявлениях
Статья • 03.03.2023 • Чтение занимает 2 мин

declaration :

  declaration-specifiers attribute-seq opt1 init-declarator-list opt ;

  static_assert-declaration

declaration-specifiers :

  storage-class-specifier declaration-specifiers необ.

  type-specifier declaration-specifiers необ.

  type-qualifier declaration-specifiers необ.

  function-specifier declaration-specifiers необ.

  alignment-specifier declaration-specifiers opt

attribute-seq 1:

  attribute 1 attribute-seq opt1

attribute 1, 2: один из

  __asm __based __cdecl __clrcall __fastcall __inline __stdcall __thiscall


__vectorcall

init-declarator-list :

  init-declarator

  init-declarator-list , init-declarator

init-declarator :

  declarator

  declarator = initializer

storage-class-specifier :

  auto

  extern

  register

  static

  _Thread_local

  typedef

  __declspec ( extended-decl-modifier-seq ) 1

extended-decl-modifier-seq 1:

  extended-decl-modifier opt

  extended-decl-modifier-seq extended-decl-modifier

extended-decl-modifier 1:

  thread

  naked

  dllimport

  dllexport

type-specifier :

  void

  char

  short

  int

  __int8 1

  __int16 1

  __int32 1

  __int64 1

  long

  float

  double

  signed

  unsigned

  _Bool

  _Complex

  atomic-type-specifier

  struct-or-union-specifier

  enum-specifier

  typedef-name

struct-or-union-specifier :

  struct-or-union identifier opt { struct-declaration-list }

  struct-or-union identifier

struct-or-union :

  struct

  union

struct-declaration-list :

  struct-declaration

  struct-declaration-list struct-declaration
struct-declaration :

  specifier-qualifier-list struct-declarator-list необ. ;

  static_assert-declaration

specifier-qualifier-list :

  type-specifier specifier-qualifier-list необ.

  type-qualifier specifier-qualifier-list необ.

  alignment-specifier specifier-qualifier-list opt

struct-declarator-list :

  struct-declarator

  struct-declarator-list , struct-declarator

struct-declarator :

  declarator

  declarator opt : constant-expression

enum-specifier :

  enum identifier opt { enumerator-list }

  enum identifier opt { enumerator-list , }

  enum identifier

enumerator-list :

  enumerator

  enumerator-list , enumerator

enumerator :

  enumeration-constant

  enumeration-constant = constant-expression

atomic-type-specifier :

  _Atomic ( type-name )

type-qualifier :

  const

  restrict

  volatile

  _Atomic

function-specifier :

  inline

  _Noreturn

alignment-specifier :

  _Alignas ( type-name )

  _Alignas ( constant-expression )

declarator :

  pointer необ. direct-declarator

direct-declarator :

  identifier

  ( declarator )

  direct-declarator [ type-qualifier-list opt assignment-expression opt ]

  direct-declarator [ static type-qualifier-list opt assignment-expression ]

  direct-declarator [ type-qualifier-list static assignment-expression ]

  direct-declarator [ type-qualifier-list opt * ]

  direct-declarator ( parameter-type-list )

  direct-declarator ( identifier-list opt ) 3

pointer :

  * type-qualifier-list необ.

  * type-qualifier-list необ. pointer

type-qualifier-list :

  type-qualifier

  type-qualifier-list type-qualifier

parameter-type-list :

  parameter-list

  parameter-list , ...

parameter-list :

  parameter-declaration

  parameter-list , parameter-declaration

parameter-declaration :

  declaration-specifiers declarator

  declaration-specifiers abstract-declarator необ.

identifier-list : /* Для оператора объявления старого стиля */

  identifier

  identifier-list , identifier

type-name :

  specifier-qualifier-list abstract-declarator необ.

abstract-declarator :

  pointer

  pointer необ. direct-abstract-declarator

direct-abstract-declarator :

  ( abstract-declarator )

  direct-abstract-declarator [ type-qualifier-list opt assignment-expression opt ]

  direct-abstract-declarator [ static type-qualifier-list opt assignment-expression ]

  direct-abstract-declarator [ type-qualifier-list static assignment-expression ]

  direct-abstract-declarator [ type-qualifier-list opt * ]

  direct-abstract-declarator opt ( parameter-type-list opt )

typedef-name :

  identifier

initializer :

  assignment-expression

  { initializer-list }

  { initializer-list , }

initializer-list :

  designation необ. initializer

  initializer-list , designation opt initializer

designation :

  designator-list =

designator-list :

  designator

  designator-list designator

designator :

  [ constant-expression ]

  . identifier

static_assert-declaration :

  _Static_assert ( constant-expression , string-literal ) ;


1
Этот элемент грамматики используется только в системах Майкрософт.

2
Дополнительные сведения об этих элементах см. в статьях о __asm, __clrcall,
__stdcall, __based, __fastcall, __thiscall, __cdecl, __inline и __vectorcall.

3
Этот стиль устарел.

См. также раздел


Соглашения о вызовах

Грамматика структуры фразы

Устаревшие соглашения о вызовах


Сводка по операторам C
Статья • 03.03.2023 • Чтение занимает 2 мин

statement :

  labeled-statement

  compound-statement

  expression-statement

  selection-statement

  iteration-statement

  jump-statement

  try-except-statement /* Только для систем Майкрософт */

  try-finally-statement /* Только для систем Майкрософт */

jump-statement :

  goto identifier ;

  continue ;

  break ;

  return expression необ. ;

  __leave ; /* Только для систем Майкрософт1 */

compound-statement :

  { declaration-list opt statement-list opt }

declaration-list :

  declaration

  declaration-list declaration

statement-list :

  statement

  statement-list statement

expression-statement :

  expression необ. ;

iteration-statement :

  while ( expression ) statement

  do statement while ( expression ) ;

  for ( expression opt ; expression opt ; expression opt ) statement


selection-statement :

  if ( expression ) statement

  if ( expression ) statement else statement

  switch ( expression ) statement

labeled-statement :

  identifier : statement

  case constant-expression : statement

  default : statement

try-except-statement : /* Только для систем Майкрософт */


  __try compound-statement __except ( expression ) compound-statement

try-finally-statement : /* Только для систем Майкрософт */

  __try compound-statement __finally compound-statement

1 Ключевое слово __leave допустимо только в блоке __try try-except-statement


или try-finally-statement .

См. также раздел


Грамматика структуры фразы
Внешние определения
Статья • 03.03.2023 • Чтение занимает 2 мин

translation-unit :

  external-declaration

  translation-unit external-declaration

external-declaration : /* Разрешено только во внешней (файловой) области */

  function-definition

  declaration

function-definition : /* Этот декларатор является декларатором функции */

  declaration-specifiers opt declarator declaration-list opt compound-statement

См. также
Грамматика структуры фразы
Поведение, определяемое
реализацией
Статья • 03.03.2023 • Чтение занимает 2 мин

Стандарт ANSI X3.159-1989, American National Standard for Information Systems -


Programming Language - C содержит раздел "Проблемы переносимости". В этом
разделе ANSI описываются области языка C, которые ANSI оставляет открытыми
для каждой конкретной реализации. В этом разделе описывается, как в Microsoft C
обрабатываются эти области языка C, определяемые его реализацией.

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


соответствующем разделе стандарта ANSI. Каждый описываемый элемент
содержит ссылки на стандарт ANSI, в котором поясняется поведение,
определяемое реализацией.

7 Примечание

В этом разделе описывается только версия компилятора C для американского


варианта английского языка. Реализации Microsoft C для других языков могут
немного отличаться.

См. также
Справочник по языку C
Перевод: Диагностика
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 2.1.1.3 Определение диагностики

Microsoft C создает сообщения об ошибках в следующем виде:

filename(line-number) :diagnosticCnumbermessage

Где filename обозначает имя исходного файла, в котором была обнаружена


ошибка; line-number — номер строки, в которой компилятор обнаружил ошибку;
diagnostic имеет значение "error" (ошибка) или "warning" (предупреждение); number
— уникальное четырехзначное число, определяющее ошибку или предупреждение
(с предшествующим символом C, как указано в синтаксисе); а message —
поясняющее сообщение.

См. также
Поведение, определяемое реализацией
Среда
Статья • 03.03.2023 • Чтение занимает 2 мин

Аргументы функции main

Интерактивные устройства

См. также
Поведение, определяемое реализацией
Аргументы функции main
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 2.1.2.2.1Семантика аргументов функции main

В Microsoft C вызывается функция, вызываемая main при запуске программы.


Прототип не объявлен для main , и его можно определить с нулевым, двумя или
тремя параметрами:

int main( void )

int main( int argc, char *argv[] )

int main( int argc, char *argv[], char *envp[] )

Третья строка выше, где main принимает три параметра, является расширением
Майкрософт для стандарта ANSI C. Третий параметр envp — это массив указателей
на переменные среды. Массив envp завершается указателем NULL.
Дополнительные сведения и main envp сведения см. в разделе " main Выполнение
функции и программы".

Переменная argc никогда не содержит отрицательное значение.

Массив строк заканчивается argv[argc] на , который содержит пустой указатель.

Все элементы массива argv являются указателями на строки.

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


для argc , так как имя исполняемого файла помещается в argv[0] . (В версиях MS-
DOS до 3.0 имя исполняемого файла недоступно. Буква "C" помещается в argv[0] .)
Строки, на которые указывает argv[1] argv[argc - 1] представление параметров
программы.

Параметры argc и argv являются изменяемыми и сохраняют свои последние


сохраненные значения между запуском программы и завершением программы.

См. также
Среда
Интерактивные устройства
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 2.1.2.3 Описание интерактивного устройства

Microsoft C определяет клавиатуру и дисплей как интерактивные устройства.

См. также
Среда
Поведение идентификаторов
Статья • 03.03.2023 • Чтение занимает 2 мин

Значимые символы без внешней компоновки

Значимые символы с внешней компоновкой

Верхний и нижний регистр

См. также
Использование ключевого слова extern для задания компоновки
Значимые символы без внешней
компоновки
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 3.1.2 Число значимых символов без внешней компоновки

Идентификаторы содержат до 247 значимых символов. Компилятор не


ограничивает количество символов, которые можно использовать в
идентификаторе, а просто игнорирует все символы, превышающие предел.

См. также
Использование ключевого слова extern для задания компоновки
Значимые символы с внешней
компоновкой
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 3.1.2 Число значимых символов при использовании внешней компоновки

Идентификаторы, объявленные extern в программах, которые скомпилированы с


использованием языка Microsoft C, являются значимыми до 247 символов. Это
значение по умолчанию можно изменить на меньшее число, воспользовавшись
параметром /H (ограничивает длину внешних имен).

См. также
Использование ключевого слова extern для задания компоновки
Верхний и нижний регистр
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 3.1.2 Учитываются ли различия в регистре символов

Microsoft C обрабатывает идентификаторы в единице компиляции с учетом


регистра.

Компоновщик Microsoft учитывает регистр. Необходимо указывать все


идентификаторы единообразно с учетом регистра.

См. также
Поведение идентификаторов
Знаки
Статья • 03.03.2023 • Чтение занимает 2 мин

Набор символов ASCII

Многобайтовая кодировка

Число битов на символ

Кодировки

Непредставимые символьные константы

Расширенные символы

Преобразование многобайтовых символов

Диапазон значений char

См. также
Поведение, определяемое реализацией
Набор символов ASCII
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 2.2.1 Члены исходной кодировки и кодировки выполнения

Исходная кодировка — это набор допустимых символов, которые могут


использоваться в исходных файлах. Для Microsoft C исходной кодировкой является
стандартный набор символов ASCII.

7 Примечание

Внимание! Поскольку драйверы клавиатуры и консоли могут менять


сопоставление кодировки, в программах для международного
распространения необходимо проверять код страны/региона.

См. также
Символы
Многобайтовые символы
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 2.2.1.2 Состояния сдвига для многобайтовых символов

В некоторых реализациях, включая Microsoft C, многобайтовые символы


используются для отображения письменности языков, не включенных в базовый
набор символов. Однако кодировки, зависящие от состояния, в Microsoft C не
поддерживаются. Поэтому состояния сдвига отсутствуют. Дополнительные
сведения см. в разделе Многобайтовая кодировка и расширенные символы.

См. также
Символы
Число битов на символ
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 2.2.4.2.1 Число битов в символе

Количество битов в символе определяется константой манифеста CHAR_BIT. В


файле LIMITS.H для константы CHAR_BIT задано значение 8.

См. также
Символы
Кодировки
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 3.1.3.4 Сопоставление членов кодировки исходного кода

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


перечисленные в следующей таблице. В таблице также приведены escape-
последовательности.

Escape-последовательность
Escape-последовательность Знак Значение ASCII

\a Предупреждение/звонок 7

\b Backspace 8

\f Перевод страницы 12

\n Новая строка 10

\r Возврат каретки 13

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

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

\" Двойная кавычка 34

\' Одиночная кавычка 39

\\ Обратная косая черта 92

См. также
Символы

Непредставимые символьные
константы
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 3.1.3.4 Значение целочисленной символьной константы, которая содержит


символ или escape-последовательность, не представленную в базовой кодировке
выполнения или в расширенной кодировке для расширенной символьной
константы

Все символьные константы или escape-последовательности можно представить в


виде расширенной кодировки.

См. также
Символы
Расширенные символы
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 3.1.3.4 Значение целочисленной символьной константы, которая содержит


более одного символа, или расширенной символьной константы, которая
содержит более одного многобайтового символа

Обычная символьная константа, ab, имеет целочисленное значение (int)0x6162.


Если размер составляет более одного байта, то ранее считанные байты сдвигаются
влево на значение CHAR_BIT, а следующий байт сравнивается (при помощи
оператора побитового ИЛИ) с младшими битами значения CHAR_BIT. Число
байтов в многобайтовой символьной константе не может превышать (int) —
4 байта в коде для 32-разрядной системы.

Многобайтовая символьная константа считывается так же и преобразуется в


расширенную символьную константу с помощью функции времени выполнения
mbtowc . Если результат не является допустимой многобайтовой символьной

константой, выводится ошибка. В любом случае число байтов, проверяемое


функцией mbtowc , ограничено значением MB_CUR_MAX .

См. также
Символы
Преобразование многобайтовых
символов
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 3.1.3.4 Текущий языковой стандарт, используемый для преобразования


многобайтовых символов в соответствующие расширенные символы (коды) для
расширенной символьной константы

Текущий языковой стандарт — это языковой стандарт C по умолчанию. Его можно


изменить с помощью директивы #pragma setlocale.

См. также
Символы
Диапазон значений типа char
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 3.2.1.1 Имеет ли "простой" тип char тот же диапазон значений, что и signed
char или unsigned char

Все знаковые значения char находятся в диапазоне от –128 до 127. Все беззнаковые
значения char находятся в диапазоне от 0 до 255.

Параметр компилятора /J указывает, что для char по умолчанию вместо signed


char будет использоваться unsigned char .

См. также
Символы
Целые числа
Статья • 03.03.2023 • Чтение занимает 2 мин

Диапазон целочисленных значений

Понижение уровня целых чисел

Поразрядные операции со знаком

Остатки от деления

Сдвиги вправо

См. также
Поведение, определяемое реализацией
Диапазон целочисленных значений
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 3.1.2.5 Представления и наборы значений различных целочисленных типов


данных

Целочисленные типы данных содержат 32 бита (4 байта). Знаковые целочисленные


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

Type Минимальное и максимальное значение

unsigned short От 0 до 65535

signed short От -32768 до 32767

unsigned long От 0 до 4294967295

signed long От -2147483648 до 2147483647

См. также
Целые числа
Понижение уровня целых чисел
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 3.2.1.2 Результат преобразования целого числа в более короткое целое число


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

Когда целое число типа long приводится к типу short или тип short приводится к
типу char , младшие байты сохраняются.

Например, строкой

short x = (short)0x12345678L;

переменной x присваивается значение 0x5678, а строкой

char y = (char)0x1234;

переменной y присваивается значение 0x34.

При преобразовании переменных signed в переменные unsigned и наоборот


битовые шаблоны не изменяются. Например, при приведении значения -2 (0xFE) к
значению unsigned получается значение 254 (также 0xFE).

См. также
Целые числа
Битовые операции со знаком
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 3.3 Результаты выполнения побитовых операций над знаковыми


целочисленными значениями

Побитовые операции над знаковыми целочисленными значениями работают так


же, как и над беззнаковыми. Например, значение -16 & 99 можно представить в
двоичном виде следующим образом:

11111111 11110000

& 00000000 01100011

_________________

00000000 01100000

Результат выполнения побитовой операции И будет равен 96.

См. также
Целые числа
Остатки от деления
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 3.3.5 Знак остатка при целочисленном делении

Знак остатка совпадает со знаком делимого. Например, примененная к объекту


директива

50 / -6 == -8

50 % -6 == 2

-50 / 6 == -8

-50 % 6 == -2

См. также
Целые числа
Сдвиги вправо
Статья • 03.03.2023 • Чтение занимает 2 мин

Результат сдвига вправо отрицательного целого типа со знаком

Сдвиг вправо отрицательного значения соответствует делению абсолютного


значения на два с округлением вниз. Например, значение signed short , равное –
253 (шестнадцатеричное 0xFF03, двоичное 11111111 00000011), сдвинутое вправо
на один бит, дает значение –127 (шестнадцатеричное 0xFF81, двоичное 11111111
10000001). Положительное значение 253, сдвинутое вправо, дает значение +126.

При сдвиге вправо бит знака целочисленных типов со знаком сохраняется. При
сдвиге вправо целого числа со знаком старший значащий бит остается
установленным. Например, если число 0xF0000000 имеет int со знаком, то при
сдвиге вправо получается число 0xF8000000. Если 32 раза выполнить сдвиг вправо
отрицательного int , в результате получается число 0xFFFFFFFF.

При сдвиге вправо целого числа без знака старший значащий бит очищается.
Например, если число 0xF000 не имеет знака, в результате получается 0x7800. При
сдвиге 32 раза unsigned или положительного числа int вправо получается число
0x00000000.

См. также
Целые числа
Вычисления с плавающей запятой
Статья • 03.03.2023 • Чтение занимает 2 мин

Значения

Приведение целочисленных значений к значениям с плавающей запятой

Усечение значений с плавающей запятой

См. также
Поведение, определяемое реализацией
Значения
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 3.1.2.5 Представления и наборы значений различных типов чисел с


плавающей запятой

Тип float представлен 32 битами: 1 для знака, 8 для экспоненты и 23 для мантиссы.
Он имеет диапазон +/–3,4E38 с точностью не менее 7 знаков.

Тип double представлен 64 битами: 1 для знака, 11 для экспоненты и 52 для


мантиссы. Он имеет диапазон +/–1,7E308 с точностью не менее 15 знаков.

Тип long double отличается, но имеет такое же представление, как и тип double в
компиляторе C Майкрософт.

См. также
Вычисления с плавающей запятой
Приведение целочисленных
значений к значениям с плавающей
запятой
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 3.2.1.3 Направление усечения при преобразовании целого числа в число с


плавающей запятой, которое не может точно представить исходное значение

Если целое число приводится к значению с плавающей запятой, которое точно не


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

Например, приведение числа типа unsigned long (точность — 32 бита) к типу float
(мантисса которого имеет точность 23 бита) приводит к округлению числа до
ближайшего кратного 256. Значения long в диапазоне от 4 294 966 913 до
4 294 967 167 округляются до значения 4 294 967 040 с типом float .

См. также
Вычисления с плавающей запятой
Усечение значений с плавающей
запятой
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 3.2.1.4 Направление усечения или округления при преобразовании числа с


плавающей запятой в формат с плавающей запятой меньшего размера

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


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

См. также
Вычисления с плавающей запятой
Массивы и указатели
Статья • 03.03.2023 • Чтение занимает 2 мин

Максимальный размер массива

Вычитание указателей

См. также
Поведение, определяемое реализацией
Максимальный размер массива
Статья • 26.09.2022 • Чтение занимает 2 мин

ANSI 3.3.3.4, 4.1.1 Тип целочисленного значения, необходимый для хранения


массива максимального размера, — т. е. размер значения size_t

Определение типа (typedef) для типа size_t на 32-разрядных платформах (x86):


unsigned int . Определение типа (typedef) для size_t на 64-разрядных платформах:

unsigned __int64 .

См. также
Массивы и указатели
Вычитание указателей
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 3.3.6, 4.1.1 Тип целого числа, необходимый для сохранения различия между
двумя указателями на элементы одного массива, ptrdiff_t

Определение типа (typedef) для типа ptrdiff_t на 32-разрядных платформах (x86):


int . Определение типа (typedef) для ptrdiff_t на 64-разрядных платформах:

__int64 .

См. также
Массивы и указатели
Регистры: доступность регистров
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 3.5.1. В какой степени можно управлять фактическим размещением объектов


в регистрах при помощи описателя класса регистрового хранения

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


При оптимизации компилятор принимает решения самостоятельно.

См. также
Поведение, определяемое реализацией
Структуры, объединения,
перечисления и битовые поля
Статья • 03.03.2023 • Чтение занимает 2 мин

Неправильный доступ к объединению

Заполнение и выравнивание членов структуры

Знак битовых полей

Хранение битовых полей

Тип перечисления

См. также
Поведение, определяемое реализацией
Неправильный доступ к объединению
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 3.3.2.3 Доступ к члену объекта объединения через члена другого типа

Если объявляется объединение двух типов и сохраняется одно значение, но для


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

Например, объявлено объединение float и int . Сохраняется значение float , но


программа позднее использует это значение как int . В таком случае полученное
значение будет зависеть от внутренних хранимых данных для значений float .
Целочисленное значение будет ненадежным.

См. также
Структуры, объединения, перечисления и битовые поля
Заполнение и выравнивание членов
структуры
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 3.5.2.1 Заполнение и выравнивание членов структур, а также выяснение,


может ли битовое поле пересекать границу блоков хранения в памяти

Структурные элементы сохраняются последовательно, в порядке объявления:


первый элемент имеет самый низкий адрес памяти, а последний — наивысший.

Каждый объект данных имеет требования-к-выравниванию. Требование


выравнивания для всех данных, кроме структур, объединений и массивов, равно
размеру объекта или текущему упаковочному размеру (задается меньшим из двух
значений: параметр /Zp или директива #pragma pack ). Для структур, объединений
и массивов требование к выравниванию равно наибольшему требованию к
выравниванию для их членов. Каждому объекту задается такое смещение, чтобы

offset%alignment-requirement== 0

Смежные битовые поля упакованы в тот же одно-, двух- или четырехбайтовый


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

См. также
Структуры, объединения, перечисления и битовые поля
Знак битовых полей
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 3.5.2.1 Два типа обработки обычного поля int : как битовое поле signed int
или как битовое поле unsigned int

Битовые поля могут быть со знаком или без знака. Обычные битовые поля
обрабатываются как поля со знаком.

См. также
Структуры, объединения, перечисления и битовые поля
Хранение битовых полей
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 3.5.2.1 Порядок назначения битовых полей в целом числе

Битовые поля в целом числе назначаются в направлении от младшего разряда к


старшему. В приведенном ниже коде

struct mybitfields

unsigned a : 4;

unsigned b : 5;

unsigned c : 7;

} test;

int main( void )

test.a = 2;

test.b = 31;

test.c = 0;

биты размещаются следующим образом:

00000001 11110010

cccccccb bbbbaaaa

Поскольку в процессорах 80x86 младший байт целочисленных значений


размещается перед старшим байтом, указанное выше целое число 0x01F2 будет
храниться в физической памяти в следующем виде: 0xF2 0x01.

См. также
Структуры, объединения, перечисления и битовые поля
Тип enum
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 3.5.2.2 Целочисленный тип, выбранный для представлений значений типа


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

Переменная, объявленная как enum , является int .

См. также
Структуры, объединения, перечисления и битовые поля
Квалификаторы: доступ к временным
объектам
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 3.5.5.3 Описание доступа к объекту, тип которого определен с


квалификатором volatile

Доступом является любая ссылка на тип с квалификатором volatile.

См. также
Поведение, определяемое реализацией
Деклараторы: максимальное число
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 3.5.4 Максимальное число деклараторов, которые могут изменять


арифметический тип, тип структуры или тип объединения

Microsoft C не ограничивает количество деклараторов. Это число ограничивается


только объемом доступной памяти.

См. также
Поведение, определяемое реализацией
Операторы: ограничения операторов
switch
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 3.6.4.2 Максимальное число значений case в операторе switch

Microsoft C не ограничивает количество значений case в операторе switch . Это


число ограничивается только объемом доступной памяти.

См. также
Поведение, определяемое реализацией
Директивы предварительной
обработки
Статья • 03.03.2023 • Чтение занимает 2 мин

Символьные константы и условное включение

Включение имен файлов в скобках

Включение имен файлов в кавычках

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

Директивы pragma

Дата и время по умолчанию

См. также
Поведение, определяемое реализацией
Символьные константы и условное
включение
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 3.8.1 Соответствует ли значение односимвольной константы в константном


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

Набор символов в операторах препроцессора совпадает с кодировкой


выполнения. Препроцессор распознает отрицательные значения символов.

См. также
Директивы предварительной обработки
Включение имен файлов в скобках
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 3.8.2 Метод поиска включаемых файлов исходного кода

Для спецификаций файлов, заключенных в угловые скобки, препроцессор не ищет


каталоги родительских файлов. "Родительский" файл — это файл, содержащий
директиву #include. Вместо этого он начинает поиск файла в каталогах, указанных в
командной строке компилятора после /I. Если параметр /I не указан или вызывает
ошибку, то препроцессор ищет все включаемые файлы, заданные в угловых
скобках, в каталогах, которые определены в переменной среды INCLUDE.
Переменная среды INCLUDE может содержать несколько путей, разделенных
точкой с запятой ( ; ). Если параметром /I или переменной среды INCLUDE задано
несколько каталогов, то препроцессор просматривает их в том порядке, в котором
они указаны.

См. также
Директивы предварительной обработки
Включение имен файлов в кавычках
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 3.8.2 Поддержка заключенных в кавычки имен для включаемых файлов


исходного кода

Если указана полная и неоднозначная спецификация пути для включаемого файла


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

Для включаемых файлов, заданных в виде #include "path-spec", поиск по каталогам


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

См. также
Директивы предварительной обработки
Последовательности символов
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 3.8.2 Сопоставление последовательностей символов в исходных файлах

В операторах препроцессора используется тот же набор символов, что и в


операторах исходного файла, однако escape-последовательности не
поддерживаются.

Таким образом, при определении пути к включаемому файлу необходимо


указывать только одну обратную косую черту.

#include "path1\path2\myfile"

Однако в самом исходном коде необходимо указывать две обратные косые черты
подряд:

fil = fopen( "path1\\path2\\myfile", "rt" );

См. также
Директивы предварительной обработки
Директивы pragma
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 3.8.6 Поведение для каждой распознаваемой директивы #pragma.

Для компилятора Microsoft C определены следующие директивы pragma C.

alloc_text

auto_inline

check_stack

code_seg

comment

data_seg

function

hdrstop

include_alias

inline_depth

inline_recursion

intrinsic

message

optimize

pack

setlocale

warning

См. также
Директивы предварительной обработки
Дата и время по умолчанию
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 3.8.8. Определения для __DATE__ и __TIME__ , когда недоступно


преобразование даты и времени соответственно.

Если операционная система не предоставляет преобразование даты и времени,


_DATE __DATE__ и _TIME __TIME__ по умолчанию получают значения May 03 1957 и
17:00:00 .

См. также
Директивы предварительной обработки
Функции библиотеки
Статья • 03.03.2023 • Чтение занимает 2 мин

Макрос NULL

Диагностика, напечатанная функцией assert

Тестирование символов

Ошибки домена

Потеря точности значений с плавающей запятой

Функция fmod

Функция signal

Сигналы по умолчанию

Завершающие символы новой строки

Пустые строки

Символы NULL

Позиция файла в режиме добавления

Усечение текстовых файлов

Буферизация файла

Файлы нулевой длины

Имена файлов

Ограничения доступа к файлам

Удаление открытых файлов

Переименование с использованием существующего имени

Чтение значений указателя

Диапазоны чтения

Ошибки положения файла

Сообщения, созданные функцией perror


Выделение обнуленной памяти

Функция abort

Функция atexit

Имена сред

Функция system

Функция strerror

Часовой пояс

Функция clock

См. также
Поведение, определяемое реализацией
Макрос NULL
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 4.1.5 Константа пустых указателей, на которую расширяется макрос NULL

Несколько включаемых файлов определяют макрос NULL как ((void *)0) .

См. также
Функции библиотеки
Диагностика, напечатанная функцией
assert
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 4.2 Диагностика, выведенная функцией assert, и поведение завершения


функции assert

Функция assert выводит диагностическое сообщение и вызывает подпрограмму


abort, если значение выражения — false (0). Диагностическое сообщение имеет
форму

Assertion failed: expression, filefilename, linelinenumber

где filename — это имя исходного файла, а linenumber — номер строки


утверждения, завершившегося сбоем в файле исходного кода. Если expression
равно true (ненулевое), никакие действия не предпринимаются.

См. также
Функции библиотеки
Тестирование символов
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 4.3.1 Наборы символов, тестируемые функциями isalnum , isalpha , iscntrl ,


islower , isprint и isupper

В следующей таблице приведено описание этих функций в соответствии с


реализацией компилятором Microsoft C.

Функция Тестируемые наборы символов

isalnum Символы 0–9, A–Z, a–z (коды ASCII 48–57, 65–90, 97–122)

isalpha Символы A–Z, a–z (коды ASCII 65–90, 97–122)

iscntrl Коды ASCII 0–31, 127

islower Символы a–z (коды ASCII 97–122)

isprint Символы A–Z, a–z, 0–9, пунктуация, пробел (коды ASCII 32–126)

isupper Символы A–Z (коды ASCII 65–90)

См. также
Функции библиотеки
Ошибки домена
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 4.5.1 Значения, возвращаемые математическими функциями в случае ошибок


домена

Файл ERRNO.H определяет для константы ошибки домена EDOM значение 33. См.
сведения о возвращаемом значении в разделе справки, посвященном конкретной
функции, вызвавшей ошибку.

См. также
Функции библиотеки
Потеря точности значений с
плавающей запятой
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 4.5.1 Задают ли математические функции значение макроса ERANGE для


целочисленного выражения errno при ошибках потери значимости

При потере точности числа с плавающей запятой выражение errno не получает


значения ERANGE . Если значение стремится к нулю и, наконец, теряет значимость,
значение устанавливается равным 0.

См. также
Функции библиотеки
Функция fmod
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 4.5.6.4 Действие в случае, если второй аргумент функции fmod равен нулю:
возникает ошибка домена или возвращается нулевое значение

Если второй аргумент функции fmod равен нулю, функция возвращает ноль.

См. также
Функции библиотеки
Функция signal (C)
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 4.7.1.1 Набор сигналов для функции signal

Первый аргумент, переданный в функцию signal, должен быть одной из


символьных констант, описанных в справочнике библиотеки времени выполнения
для функции signal. В справочнике библиотеки времени выполнения также
перечислены режимы работы, поддерживаемые для каждого сигнала. Эти
константы также определены в файле SIGNAL.H.

См. также
Функции библиотеки
Сигналы по умолчанию
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 4.7.1.1 Если до вызова обработчика сигнала не выполняется эквивалент


signal(sig, SIG_DFL) , сигнал блокируется.

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

См. также
Функции библиотеки
Завершающие символы новой строки
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 4.9.2 Необходимость в наличии конечного символа новой строки в


последней строке текстового потока

Функции потока распознают новую строку или конец файла как конечный символ
строки.

См. также
Функции библиотеки
Пустые строки
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 4.9.2 Отображаются ли при считывании пробелы, которые записаны в


текстовый поток непосредственно перед символом новой строки

Пробелы сохраняются.

См. также
Функции библиотеки
Символы NULL
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 4.9.2 Количество нуль-символов, которые можно добавить к данным,


записываемым в двоичный поток

В двоичный поток можно добавить любое количество нуль-символов.

См. также
Функции библиотеки
Позиция файла в режиме добавления
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 4.9.3 Индикатор позиции в файле потока в режиме добавления изначально


указывает на начале или конец файла

Когда файл открывается в режиме добавления, исходное положение индикатора


позиции файла указывает на конец файла.

См. также
Функции библиотеки
Усечение текстовых файлов
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 4.9.3 Усекается ли соответствующий файл после точки записи в текстовый


поток

Запись в текстовый поток не приводит к усечению файла после этой точки.

См. также
Функции библиотеки
Буферизации файла
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 4.9.3 Характеристики буферизации файлов

Дисковые файлы, полученные через стандартные функции ввода-вывода,


буферизуются полностью. По умолчанию буфер удерживает 512 байтов.

См. также
Функции библиотеки
Файлы нулевой длины
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 4.9.3 Существует ли фактически файл нулевой длины

Файлы нулевой длины разрешены.

См. также
Функции библиотеки
Имена файлов
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 4.9.3 Правила составления допустимых имен файлов

Спецификация файла может содержать необязательную букву диска (с


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

Дополнительные сведения см. в статье Именование файлов.

См. также
Функции библиотеки
Ограничения доступа к файлам
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 4.9.3 Можно ли несколько раз открыть один и тот же файл

Открытие уже открытого файла не допускается.

См. также
Функции библиотеки
Удаление открытых файлов
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 4.9.4.1 Результат функции удаления для открытого файла

Функция удаления удаляет файл. Если файл открыт, эта функция не выполняется и
возвращает значение -1.

См. также
Функции библиотеки
Переименование с использованием
существующего имени
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 4.9.4.2 Что происходит, если файл с новым именем, указанным в вызове


функции rename, уже существует

При попытке переименовать файл с использованием уже существующего имени


функция rename завершается неудачей и возвращает код ошибки.

См. также
Функции библиотеки
Чтение значений указателя
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 4.9.6.2 Входные данные для преобразования %p в функции fscanf

Если указан символ формата %p, функция fscanf преобразует указатели из


шестнадцатеричных значений ASCII в соответствующий адрес.

См. также
Функции библиотеки
Диапазоны чтения
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 4.9.6.2 Интерпретация символа дефиса (-), который не является ни первым,


ни последним символом в списке сканирования для преобразования % [ в функции
fscanf

В следующей строке

fscanf( fileptr, "%[A-Z]", strptr);

считывается любое количество символов в диапазоне A–Z в строке, на которую


указывает strptr .

См. также
Функции библиотеки
Ошибки позиции файла
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 4.9.9.1, 4.9.9.4 Значение, задаваемое макросу errno функциями fgetpos и


ftell при ошибке

В случае сбоя функции fgetpos или ftell для макроса errno задается значение
константы EINVAL из манифеста, если недопустима позиция, или значение EBADF ,
если недопустим номер файла. Эти константы определены в файле ERRNO.H.

См. также
Функции библиотеки
Сообщения, созданные функцией
perror
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 4.9.10.4 Сообщения, создаваемые функцией perror

Функция perror создает следующие сообщения.

0 Error 0

2 No such file or directory

7 Arg list too long

8 Exec format error

9 Bad file number

10

11

12 Not enough core

13 Permission denied

14

15

16

17 File exists

18 Cross-device link

19

20

21

22 Invalid argument

23

24 Too many open files

25

26

27

28 No space left on device

29

30

31

32

33 Math argument

34 Result too large

35

36 Resource deadlock would occur

См. также
Функции библиотеки
Выделение обнуленной памяти
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 4.10.3 Поведение функции calloc , malloc или realloc , если запрошенный


размер равен нулю

Функции calloc , malloc и realloc принимают в качестве аргумента нуль.


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

См. также
Функции библиотеки
Функция abort (C)
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 4.10.4.1 Поведение функции abort в отношении открытых и временных


файлов

Функция abort не закрывает открытые и временные файлы. Она не очищает


буферы потоков. Дополнительные сведения см. в разделе abort.

См. также
Функции библиотеки
Функция atexit (C)
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 4.10.4.3 Состояние, возвращаемое функцией atexit , если значение аргумента


отлично от нуля, EXIT_SUCCESS или EXIT_FAILURE

Функция atexit возвращает нуль в случае успешного выполнения операции или


значение, отличное от нуля, если операция завершилась неудачно.

См. также
Функции библиотеки
Имена сред
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 4.10.4.4 Набор имен среды и метод, который используется для изменения
списка среды, используемого функцией getenv

Набор имен среды не ограничен.

Чтобы изменить переменные среды из программы на языке C, вызовите функцию


_putenv. Чтобы изменить переменные среды из командной строки в Windows,
используйте команду SET (например, SET LIB = D:\ LIBS).

Переменные среды, заданные в программе на языке C, существуют, только пока


выполняется копия узла командной оболочки операционной системы (CMD.EXE
или COMMAND.COM). Например, в строке

system( SET LIB = D:\LIBS );

выполняется копия командной оболочки (CMD.EXE), задается переменная среды


LIB и выполняется возврат в программу на языке C при выходе из дополнительной
копии CMD.EXE. При выходе из этой копии CMD.EXE временная переменная среды
LIB удаляется.

Таким же образом, изменения, выполненные функцией _putenv , действуют только


до завершения программы.

См. также
Функции библиотеки

_putenv, _wputenv

getenv, _wgetenv
Функция system
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 4.10.4.5 Содержимое и режим выполнения строки функцией system

Системная функция выполняет внутреннюю команду операционной системы или


.EXE ,COM, . CMD или .BAT файл из программы C, а не из командной строки.

Системная функция находит интерпретатор команд, который обычно CMD.EXE в


операционной системе Windows. Затем функция system передает строку аргумента
в интерпретатору команд.

Дополнительные сведения см. в описании system, _wsystem.

См. также
Функции библиотеки
Функция strerror
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 4.11.6.2 Содержимое строк сообщения об ошибке, возвращаемых функцией


strerror

Функция strerror создает следующие сообщения.

0 Error 0

2 No such file or directory

7 Arg list too long

8 Exec format error

9 Bad file number

10

11

12 Not enough core

13 Permission denied

14

15

16

17 File exists

18 Cross-device link

19

20

21

22 Invalid argument

23

24 Too many open files

25

26

27

28 No space left on device

29

30

31

32

33 Math argument

34 Result too large

35

36 Resource deadlock would occur


См. также
Функции библиотеки
Часовой пояс
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 4.12.1 Местный часовой пояс и переход на летнее время

В качестве местного часового пояса установлено стандартное тихоокеанское


время. Microsoft C поддерживает летнее время.

См. также
Функции библиотеки
Функция clock (C)
Статья • 03.03.2023 • Чтение занимает 2 мин

ANSI 4.12.2.1 Эра для функции clock

Эра функции clock начинается (со значения 0) при запуске программы C на


выполнение. Она возвращает значения времени, измеренные в следующих
единицах: 1/CLOCKS_PER_SEC (1/1000 для Microsoft C).

См. также
Функции библиотеки
Справочник по препроцессору в
C/C++
Статья • 26.09.2022 • Чтение занимает 2 мин

В справочнике по препроцессору C/c++ описывается препроцессор, реализованный


в Microsoft C/c++. Препроцессор выполняет предварительные операции с
файлами C и C++ перед их передачей компилятору. Препроцессор можно
использовать для условной компиляции кода, вставки файлов, задания сообщений
для ошибок времени компиляции, а также для применения правил, зависящих от
компьютера, к разделам кода.

в Visual Studio 2019 параметр компилятора /zc: препроцессор предоставляет


полностью согласованный препроцессор C11 и C17. Это значение по умолчанию
при использовании флага /std:c11 компилятора или /std:c17 .

Содержимое раздела
Препроцессор

Предоставляет общие сведения о традиционных и новых препроцессорах.

Директивы препроцессора

Описание директив, обычно используемых, чтобы исходные программы можно


было легко изменять и компилировать в разных средах выполнения.

Операторы препроцессора

Описание четырех относящихся к препроцессору операторов, используемых в


контексте директивы #define .

Предустановленные макросы

Описывает стандартные макросы, указанные в стандартах C и C++, а также в


Microsoft C++.

Директивы pragma

Описание директив #pragma, которые позволяют каждому компилятору


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

Связанные разделы
Справочник по языку C++

Справочные материалы по реализации языка C++ корпорации Microsoft.

Справочник по языку C

Справочные материалы по реализации языка C корпорации Microsoft.

Образец построения C/C++

Ссылки на разделы, в которых рассматриваются параметры компилятора и


компоновщика.

Проекты Visual Studio — C++

Описание пользовательского интерфейса в Visual Studio, позволяющего


определять каталоги, в которых система проектов будет выполнять поиск файлов
для проекта C++.
Справочник по библиотеке среды
выполнения Microsoft C (CRT)
Статья • 25.10.2022 • Чтение занимает 2 мин

Библиотека среды выполнения Майкрософт предоставляет процедуры


программирования операционной системы Microsoft Windows. Эти подпрограммы
автоматизируют многие распространенные задачи программирования, которые не
предоставляются языками C и C++.

Примеры программ включены в отдельные справочные статьи для большинства


подпрограмм в библиотеке.

В этом разделе
Подпрограммы универсальной среды выполнения C по категориям

Предоставляет ссылки на библиотеку среды выполнения по категориям.

Глобальные переменные и стандартные типы

Предоставляет ссылки на глобальные переменные и стандартные типы,


предоставляемые библиотекой среды выполнения.

Глобальные константы

Предоставляет ссылки на глобальные константы, определенные библиотекой


среды выполнения.

Глобальное состояние

Описывает область глобального состояния в библиотеке среды выполнения C.

Универсальные текстовые сопоставления

Содержит ссылки на универсальные текстовые сопоставления, определенные в


файле Tchar.h.

Алфавитный указатель функций

Ссылки на функции библиотеки среды выполнения C, упорядоченные по алфавиту.

Общие сведения о семействе функций

Ссылки на функции библиотеки среды выполнения C, упорядоченные по семейству


функций.

Строки языка и страны и региона

Описывает способы использования функции setlocale для задания языка и строк


страны или региона.

Файлы среды выполнения C (CRT) и стандартной библиотеки C++ (STL) .lib

Список файлов, составляющих .lib библиотеки среды выполнения C и связанные


с ними параметры компилятора и директивы препроцессора.

Связанные разделы
Подпрограммы отладки

Содержит ссылки на отладочные версии подпрограмм библиотеки времени


выполнения.

Проверка ошибок во время выполнения

Ссылки на функции, поддерживающие проверки ошибок среды выполнения.

Библиотеки DLL и поведение библиотеки среды выполнения Visual C++

Описание точки входа и кода запуска, используемого для DLL.

Отладка

Ссылки на разделы, описывающие использование отладчика Visual Studio для


устранения логических ошибок в приложениях и хранимых процедурах.

Вам также может понравиться