Spec-Zone.ru › C

Другие операторы

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

Оператор Название оператора Пример Описание
(...) вызов функции f(...) вызов функции f(), с нулем или более аргументами
, оператор запятая a, b вычислить выражение a, игнорировать его возвращаемое значение и выполнить все побочные эффекты, затем вычислить выражение b, возвращая тип и результат этого вычисления
(type) приведение типов (type)a привести тип a к типу type
? : условный оператор a ? b : c если a логически истинно (не равно нулю), то вычислить выражение b, в противном случае вычислить выражение c
sizeof оператор sizeof sizeof a размер a в байтах
_Alignof
(с C11)
оператор _Alignof _Alignof(type) выравнивание, требуемое для type
typeof операторы typeof typeof(a) тип a

Вызов функции

Выражение вызова функции имеет вид

выражение ( список_аргументов (необязательно) )

где

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

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

Вызов функции с прототипом

1) Количество параметров должно быть равно количеству аргументов (если не используется параметр с эллипсисом).
2) Тип каждого параметра должен быть таким, чтобы существовало неявное преобразование, как при присваивании, которое преобразует неквалифицированный тип соответствующего аргумента в тип параметра.
Кроме того, для каждого параметра типа массива, использующего ключевое слово static между [ и ], выражение аргумента должно обозначать указатель на элемент массива с, по крайней мере, таким же количеством элементов, как указано в выражении размера параметра. (с C99)
3) Аргументы вычисляются в неопределенном порядке и без последовательности.
4) Присваивание выполняется для копирования значения каждого аргумента в соответствующий параметр функции, игнорируя любые квалификаторы типа в типе параметра и его потенциально рекурсивных элементов или членов, если таковые имеются (примечание: функция может изменять свои параметры, и эти изменения не влияют на аргументы; вызовы функций C - только по значению).
  • если есть параметр с эллипсисом, по умолчанию выполняются повышения аргументов для оставшихся аргументов, которые предоставляются va_list.
5) Функция выполняется, и возвращаемое ею значение становится значением выражения вызова функции (если функция возвращает void, выражение вызова функции является выражением void)
void f(char* p, int x) {}
int main(void)
{
    f("abc", 3.14); // array to pointer and float to int conversions
}

Вызов функции без прототипа

1) Аргументы вычисляются в неопределенном порядке и без последовательности. 2) По умолчанию выполняются повышения аргументов для каждого выражения аргумента. 3) Присваивание выполняется для копирования значения каждого аргумента в соответствующий параметр функции, игнорируя любые квалификаторы типа в типе параметра и его потенциально рекурсивных элементов или членов, если таковые имеются. 4) Функция выполняется, и возвращаемое ею значение становится значением выражения вызова функции (если функция возвращает void, выражение вызова функции является выражением void)
void f(); // no prototype
int main(void)
{
    f(1, 1.0f); // UB unless f is defined to take an int and a double
}
void f(int a, double c) {}

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

  • количество аргументов не соответствует количеству параметров.
  • повышенные типы аргументов не совместимы с повышенными типами параметров, за исключением того, что
    • знаковый и беззнаковый варианты одного целого типа считаются совместимыми, если значение аргумента представимо обоими типами.
    • указатели на void и указатели на (возможно, cvr-квалифицированные) типы символов считаются совместимыми
(до C23)

Примечания

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

(*pf[f1()]) (f2(), f3() + f4()); // f1, f2, f3, f4 may be called in any order

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

int f(void) { return 1; }
int (*pf)(void) = f;
 
int main(void)
{
    f();    // convert f to pointer, then call
    (&f)(); // create a pointer to function, then call
 
    pf();    // call the function
    (*pf)(); // obtain the function designator, convert to pointer, then calls
 
    (****f)(); // convert to pointer, obtain the function, repeat 4x, then call
    (****pf)(); // also OK
}

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

