Синтаксис C - C syntax

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

Синтаксис C использует максимальный перекус принцип.

Структуры данных

Примитивные типы данных

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

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

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

Целочисленные типы C бывают разных фиксированных размеров, способных представлять различные диапазоны чисел. Тип char занимает ровно один байт (наименьший адресный блок памяти), который обычно имеет ширину 8 бит. (Несмотря на то что char может представлять любой из "основных" символов языка Си, для международных наборов символов может потребоваться более широкий тип.) Большинство целочисленных типов имеют оба подписанный и неподписанный сорта, обозначенные подписанный и беззнаковый ключевые слова. Знаковые целочисленные типы могут использовать два дополнения, дополнение, или же знак и величина представление. Во многих случаях существует несколько эквивалентных способов обозначения типа; Например, подписанный короткий int и короткая являются синонимами.

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

Спецификации стандартных целочисленных типов
Кратчайшая форма спецификатораМинимальная ширина (бит)
_Bool1
char8
подписанный символ8
беззнаковый символ8
короткая16
беззнаковый короткий16
int16
беззнаковое целое16
длинный32
беззнаковый длинный32
долго долго[1]64
беззнаковый длинный длинный[1]64

В char тип отличается от обоих подписанный символ и беззнаковый символ, но гарантированно будет иметь то же представление, что и один из них. В _Bool и долго долго типы стандартизированы с 1999 года и могут не поддерживаться более старыми компиляторами C. Тип _Bool обычно доступен через typedef имя bool определяется стандартным заголовком stdbool.h.

В общем, ширина и схема представления, реализованная для любой данной платформы, выбирается на основе архитектуры машины, с некоторым учетом простоты импорта исходного кода, разработанного для других платформ. Ширина int тип особенно широко варьируется среди реализаций C; он часто соответствует наиболее «естественному» размеру слова для конкретной платформы. Стандартный заголовок limits.h определяет макросы для минимальных и максимальных представимых значений стандартных целочисленных типов, реализованных на любой конкретной платформе.

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

Целочисленные константы могут быть указаны в исходном коде несколькими способами. Числовые значения могут быть указаны как десятичный (пример: 1022), восьмеричный с нулем (0) в качестве префикса (01776), или же шестнадцатеричный с 0x (ноль x) в качестве префикса (0x3FE). Символ в одинарных кавычках (пример: 'Р'), называемая «символьной константой», представляет значение этого символа в наборе символов выполнения с типом int. За исключением символьных констант, тип целочисленной константы определяется шириной, необходимой для представления указанного значения, но всегда по крайней мере такой же ширины, как int. Это можно изменить, добавив явный модификатор длины и / или подписи; Например, 12lu имеет тип беззнаковый длинный. Нет отрицательных целочисленных констант, но тот же эффект часто можно получить с помощью унарного оператора отрицания «-».

Нумерованный тип

В перечислимый тип в C, указанный с перечислить ключевое слово, которое часто называют просто "перечислением" (обычно произносится ee'-num /ˌI.nʌm/ или ee'-noom /ˌI.nuːm/) - это тип, предназначенный для представления значений в серии именованных констант. Каждая из перечисленных констант имеет тип int. Каждый перечислить сам тип совместим с char или целочисленный тип со знаком или без знака, но каждая реализация определяет свои собственные правила выбора типа.

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

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

перечислить цвета { КРАСНЫЙ, ЗЕЛЕНЫЙ, СИНИЙ = 5, ЖЕЛТЫЙ } paint_color;

Это заявляет перечислить цвета тип; в int константы КРАСНЫЙ (значение которого равно 0), ЗЕЛЕНЫЙ (значение которого на единицу больше, чем КРАСНЫЙ, 1), СИНИЙ (значение которого равно заданному значению, 5), и ЖЕЛТЫЙ (значение которого на единицу больше, чем СИНИЙ, 6); и перечислить цвета Переменная paint_color. Константы могут использоваться вне контекста перечисления (где разрешено любое целочисленное значение), а значения, отличные от констант, могут быть присвоены paint_color, или любая другая переменная типа перечислить цвета.

Типы с плавающей запятой

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

Типы с плавающей точкой
Спецификаторы типаТочность (десятичные цифры)Диапазон экспоненты
МинимумIEEE 754МинимумIEEE 754
плавать67.2 (24 бит)±37± 38 (8 бит)
двойной1015,9 (53 бит)±37± 307 (11 бит)
длинный двойной1034,0 (113 бит)±37± 4931 (15 бит)

Константы с плавающей запятой могут быть записаны в десятичная запись, например 1.23. Десятичная научная запись можно использовать, добавив е или же E за которым следует десятичная экспонента, также известная как Обозначение E, например 1.23e2 (что имеет значение 1,23 × 102 = 123,0). Требуется либо десятичная точка, либо показатель степени (в противном случае число анализируется как целочисленная константа). Шестнадцатеричные константы с плавающей запятой следуйте аналогичным правилам, за исключением того, что они должны начинаться с префикса 0x и использовать п или же п для указания двоичной экспоненты, например 0xAp-2 (который имеет значение 2,5, поскольку Aчас × 2−2 = 10 × 2−2 = 10 ÷ 4). И десятичные, и шестнадцатеричные константы с плавающей запятой могут иметь суффикс ж или же F для обозначения константы типа плавать, к л (письмо л) или же L указать тип длинный двойной, или оставлено незафиксированным на двойной постоянный.

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

