Синтаксис C

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

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

В синтаксисе C используется принцип Количество еды.

Содержание

  • 1 Структуры данных
    • 1.1 Примитивные типы данных
      • 1.1.1 Целочисленные типы
      • 1.1.2 Перечислимый тип
      • 1.1.3 Типы с плавающей запятой
      • 1.1.4 Класс хранение спецификаторов
      • 1.1.5 Квалификаторы типов
    • 1.2 Неполные типы
    • 1.3 Указатели
      • 1.3.1 Ссылка на
      • 1.3.2 Разыменование
    • 1.4 Массивы
      • 1.4.1 Определение массива
      • 1.4.2 Доступ к элементам
      • 1.4.3 Массивы длины
      • 1.4.4 Динамические массивы
      • 1.4.5 Многомерные массивы
    • 1.5 Строки
      • 1.5.1 Экраны с обратной косой чертой
      • 1.5.2 Конкатенация строковых литералов
      • 1.5.3 Символьные константы
      • 1.5.4 Широкие символьные строки
      • 1.5.5 Строки форматны ширины
      • 1.5.6 Библиотечные функции
    • 1.6 Структуры и объединения
      • 1.6.1 Структуры
      • 1.6.2 Объединения
      • 1.6.3 Объявление
      • 1.6.4 Доступ к элементам
      • 1.6.5 Назначение
      • 1.6.6 операции
      • 1.6.7 Битовые поля
    • 1.7 Инициализация
      • 1.7.1 Назначенные инициализа торы
      • 1.7.2 Составные литералы
  • 2 Операторы
  • 3 Управляющие структуры
    • 3.1 Составное состояние ments
    • 3.2 Операторы выбора
    • 3.3 Операторы итераций
    • 3.4 Операторы перехода
      • 3.4. 1 Сохранение адреса метки
  • 4 Функции
    • 4.1 Синтаксис
      • 4.1.1 Указатели функций
    • 4.2 Глобальная структура
    • 4.3 Передача аргумента
      • 4.3.1 Параметры массива
  • 5 Разное
    • 5.1 Зарезервированные ключевые слова
    • 5.2 Чувствительность к регистру
    • 5.3 Комментарии
    • 5.4 Аргументы системы строки
    • 5.5 Порядок оценки
    • 5.6 Неопределенное поведение
  • 6 См. Также
  • 7 Ссылки
  • 8 Внешние ссылки

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

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

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

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

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

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

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

Параметры стандартных целочисленных типов
Краткая форма спецификатораМинимальная ширина (биты)
_Bool1
char8
signed char8
unsigned char8
short16
unsigned short16
int16
unsigned int16
long32
unsigned long32
long long64
unsigned long long64

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

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

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

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

Перечислимый тип

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

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

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

enum colors {RED, GREEN, BLUE = 5, YELLOW} paint_color;

Объявляет тип enum colors; константы intRED(значение которых равно 0), GREEN(значение которого на единицу больше, чем RED, 1), СИНИЙ(значение которого равно заданному значению, 5) и ЖЕЛТЫЙ(значение которого на единицу больше, чем СИНИЙ, 6); и enum colorsпеременная paint_color. Константы значения вне контекста перечисления (где разрешено любое целочисленное значение), а, отличные от констант, могут быть присвоены paint_colorили любой другой вариант типа enum colors..

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

struct thing * pt;

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

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

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

typedef struct Bert Bert; typedef struct Wilma Wilma; struct Bert {Wilma * wilma; }; struct Wilma {Bert * bert; };

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

Указатели

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

int * ptr;

Ссылка на

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

int a = 0; int * ptr = a;

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

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

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

int a = 10; int * p; p = a; int b = * p;

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

Массивы

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

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

int array [100];

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

int (* ptr_to_array) [100] = array;

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

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

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

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

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

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

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

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

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

int n =...; int a [n]; а [3] = 10;

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

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

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

#include / * объявляет malloc * /... int * a = malloc (n * sizeof * a); а [3] = 10;

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

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

В качестве меры безопасности некоторые программисты затем устанавливают для переменной-указателя значение NULL:

free (a); a = NULL;

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

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

int (* a) [100] = malloc (sizeof * a);

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

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

(* a) [index]; index [* a];

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

for (int i = 0; i < 100; i++) (*a)[i]; for (int *i = a[0]; i < a[1]; i++) *i;

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

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

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

int array2d [ROWS] [COLUMNS];

где ROWS и COLUMNS - константы. Это определяет двумерный массив. Если читать индексы слева направо, array2d представляет собой массив длиной ROWS, каждый элемент которого представляет собой массив целых чисел COLUMNS.

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

array2d [4] [3]

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

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

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

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

