Jump to content

Синтаксис Си

(Перенаправлено из класса хранилища )
Фрагмент кода C , который печатает «Hello, World!»

Синтаксис объектным языка программирования C представляет собой набор правил, регулирующих написание программного обеспечения на 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

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

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

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

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

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

[ редактировать ]

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

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

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

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

Это заявляет о enum colors тип; тот int константы RED (значение которого равно 0), GREEN (чье значение на единицу больше, чем RED, 1), BLUE (чье значение равно заданному значению 5) и YELLOW (чье значение на единицу больше, чем BLUE, 6); и enum colors переменная paint_color. Константы могут использоваться вне контекста enum (где допускается любое целочисленное значение), а значения, отличные от констант, могут быть присвоены paint_colorили любая другая переменная типа enum colors.

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

[ редактировать ]

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

Типы с плавающей запятой
Спецификаторы типа Точность (десятичные цифры) Диапазон экспоненты
Минимум ИЭЭЭ 754 Минимум ИЭЭЭ 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 Программа Глобальный (вся программа) Ноль
_Thread_local Нить
(никто) 1 Динамический (куча) Неинициализированный (инициализируется 0 если использовать calloc())
1 Выделяется и освобождается с помощью malloc() и free() библиотечные функции.

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

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

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

The _Thread_local ( thread_local в C++ , начиная с C23 , и в более ранних версиях C, если заголовок <threads.h> включен) спецификатор класса хранения, представленный в C11 , используется для объявления локальной переменной потока. Его можно комбинировать с static или extern чтобы определить связь.

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

Спецификаторы типа

[ редактировать ]

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

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

[ редактировать ]

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

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

struct thing *pt;

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

struct thing {
    int num;
}; /* thing struct type is now completed */

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

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 и т. д. могут быть случайно указаны с неопределенными результатами.

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

Индексы массива и арифметика указателей
Элемент Первый Второй Третий н- й
Индекс массива 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) в пределах блока. Такие переменные массива выделяются на основе целочисленного значения во время выполнения при входе в блок и освобождаются в конце блока. [1] Начиная с версии C11, эта функция больше не требуется для реализации компилятором.

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

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

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

[ редактировать ]

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

#include <stdlib.h> /* declares malloc */
...
int *a = malloc(n * sizeof *a);
a[3] = 10;

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

Когда динамически выделяемая память больше не нужна, ее следует вернуть обратно в систему времени выполнения. Это делается с помощью вызова 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] для доступа к четвертому целому числу.

Индексы массива и арифметика указателей [2]
Элемент Первый Вторая строка, второй столбец 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 значения с дополнительным нулевым завершающим символом (0-значным) кодом, обозначающим конец строки.

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

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

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

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

[ редактировать ]

Поскольку определенные символы не могут быть частью буквального строкового выражения напрямую, вместо этого они идентифицируются escape-последовательностью, начинающейся с обратной косой черты ( \). Например, обратная косая черта в "This string contains \"double quotes\"." указать (компилятору), что внутренняя пара кавычек предназначена как фактическая часть строки, а не как чтение по умолчанию как разделитель (конечная точка) самой строки.

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

Побег Значение
\\ Буквальная обратная косая черта
\" Двойная кавычка
\' Одиночная цитата
\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 "
           "world\n", __LINE__);

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

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

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

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

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

[ редактировать ]

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

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

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

Как и строковые литералы, символьные константы также можно изменять с помощью префиксов, например L'A' имеет тип wchar_t и представляет значение символа «A» в кодировке расширенных символов.

Широкие строки символов

[ редактировать ]

Поскольку тип 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, поэтому для компилятора Microsoft указанная выше строка будет иметь длину 26 байт; мир Unix предпочитает UTF-32, поэтому такие компиляторы , как GCC, будут генерировать 52-байтовую строку. Ширина 2 байта wchar_t имеет те же ограничения, что и char, поскольку определенные символы (вне BMP ) не могут быть представлены в одном wchar_t; но должны быть представлены с использованием суррогатных пар .

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

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

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

[ редактировать ]

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

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

[ редактировать ]

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

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

[ редактировать ]

Структуры

[ редактировать ]

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

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

Декларация

[ редактировать ]

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

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

struct s {
    int   x;
    float y;
    char  *z;
} tee;

И следующий оператор объявит аналогичный союз с именем 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

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

