其他运算符

来自cppreference.com
< c‎ | language


汇集并不适合任何其他主要类别的运算符。

运算符 运算符名 示例 描述
(...) 函数调用 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); // 数组到指针和 float 到 int 转换
}

调用无原型函数

1) 以未指定顺序无定序地求值各实参。
2) 在每个实参表达式上进行默认实参提升
3) 进行赋值,复制每个实参到对应的函数形参,忽略形参类型及其可能为递归的元素或成员上的类型限定符,若存在。
4) 执行函数,而其所返回的值成为函数调用表达式的值(若函数返回 void,则函数调用表达式为 void 表达式)
void f(); // 无原型
int main(void)
{
    f(1, 1.0f); // UB,除非定义 f 为接收一个 int 和一个 double
}
void f(int a, double c) {}

以下情况下调用无原型函数的行为未定义

  • 实参数量不匹配形参数量
  • 提升后的实参类型与提升后形参类型不兼容,除了
  • 若实参的值以两个类型均可表示,则认为相同整数类型的有符号和无符号版本兼容。
  • 认为指向 void 指针和指向(可有 cvr 限定的)字符类型指针兼容
(C23 前)

注解

指代所调用的函数的 表达式 和所有实参的求值,互相之间无定序(但在函数开始执行前有一个序列点)

(*pf[f1()]) (f2(), f3() + f4()); // 可以以任何顺序调用 f1、f2、f3、f4

尽管函数调用只对指向函数指针定义,它亦能作用于函数指代符,因为函数到指针隐式转换

int f(void) { return 1; }
int (*pf)(void) = f;
 
int main(void)
{
    f();    // 转换 f 为指针,然后调用
    (&f)(); // 创建指向函数指针,然后调用
 
    pf();    // 调用函数
    (*pf)(); // 获得函数指代器,转换为指针,然后滴啊用、
 
    (****f)(); // 转换为指针,获得函数,重复 4 次,然后调用
    (****pf)(); // 亦 OK
}

必须用在作用域中的原型调用忽略不使用实参的函数,如 printf(这种函数的原型需要使用尾随省略号形参),以避免未定义行为。

当前准备函数形参的语义的标准遣词是有缺陷的,因为它指定在调用时形参从实参赋值,这错误地拒绝了 const 限定的形参或成员类型,并且不恰当地应用了在许多平台上对于函数形参无法实现的 volatile 语义。C11 后的缺陷报告 DR427 提议将该语义从赋值改为初始化,但被作为非缺陷关闭。

表达式 完全由一个标识符组成,而未声明该标识符的函数调用表达式,表现为通过将该标识符声明如下

extern int identifier(); // 返回 int 且无原型

故下列完整程序在 C89 中合法:

main() {
    int n = atoi("123"); // 隐式声明 atoi 为 int atoi()
}
(C99 前)

逗号运算符

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

lhs , rhs

其中

lhs - 任何表达式
rhs - 除了另一逗号运算符外的任何表达式(换言之,逗号运算符的结合性为从左到右)

首先,求值左运算数 lhs 并舍弃其结果值。

然后,发生序列点,从而 lhs 的所有副效应完成。

然后,求值右运算数 rhs,而逗号运算符作为非左值返回其结果。

注意

lhs 的类型可为 void(即可为对返回 void 的函数的调用,或能为 转型void 的表达式)。

逗号运算符在 C++ 中可为左值,但 C 中决不是。

逗号运算符可返回结构体(其他返回结构体的表达式仅有复合字面量、函数调用、赋值和条件运算符)。

下列语境中,逗号运算符不能出现于表达式的顶层,因为逗号有不同含义:

若必须在这种语境中使用逗号运算符,则必须加括号:

// int n = 2,3; // 错误:认为逗号开始下个声明器
// int a[2] = {1,2,3}; // 错误:初始化器多于元素
int n = (2,3), a[2] = {(1,2),3}; // OK
 
f(a, (t=3, t+2), c); // OK:存储 3 于 t,然后以三个参数调用 f

数组边界中亦禁止顶层逗号

// int a[2,3]; // 错误
int a[(2,3)]; // OK:大小为 3 的 VLA 数组(因为 (2, 3) 不是常量表达式,故为 VLA)

常量表达式中不允许逗号,不管它是否出现于顶层

// static int n = (1,2); // 错误:常量表达式不能调用逗号运算符

转型运算符

转型运算符

条件运算符

条件运算符表达式的形式为

条件 ? 表达式真 : 表达式假

其中

条件 - 标量类型的表达式
表达式真 - 当条件比较不等于零时将求值的表达式
表达式假 - 当条件比较等于零时将求值的表达式

仅允许下列表达式为 表达式真表达式假

  • 两个任何算术类型的表达式
  • 两个相同结构体联合体类型的表达式
  • 两个 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 指针,则结果为指向具有合并的 cvr 限定符的 void 的指针。
7) 若两者均拥有 nullptr_t 类型,则共同类型亦为 nullptr_t
(C23 起)
#define ICE(x) (sizeof(*(1 ? ((void*)((x) * 0l)) : (int*)1)))
 
// 若 x 是整数常量表达式,则宏展开成
 
 sizeof( 1 ? NULL : (int *) 1)  // (void *)((x)*0l)) -> NULL
 
// 按照第 (4) 点这进一步转换成
 
 sizeof(int)
 
// 若 x 不是整数常量表达式,则宏按照第 (6) 点展开成
 
 (sizeof(*(void *)(x))           // 因不完整类型而错误

注解

条件运算符决不是左值表达式,尽管它可以返回结构体/联合体类型的对象。其他可以返回结构体的表达式仅有赋值逗号函数调用复合字面量

注意 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 Function calls (第 TBD 页)
  • 6.5.3.4 The sizeof and _Alignof operators (第 TBD 页)
  • 6.5.4 Cast operators (第 TBD 页)
  • 6.5.15 Conditional operator (第 TBD 页)
  • 6.5.17 Comma operator (第 TBD 页)
  • 6.7.3.5 Typeof specifiers (第 115-118 页)
  • C17 标准(ISO/IEC 9899:2018):
  • 6.5.2.2 Function calls (第 58-59 页)
  • 6.5.3.4 The sizeof and _Alignof operators (第 64-65 页)
  • 6.5.4 Cast operators (第 65-66 页)
  • 6.5.15 Conditional operator (第 71-72 页)
  • 6.5.17 Comma operator (第 75 页)
  • C11 标准(ISO/IEC 9899:2011):
  • 6.5.2.2 Function calls (第 81-82 页)
  • 6.5.3.4 The sizeof and _Alignof operators (第 90-91 页)
  • 6.5.4 Cast operators (第 91 页)
  • 6.5.15 Conditional operator (第 100 页)
  • 6.5.17 Comma operator (第 105 页)
  • C99 标准(ISO/IEC 9899:1999):
  • 6.5.2.2 Function calls (第 71-72 页)
  • 6.5.3.4 The sizeof operator (第 80-81 页)
  • 6.5.4 Cast operators (第 81 页)
  • 6.5.15 Conditional operator (第 90-91 页)
  • 6.5.17 Comma operator (第 94 页)
  • C89/C90 标准(ISO/IEC 9899:1990):
  • 3.3.2.2 Function calls
  • 3.3.3.4 The sizeof operator
  • 3.3.4 Cast operators
  • 3.3.15 Conditional operator
  • 3.3.17 Comma operator

参阅

常用运算符
赋值 自增
自减
算术 逻辑 比较 成员
访问
其他

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 起)