Спецификаторы класса хранения

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

Классы хранения
СпецификаторыПродолжительность жизниОбъемИнициализатор по умолчанию
автоБлок (стек)БлокироватьНеинициализированный
регистрБлок (стек или регистр ЦП)БлокироватьНеинициализированный
статическийПрограммаБлок или единица компиляцииНуль
внешнийПрограммаГлобальный (вся программа)Нуль
(никто)1Динамический (куча)Неинициализировано (инициализировано 0 при использовании calloc ())
1 Выделяется и освобождается с помощью malloc () и свободный() библиотечные функции.

Переменные, объявленные в блоке по умолчанию, имеют автоматическое хранение, как и переменные, явно объявленные с авто[2] или же регистр спецификаторы класса хранения. В авто и регистр спецификаторы могут использоваться только в функциях и объявлениях аргументов функций; как таковой, авто спецификатор всегда избыточен. Объекты, объявленные вне всех блоков и явно объявленные с статический спецификатор класса хранения имеет статическую продолжительность хранения. Статические переменные по умолчанию инициализируются нулевым значением компилятор.

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

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

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

Квалификаторы типа

Типы могут быть квалифицированы для указания особых свойств их данных. Квалификатор типа const указывает, что значение не изменяется после инициализации. Попытка изменить const квалифицированное значение приводит к неопределенному поведению, поэтому некоторые компиляторы C сохраняют их в Родата или (для встроенных систем) в только для чтения памяти (ПЗУ). Квалификатор типа летучий указывает на оптимизирующий компилятор что он не может удалять явно избыточные операции чтения или записи, поскольку значение может измениться, даже если оно не было изменено каким-либо выражением или оператором, или может потребоваться несколько операций записи, например, для ввод-вывод с отображением памяти.

Неполные типы

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

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

структура вещь *pt;

Это заявляет pt как указатель на структура вещь и неполный тип структура вещь. Указатели на данные всегда имеют одинаковую ширину байта независимо от того, на что они указывают, поэтому этот оператор действителен сам по себе (до тех пор, пока pt не разыменовывается). Неполный тип можно завершить позже в той же области, повторно объявив его:

структура вещь {    int число;}; / * тип структуры объекта завершен * /

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

typedef структура Берт Берт;typedef структура Вильма Вильма;структура Берт {    Вильма *Wilma;};структура Вильма {    Берт *Берт;};

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

Указатели

В объявлениях модификатор звездочка (*) указывает тип указателя. Например, где спецификатор int будет относиться к целочисленному типу, спецификатор int * относится к типу «указатель на целое число». Значения указателя связывают две части информации: адрес памяти и тип данных. Следующая строка кода объявляет переменную указателя на целое число с именем ptr:

int *ptr;

Ссылка

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

int а = 0;int *ptr = &а;

Для этого оператор «адреса» (унарный &) используется. Он создает ячейку памяти для следующего объекта данных.

Разыменование

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

int а=10;int *п;п = &а;int б = *п;

Чтобы выполнить эту задачу, унарный оператор разыменования, обозначается звездочкой (*). Он возвращает данные, на которые указывает его операнд, который должен иметь тип указателя. Таким образом, выражение *п обозначает то же значение, что и а. Разыменование нулевой указатель незаконно.

Массивы

Определение массива

Массивы используются в C для представления структур последовательных элементов одного типа. Определение массива (фиксированного размера) имеет следующий синтаксис:

int множество[100];

который определяет массив с именем множество для хранения 100 значений примитивного типа int. Если объявляется в функции, измерение массива также может быть непостоянным выражением, и в этом случае будет выделена память для указанного количества элементов. В большинстве случаев при дальнейшем использовании упоминание переменной множество преобразуется в указатель на первый элемент в массиве. В размер оператор является исключением: sizeof массива дает размер всего массива (то есть в 100 раз больше размера int, и sizeof (массив) / sizeof (число) вернет 100). Другим исключением является оператор & (адрес-из), который возвращает указатель на весь массив, например

int (*ptr_to_array)[100] = &множество;

Доступ к элементам

Основным средством доступа к значениям элементов массива является оператор индекса массива. Чтобы получить доступ к я-индексированный элемент множество, синтаксис будет массив [я], который относится к значению, хранящемуся в этом элементе массива.

Нумерация индексов массива начинается с 0 (см. Индексирование с нуля ). Таким образом, наибольший допустимый индекс массива равен количеству элементов в массиве минус 1. Чтобы проиллюстрировать это, рассмотрим массив а объявлен как имеющий 10 элементов; первый элемент будет а [0] и последний элемент будет а [9].