struct s *ptr_to_tee = &tee;

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

(*ptr_to_tee).y

Что идентично более простому 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 сверху:

tee.x = 74;

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

ptr_to_tee->x = 74;

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

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

[ редактировать ]

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

Битовые поля

[ редактировать ]

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

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

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

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

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

struct f {
    unsigned int  flag : 1;  /* a bit flag: can either be on (1) or off (0) */
    signed int    num  : 4;  /* a signed 4-bit field; range -7...7 or -8...7 */
    signed int         : 3;  /* 3 bits of padding to round out to 8 bits */
} g;

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

[ редактировать ]

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

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

int x = 12;
int y = { 23 };     //Legal, no warning
int z = { { 34 } }; //Legal, expect a warning

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

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

struct s {
    int   x;
    float y;
    char  *z;
};

struct s pi = { 3, 3.1415, "Pi" };

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

[ редактировать ]

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

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 объединение инициализировалось одним значением, примененным к его первому члену. То есть объединение u, определенное выше, может иметь только int x инициализированный член :

union u value = { 3 };

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

union u value = { .y = 3.1415 };

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

int x[] = { 0, 1, 2 } ;

Составные обозначения могут использоваться для обеспечения явной инициализации, когда списки инициализаторов без дополнений может быть неправильно понято. В примере ниже w объявляется как массив структур, каждая структура состоит из члена a (массив из 3 int) и член b (ан 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 невозможно указать повторение инициализатора.

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

[ редактировать ]

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

// pointer created from array literal.
int *ptr = (int[]){ 10, 20, 30, 40 };

// pointer to array.
float (*foo)[3] = &(float[]){ 0.5f, 1.f, -0.5f };

struct s pi = (struct s){ 3, 3.1415, "Pi" };

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

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

Операторы

[ редактировать ]

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

[ редактировать ]

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

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

Сложные утверждения

[ редактировать ]

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

{
    <optional-declaration-list>
    <optional-statement-list>
}

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

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

[ редактировать ]

В C есть два типа операторов выбора : if заявление и switch заявление .

The if заявление имеет вид:

if (<expression>)
    <statement1>
else
    <statement2>

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

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

switch (<expression>)
{
    case <label1> :
        <statements 1>
    case <label2> :
        <statements 2>
        break;
    default :
        <statements 3>
}

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

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

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

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

[ редактировать ]

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

do
    <statement>
while ( <expression> ) ;

while ( <expression> )
    <statement>

for ( <expression> ; <expression> ; <expression> )
    <statement>

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

Заявление:

for (e1; e2; e3)
    s;

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

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

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

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

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

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

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

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

[ редактировать ]

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

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

goto <identifier> ;

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

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

while (expression)
{
    /* ... */
    cont: ;
}

do
{
    /* ... */
    cont: ;
} while (expression);

for (expr1; expr2; expr3) {
     /* ... */
     cont: ;
}

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

The break оператор используется для завершения for петля, while петля, do петля, или switch заявление. Управление передается оператору, следующему за завершенным оператором.

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

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

[ редактировать ]

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

    void *ptr = &&J1;

J1: printf("hi ");
    goto *ptr;

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

Синтаксис

[ редактировать ]

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

<return-type> functionName( <parameter-list> )
{
    <statements>
    return <expression of type return-type>;
}

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

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

int f()[3];    // Error: function returning an array
int (*g())[3]; // OK: function returning a pointer to an array.

void h()();    // Error: function returning a function
void (*k())(); // OK: function returning a function pointer

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

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

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

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

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

[ редактировать ]

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

<return-type> (*<function-name>)(<parameter-list>);

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

#include <stdio.h>

int (*operation)(int x, int y);

int add(int x, int y)
{
    return x + y;
}

int subtract(int x, int y)
{
    return x - y;
}

int main(int argc, char* args[])
{
   int  foo = 1, bar = 1;

   operation = add;
   printf("%d + %d = %d\n", foo, bar, operation(foo, bar));
   operation = subtract;
   printf("%d - %d = %d\n", foo, bar, operation(foo, bar));
   return 0;
}

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

[ редактировать ]

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

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

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

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

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

int main() {...}
int main(void) {...}
int main(int argc, char *argv[]) {...}
int main(int argc, char **argv) {...} // char *argv[] and char **argv have the same type as function parameters

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

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

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

int main(void){}

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

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

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

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

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

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

[ редактировать ]

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

void incInt(int *y)
{
    (*y)++;  // Increase the value of 'x', in 'main' below, by one
}

int main(void)
{
    int x = 0;
    incInt(&x);  // pass a reference to the var 'x'
    return 0;
}

Функция scanf работает аналогично:

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

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

#include <stdio.h>
#include <stdlib.h>

void allocate_array(int ** const a_p, const int A) {
/* 
 allocate array of A ints
 assigning to *a_p alters the 'a' in main()
*/
    *a_p = malloc(sizeof(int) * A); 
}

int main(void) {
    int * a; /* create a pointer to one or more ints, this will be the array */

 /* pass the address of 'a' */
    allocate_array(&a, 42);

/* 'a' is now an array of length 42 and can be manipulated and freed here */

    free(a);
    return 0;
}

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

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

[ редактировать ]

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

#include <stdio.h>

void setArray(int array[], int index, int value)
{
    array[index] = value;
}

int main(void)
{
    int a[1] = {1};
    setArray(a, 0, 2);
    printf ("a[0]=%d\n", a[0]);
    return 0;
}

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

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

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

Начиная с C99, программист может указать, что функция принимает массив определенного размера, используя ключевое слово static. В void setArray(int array[static 4], int index, int value) первый параметр должен быть указателем на первый элемент массива длиной не менее 4. Также можно добавить квалификаторы ( const, volatile и restrict) к типу указателя, в который преобразуется массив, путем помещения их в скобки.

Анонимные функции

[ редактировать ]
Анонимная функция не поддерживается стандартным языком программирования C, но поддерживается некоторыми диалектами C, такими как GCC. [7] и Кланг .

Разнообразный

[ редактировать ]

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

[ редактировать ]

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

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

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

[ редактировать ]

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

Комментарии

[ редактировать ]

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

/*
This line will be ignored.
/*
A compiler warning may be produced here. These lines will also be ignored.
The comment opening token above did not start a new comment,
and the comment closing token below will close the comment begun on line 1.
*/
This line and the line below it will not be ignored. Both will likely produce compile errors.
*/

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

// this line will be ignored by the compiler

/* these lines
   will be ignored
   by the compiler */

x = *p/*q;  /* this comment starts after the 'p' */

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

[ редактировать ]

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

myFilt p1 p2 p3

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

м и Ф я л т \0 п 1 \0 п 2 \0 п 3 \0
аргв[0] аргв[1] аргв[2] аргв[3]

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

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

#include <stdio.h>

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

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

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

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

[ редактировать ]

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

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

#include <stdio.h>

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

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

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

См. также

[ редактировать ]

Примечания

[ редактировать ]
  1. ^ Перейти обратно: а б The long long Модификатор был введен в стандарт C99 .
  2. ^ Значение auto — это спецификатор типа, а не спецификатор класса хранения в C++0x.
  3. ^ ссылки см UTF-8. . в первом разделе
  4. ^ Также разрешены другие типы, определенные реализацией. C++ позволяет использовать все целочисленные и перечислимые типы, и многие компиляторы C делают то же самое.
  1. ^ Перейти обратно: а б с Клеменс, Бен (2012). 21 век С. О'Рейли Медиа . ISBN  978-1449327149 .
  2. ^ Балагурусамы, Э. Программирование на ANSI C. Тата МакГроу Хилл. п. 366.
  3. ^ «Препроцессор C: поведение, определяемое реализацией» . gcc.gnu.org .
  4. ^ «Строковые и символьные литералы (C++)» . Документация по Visual C++ 19 . Проверено 20 ноября 2019 г.
  5. ^ Керниган и Ричи
  6. ^ Тэтэм, Саймон (2000). «Сопрограммы в C» . Проверено 30 апреля 2017 г.
  7. ^ «Выражения операторов (с использованием коллекции компиляторов GNU (GCC))» . gcc.gnu.org . Проверено 12 января 2022 г.
Общий
[ редактировать ]
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: daeffb0b3eeca84a47b426c17d65ab3d__1717443300
URL1:https://arc.ask3.ru/arc/aa/da/3d/daeffb0b3eeca84a47b426c17d65ab3d.html
Заголовок, (Title) документа по адресу, URL1:
C syntax - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)