Синтаксис языка программирования Си является набор правил , регулирующих написание программного обеспечения на языке C . Он предназначен для использования в программах, которые очень краткие, имеют тесную связь с результирующим объектным кодом и при этом обеспечивают абстракцию данных относительно высокого уровня . C был первым широко успешным языком высокого уровня для разработки переносимых операционных систем .
Синтаксис C использует принцип максимального жевания .
Структуры данных
Примитивные типы данных
В языке C числа представлены в трех формах: целочисленной , действительной и комплексной . Это различие отражает аналогичные различия в архитектуре набора команд большинства центральных процессоров . Целые типы данных хранят числа в наборе целых чисел , а действительные и комплексные числа представляют числа (или пары чисел) в наборе действительных чисел в форме с плавающей запятой .
Все целочисленные типы C имеют signed
и unsigned
варианты. Если signed
или unsigned
не указано явно, в большинстве случаев signed
предполагается. Однако по историческим причинам равнина char
- это тип, отличный от обоих signed char
и unsigned char
. Это может быть тип со знаком или без знака, в зависимости от компилятора и набора символов (C гарантирует, что члены базового набора символов C имеют положительные значения). Кроме того, типы битовых полей, заданные как простые, int
могут быть знаковыми или беззнаковыми, в зависимости от компилятора.
Целочисленные типы
Целочисленные типы C бывают разных фиксированных размеров, способных представлять различные диапазоны чисел. Тип char
занимает ровно один байт (наименьшая адресуемая единица хранения), который обычно имеет ширину 8 бит. (Хотя char
может представлять какой - либо из «основных» символов языка C, более широкий тип может потребоваться для международных наборов символов.) Большинство типов целочисленные оба подписанных и неподписанных сорта, назначаемые этим signed
и unsigned
ключевым словам. Подписанные целочисленные типы могут использовать двоичное дополнение , обратный код , или знак-и магнитуды представление . Во многих случаях существует несколько эквивалентных способов обозначения типа; например, signed short int
и short
являются синонимами.
Представление некоторых типов может включать в себя неиспользуемые биты «заполнения», которые занимают память, но не включены в ширину. В следующей таблице представлен полный список стандартных целочисленных типов и их минимально допустимая ширина (включая любой знаковый бит).
Кратчайшая форма спецификатора | Минимальная ширина (бит) |
---|---|
_Bool | 1 |
char | 8 |
signed char | 8 |
unsigned char | 8 |
short | 16 |
unsigned short | 16 |
int | 16 |
unsigned int | 16 |
long | 32 |
unsigned long | 32 |
long long [1] | 64 |
unsigned long long [1] | 64 |
char
Тип отличается от обоих signed char
и unsigned char
, но гарантированно иметь такое же представление , как один из них. _Bool
И long long
типы унифицированы с 1999 года, и может не поддерживаться старыми компиляторами. Тип _Bool
обычно доступен через typedef
имя, bool
определенное стандартным заголовком stdbool.h .
В общем, ширина и схема представления, реализованная для любой данной платформы, выбирается на основе архитектуры машины, с некоторым учетом простоты импорта исходного кода, разработанного для других платформ. Ширина int
типа особенно широко варьируется среди реализаций C; он часто соответствует наиболее «естественному» размеру слова для конкретной платформы. Стандартный заголовок limits.h определяет макросы для минимальных и максимальных представимых значений стандартных целочисленных типов, реализованных на любой конкретной платформе.
В дополнение к стандартным целочисленным типам могут быть другие «расширенные» целочисленные типы, которые можно использовать для typedef
s в стандартных заголовках. Для более точной спецификации ширины программисты могут и должны использовать typedef
s из стандартного заголовка stdint.h .
Целочисленные константы могут быть указаны в исходном коде несколькими способами. Числовые значения могут быть указаны как десятичные (пример :)1022
, восьмеричные с нулем (0) в качестве префикса ( 01776
) или шестнадцатеричные с 0x (ноль x) в качестве префикса ( 0x3FE
). Символ в одинарных кавычках (пример :) 'R'
, называемый «символьной константой», представляет значение этого символа в наборе символов выполнения с типом int
. За исключением символьных констант, тип целочисленной константы определяется шириной, необходимой для представления указанного значения, но всегда не меньше, чем int
. Это можно изменить, добавив явный модификатор длины и / или подписи; например, 12lu
имеет тип unsigned long
. Нет отрицательных целочисленных констант, но тот же эффект часто можно получить, используя унарный оператор отрицания «-».
Нумерованный тип
Перечислимого типа в С, указанный с enum
ключевым словом, и часто просто называют «перечисление» ( как правило , выраженный ee'-Num /ˌi.nʌm/ или ee'-noom /ˌi.nuːm/), представляет собой тип предназначен для представления значений через серию именованных констант. Каждая из перечисленных констант имеет тип int
. Каждый enum
тип сам по себе совместим с char
целочисленным типом со знаком или без знака, но каждая реализация определяет свои собственные правила для выбора типа.
Некоторые компиляторы предупреждают, если объекту с перечислимым типом присвоено значение, не являющееся одной из его констант. Однако такому объекту могут быть присвоены любые значения в диапазоне их совместимого типа, а enum
константы можно использовать везде, где ожидается целое число. По этой причине enum
значения часто используются вместо #define
директив препроцессора для создания именованных констант. Такие константы, как правило, безопаснее использовать, чем макросы, поскольку они находятся в определенном пространстве имен идентификаторов.
Перечислимый тип объявляется со enum
спецификатором и необязательным именем (или тегом ) для перечисления, за которым следует список из одной или нескольких констант, содержащихся в фигурных скобках и разделенных запятыми, и необязательный список имен переменных. Последующие ссылки на конкретный перечислимый тип используют enum
ключевое слово и имя перечисления. По умолчанию первой константе в перечислении присваивается значение ноль, а каждое последующее значение увеличивается на единицу по сравнению с предыдущей константой. Конкретные значения также могут быть присвоены константам в объявлении, и любые последующие константы без определенных значений будут получать увеличенные значения с этого момента. Например, рассмотрим следующее объявление:
перечислить цвета { КРАСНЫЙ , ЗЕЛЕНЫЙ , СИНИЙ = 5 , ЖЕЛТЫЙ } paint_color ;
Это объявляет enum colors
тип; эти int
константы RED
(значение которого равно 0), GREEN
(значение которого на единицу больше , чем RED
, 1), BLUE
(значение которого является данное значение, 5), и YELLOW
(значение которого на единицу больше , чем BLUE
, 6); и enum colors
переменная paint_color
. Константы могут использоваться вне контекста перечисления (где разрешено любое целочисленное значение), и значения, отличные от констант, могут быть присвоены paint_color
или любой другой переменной типа enum colors
.
Типы с плавающей запятой
Форма с плавающей запятой используется для представления чисел с дробной составляющей. Однако они не представляют в точности наиболее рациональные числа; вместо этого они являются близким приближением. Существует три типа реальных значений, обозначаемых их спецификаторами: одинарная точность ( float
), двойная точность ( double
) и двойная расширенная точность ( long double
). Каждый из них может представлять значения в другой форме, часто в одном из форматов с плавающей запятой IEEE .
Спецификаторы типа | Точность (десятичные цифры) | Диапазон экспоненты | ||
---|---|---|---|---|
Минимум | IEEE 754 | Минимум | IEEE 754 | |
float | 6 | 7.2 (24 бит) | ± 37 | ± 38 (8 бит) |
double | 10 | 15,9 (53 бит) | ± 37 | ± 307 (11 бит) |
long double | 10 | 34,0 (113 бит) | ± 37 | ± 4931 (15 бит) |
Константы с плавающей запятой могут быть записаны в десятичной системе счисления , например 1.23
. Десятичное научное представление может использоваться путем добавления e
или E
последующего десятичного показателя степени, также известного как обозначение E , например 1.23e2
(которое имеет значение 1,23 × 10 2 = 123,0). Требуется либо десятичная точка, либо показатель степени (в противном случае число анализируется как целочисленная константа). Шестнадцатеричные константы с плавающей запятой подчиняются аналогичным правилам, за исключением того, что они должны иметь префикс 0x
и использовать p
или P
для указания двоичной экспоненты, например 0xAp-2
(который имеет значение 2,5, поскольку A h × 2 −2 = 10 × 2 −2 = 10 ÷ 4 ). Как десятичные, так и шестнадцатеричные константы с плавающей запятой могут быть дополнены суффиксом f
или F
для обозначения константы типа float
, l
(буквой l
) или L
для обозначения типа long double
, или оставлены без суффикса для double
константы.
Стандартный заголовочный файл float.h
определяет минимальные и максимальные значения типов с плавающей точкой по осуществлению, float
, double
и long double
. Он также определяет другие ограничения, относящиеся к обработке чисел с плавающей запятой.
Спецификаторы класса хранения
У каждого объекта есть класс хранения. В основном это определяет продолжительность хранения , которая может быть статической (по умолчанию для глобальной), автоматической (по умолчанию для локальной) или динамической (выделенной) вместе с другими функциями (привязка и подсказка по регистру).
Спецификаторы | Продолжительность жизни | Сфера | Инициализатор по умолчанию |
---|---|---|---|
auto | Блок (стек) | Блокировать | Неинициализированный |
register | Блок (стек или регистр ЦП) | Блокировать | Неинициализированный |
static | Программа | Блок или единица компиляции | Нуль |
extern | Программа | Глобальный (вся программа) | Нуль |
(нет) 1 | Динамический (куча) | Неинициализировано (инициализировано, 0 если используется calloc() ) |
- 1 Выделено и освобождаться , используя
malloc()
иfree()
функции библиотеки.
Переменные, объявленные в блоке по умолчанию, имеют автоматическое хранение, как и переменные, явно объявленные с помощью спецификаторов [2] или класса хранения. Спецификаторы и могут использоваться только в функциях и объявлениях аргументов функций; как таковой спецификатор всегда избыточен. Объекты, объявленные вне всех блоков и явно объявленные с помощью спецификатора класса хранения, имеют статическую продолжительность хранения. Статические переменные по умолчанию инициализируются компилятором равными нулю .auto
register
auto
register
auto
static
Объекты с автоматическим хранением являются локальными по отношению к блоку, в котором они были объявлены, и отбрасываются при выходе из блока. Кроме того, объектам, объявленным с register
классом хранения, компилятор может дать более высокий приоритет для доступа к регистрам ; хотя компилятор может решить не сохранять ни один из них в регистре. Объекты с этим классом хранения нельзя использовать с &
унарным оператором address-of ( ). Объекты со статической памятью сохраняются на протяжении всей программы. Таким образом, функция может получить доступ к одному и тому же объекту через несколько вызовов. Объекты с выделенной продолжительностью хранения создаются и уничтожаются в явном виде с malloc
, free
и связанными с ними функциями.
Спецификатор extern
класса хранилища указывает, что хранилище для объекта было определено в другом месте. При использовании внутри блока это указывает, что хранилище было определено объявлением вне этого блока. При использовании вне всех блоков это указывает на то, что хранилище было определено вне единицы компиляции. Спецификатор extern
класса хранения является избыточным при использовании в объявлении функции. Это указывает на то, что объявленная функция была определена вне модуля компиляции.
Обратите внимание, что спецификаторы хранилища применяются только к функциям и объектам; другие вещи, такие как объявления типа и перечисления, являются частными для модуля компиляции, в котором они появляются. С другой стороны, типы имеют квалификаторы (см. Ниже).
Квалификаторы типа
Типы могут быть квалифицированы для указания особых свойств их данных. Квалификатор типа const
указывает, что значение не изменяется после инициализации. Попытка изменить const
квалифицированное значение приводит к неопределенному поведению, поэтому некоторые компиляторы C сохраняют их в роданных или (для встроенных систем) в постоянной памяти (ROM). Квалификатор типа volatile
указывает оптимизирующему компилятору, что он не может удалять явно избыточные операции чтения или записи, поскольку значение может измениться, даже если оно не было изменено каким-либо выражением или оператором, или может потребоваться несколько операций записи, например, для отображаемого в память I / вывода .
Неполные типы
Неполный тип - это тип структуры или объединения , члены которого еще не указаны, тип массива , размерность которого еще не определена, или 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 ] = & массив ;
Доступ к элементам
Основным средством доступа к значениям элементов массива является оператор индекса массива. Чтобы получить доступ к i -индексированному элементу массива , синтаксис будет иметь вид array[i]
, который относится к значению, хранящемуся в этом элементе массива.
Нумерация индексов массива начинается с 0 (см. Индексирование с нуля ). Таким образом, наибольший допустимый индекс массива равен количеству элементов в массиве минус 1. Чтобы проиллюстрировать это, рассмотрим объявленный массив a как имеющий 10 элементов; первый элемент будет a[0]
и последний элемент будет a[9]
.
C не предоставляет возможности автоматической проверки границ для использования массива. Хотя логически последний индекс в массиве из 10 элементов будет 9, индексы 10, 11 и т. Д. Могут быть случайно указаны с неопределенным результатом.
Поскольку массивы и указатели взаимозаменяемы, адреса каждого из элементов массива могут быть выражены в эквивалентной арифметике указателей . В следующей таблице показаны оба метода для существующего массива:
Элемент | Первый | Второй | В третьих | п- е |
---|---|---|---|---|
Индекс массива | array[0] | array[1] | array[2] | array[n - 1] |
Разыменованный указатель | *array | *(array + 1) | *(array + 2) | *(array + n - 1) |
Поскольку выражение a[i]
семантически эквивалентно *(a+i)
, что, в свою очередь, эквивалентно *(i+a)
, выражение также можно записать как i[a]
, хотя эта форма используется редко.
Массивы переменной длины
C99 стандартизованные массивы переменной длины (VLA) в пределах блока. Такие переменные массива выделяются на основе значения целочисленного значения во время выполнения при входе в блок и освобождаются в конце блока. [3] Начиная с C11, компилятор больше не требует реализации этой функции.
int n = ...; int a [ n ]; а [ 3 ] = 10 ;
Этот синтаксис создает массив, размер которого фиксирован до конца блока.
Динамические массивы
Массивы которые могут быть изменены динамически могут быть получены с помощью стандартной библиотеки C . malloc
Функция обеспечивает простой способ выделения памяти. Требуется один параметр: объем выделяемой памяти в байтах. После успешного выделения malloc
возвращает void
значение указателя generic ( ), указывающее на начало выделенного пространства. Возвращаемое значение указателя неявно преобразуется в соответствующий тип путем присваивания. Если выделение не может быть завершено, malloc
возвращает нулевой указатель . Следовательно, следующий сегмент аналогичен по функциям приведенному выше желаемому объявлению:
#include / * объявляет malloc * /... int * a = malloc ( n * sizeof * a ); а [ 3 ] = 10 ;
Результатом является «указатель на int
» переменную ( а ), которая указывает на первый из n смежных int
объектов; из-за эквивалентности указателя массива его можно использовать вместо фактического имени массива, как показано в последней строке. Преимущество использования этого динамического распределения заключается в том, что объем выделяемой ему памяти может быть ограничен тем, что действительно необходимо во время выполнения, и это может быть изменено по мере необходимости (с помощью стандартной библиотечной функции realloc).
Когда динамически выделяемая память больше не нужна, ее следует вернуть системе времени выполнения. Это делается с помощью вызова free
функции. Он принимает единственный параметр: указатель на ранее выделенную память. Это значение, которое было возвращено предыдущим вызовом malloc
.
В качестве меры безопасности некоторые программисты [ кто? ] затем установите для переменной-указателя значение NULL
:
бесплатно ( а ); a = NULL ;
Это гарантирует, что дальнейшие попытки разыменования указателя на большинстве систем приведут к сбою программы. Если этого не сделать, переменная становится висящим указателем, что может привести к ошибке использования после освобождения. Однако, если указатель является локальной переменной, установка для NULL
нее значения не запрещает программе использовать другие копии указателя. Статические анализаторы обычно легко распознают ошибки локального использования после устранения ошибок . Следовательно, этот подход менее полезен для локальных указателей и чаще используется с указателями, хранящимися в долгоживущих структурах. Однако в целом установка указателей на NULL
- это хорошая практика [ по мнению кого? ], поскольку он позволяет программисту NULL
проверять указатели перед разыменованием, что помогает предотвратить сбои.
Вспоминая пример с массивом, можно также создать массив фиксированного размера с помощью динамического распределения:
int ( * a ) [ 100 ] = malloc ( sizeof * a );
... Что дает указатель на массив.
Доступ к указателю на массив можно осуществить двумя способами:
( * a ) [ индекс ];index [ * a ];
Итерацию также можно выполнить двумя способами:
для ( int я = 0 ; я < 100 ; я ++ ) ( * а ) [ я ];для ( int * я = а [ 0 ]; я < а [ 1 ]; я ++ ) * я ;
Преимущество использования второго примера заключается в том, что числовое ограничение первого примера не требуется, что означает, что указатель на массив может иметь любой размер, а второй пример может выполняться без каких-либо изменений.
Многомерные массивы
Кроме того, C поддерживает массивы нескольких измерений, которые хранятся в строчном порядке . Технически многомерные массивы C - это просто одномерные массивы, элементами которых являются массивы. Синтаксис объявления многомерных массивов следующий:
ИНТ array2d [ ROWS ] [ КОЛОНКИ ];
где 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
.
Струны
В C строковые литералы заключаются в двойные кавычки ( "
), например "Hello world!"
, компилируются в массив указанных char
значений с дополнительным кодом завершающего нулевого символа ( с нулевым значением) для обозначения конца строки.
Строковые литералы не могут содержать встроенные символы новой строки; этот запрет несколько упрощает синтаксический анализ языка. Чтобы включить новую строку в строку, можно использовать обратную косую черту \n
, как показано ниже.
Существует несколько стандартных библиотечных функций для работы со строковыми данными (не обязательно константами), организованными в виде массива с char
использованием этого формата с завершающим нулем; см. ниже .
Синтаксис строкового литерала C оказал большое влияние и проник во многие другие языки, такие как C ++, Objective-C, Perl, Python, PHP, Java, Javascript, C #, Ruby. В настоящее время почти все новые языки используют строковый синтаксис в стиле C. Языки, в которых отсутствует этот синтаксис, как правило, предшествуют C.
Обратная косая черта экранируется
Если вы хотите включить двойные кавычки внутри строки, это можно сделать, экранировав ее с помощью обратной косой черты ( \
), например "This string contains \"double quotes\"."
. Чтобы вставить буквальную обратную косую черту, нужно ее удвоить, например "A backslash looks like this: \\"
.
Обратные косые черты могут использоваться для ввода управляющих символов и т. Д. В строку:
Побег | Имея в виду |
---|---|
\\ | Буквальная обратная косая черта |
\" | Двойная кавычка |
\' | Одиночная цитата |
\n | Новая строка (перевод строки) |
\r | Возврат каретки |
\b | Backspace |
\t | Горизонтальная вкладка |
\f | Подача формы |
\a | Предупреждение (звонок) |
\v | Вертикальная табуляция |
\? | Вопросительный знак (используется для экранирования триграфов ) |
%% | Знак процента, только строки формата printf (примечание \% нестандартно и не всегда распознается) |
\OOO | Знак с восьмеричным значением ООО (где ООО - это 1-3 восьмеричные цифры, '0' - '7') |
\xHH | Символ с шестнадцатеричным значением HH (где HH - это 1 или более шестнадцатеричных цифр, '0' - '9', 'A' - 'F', 'a' - 'f') |
Использование других escape-символов обратной косой черты не определено стандартом C, хотя поставщики компиляторов часто предоставляют дополнительные escape-коды в качестве языковых расширений. Одна из них - это escape-последовательность \e
для escape-символа с шестнадцатеричным значением ASCII 1B, который не был добавлен в стандарт C из-за отсутствия представления в других наборах символов (например, EBCDIC ). Он доступен в GCC , clang и tcc .
Конкатенация строковых литералов
C имеет конкатенацию строковых литералов , что означает, что смежные строковые литералы объединяются во время компиляции; это позволяет разбивать длинные строки на несколько строк, а также позволяет добавлять строковые литералы, полученные в результате определений препроцессора C, и макросы к строкам во время компиляции:
printf ( __FILE__ ":% d: Hello" "мир \ n " , __LINE__ );
расширится до
printf ( "helloworld.c" ":% d: Hello" "мир \ 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 , где комбинация компиляторов Classic Mac OS и присущая ему обратная последовательность байтов означает, что байты целого числа появляются в точном порядке символов, определенных в литерале. Определение популярной «реализация», на самом деле соответствуют: в GCC, Clang и Visual C ++ , '1234'
выходы под ASCII. [5] [6]0x31323334
Строки широких символов
Поскольку тип char
имеет ширину 1 байт, одно char
значение обычно может представлять не более 255 различных кодов символов, что недостаточно для всех символов, используемых во всем мире. Чтобы обеспечить лучшую поддержку международных символов, первый стандарт C (C89) представил широкие символы (закодированные в типе wchar_t
) и строки широких символов, которые записываются какL"Hello world!"
Широкие символы обычно имеют 2 байта (с использованием 2-байтовой кодировки, такой как UTF-16 ) или 4 байта (обычно UTF-32 ), но стандарт C не определяет ширину wchar_t
, оставляя выбор разработчику. Microsoft Windows обычно использует UTF-16, таким образом, указанная выше строка будет иметь длину 26 байт для компилятора Microsoft; Unix , мир предпочитает UTF-32, таким образом , компиляторы , такие как GCC , будет генерировать строку 52 байт. 2-байтовый размер wchar_t
имеет то же ограничение char
, что и определенные символы (те, которые не входят в BMP ) не могут быть представлены в виде одного wchar_t
; но должны быть представлены с помощью суррогатных пар .
Исходный стандарт C определял только минимальные функции для работы с широкими символьными строками; в 1995 году стандарт был изменен, чтобы включить гораздо более обширную поддержку, сравнимую с поддержкой char
струнных. Соответствующие функции в основном названы в честь их char
эквивалентов с добавлением «w» или заменой «str» на «wcs»; они указаны в
,
содержащем функции классификации и отображения широких символов.
В настоящее время обычно рекомендуемый метод [7] поддержки международных символов - через UTF-8 , который хранится в char
массивах и может быть записан непосредственно в исходный код при использовании редактора UTF-8, поскольку UTF-8 является прямым расширением ASCII. .
Струны переменной ширины
Распространенной альтернативой wchar_t
является использование кодировки переменной ширины , при которой логический символ может занимать несколько позиций в строке. Строки переменной ширины могут быть дословно закодированы в литералы, рискуя запутать компилятор, или с использованием числовых символов обратной косой черты (например, "\xc3\xa9"
для «é» в UTF-8). UTF-8 кодировка была специально разработана ( в соответствии с планом 9 ) для совместимости со стандартной библиотеки строковых функций; Вспомогательные функции кодирования включают отсутствие встроенных нулей, правильную интерпретацию подпоследовательностей и тривиальную ресинхронизацию. Кодировки, в которых отсутствуют эти функции, могут оказаться несовместимыми со стандартными библиотечными функциями; В таких случаях часто используются строковые функции с поддержкой кодирования.
Библиотечные функции
Строками , как постоянными, так и переменными, можно управлять без использования стандартной библиотеки . Однако библиотека содержит множество полезных функций для работы со строками с завершающим нулем.
Структуры и союзы
Структуры
Структуры и объединения в C определяются как контейнеры данных, состоящие из последовательности именованных элементов различных типов. Они похожи на записи на других языках программирования. Члены структуры хранятся в последовательных местах в памяти, хотя компилятору разрешено вставлять отступы между членами или после них (но не перед первым членом) для эффективности или в качестве заполнения, необходимого для правильного выравнивания целевой архитектурой. Размер структуры равен сумме размеров ее членов плюс размер отступа.
Союзы
Объединения в C связаны со структурами и определяются как объекты, которые могут содержать (в разное время) объекты разных типов и размеров. Они аналогичны вариантным записям в других языках программирования. В отличие от структур, все компоненты объединения относятся к одному и тому же месту в памяти. Таким образом, объединение может использоваться в разное время для хранения разных типов объектов без необходимости создавать отдельный объект для каждого нового типа. Размер объединения равен размеру его самого большого типа компонента.
Декларация
Структуры объявляются с помощью struct
ключевого слова, а объединения объявляются с помощью union
ключевого слова. За ключевым словом спецификатора следует необязательное имя идентификатора, которое используется для идентификации формы структуры или объединения. За идентификатором следует объявление структуры или тела объединения: список объявлений членов, содержащихся в фигурных скобках, где каждое объявление заканчивается точкой с запятой. Наконец, объявление завершается дополнительным списком имен идентификаторов, которые объявляются как экземпляры структуры или объединения.
Например, следующий оператор объявляет названную структуру s
, содержащую три члена; он также объявит экземпляр структуры, известной как tee
:
struct s { int x ; float y ; char * z ; } тройник ;
И следующий оператор объявит похожее объединение с именем u
и именем его экземпляра n
:
объединение 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 , известный как ptr_to_tee :
struct s * ptr_to_tee = & tee ;
Затем можно получить доступ к члену y элемента tee путем разыменования ptr_to_tee и использования результата в качестве левого операнда:
( * ptr_to_tee ). у
Что идентично более простому, tee.y
приведенному выше, если ptr_to_tee указывает на 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 сверху:
тройник . х = 74 ;
И то же назначение с использованием ptr_to_tee вместо tee будет выглядеть так:
ptr_to_tee -> x = 74 ;
Назначение с членами профсоюзов идентично.
Прочие операции
Согласно стандарту C единственные допустимые операции, которые могут выполняться над структурой, - это ее копирование, присвоение ей как единицы (или ее инициализация), получение ее адреса с помощью &
унарного оператора address-of ( ) и доступ к ее членам. . У профсоюзов такие же ограничения. Одна из операций неявно запретных является сравнение: структуры и объединения не могут быть сравнены с использованием стандартных средств сравнения языка C ( ==
, >
, <
и т.д.).
Битовые поля
C также предоставляет особый тип элемента структуры, известный как битовое поле , которое представляет собой целое число с явно указанным количеством битов. Битовое поле объявляется как член структуры типа int
, signed int
, unsigned int
, или _Bool
, следуя имя элемента двоеточием ( :
) , и число битов , он должен занимать. Общее количество битов в одном битовом поле не должно превышать общее количество бит в его объявленном типе.
В качестве особого исключения из обычных правил синтаксиса C определяется реализацией, является ли битовое поле, объявленное как тип int
, без указания signed
или unsigned
, со знаком или без знака. Таким образом, рекомендуется явно указать signed
или unsigned
на всех элементах структуры для переносимости.
Также разрешены безымянные поля, состоящие только из двоеточия, за которым следует несколько бит; они указывают на заполнение . Указание нулевой ширины для безымянного поля используется для принудительного выравнивания по новому слову. [8]
Члены битовых полей не имеют адресов и, как таковые, не могут использоваться с &
унарным оператором address-of ( ). sizeof
Оператор не может быть применен к битовым полям.
Следующее объявление объявляет новый тип структуры, известный как, f
и его экземпляр, известный как g
. Комментарии содержат описание каждого из участников:
struct f { unsigned int flag : 1 ; / * битовый флаг: может быть включен (1) или выключен (0) * / signed int num : 4 ; / * подписанное 4-битное поле; диапазон -7 ... 7 или -8 ... 7 * / целое число со знаком : 3 ; / * 3 бита заполнения для округления до 8 бит * / } g ;
Инициализация
Инициализация по умолчанию зависит от спецификатора класса хранения , описанного выше.
Из-за грамматики языка скалярный инициализатор может быть заключен в любое количество пар фигурных скобок. Однако большинство компиляторов выдают предупреждение, если таких пар несколько.
int x = 12 ; int y = { 23 }; // Допустимо, без предупреждения int z = { { 34 } }; // Законно, ожидайте предупреждения
Структуры, объединения и массивы можно инициализировать в своих объявлениях с помощью списка инициализаторов. Если не используются указатели, компоненты инициализатора соответствуют элементам в том порядке, в котором они определены и сохранены, поэтому все предыдущие значения должны быть предоставлены перед любым конкретным значением элемента. Любые неуказанные элементы обнуляются (кроме объединений). Упоминание слишком большого количества значений инициализации приводит к ошибке.
Следующий оператор инициализирует новый экземпляр структуры S , известную как пи :
struct s { int x ; float y ; char * z ; };struct s pi = { 3 , 3,1415 , "Пи" };
Назначенные инициализаторы
Назначенные инициализаторы позволяют инициализировать элементы по имени в любом порядке и без явного указания предшествующих значений. Следующая инициализация эквивалентна предыдущей:
struct s pi = { . г = "Пи" , . х = 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 объединение было инициализировано одним значением, примененным к его первому члену. То есть для указанного выше объединения u может быть инициализирован только член int x :
объединение u значение = { 3 };
При использовании назначенного инициализатора инициализируемый член не обязательно должен быть первым членом:
объединение u значение = { . 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 ]. а = { 1 }, [ 1 ]. а [ 0 ] = 2 };
Это эквивалентно:
struct { int a [ 3 ], b ; } Ш [] = { { { 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 , "Пи" };
Составные литералы часто объединяются с назначенными инициализаторами, чтобы сделать объявление более читаемым: [3]
пи = ( структура s ) { . г = "Пи" , . х = 3 , . y = 3,1415 };
Операторы
Структуры управления
C - это язык свободной формы .
Стиль поддержки варьируется от программиста к программисту и может быть предметом споров. Подробнее см. Стиль отступа .
Составные заявления
В элементах этого раздела любой
{ < необязательная - декларация - список > < необязательная - инструкция - список > }
и используются как тело функции или в любом месте, где ожидается одиночный оператор. Список-деклараций объявляет переменные, которые будут использоваться в этой области , а список-операторов - действия, которые необходимо выполнить. Скобки определяют свою собственную область видимости, и переменные, определенные внутри этих скобок, будут автоматически освобождены в закрывающей скобке. Объявления и операторы можно свободно смешивать в составном операторе (как в C ++ ).
Заявления о выборе
В C есть два типа операторов выбора : ifоператор и switchоператор .
if
Утверждение в виде:
if ( < выражение > ) < оператор1 > else < выражение2 >
В if
операторе, если значение
в круглых скобках не равно нулю (истина), управление переходит к
. Если else
предложение присутствует и значение
равно нулю (ложь), управление перейдет к
. Эта else
часть является необязательной, и, если она отсутствует, false
просто приведет к пропуску
. else
Всегда совпадает с ближайшим предыдущим непревзойденным if
; фигурные скобки могут использоваться для отмены этого при необходимости или для ясности.
В switch
заявлении причины управления должны быть переданы одному из нескольких операторов в зависимости от значения в выражение , которое должно иметь целочисленный тип . Подразделение, управляемое переключателем, обычно является составным. Любой оператор в подвыложении может быть помечен одной или несколькими case
метками, которые состоят из ключевого слова, case
за которым следует постоянное выражение, а затем двоеточие (:). Синтаксис следующий:
switch ( < выражение > ) { case < label1 > : < операторы 1 > case < label2 > : < операторы 2 > break ; по умолчанию : < операторы 3 > }
Никакие две константы case, связанные с одним и тем же переключателем, не могут иметь одинаковое значение. С default
переключателем может быть связано не более одной метки. Если ни одна из меток case не равна выражению в следующих скобках switch
, управление передается default
метке или, если default
метки нет , выполнение возобновляется сразу за пределами всей конструкции.
Переключатели могут быть вложенными; case
или default
метка связана с сокровенным , switch
который его содержит. Операторы Switch могут «проваливаться», то есть, когда один раздел case завершил свое выполнение, операторы будут продолжать выполняться вниз, пока break;
не встретится оператор. Падение полезно при некоторых обстоятельствах, но обычно нежелательно. В предыдущем примере, если
достигнуто,
выполняются операторы и больше ничего внутри фигурных скобок. Однако, если
достигается, оба
и
выполняются, поскольку нет break
разделения двух операторов case.
Возможно, хотя и необычно, вставлять switch
метки в подблоки других структур управления. Примеры этого включают устройство Даффа и реализацию сопрограмм Саймона Тэтэма в Putty . [9]
Операторы итерации
В C есть три формы итерационного оператора:
do < оператор > while ( < выражение > ) ;while ( < выражение > ) < оператор >for ( < выражение > ; < выражение > ; < выражение > ) < выражение >
В операторах whileи do
подоператор выполняется многократно до тех пор, пока значение expression
остается ненулевым (эквивалентно истине). С while
, тест, включая все побочные эффекты из
, происходит перед каждой итерацией (выполнением
); с do
, проверка выполняется после каждой итерации. Таким образом, do
оператор всегда выполняет свой подоператор, по крайней мере, один раз, в то время как он while
может не выполняться вообще.
Заявление:
для ( e1 ; e2 ; e3 ) s ;
эквивалентно:
e1 ; в то время как ( e2 ) { s ; продолжение : e3 ; }
за исключением поведения continue;
оператора (который в for
цикле переходит к e3
вместо e2
). Если e2
поле пусто, его необходимо заменить на 1
.
Любое из трех выражений в for
цикле может быть опущено. Отсутствие второго выражения делает while
тест всегда ненулевым, создавая потенциально бесконечный цикл.
Начиная с C99 , первое выражение может принимать форму объявления, обычно включающего инициализатор, например:
for ( int i = 0 ; i < limit ; ++ i ) { // ... }
Объем объявления ограничен размером for
цикла.
Заявления о прыжках
Операторы перехода безоговорочно передают управление. Есть четыре типа операторов перехода в C: goto, continue
, break
, и return.
goto
Заявление выглядит следующим образом :
goto < идентификатор > ;
Идентификатор должен быть ярлык ( с последующим двоеточием) , расположенной в текущей функции. Управление переходит к помеченной выписке.
continue
Утверждение может появиться только в пределах заявления итерации и причины управления , чтобы перейти к петлевому продолжению части самых внутреннему заключающей итерации утверждения. То есть в каждом из утверждений
в то время как ( выражение ) { / * ... * / продолжение : ; }делать { / * ... * / продолжение : ; } while ( выражение );для ( expr1 ; expr2 ; expr3 ) { / * ... * / продолжение : ; }
a, continue
не содержащийся во вложенном операторе итерации, совпадает с goto cont
.
Оператор break
используется для завершения for
цикла, while
цикла, do
цикла или switch
оператора. Управление передается оператору, следующему за завершенным оператором.
Функция возвращается к вызывающему с помощью return
оператора. Когда return
следует выражение, значение возвращается вызывающей стороне как значение функции. Встреча с концом функции эквивалентна return
без выражения. В этом случае, если функция объявлена как возвращающая значение и вызывающая сторона пытается использовать возвращаемое значение, результат не определен.
Сохранение адреса этикетки
GCC расширяет язык C с помощью унарного &&
оператора, который возвращает адрес метки. Этот адрес может быть сохранен в виде void*
переменной и может использоваться позже в goto
инструкции. Например, следующая печать "hi "
в бесконечном цикле:
void * ptr = && J1 ;J1 : printf ( "привет" ); goto * ptr ;
Эта функция может использоваться для реализации таблицы переходов .
Функции
Синтаксис
Определение функции AC состоит из типа возвращаемого значения ( void
если значение не возвращается), уникального имени, списка параметров в круглых скобках и различных операторов:
< Возврат - тип > FunctionName ( < параметр - список > ) { < заявления > возврат < выражение из типа возврата - тип > ; }
Функция с void
невозвратным типом должна включать хотя бы один return
оператор. Параметры задаются
, разделенный запятыми список объявлений параметров, каждый элемент в списке , являющегося типом данных с последующим идентификатором:
.
Если параметров нет,
можно оставить пустым или, при желании, указать одно слово void
.
Можно определить функцию как принимающую переменное количество параметров, указав ...
ключевое слово в качестве последнего параметра вместо типа данных и идентификатора переменной. Часто используемая функция, которая делает это, - это стандартная библиотечная функция printf
, которая имеет объявление:
int printf ( const char * , ...);
Манипулировать этими параметрами можно с помощью подпрограмм в заголовке стандартной библиотеки .
Указатели функций
Указатель на функцию можно объявить следующим образом:
< возврат - тип > ( * < функция - имя > ) ( < параметр - список > );
Следующая программа показывает использование указателя функции для выбора между сложением и вычитанием:
#include int ( * операция ) ( 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 сродни подпрограммам Фортрана или процедурам Паскаля .
Определение представляет собой особый тип декларации. Определение переменной выделяет память и, возможно, инициализирует ее, определение функции предоставляет ее тело.
Реализация 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()
). Возвращаемое значение main
(которое должно быть int
) служит статусом завершения, возвращаемым в среду хоста.
Стандарт C определяет возвращаемые значения 0
и EXIT_SUCCESS
как указывающие на успех, и EXIT_FAILURE
как на отказ. ( EXIT_SUCCESS
и EXIT_FAILURE
определены в ). Другие возвращаемые значения имеют значение, определяемое реализацией; например, в Linux программа, завершенная сигналом, дает код возврата, состоящий из числового значения сигнала плюс 128.
Минимальная правильная программа на C состоит из пустой main
процедуры, не принимающей аргументов и ничего не делающей:
int main ( void ) {}
Поскольку return
оператора нет, main
при выходе возвращает 0. [3] (Это особая функция, представленная в C99 и применимая только к main
.)
main
Функция, как правило , вызывать другие функции , чтобы помочь ему выполнить свою работу.
Некоторые реализации не размещаются, обычно потому, что они не предназначены для использования с операционной системой . Такие реализации называются автономными в стандарте C. Отдельная реализация может свободно определять, как она обрабатывает запуск программы; в частности, для определения main
функции не требуется программа .
Функции могут быть написаны программистом или предоставлены существующими библиотеками. Интерфейсы для последних обычно объявляются путем включения файлов заголовков - с помощью #include
директивы предварительной обработки - и объекты библиотеки связаны в окончательный исполняемый образ. Некоторые библиотечные функции, такие как printf, определены стандартом C; они называются стандартными библиотечными функциями.
Функция может возвращать значение вызывающей стороне (обычно другая функция C или среда размещения для функции main
). printf
Функция упоминалась выше доходностей , как были напечатаны много символов, но это значение часто игнорируются.
Передача аргумента
В C аргументы передаются функциям по значению, в то время как другие языки могут передавать переменные по ссылке . Это означает, что принимающая функция получает копии значений и не имеет прямого способа изменить исходные переменные. Чтобы функция изменила переменную, переданную из другой функции, вызывающий должен передать свой адрес ( указатель на него), который затем может быть разыменован в принимающей функции. См. Указатели для получения дополнительной информации.
пустота 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 недействительный allocate_array ( INT ** Const A_P , Const INT ) { / * выделить массив целых чисел A присваивание * A_P изменяет 'а' в главном () * / * A_P = таНос ( SizeOf ( INT ) * ); } int main ( void ) { int * a ; / * создаем указатель на один или несколько int, это будет массив * / / * передаем адрес 'a' * / allocate_array ( & a , 42 ); / * 'a' теперь является массивом длиной 42, и здесь можно манипулировать и освобождать его * / бесплатно ( а ); возврат 0 ; }
Параметр int **a_p
представляет собой указатель на указатель на объект int
, который в данном случае является адресом указателя, p
определенного в основной функции.
Параметры массива
Функциональные параметры типа массива могут на первый взгляд показаться исключением из правила передачи по значению языка Си. Следующая программа напечатает 2, а не 1:
#include void setArray ( int array [], int index , int value ) { массив [ индекс ] = значение ; }int main ( void ) { int a [ 1 ] = { 1 }; setArray ( а , 0 , 2 ); printf ( "a [0] =% d \ n " , a [ 0 ]); возврат 0 ; }
Однако есть другая причина такого поведения. Фактически, параметр функции, объявленный с типом массива, рассматривается как параметр, объявленный как указатель. То есть предыдущее объявление setArray
эквивалентно следующему:
аннулируются setArray ( INT * массив , INT индекс , INT значение )
В то же время правила C для использования массивов в выражениях приводят к преобразованию значения a
в вызове в setArray
указатель на первый элемент массива a
. Таким образом, на самом деле это все еще пример передачи по значению с оговоркой, что это адрес первого элемента массива, передаваемого по значению, а не содержимое массива.
Разнообразный
Зарезервированные ключевые слова
Следующие слова зарезервированы и не могут использоваться в качестве идентификаторов:
|
|
|
|
Реализации могут зарезервировать другие ключевые слова, например asm
, хотя реализации обычно предоставляют нестандартные ключевые слова, которые начинаются с одного или двух знаков подчеркивания.
Чувствительность к регистру
Идентификаторы C чувствительны к регистру (например, foo
, FOO
, и Foo
имена различных объектов). Некоторые компоновщики могут сопоставлять внешние идентификаторы с одним случаем, хотя в большинстве современных компоновщиков это нечасто.
Комментарии
Текст, начинающийся с токена /*
, рассматривается как комментарий и игнорируется. Комментарий заканчивается следующим */
; это может происходить внутри выражений и может занимать несколько строк. Случайное пропускание признака конца комментария проблематично, поскольку правильно построенный признак конца комментария следующего комментария будет использоваться для завершения начального комментария, а весь код между комментариями будет считаться комментарием. Комментарии в стиле C не вкладываются; то есть случайное размещение комментария в комментарии приводит к непредвиденным результатам:
/ *Эта строка будет проигнорирована./ *Здесь может появиться предупреждение компилятора. Эти строки также будут проигнорированы.Токен открытия комментария выше не запускал новый комментарий,а токен закрытия комментария ниже закроет комментарий, начатый в строке 1.* /Эта линия и линия ниже нее будут не будут игнорироваться . Оба будут , вероятно , производить компиляции ошибок . * /
Комментарии к строке в стиле 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]
, как показано в следующей программе:
#include int main ( int argc , char * argv []) { printf ( "argc \ t =% d \ n " , argc ); для ( int i = 0 ; i < argc ; i ++ ) printf ( "argv [% i] \ t =% s \ n " , i , argv [ i ]); }
Порядок оценки
В любом достаточно сложном выражении, возникает выбор , как в порядке , в котором для оценки части выражения: могут быть оценены в порядке , , , , или в порядке , , , . Формально соответствующий компилятор C может оценивать выражения в любом порядке между точками последовательности (это позволяет компилятору выполнить некоторую оптимизацию). Точки последовательности определяются:(1+1)+(3+3)
(1+1)+(3+3)
(2)+(3+3)
(2)+(6)
(8)
(1+1)+(3+3)
(1+1)+(6)
(2)+(6)
(8)
- Заявление заканчивается точкой с запятой.
- Оператор последовательности : запятая. Однако запятые, разделяющие аргументы функции, не являются точками последовательности.
- Операторы короткого замыкания : логическое and (
&&
которое может быть прочитано и затем ) и логическое or (||
которое может быть прочитано или иначе ). - Тройной оператор (
?:
): Этот оператор оценивает свое первое суб-выражение, а затем его второго или третьего (никогда оба из них) , на основе значения первого. - Вход и выход из вызова функции (но не между оценками аргументов).
Выражения перед точкой последовательности всегда оцениваются перед выражениями после точки последовательности. В случае оценки короткого замыкания второе выражение может не оцениваться в зависимости от результата первого выражения. Например, в выражении , если первый аргумент имеет ненулевое значение (истина), результатом всего выражения не может быть ничего, кроме истины, поэтому он не оценивается. Точно так же в выражении , если первый аргумент равен нулю (ложь), результатом всего выражения не может быть ничего, кроме ложного, поэтому он не оценивается.(a() || b())
b()
(a() && b())
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 ++» нет точки последовательности , можно выполнять шаги оценки более чем в одном порядке, что приводит к неоднозначному утверждению. Это можно исправить, переписав код, чтобы вставить точку последовательности, чтобы обеспечить однозначное поведение, например:
а = b ++ ; а + = b ++ ;
Смотрите также
- Блоки (расширение языка C)
- Язык программирования C
- Типы и объявления переменных C
- Операторы в C и C ++
- Стандартная библиотека C
- Список языков программирования C-семейства (C-зависимые языки)
Рекомендации
- ^ Б The
long long
модификатора был введен в C99 стандарта. - ^ Значение auto - это спецификатор типа, а не спецификатор класса хранения в C ++ 0x
- ^ a b c Клеменс, Бен (2012). С 21 - го века . O'Reilly Media . ISBN 978-1449327149.
- ^ Balagurusamy, Е. Программирование в ANSI C . Тата МакГроу Хилл. п. 366.
- ^ «Препроцессор C: поведение, определяемое реализацией» . gcc.gnu.org .
- ^ «Строковые и символьные литералы (C ++)» . Документация по Visual C ++ 19 . Проверено 20 ноября 2019 года .
- ^ см.ссылки в первом разделе UTF-8
- ^ Керниган и Ричи
- ^ Татхам, Саймон (2000). «Сопрограммы на языке C» . Проверено 30 апреля 2017 .
- Общий
- Керниган, Брайан В .; Ричи, Деннис М. (1988). Язык программирования C (2-е изд.). Река Аппер Сэдл, Нью-Джерси: Prentice Hall PTR. ISBN 0-13-110370-9.
- Американский национальный стандарт информационных систем - Язык программирования - C - ANSI X3.159-1989
Внешние ссылки
- Синтаксис C в форме Бэкуса-Наура
- Программирование на C
- Страница часто задаваемых вопросов на comp.lang.c