命名空间
变体
操作

其他运算符

来自 cppreference.cn
< 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 的类型

目录

[编辑] 函数调用

函数调用表达式的形式为

表达式 ( 参数列表 (可选) )

其中

表达式 - 任何指向函数类型的表达式(在左值转换之后)
参数列表 - 由逗号分隔的任何完整对象类型的表达式列表(不能是逗号运算符)。调用不带参数的函数时可以省略。

函数调用表达式的行为取决于被调用函数的原型在调用点是否在作用域内

[编辑] 带原型的函数调用

1) 参数数量必须等于实参数量(除非使用省略号参数)。
2) 每个参数的类型必须是这样一种类型:存在如同赋值的隐式转换,将相应实参的非限定类型转换为参数的类型。
此外,对于在 [] 之间使用关键字 static 的每个数组类型参数,实参表达式必须指定一个指向数组元素的指针,该数组至少包含参数大小表达式中指定的那么多元素。
(C99 起)
3) 实参以未指定顺序且不带序列化地进行评估。
4) 执行赋值以将每个实参的值复制到相应的函数参数,忽略参数类型及其可能递归的元素或成员(如果有)上的任何类型限定符(注意:函数可以修改其参数,这些更改不会影响实参;C 函数调用仅为值传递)。
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-qualified 参数或成员类型,并且不恰当地应用了 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)

[编辑] 逗号运算符

逗号运算符表达式的形式为

lhs , rhs

其中

lhs - 任何表达式
rhs - 任何表达式,除了另一个逗号运算符(换句话说,逗号运算符的结合性是从左到右的)

首先,评估左操作数lhs,并丢弃其结果值。

然后,发生一个序列点,以便lhs的所有副作用都完成。

然后,评估右操作数rhs,其结果由逗号运算符作为非左值返回。

[编辑] 注意

lhs 的类型可以是 void(也就是说,它可以是对返回 void 的函数的调用,或者它可以是将表达式强制转换void 的表达式)

逗号运算符在 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-限定符后是兼容的
(自 C23 起)
  • 一个表达式是指针,另一个是空指针常量(例如 NULLnullptr_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:2024)
  • 6.5.2.2 函数调用 (p: 待定)
  • 6.5.3.4 sizeof 和 _Alignof 运算符 (p: 待定)
  • 6.5.4 转换运算符 (p: 待定)
  • 6.5.15 条件运算符 (p: 待定)
  • 6.5.17 逗号运算符 (p: 待定)
  • 6.7.3.5 Typeof 说明符 (p: 115-118)
  • C17 标准 (ISO/IEC 9899:2018)
  • 6.5.2.2 函数调用 (p: 58-59)
  • 6.5.3.4 sizeof 和 _Alignof 运算符 (p: 64-65)
  • 6.5.4 转换运算符 (p: 65-66)
  • 6.5.15 条件运算符 (p: 71-72)
  • 6.5.17 逗号运算符 (p: 75)
  • C11 标准 (ISO/IEC 9899:2011)
  • 6.5.2.2 函数调用 (p: 81-82)
  • 6.5.3.4 sizeof 和 _Alignof 运算符 (p: 90-91)
  • 6.5.4 转换运算符 (p: 91)
  • 6.5.15 条件运算符 (p: 100)
  • 6.5.17 逗号运算符 (p: 105)
  • C99 标准 (ISO/IEC 9899:1999)
  • 6.5.2.2 函数调用 (p: 71-72)
  • 6.5.3.4 sizeof 运算符 (p: 80-81)
  • 6.5.4 转换运算符 (p: 81)
  • 6.5.15 条件运算符 (p: 90-91)
  • 6.5.17 逗号运算符 (p: 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 起)
(直至 C23)

alignof
(自 C23 起)

C++ 文档,关于 其他运算符