Текущая формулировка стандарта по подготовке параметров функции некорректна, потому что она указывает, что параметры присваиваются из аргументов во время вызова, что неверно отвергает параметры или члены типа с квалификатором const и некорректно применяет семантику volatile, которая не может быть реализована для параметров функции на многих платформах. Отчет о дефекте после C11 DR427 предложил изменить такую семантику с присваивания на инициализацию, но он был закрыт как не-дефект.

Выражение вызова функции, где выражение состоит только из идентификатора, и этот идентификатор не объявлен, ведет себя так, как если бы идентификатор был объявлен как

extern int identifier(); // returns int and has no prototype

Таким образом, следующая полная программа является допустимым C89:

main()
{
    int n = atoi("123"); // implicitly declares atoi as int atoi()
}
(до C99)

Оператор запятая

Выражение оператора запятой имеет вид

левое_операнд , правое_операнд

где

левое_операнд - любое выражение
правое_операнд - любое выражение, кроме другого оператора запятой (другими словами, ассоциативность оператора запятой слева направо)

Сначала вычисляется левое операнд, левое_операнд, и его результат отбрасывается.

Затем происходит точка последовательности, так что все побочные эффекты левое_операнд завершены.

Затем вычисляется правое операнд, правое_операнд, и его результат возвращается оператором запятой как не-lvalue.

Примечания

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

Оператор запятая может быть lvalue в C++, но никогда в C

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

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

  • список аргументов в вызове функции
  • выражение инициализации или список инициализации
  • обобщенное выбор

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

// int n = 2,3; // error, comma assumed to begin the next declarator
// int a[2] = {1,2,3}; // error: more initializers than elements
int n = (2,3), a[2] = {(1,2),3}; // OK
 
f(a, (t=3, t+2), c); // OK, first, stores 3 in t, then calls f with three arguments

Оператор запятой на верхнем уровне также запрещён в границах массива

// int a[2,3]; // error
int a[(2,3)]; // OK, VLA array of size 3 (VLA because (2,3) is not a constant expression)

Оператор запятой не разрешён в постоянных выражениях, независимо от того, находится он на верхнем уровне или нет

// static int n = (1,2); // Error: constant expression cannot call the comma operator

Оператор приведения типа

См. оператор приведения типа

Условный оператор

Выражение условного оператора имеет вид

условие ? выражение-истина : выражение-ложь

где

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

Разрешены только следующие выражения в качестве выражение-истина и выражение-ложь

  • два выражения любого арифметического типа
  • два выражения одного и того же типа структуры или объединения
  • два выражения типа void
  • два указателя на типы, совместимые, игнорируя cvr-квалификаторы
  • два выражения типа nullptr_t
(с C23)
  • одно выражение — указатель, другое — константа нулевого указателя (например, NULL)или значение nullptr_t(с C23)
  • одно выражение — указатель на объект, другое — указатель на void (возможно, с квалификаторами)
1) Сначала вычисляется условие. После этого вычисления стоит точка последовательности.
2) Если результат условия не равен нулю, выполняется выражение-истина, в противном случае выполняется выражение-ложь
3) Выполняется преобразование результата вычисления к общему типу, определяемому следующим образом:
1) если выражения имеют арифметический тип, общий тип — это тип после обычных арифметических преобразований
2) если выражения имеют тип структуры/объединения, общий тип — это тип этой структуры/объединения
3) если оба выражения имеют тип void, всё выражение условного оператора — выражение типа void
4) если одно — указатель, а другое — константа нулевого указателяили значение nullptr_t(с C23), тип — тип этого указателя
5) если оба — указатели, результатом является указатель на тип, объединяющий cvr-квалификаторы обоих типов (то есть, если один — const int*, а другой — volatile int*, результатом является const volatile int*), и если типы различались, тип указываемого объекта — составной тип.
6) если одно — указатель на void, результатом является указатель на void с объединёнными cvr-квалификаторами
7) если оба имеют тип nullptr_t , общий тип также nullptr_t (с C23)
#define ICE(x) (sizeof(*(1 ? ((void*)((x) * 0l)) : (int*)1)))
 