C не предоставляет возможности для автоматического проверка границ для использования массива. Хотя логически последним индексом в массиве из 10 элементов будет 9, индексы 10, 11 и т. Д. Могут быть случайно указаны с неопределенным результатом.

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

Индексы массива против арифметики указателя
ЭлементПервыйВторойВ третьихпth
Индекс массивамножество[0]множество[1]множество[2]множество[п - 1]
Разыменованный указатель*множество*(множество + 1)*(множество + 2)*(множество + п - 1)

Поскольку выражение а [я] семантически эквивалентно * (а + я), что, в свою очередь, эквивалентно * (я + а); выражение также можно записать как я], хотя эта форма используется редко.

Массивы переменной длины

C99 стандартизированный массивы переменной длины (VLA) в пределах блока. Такие переменные массива выделяются на основе значения целого числа во время выполнения при входе в блок и освобождаются в конце блока.[3] По состоянию на C11 компилятор больше не требует реализации этой функции.

int п = ...;int а[п];а[3] = 10;

Этот синтаксис создает массив, размер которого фиксирован до конца блока.

Динамические массивы

Массивы, размер которых можно динамически изменять, можно создавать с помощью Стандартная библиотека C. В [[malloc]] Функция предоставляет простой метод распределения памяти. Требуется один параметр: объем выделяемой памяти в байтах. После успешного распределения маллок возвращает универсальный (пустота) значение указателя, указывающее на начало выделенного пространства. Возвращаемое значение указателя неявно преобразуется в соответствующий тип путем присваивания. Если распределение не может быть завершено, маллок возвращает нулевой указатель. Следовательно, следующий сегмент аналогичен по функциям приведенному выше желаемому объявлению:

#включают  / * объявляет malloc * /...int *а = маллок(п * размер *а);а[3] = 10;

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

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

В качестве меры безопасности некоторые программисты[ВОЗ? ] затем установите переменную указателя на НОЛЬ:

свободный(а);а = НОЛЬ;

Это гарантирует, что дальнейшие попытки разыменования указателя приведут к сбою программы. Если этого не сделать, переменная становится висячий указатель что может привести к ошибке использования после бесплатного использования. Однако, если указатель является локальной переменной, установите для нее значение НОЛЬ не запрещает программе использовать другие копии указателя. Локальное использование после устранения ошибок обычно легко статические анализаторы распознавать. Следовательно, этот подход менее полезен для локальных указателей и чаще используется с указателями, хранящимися в долгоживущих структурах. В общем, установка указателей на НОЛЬ это хорошая практика[согласно кому? ] поскольку это позволяет программисту НОЛЬ-проверьте указатели перед разыменованием, что поможет предотвратить сбои.

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

int (*а)[100] = маллок(размер *а);

... Что дает указатель на массив.

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

(*а)[индекс];индекс[*а];

Итерацию также можно выполнить двумя способами:

за (int я = 0; я < 100; я++)    (*а)[я];за (int *я = а[0]; я < а[1]; я++)    *я;

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

Многомерные массивы

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

int array2d[РЯДЫ][КОЛОННЫ];

куда РЯДЫ и КОЛОННЫ являются константами. Это определяет двумерный массив. Читая индексы слева направо, array2d это массив длины РЯДЫ, каждый элемент которого представляет собой массив КОЛОННЫ целые числа.

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

array2d[4][3]

Опять же, читая слева направо, мы получаем доступ к 5-й строке и 4-му элементу в этой строке. Выражение array2d [4] - это массив, который мы затем индексируем с помощью [3] для доступа к четвертому целому числу.

Индексы массива против арифметики указателя[4]
ЭлементПервыйВторая строка, второй столбецябросать, jй столбец
Индекс массивамножество[0][0]множество[1][1]множество[я - 1][j - 1]
Разыменованный указатель*(*(множество + 0) + 0)*(*(множество + 1) + 1)*(*(множество + я - 1) + j - 1)

Подобным образом можно объявлять и многомерные массивы.

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

Струны

