其他运算符
不属于任何其他主要类别的运算符集合。
本节不完整 原因:考虑为此表和其他涵盖多个主题的表提供更通用的目录 |
运算符 | 运算符名称 | 示例 | 描述 |
---|---|---|---|
(...) | 函数调用 | 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 的类型 |
目录 |
[编辑] 函数调用
函数调用表达式的形式为
表达式 ( 参数列表 (可选) ) |
|||||||||
其中
表达式 | - | 任何指向函数类型的表达式(在左值转换之后) |
参数列表 | - | 由逗号分隔的任何完整对象类型的表达式列表(不能是逗号运算符)。调用不带参数的函数时可以省略。 |
函数调用表达式的行为取决于被调用函数的原型在调用点是否在作用域内。
[编辑] 带原型的函数调用
(C99 起) |
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) {} 如果出现以下情况,调用不带原型的函数的行为是未定义的:
|
(直至 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
[编辑] 转换运算符
参见转换运算符
[编辑] 条件运算符
条件运算符表达式的形式为
条件 ? 表达式-真 : 表达式-假 |
|||||||||
其中
条件 | - | 标量类型的表达式 |
表达式-真 | - | 如果条件与零不相等,则评估的表达式 |
表达式-假 | - | 如果条件与零相等,则评估的表达式 |
只允许以下表达式作为表达式-真和表达式-假
|
(自 C23 起) |
(自 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
运算符
[编辑] _Alignof
运算符
[编辑] 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 |
+a |
!a |
a == b |
a[b] |
a(...) |
C++ 文档,关于 其他运算符
|