// if x is an Integer Constant Expression then macro expands to
 
sizeof(*(1 ? NULL : (int *) 1))  // (void *)((x)*0l)) -> NULL
 
// according to point (4) this further converts into
 
sizeof(int)
 
// if x is not an Integer Constant Expression then macro expands to
// according to point (6)
 
(sizeof(*(void *)(x))           // Error due incomplete type

Примечания

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

Обратите внимание, что в C++ он может быть выражением-ссылкой.

См. приоритет операторов для получения подробностей о взаимном приоритете этого оператора и присваивания.

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

#include <assert.h>
 
enum vehicle { bus, airplane, train, car, horse, feet };
 
enum vehicle choose(char arg)
{
    return arg == 'B' ? bus      :
           arg == 'A' ? airplane :
           arg == 'T' ? train    :
           arg == 'C' ? car      :
           arg == 'H' ? horse    :
                        feet     ;
}
 
int main(void)
{
    assert(choose('H') == horse && choose('F') == feet);
}

Оператор sizeof

См. оператор sizeof

Оператор _Alignof

См. оператор _Alignof

Операторы typeof

См. операторы typeof

Ссылки

  • Стандарт C23 (ISO/IEC 9899:2023):
    • 6.5.2.2 Вызовы функций (с. TBD)
    • 6.5.3.4 Операторы sizeof и _Alignof (с. TBD)
    • 6.5.4 Операторы приведения типа (с. TBD)
    • 6.5.15 Условный оператор (с. TBD)
    • 6.5.17 Оператор запятой (с. TBD)
    • 6.7.3.5 Спецификаторы typeof (с. 115-118)
  • Стандарт C17 (ISO/IEC 9899:2018):
    • 6.5.2.2 Вызовы функций (с. 58-59)
    • 6.5.3.4 Операторы sizeof и _Alignof (с. 64-65)
    • 6.5.4 Операторы приведения типа (с. 65-66)
    • 6.5.15 Условный оператор (с. 71-72)
    • 6.5.17 Оператор запятой (с. 75)
  • Стандарт C11 (ISO/IEC 9899:2011):
    • 6.5.2.2 Вызовы функций (с. 81-82)
    • 6.5.3.4 Операторы sizeof и _Alignof (с. 90-91)
    • 6.5.4 Операторы приведения типа (с. 91)
    • 6.5.15 Условный оператор (с. 100)
    • 6.5.17 Оператор запятой (с. 105)
  • Стандарт C99 (ISO/IEC 9899:1999):
    • 6.5.2.2 Вызовы функций (с. 71-72)
    • 6.5.3.4 Оператор sizeof (с. 80-81)
    • 6.5.4 Операторы приведения типа (с. 81)
    • 6.5.15 Условный оператор (с. 90-91)
    • 6.5.17 Оператор запятой (с. 94)
  • Стандарт C89/C90 (ISO/IEC 9899:1990):
    • 3.3.2.2 Вызовы функций
    • 3.3.3.4 Оператор sizeof
    • 3.3.4 Операторы приведения типа
    • 3.3.15 Условный оператор
    • 3.3.17 Оператор запятой

См. также

  • Приоритет операторов
Общие операторы
присваивание инкремент
декремент
арифметические логические сравнения доступ к членам
структуры
другие

a = b a += b a -= b a *= b a /= b a %= b a &= b a |= b a ^= b a <<= b a >>= b

++a --a a++ a--

+a -a a + b a - b a * b a / b a % b ~a a & b a | b a ^ b a << b a >> b

!a a && b a || b

a == b a != b a < b a > b a <= b a >= b

a[b] *a &a a->b a.b

a(...) a, b (type) a a ? b : c sizeof

_Alignof
(с C11)

Документация C++ для других операторов

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/c/language/operator_other

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API