В C строковые литералы заключаются в двойные кавычки ("), например "Привет, мир!" и компилируются в массив указанных char значения с дополнительным нулевой завершающий символ (0-значной) код для обозначения конца строки.

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

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

Синтаксис строкового литерала C оказал большое влияние и проник во многие другие языки, такие как C ++, Objective-C, Perl, Python, PHP, Java, Javascript, C #, Ruby. В настоящее время почти все новые языки используют синтаксис строк в стиле C. Языки, в которых отсутствует этот синтаксис, как правило, предшествуют C.

Обратная косая черта экранирует

Если вы хотите включить двойные кавычки внутри строки, это можно сделать, экранировав ее с помощью обратной косой черты (\), Например, "Эта строка содержит " двойные кавычки ".". Чтобы вставить буквальную обратную косую черту, нужно ее удвоить, например "Обратная косая черта выглядит так: ".

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

ПобегСмысл
\\Буквальный обратный слеш
\"Двойная кавычка
\'Одиночная цитата
пНовая строка (перевод строки)
рВозврат каретки
bBackspace
тГоризонтальная вкладка
fПодача формы
аПредупреждение (звонок)
vВертикальная табуляция
\?Вопросительный знак (используется для побега триграфы )
%%Знак процента, строки формата printf только (Примечание \% нестандартно и не всегда распознается)
\ОООСимвол с восьмеричным значением ООО (куда ООО 1-3 восьмеричные цифры, '0' - '7')
ИксHHСимвол с шестнадцатеричным значением HH (куда HH 1 или более шестнадцатеричных цифр, '0' - '9', 'A' - 'F', 'a' - 'f')

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

Конкатенация строковых литералов

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

    printf(__ФАЙЛ__ ":% d: Привет"           "Мир п", __ЛИНИЯ__);

расширится до

    printf("helloworld.c" ":% d: Привет"           "Мир п", 10);

что синтаксически эквивалентно

    printf("helloworld.c:% d: Привет, мир п", 10);

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

Отдельные символьные константы заключаются в одинарные кавычки, например 'А', и имеют тип int (в C ++, char). Разница в том, что "А" представляет собой массив из двух символов с завершающим нулем, 'A' и ' 0', тогда как 'А' непосредственно представляет символьное значение (65, если используется ASCII). Поддерживаются те же символы обратной косой черты, что и для строк, за исключением (конечно) " может действительно использоваться как символ без экранирования, тогда как ' теперь нужно убежать.

Символьная константа не может быть пустой (т.е. '' недопустимый синтаксис), хотя строка может быть (она все еще имеет нулевой символ завершения). Многосимвольные константы (например, 'ху') действительны, хотя и редко используются - они позволяют хранить несколько символов в виде целого числа (например, 4 символа ASCII могут поместиться в 32-битное целое число, 8 - в 64-битное). Поскольку порядок, в котором символы упакованы в int не указан (оставлено на усмотрение реализации), переносимое использование многосимвольных констант затруднено.

Тем не менее, в ситуациях, ограниченных конкретной платформой и реализацией компилятора, многосимвольные константы действительно находят свое применение при указании сигнатур. Один из распространенных вариантов использования - OSType, где сочетание компиляторов классической Mac OS и присущей ему прямой последовательности байтов означает, что байты в целом числе появляются в точном порядке символов, определенных в литерале. На самом деле определения популярных «реализаций» согласованы: в GCC, Clang и Visual C ++, '1234' дает 0x31323334 под ASCII.[5][6]

Строки широких символов

Поскольку тип char имеет ширину 1 байт, один char value обычно может представлять не более 255 различных кодов символов, что недостаточно для всех символов, используемых во всем мире. Чтобы обеспечить лучшую поддержку международных символов, первый стандарт C (C89) представил широкие персонажи (закодировано в типе wchar_t) и строки широких символов, которые записываются как L "Привет, мир!"

Широкие символы обычно имеют длину 2 байта (с использованием 2-байтовой кодировки, например UTF-16 ) или 4 байта (обычно UTF-32 ), но Стандарт C не определяет ширину для wchar_t, оставляя выбор разработчику. Майкрософт Виндоус обычно используется UTF-16, поэтому длина указанной выше строки для компилятора Microsoft составляет 26 байт; в Unix world предпочитает UTF-32, поэтому компиляторы, такие как GCC, будут генерировать 52-байтовую строку. 2-байтовый wchar_t имеет то же ограничение, что и char, в том, что определенные символы (за пределами BMP ) не могут быть представлены в одном wchar_t; но должен быть представлен с использованием суррогатные пары.

Исходный стандарт C определял только минимальные функции для работы с широкими символьными строками; в 1995 году стандарт был изменен и теперь включает гораздо более широкую поддержку, сопоставимую с char струны. Соответствующие функции в основном названы в честь их char эквиваленты, с добавлением «w» или заменой «str» на «wcs»; они указаны в <wchar.h>, с <wctype.h> содержащие функции классификации и отображения широких символов.

В настоящее время обычно рекомендуемый метод[7] поддержки международных персонажей UTF-8, который хранится в char массивы и могут быть записаны непосредственно в исходный код при использовании редактора UTF-8, потому что UTF-8 является прямым Расширение ASCII.

Струны переменной ширины

Обычная альтернатива wchar_t использовать кодирование с переменной шириной, в результате чего логический символ может занимать несколько позиций в строке. Строки переменной ширины могут быть дословно закодированы в литералы с риском запутать компилятор или с использованием числовых символов обратной косой черты (например, " xc3 xa9" для "é" в UTF-8). В UTF-8 кодировка была специально разработана (под План 9 ) для совместимости со строковыми функциями стандартной библиотеки; Поддерживающие функции кодирования включают отсутствие встроенных нулей, правильную интерпретацию подпоследовательностей и тривиальную ресинхронизацию. Кодировки, в которых отсутствуют эти функции, могут оказаться несовместимыми со стандартными библиотечными функциями; В таких случаях часто используются строковые функции с поддержкой кодирования.

Библиотечные функции

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

Структуры и союзы

Структуры

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

Союзы

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

Декларация

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

Например, следующий оператор объявляет структуру с именем s который содержит три члена; он также объявит экземпляр структуры, известной как тройник:

структура s {    int   Икс;    плавать у;    char  *z;} тройник;

И следующее утверждение объявит подобный союз с именем ты и его экземпляр с именем п:

союз ты {    int   Икс;    плавать у;    char  *z;} п;

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

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

структура s р;

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

typedef структура {...} s_type;

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

Доступ к участникам

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

тройник.у

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

структура s *ptr_to_tee = &тройник;

Член у из тройник затем можно получить доступ путем разыменования ptr_to_tee и используя результат как левый операнд:

(*ptr_to_tee).у

Что идентично более простому тройник выше, пока ptr_to_tee указывает на тройник. Из-за приоритет оператора («.» выше, чем «*»), тем короче * ptr_to_tee.y не подходит для этой цели, вместо этого анализируется как * (ptr_to_tee.y) поэтому круглые скобки необходимы. Поскольку это обычная операция, C предоставляет сокращенный синтаксис для доступа к члену непосредственно из указателя. В этом синтаксисе имя экземпляра заменяется именем указателя, а точка заменяется последовательностью символов. ->. Таким образом, следующий метод доступа к у идентична двум предыдущим:

ptr_to_tee->у

Таким же образом осуществляется доступ к членам профсоюзов.

Это можно связать цепью; например, в связанном списке можно ссылаться на n-> следующий-> следующий для второго следующего узла (при условии, что n-> следующий не равно нулю).

Назначение

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

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

Например, следующий оператор присваивает значение 74 (кодовая точка ASCII для буквы 't') члену с именем Икс в структуре тройник, сверху:

тройник.Икс = 74;

И такое же задание с использованием ptr_to_tee на месте тройник, будет выглядеть так:

ptr_to_tee->Икс = 74;

Назначение с членами профсоюзов идентично.

Прочие операции

Согласно стандарту C единственные допустимые операции, которые могут выполняться над структурой, - это ее копирование, присвоение ей как единицы (или ее инициализация), взятие ее адреса с адресом (&) унарный оператор и доступ к его членам. У профсоюзов такие же ограничения. Одной из неявно запрещенных операций является сравнение: структуры и объединения нельзя сравнивать с помощью стандартных средств сравнения языка Си (==, >, <, так далее.).

Битовые поля

C также предоставляет особый тип элемента структуры, известный как битовое поле, которое является целым числом с явно указанным количеством бит. Битовое поле объявлено как член структуры типа int, подписанный int, беззнаковое целое, или же _Bool, после имени члена двоеточием (:) и количество бит, которое он должен занимать. Общее количество битов в одном битовом поле не должно превышать общее количество бит в его объявленном типе.

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

Также разрешены безымянные поля, состоящие только из двоеточия, за которым следует несколько бит; это указывает набивка. Указание нулевой ширины для безымянного поля используется для принудительного выравнивание к новому слову.[8]

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

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

структура ж {    беззнаковый int  флаг : 1;  / * битовый флаг: может быть включен (1) или выключен (0) * /    подписанный int    число  : 4;  / * подписанное 4-битное поле; диапазон -7 ... 7 или -8 ... 7 * /    подписанный int         : 3;  / * 3 бита заполнения для округления до 8 бит * /} грамм;

Инициализация

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

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

int Икс = 12;int у = { 23 };     // Законно, без предупрежденияint z = { { 34 } }; // Законно, ожидайте предупреждения

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

Следующий оператор инициализирует новый экземпляр структуры s известный как число Пи:

структура s {    int   Икс;    плавать у;    char  *z;};структура s число Пи = { 3, 3.1415, "Число Пи" };

Назначенные инициализаторы

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

структура s число Пи = { .z = "Число Пи", .Икс = 3, .у = 3.1415 };

Использование обозначения в инициализаторе перемещает «курсор» инициализации. В примере ниже, если МАКСИМУМ больше 10, в середине будут какие-то нулевые элементы. а; если оно меньше 10, некоторые из значений, предоставленных первыми пятью инициализаторами, будут переопределены вторыми пятью (если МАКСИМУМ меньше 5, будет ошибка компиляции):

int а[МАКСИМУМ] = { 1, 3, 5, 7, 9, [МАКСИМУМ-5] = 8, 6, 4, 2, 0 };

В C89, объединение было инициализировано одним значением, примененным к его первому члену. То есть союз ты определенное выше могло иметь только int x член инициализирован:

союз ты ценить = { 3 };

При использовании назначенного инициализатора инициализируемый член не обязательно должен быть первым членом:

союз ты ценить = { .у = 3.1415 };

Если размер массива неизвестен (т. Е. Массив был неполный тип ) количество инициализаторов определяет размер массива и его тип становится полным:

int Икс[] = { 0, 1, 2 } ;

Составные указатели могут использоваться для обеспечения явной инициализации, когда неприкрашенные списки инициализаторов могут быть неправильно поняты. В приведенном ниже примере ш объявляется как массив структур, каждая из которых состоит из члена а (массив из 3 int) и член б (ан int). Инициализатор устанавливает размер ш равным 2 и устанавливает значения первого элемента каждого а:

структура { int а[3], б; } ш[] = { [0].а = {1}, [1].а[0] = 2 };

Это эквивалентно:

структура { int а[3], б; } ш[] ={   { { 1, 0, 0 }, 0 },   { { 2, 0, 0 }, 0 } };

В стандартном C. нет возможности указать повторение инициализатора.

Составные литералы

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

// указатель создан из литерала массива.int *ptr = (int[]){ 10, 20, 30, 40 };// указатель на массив.плавать (*фу)[3] = &(плавать[]){ 0,5f, 1.f, -0,5f };структура s число Пи = (структура s){ 3, 3.1415, "Число Пи" };

Составные литералы часто объединяются с назначенными инициализаторами, чтобы сделать объявление более читаемым:[3]

число Пи = (структура s){ .z = "Число Пи", .Икс = 3, .у = 3.1415 };

Операторы

Структуры управления

C - это язык свободной формы.

Укрепляющий стиль варьируется от программист программисту и может быть предметом споров. Видеть Стиль отступа Больше подробностей.

Составные заявления

В элементах этого раздела любой можно заменить на составное заявление. Составные утверждения имеют вид:

{    <необязательный-декларация-список>    <необязательный-утверждение-список>}

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

Заявления о выборе

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

В если выписка имеет вид:

если (<выражение>)    <заявление1>еще    <заявление2>

в если заявление, если <expression> в скобках ненулевое значение (истина), управление переходит к <statement1>. Если еще оговорка присутствует и <expression> равен нулю (ложь), управление перейдет к <statement2>. В else часть является необязательной и, если отсутствует, ложной <expression> просто приведет к пропуску <statement1>. An еще всегда соответствует ближайшему предыдущему несогласованному если; фигурные скобки могут использоваться для отмены этого при необходимости или для ясности.

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

выключатель (<выражение>){    дело <label1> :        <заявления 1>    дело <label2> :        <заявления 2>        перемена;    дефолт :        <заявления 3>}

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

Переключатели могут быть вложенными; а дело или же дефолт ярлык связан с самым сокровенным выключатель который его содержит. Операторы Switch могут «проваливаться», то есть, когда один раздел case завершил свое выполнение, операторы будут продолжать выполняться вниз, пока не появится перемена; заявление встречается. Падение полезно в некоторых обстоятельствах, но обычно нежелательно. В предыдущем примере, если <label2> достигнуто, заявления <statements 2> выполнены и больше ничего внутри скобок. Однако если <label1> достигается, как <statements 1> и <statements 2> выполняются, так как нет перемена для разделения двух операторов case.

Возможно, хотя и необычно, вставить выключатель метки в подблоки других структур управления. Примеры этого включают Устройство Даффа и Саймон Тэтхам реализация сопрограммы в Шпатлевка.[9]

Операторы итерации

C имеет три формы итерация утверждение:

делать    <утверждение>пока ( <выражение> ) ;пока ( <выражение> )    <утверждение>за ( <выражение> ; <выражение> ; <выражение> )    <утверждение>

в пока и делать операторов, подоператор выполняется многократно до тех пор, пока значение выражение остается ненулевым (эквивалентно истине). С пока, тест, включая все побочные эффекты от <expression>, происходит перед каждой итерацией (выполнение <statement>); с делать, проверка выполняется после каждой итерации. Таким образом, делать оператор всегда выполняет свой под-оператор хотя бы один раз, тогда как пока может вообще не выполнять под-инструкцию.

Заявление:

за (e1; e2; e3)    s;

эквивалентно:

e1;пока (e2){    s;продолжение:    e3;}

за исключением поведения Продолжить; заявление (которое в за петля переходит к e3 вместо e2). Если e2 пусто, его необходимо заменить на 1.

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

С C99, первое выражение может принимать форму объявления, обычно включающего инициализатор, например:

за (int я = 0; я < предел; ++я) {    // ...}

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

Заявления о прыжках

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

В идти к заявление выглядит так:

идти к <идентификатор> ;

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

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

пока (выражение){    /* ... */    продолжение: ;}делать{    /* ... */    продолжение: ;} пока (выражение);за (expr1; expr2; expr3) {     /* ... */     продолжение: ;}

а Продолжить не содержится во вложенном операторе итерации, то же самое, что goto cont.

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

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

Сохранение адреса этикетки

GCC расширяет язык C унарным && оператор, возвращающий адрес метки. Этот адрес можно сохранить в пустота* тип переменной и может быть использован позже в идти к инструкция. Например, следующие отпечатки "Здравствуй " в бесконечном цикле:

    пустота *ptr = &&J1;J1: printf("Здравствуй ");    идти к *ptr;

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

Функции

Синтаксис

Определение функции C состоит из тип возврата (пустота если значение не возвращается), уникальное имя, список параметров в круглых скобках и различные инструкции:

<возвращаться-тип> functionName( <параметр-список> ){    <заявления>    возвращаться <выражение из тип возвращаться-тип>;}

Функция с не-пустота возвращаемый тип должен включать как минимум один возвращаться утверждение. Параметры задаются <parameter-list>, разделенный запятыми список объявлений параметров, где каждый элемент в списке является типом данных, за которым следует идентификатор: <тип-данных> <идентификатор-переменной>, <тип-данных> <идентификатор-переменной>, ....

Если параметров нет, <parameter-list> может быть оставлено пустым или может быть определено одним словом пустота.

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

int printf (const char*, ...);

Манипулировать этими параметрами можно с помощью подпрограмм в заголовке стандартной библиотеки. <stdarg.h>.

Указатели функций

Указатель на функцию можно объявить следующим образом:

<возвращаться-тип> (*<функция-имя>)(<параметр-список>);

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

#включают <stdio.h>int (*операция)(int Икс, int у);int Добавить(int Икс, int у){    возвращаться Икс + у;}int вычесть(int Икс, int у){    возвращаться Икс - у;}int главный(int argc, char* аргументы[]){   int  фу = 1, бар = 1;   операция = Добавить;   printf("% d +% d =% d п", фу, бар, операция(фу, бар));   операция = вычесть;   printf("% d -% d =% d п", фу, бар, операция(фу, бар));   возвращаться 0;}

Глобальная структура

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

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

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

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

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

int главный() {...}int главный(пустота) {...}int главный(int argc, char *argv[]) {...}int главный(int argc, char **argv) {...}

Первые два определения эквивалентны (и оба совместимы с C ++).Какой из них использовать, вероятно, зависит от индивидуальных предпочтений (текущий стандарт C содержит два примера главный() и два из основной (пусто), но в проекте стандарта C ++ используется главный()). Возвращаемое значение главный (что должно быть int) служит в качестве статус прекращения вернулся в среду хоста.

Стандарт C определяет возвращаемые значения 0 и EXIT_SUCCESS как указание на успех и EXIT_FAILURE как указание на отказ. (EXIT_SUCCESS и EXIT_FAILURE определены в <stdlib.h> ). Другие возвращаемые значения имеют значение, определяемое реализацией; например, под Linux программа убита сигнал дает код возврата числового значения сигнала плюс 128.

Минимальная правильная программа на C состоит из пустого главный рутина, без аргументов и ничего не делая:

int главный(пустота){}

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

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

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

Функции могут быть написаны программистом или предоставлены существующими библиотеками. Интерфейсы для последних обычно объявляются включением файлов заголовков - с #включают директива предварительной обработки - объекты библиотеки связаны в окончательный исполняемый образ. Некоторые библиотечные функции, такие как printf, определены стандартом C; они называются стандартная библиотека функции.

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

Передача аргумента

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

пустота incInt(int *у){    (*у)++;  // Увеличиваем значение 'x' в 'main' ниже на единицу}int главный(пустота){    int Икс = 0;    incInt(&Икс);  // передаем ссылку на переменную 'x'    возвращаться 0;}

Функция сканф работает так же:

int Икс;сканф("% d", &Икс);

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

#включают <stdio.h>#включают <stdlib.h>пустота allocate_array(int ** const a_p, const int А) {/*  выделить массив целых чисел присвоение * a_p изменяет 'a' в main ()*/    *a_p = маллок(размер(int) * А); }int главный(пустота) {    int * а; / * создаем указатель на один или несколько целых чисел, это будет массив * / / * передаем адрес 'a' * /    allocate_array(&а, 42);/ * 'a' теперь является массивом длиной 42, и здесь можно манипулировать и освобождать его * /    свободный(а);    возвращаться 0;}

Параметр int ** a_p указатель на указатель на int, который является адресом указателя п определено в главный функции в этом случае.

Параметры массива

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

#включают <stdio.h>пустота setArray(int множество[], int индекс, int ценить){    множество[индекс] = ценить;}int главный(пустота){    int а[1] = {1};    setArray(а, 0, 2);    printf ("a [0] =% d п", а[0]);    возвращаться 0;}

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

пустота setArray(int *множество, int индекс, int ценить)

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

Разное

Зарезервированные ключевые слова

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

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

Чувствительность к регистру

Идентификаторы C чувствительны к регистру (например, фу, FOO, и Фу являются названиями разных объектов). Некоторые компоновщики могут сопоставлять внешние идентификаторы с одним случаем, хотя в большинстве современных компоновщиков это редкость.

Комментарии

Текст, начинающийся с жетон /* рассматривается как комментарий и проигнорировал. Комментарий заканчивается на следующем */; это может происходить внутри выражений и может занимать несколько строк. Случайное пропускание признака конца комментария проблематично, поскольку правильно построенный признак конца комментария следующего комментария будет использоваться для завершения начального комментария, а весь код между комментариями будет рассматриваться как комментарий. Комментарии в стиле C не вкладываются; то есть случайное размещение комментария внутри комментария приводит к непредвиденным результатам:

1 /*2 Эта строка будет проигнорирована.3 /*4 Здесь может появиться предупреждение компилятора. Эти строки также будут проигнорированы.5 Токен открытия комментария выше не начал новый комментарий,6 а токен закрытия комментария ниже закроет комментарий, начатый в строке 1.7 */8 Этот линия и в линия ниже Это буду нет быть игнорируется. Обе буду скорее всего производить компилировать ошибки.9 */

C ++ комментарии к строке стиля начинаются с // и продлите до конца строки. Этот стиль комментариев возник в BCPL и стал действующим синтаксисом C в C99; его нет ни в оригинальном K&R C, ни в ANSI C:

// эта строка будет проигнорирована компилятором/ * эти строки   будут проигнорированы   компилятором * /Икс = *п/ * q; / * этот комментарий начинается после 'p' * /

Аргументы командной строки

В параметры дано на командная строка передаются программе на C с двумя предопределенными переменными - счетчиком аргументов командной строки в argc и отдельные аргументы в качестве строки символов в массиве указателей argv. Итак, команда:

myFilt p1 p2 p3

приводит к чему-то вроде:

муFялт\0п1\0п2\0п3\0
argv [0]argv [1]argv [2]argv [3]

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

Название программы, argv [0], может быть полезно при печати диагностических сообщений или для использования одного двоичного файла в нескольких целях. Доступ к отдельным значениям параметров можно получить с помощью argv [1], argv [2], и argv [3], как показано в следующей программе:

#включают <stdio.h>int главный(int argc, char *argv[]){    printf("argc" т=% d п", argc);    за (int я = 0; я < argc; я++)        printf("argv [% i] т=% s п", я, argv[я]);}

Порядок оценки

В любом достаточно сложном выражении возникает выбор относительно порядка, в котором оценивать части выражения: (1+1)+(3+3) могут быть оценены в порядке (1+1)+(3+3), (2)+(3+3), (2)+(6), (8), или в порядке (1+1)+(3+3), (1+1)+(6), (2)+(6), (8). Формально соответствующий компилятор C может оценивать выражения в любой порядок между точки последовательности (это позволяет компилятору произвести некоторую оптимизацию). Точки последовательности определяются:

  • Заявление заканчивается через точку с запятой.
  • В оператор последовательности: запятая. Однако запятые, разделяющие аргументы функции, не являются точками последовательности.
  • В операторы короткого замыкания: логический и (&&, который можно прочитать а потом) и логичный или же (||, который можно прочитать или иначе).
  • В тернарный оператор (?:): Этот оператор сначала вычисляет свое первое подвыражение, а затем его второе или третье (никогда оба) на основе значения первого.
  • Въезд и выезд из вызов функции (но не между оценками аргументов).

Выражения перед точкой последовательности всегда оцениваются перед выражениями после точки последовательности. В случае оценки короткого замыкания второе выражение может не оцениваться в зависимости от результата первого выражения. Например, в выражении (а() || б()), если первый аргумент имеет ненулевое значение (истина), результатом всего выражения не может быть ничего, кроме истины, поэтому б () не оценивается. Аналогично в выражении (а() && б()), если первый аргумент равен нулю (ложь), результатом всего выражения не может быть ничего, кроме false, поэтому б () не оценивается.

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

 printf("%SS п", argv[я = 0], argv[++я]);

Неопределенное поведение

Аспект стандарта C (не уникальный для C) заключается в том, что поведение определенного кода называется «неопределенным». На практике это означает, что программа, созданная из этого кода, может делать все, что угодно, от работы по замыслу программиста до сбоев при каждом запуске.

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

#включают <stdio.h>int главный(пустота){    int б = 1;    int а = б++ + б++;    printf("% d п", а);}

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

а = б++;а += б++;

Смотрите также

Рекомендации

  1. ^ а б В долго долго модификатор был введен в C99 стандарт.
  2. ^ Значение auto - это спецификатор типа, а не спецификатор класса хранения в C ++ 0x
  3. ^ а б c Клеменс, Бен (2012). 21 век C. O'Reilly Media. ISBN  1449327141.
  4. ^ Балагурусамы, Э. Программирование на ANSI C. Тата МакГроу Хилл. п. 366.
  5. ^ «Препроцессор C: поведение, определяемое реализацией». gcc.gnu.org.
  6. ^ «Строковые и символьные литералы (C ++)». Документация по Visual C ++ 19. Получено 20 ноября 2019.
  7. ^ видеть UTF-8 первый раздел для ссылок
  8. ^ Керниган и Ричи
  9. ^ Татхам, Саймон (2000). "Сопрограммы на C". Получено 2017-04-30.
Общий

внешняя ссылка