Strings

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

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

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

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

экранирование обратной косой черты

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

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

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

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

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

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

printf (__ FILE__ ":% d: Hello "" мир \ n ", __LINE__);

заменяется на

printf ("helloworld.c" ":% d: Hello" "world \ n", 10);

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

printf ("helloworld.c:% d: Hello world \ n", 10);

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

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

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

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

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

Поскольку тип charимеет ширину 1 байт, одно значение charобычно может представлять не более 255 различных кодов символов, что недостаточно для все персонажи, используемые во всем мире. To provide better support for international characters, the first C standard (C89) introduced wide characters (encoded in type wchar_t) and wide character strings, which are written as L"Hello world!"

Wide characters are most commonly either 2 bytes (using a 2-byte encoding such as UTF-16 ) or 4 bytes (usually UTF-32 ), but Standard C does not specify the width for wchar_t, leaving the choice to the implementor. Microsoft Windows generally uses UTF-16, thus the above string would be 26 bytes long for a Microsoft compiler; the Unix world prefers UTF-32, thus compilers such as GCC would generate a 52-byte string. A 2-byte wide wchar_tsuffers the same limitation as char, in that certain characters (those outside the BMP ) cannot be represented in a single wchar_t; but must be represented using surrogate pairs.

The original C standard specified only minimal functions for operating with wide character strings; in 1995 the standard was modified to include much more extensive support, comparable to that for charstrings. The relevant functions are mostly named after their charequivalents, with the addition of a "w" or the replacement of "str" with "wcs"; they are specified in , with containing wide-character classification and mapping functions.

The now generally recommended method of supporting international characters is through UTF-8, which is stored in chararrays, and can be written directly in the source code if using a UTF-8 editor, because UTF-8 is a direct ASCII extension.

Variable width strings

A common alternative to wchar_tis to use a variable-width encoding, whereby a l Органический символ может занимать несколько позиций в строке. Строки переменной ширины могут быть дословно закодированы в литералы, рискуя запутать компилятор, или с использованием числовых символов обратной косой черты (например, "\ xc3 \ xa9"для "é" в UTF-8). Кодировка UTF-8 была специально разработана (в рамках Plan 9 ) для совместимости со строковыми функциями стандартной библиотеки; Вспомогательные функции кодирования включают отсутствие встроенных нулей, правильную интерпретацию подпоследовательностей и тривиальную ресинхронизацию. Кодировки, в которых отсутствуют эти функции, могут оказаться несовместимыми со стандартными библиотечными функциями; В таких случаях часто используются строковые функции с поддержкой кодирования.

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

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

Структуры и объединения

Структуры

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

Объединения

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

Объявление

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

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

struct s {int x; float y; char * z; } тройник;

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

union u {int x; float y; char * z; } n;

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

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

struct s r;

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

typedef struct {...} s_type;

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

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

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

tee.y

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

struct s * ptr_to_tee = tee;

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

(* ptr_to_tee).y

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

ptr_to_tee->y

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

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

Присвоение

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

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

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

tee.x = 74;

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

ptr_to_tee->x = 74;

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

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

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

Битовые поля

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

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

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

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

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

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

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

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

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

int x = 12; int y = {23}; // Допустимо, без предупреждения int z = {{34}}; // Допустимо, ожидать предупреждения

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

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

struct s {int x; float y; char * z; }; struct s pi = {3, 3,1415, "Пи"};

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

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

struct s pi = {.z = "Pi",.x = 3,.y = 3.1415};

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

int a [MAX] = {1, 3, 5, 7, 9, [MAX-5] = 8, 6, 4, 2, 0};

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

union u value = {3};

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

union u value = {.y = 3.1415};

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

int x = {0, 1, 2};

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

struct {int a [3], b; } w = {[0].a = {1}, [1].a [0] = 2};

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

struct {int a [3], b; } w = {{{1, 0, 0}, 0}, {{2, 0, 0}, 0}};

Нет способа указать повторение инициализатора в стандарте C.

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

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

// указатель создан из литерала массива. int * ptr = (int) {10, 20, 30, 40}; // указатель на массив. float (* foo) [3] = (float) {0.5f, 1.f, -0.5f}; struct s pi = (struct s) {3, 3.1415, "Пи"};

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

pi = (struct s) {.z = "Pi",.x = 3,.y = 3.1415};

Операторы

Управляющие структуры

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

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

Составные операторы

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

{ }

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

Операторы выбора

C имеет два типа операторов выбора : оператор if и оператор switch.

Оператор ifимеет формулу:

if () else 

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

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

переключатель () {case : case : break; По умолчанию: }

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

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

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

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

C имеет три формы оператора итерация :

do while (); пока () для (; ; ) 

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

Выражение :

for (e1; e2; e3) s;

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

e1; while (e2) {s; cont: e3;}

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

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

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

for (int i = 0; i < limit; ++i) { //... }

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

Операторы перехода

Операторы перехода безоговорочно передают управление. Существует типа операторов перехода в C: goto, continue, breakи return.

Оператор gotoвыглядит следующим образом:

goto ;

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

A continueможет появиться только внутри оператора итерации. То есть в каждом из операторов

while (выражение) {/ *... * / cont :;} do {/ *... * / cont :;} while (выражение); для (expr1; expr2; expr 3) {/ *... * / продолжение :; }

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

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

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

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

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

void * ptr = J1; J1: printf ("привет"); goto * ptr;

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

Функции

Синтаксис

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

functionName () {return ; }

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

Если параметров нет, можно оставить пустым или, при желании, указать одним словом void.

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

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

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

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

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

(*)();

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

#include int (* operation) (int x, int y); int add (int x, int y) {вернуть x + y; } int subtract (int x, int y) {вернуть x - y; } int main (int argc, char * args) {int foo = 1, bar = 1; операция = добавить; printf ("% d +% d =% d \ n", foo, bar, operation (foo, bar)); операция = вычитание; printf ("% d -% d =% d \ n", foo, bar, operation (foo, bar)); возврат 0; }

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

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

В объявлениях представлены функции, переменные и типы. Функции C аналогичны подпрограммам Fortran или процедурам Pascal.

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

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

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

int main () {...} int main (void) {...} int main (int argc, char * argv) {...} int main (int argc, char ** argv) {...}

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

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

Минимальная правильная программа на C состоит из пустая процедура main, не принимающая аргументов и ничего не делающая:

int main (void) {} ​​

Поскольку нет оператора return, mainвозвращает 0 при выходе. (Это особая функция, представленная в C99, которая применяется только к main.)

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

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

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

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

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

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

void incInt (int * y) {(* y) ++; // Увеличиваем значение 'x' в 'main' ниже на единицу} int main (void) {int x = 0; incInt (x); // передаем ссылку на переменную 'x' return 0; }

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

int x; scanf ("% d", x);

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

#include #include void allocate_array (int ** const a_p, const int A) {/ * выделить массив целых чисел A, присвоение * a_p изменяет 'a' в main () * / * a_p = malloc (sizeof (int) * A); } int main (void) {int * a; / * создаем указатель на один или несколько целых значений, это будет массив * / / * передать адрес 'a' * / allocate_array (a, 42); / * 'a' теперь является массивом длиной 42, и здесь можно манипулировать и освобождать его * / free (a); возврат 0; }

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

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

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

#include void setArray (int array, int index, int value) {array [index] = value; } int main (void) {int a [1] = {1}; setArray (а, 0, 2); printf ("a [0] =% d \ n", a [0]); возврат 0; }

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

void setArray (int * array, int index, int value)

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

Разное

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

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

auto
_Bool
break
case
char
_Complex
const
continue
default
do
double
else
enum
extern
float
для
goto
if
_Imaginary
inline
int
long
register
restrict
return
short
signed
sizeof
static
struct
switch
typedef
union
unsigned
void
volatile
, в то время как

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

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

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

Комментарии

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

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

Комментарии к строке в стиле C ++ начинаются с //и продолжаются до конца строки. Этот стиль комментариев возник в BCPL и стал допустимым синтаксисом C в C99 ; он недоступен ни в исходном KR C, ни в ANSI C :

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

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

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

myFilt p1 p2 p3

дает что-то вроде:

myFilt\0p1\0p2\0p3\ 0
argv[0ptingargv [1]argv[2ptingargv [3]

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

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

#include int main (int argc, char * argv) {printf ("argc \ t =% d \ n", argc); for (int i = 0; i < argc; i++) printf("argv[%i]\t= %s\n", i, argv[i]); }

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

В любом достаточно сложном выражении возникает выбор относительно порядка, в котором оценивать части выражения: (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 может оценивать выражения в любом порядке между точки последовательности (это позволяет компилятору выполнить некоторую оптимизацию). Точки последовательности определяются следующим образом:

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

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

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

printf ("% s% s \ n", argv [i = 0], argv [++ i]);

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

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

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

#include int main (void) {int b = 1; int a = b ++ + b ++; printf ("% d \ n", а); }

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

a = b ++; а + = b ++;

См. Также

References

General

External links

Последняя правка сделана 2021-05-13 12:46:12
Содержание доступно по лицензии CC BY-SA 3.0 (если не указано иное).
Обратная связь: support@alphapedia.ru
Соглашение
О проекте