历史上

在最早的通用编程语言中,如FORTRAN,这种区别是非常清楚的。在FORTRAN中,一个语句是一个执行单位,是你做的一件事。它不被称为 "行 "的唯一原因是有时它跨越了很多行。一个表达式本身不能做任何事情,你必须把它分配给一个变量。在FORTRAN中表达式是一个错误,因为它不做任何事情。你必须对这个表达式做一些事情。
FORTRAN并没有我们今天所知的语法,这个语法是和Backus-Naur Form(BNF)一起发明的,是Algol-60定义的一部分。当时,语义上的区别("有一个值 "与 "做某事")被载入了语法中:一种是表达式,另一种是语句,解析器可以将它们区分开来。
后来语言的者模糊了这种区别:他们允许表达式做事情,也允许语句有一个值。的设计者意识到,如果允许你评估一个表达式并丢弃其结果,就不会造成任何伤害。在C语言中,每一个语法表达式都可以通过在结尾处加一个分号而成为一个语句。
表达式和语句的这种模糊性出现在所有的C语言衍生语言中(C、C++、C#和),它们仍然有一些语句(如while),但几乎允许任何表达式作为语句使用。
这两个 "语法类别" 会导致重复劳动。例如,C语言有两种形式的条件,if语句和?:表达式。
有时,人们希望有这样的重复:例如,在标准的C语言中,只有语句可以声明一个新的局部变量,但这种能力非常有用,GNU C编译器提供了一个GNU扩展(语句表达式),使表达式也可以声明一个局部变量。
而其他语言的设计者不喜欢这种重复,他们很早就看到,如果表达式可以有副作用,也可以有值,那么语句和表达式之间的语法区别就不是那么有用了,所以他们把它去掉了。Haskell、Icon、Lisp和ML都是没有语句的语言,它们只有表达式。即使是类似结构的循环和条件形式也被认为是表达式,而且它们有值。
一些区别描述
- 表达式会进行计算产生一个结果(结果的三个方面:值、类型和值类别),而语句不会。
- 表达式可以在语句中使用,但不能反过来使用。不过,一个例外是lambda表达式,它表示一个函数,因此可以包含函数可以包含的任何内容,除非该语言只允许有限的lambdas。
- 两种类型都可以组合,但只要类型匹配,大多数表达式都可以任意组合。每种类型的语句都有自己的组合其他语句的方式。表达式可以使用操作符“水平地”组合成更大的表达式,而语句只能通过一个接一个地编写或使用块结构来“垂直地”组合。
- 命令式语言(Fortran, C, Java,…)强调语句来构造程序,而表达式则是一种事后思考。函数式语言强调表达式。纯函数式语言具有比语句更强大的表达式。
- 一个例子
#include <tream>
int main() {
int x;
x = 2, 3;
std::cout << x << std::endl;
return 0;
}
结果:2
#include <iostream>
int f(){ return 2, 3; }
int main() {
int x;
x = f();
std::cout << x << std::endl;
return 0;
}
结果:3
分析如下:
x=2,3是一个表达式,表达式中逗号操作符“,”的优先级低于赋值运算符“=”。所以这个表达式语句相当于(x=2),3;
return 2, 3;是返回语句,return不属于表达式,只有2,3为表达式。逗号操作符是从左到右计算,所以2,3表达式结算结果是3。
Can programming be liberated from the von Neumann style?: a functional style and its algebra of programs(编程能从冯·诺依曼风格中解放出来吗?:一种函数式风格及其程序代数)
FP,是John Backus创立的支持函数级编程范式的编程语言。它允许消去命名变量。这种语言是在Backus的1977年图灵奖获奖演讲论文《编程可以从冯诺依曼风格中解放出来吗?程序的函数式风格及其代数》中提出的。这篇论文点燃了对函数式语言研究的兴趣,最终导致了现代函数式语言,但不是Backus曾希望的函数级范式。
在他的这篇图灵奖论文中,Backus描述了FP风格与基于lambda演算的语言有着如何不同:
FP系统基于了对叫做泛函形式的一组固定的组合形式的利用。它们加上简单的定义,就是从现存函数建造新函数的唯一方式;它们不使用变量或替代规则,并且它们成为程序相关的代数的运算操作。FP系统的所有函数都是一种类型的:它们映射对象到对象之上并总是接受一个单一实际参数。
FP自身在学术界之外从未被大量使用。在1980年代,Backus创建了后继语言FL,它也保持为研究项目。
冯诺依曼风格是一种语句与表达式的编程风格,而函数式编程风格是建立在使用组合形式创建程序的基础上。
C++表达式
表达式(expression)是运算符和它们的操作数的序列,它指定一项计算。表达式的求值可以产生一个结果(比如 2+2 的求值产生结果 4),也可能产生副作用(比如i = 5; i++;的副作用i变成6)。
| 常见运算符 |
||||||
| 赋值 |
自增 |
算术 |
逻辑 |
比较 |
成员访问 |
其他 |
| a = b |
++a |
+a |
!a |
a == b |
a[b] |
a(...) |
| 特殊运算符 |
||||||
| static_cast 转换一个类型为另一相关类型 |
||||||
初等表达式:任何运算符的操作数都可以是其他的表达式或初等表达式。
初等表达式包括以下各项:
- 字面量(例如 2 或 "Hello, world")
- 标识表达式,包括经过适当声明的无限定的标识符(例如 n 或 cout)或者经过适当声明的有限定的标识符(例如 std::string::npos)
- lambda 表达式 (C++11)
- 折叠表达式 (C++17)
- requires 表达式 (C++20)
- 括号中的任何表达式也被归类为初等表达式
常量表达式:定义能在编译时求值的表达式。这种表达式能用做非类型模板实参、数组大小,并用于其他要求常量表达式的语境(上下文)。
不求值表达式:运算符 typeid、sizeof、noexcept 、 decltype (C++11 起)和requires 表达式 的操作数是不求值表达式,因为这些运算符仅查询其操作数的编译期性质。因此,std::size_t n = sizeof(std::cout << 42); 不进行控制台输出。不求值的运算数被当做完整表达式 (C++14 起),所以要符合表达式的一些要求。
弃值表达式:弃值表达式是仅用来实施其副作用的表达式。从这种表达式计算的值被舍弃。这样的表达式包括任何表达式语句的完整表达式,内建逗号运算符的左边的实参,以及转型到类型 void 的转型表达式的实参。运算符delete是弃值表达式,因其返回值是void类型。
求值顺序:求值任何表达式的任何部分,包括求值函数参数的顺序都是未说明的(除了一些例外)。编译器能以任何顺序求值任何操作数和其他子表达式,并且可以在再次求值同一表达式时选择另一顺序。
#include <cstdio>
int a() { return std::puts("a"); }
int b() { return std::puts("b"); }
int c() { return std::puts("c"); }
void z(int, int, int) {}
int main() {
z(a(), b(), c()); // 允许全部 6 种输出排列
return a() + b() + c(); // 允许全部 6 种输出排列
}
可能的输出:
b
c
a
c
a
b
优先级和结合性:优先级 指定包含多个运算符的表达式中的运算顺序。 结合性指定是否在包含多个具有相同优先级的运算符的表达式中,将操作数分组在其左侧或右侧的一个(a=b=c)。
结合性规定对于一元运算符是冗余的,只为完备而给出:一元前缀运算符始终从右到左结合(delete ++*p 为 delete(++(*p)))而一元后缀运算符始终从左到右结合(a[1][2]++ 为 ((a[1])[2])++)。
优先级和结合性是编译时概念,与求值顺序无关,后者是运行时概念。
| 优先级 |
运算符 |
描述 |
结合性 |
| 1 |
:: |
作用域解析 |
从左到右 |
| 2 |
a++ a-- |
后缀自增与自减 |
|
| type() type{} |
函数风格转型 |
||
| a() |
函数调用 |
||
| a[] |
下标 |
||
| . -> |
成员访问 |
||
| 3 |
++a --a |
前缀自增与自减 |
从右到左 |
| +a -a |
一元加与减 |
||
| ! ~ |
逻辑非和逐位非 |
||
| (type) |
C 风格转型 |
||
| *a |
间接(解引用) |
||
| &a |
取址 |
||
| sizeof |
取大小[注 1] |
||
| co_await |
await 表达式 (C++20) |
||
| new new[] |
动态内存分配 |
||
| delete delete[] |
动态内存分配 |
||
| 4 |
.* ->* |
成员指针 |
从左到右 |
| 5 |
a*b a/b a%b |
乘法、除法与余数 |
|
| 6 |
a+b a-b |
加法与减法 |
|
| 7 |
<< >> |
逐位左移与右移 |
|
| 8 |
<=> |
三路比较运算符(C++20 起) |
|
| 9 |
< <= |
分别为 < 与 ≤ 的关系运算符 |
|
| > >= |
分别为 > 与 ≥ 的关系运算符 |
||
| 10 |
== != |
分别为 = 与 ≠ 的相等性运算符 |
|
| 11 |
a&b |
逐位与 |
|
| 12 |
^ |
逐位异或(互斥或) |
|
| 13 |
| |
逐位或(可兼或) |
|
| 14 |
&& |
逻辑与 |
|
| 15 |
|| |
逻辑或 |
|
| 16 |
a?b:c |
三元条件[注 2] |
从右到左 |
| throw |
throw 运算符 |
||
| co_yield |
yield 表达式 (C++20) |
||
| = |
直接赋值(C++ 类默认提供) |
||
| += -= |
以和及差复合赋值 |
||
| *= /= %= |
以积、商及余数复合赋值 |
||
| <<= >>= |
以逐位左移及右移复合赋值 |
||
| &= ^= |= |
以逐位与、异或及或复合赋值 |
||
| 17 |
, |
逗号 |
从左到右 |
C++语句
语句(statement)是依序执行的 C++ 程序片段。任何函数体都是语句的序列。
类型:
- 表达式语句(expression statement);
- 复合语句(compound statement);
- 选择语句(selection statement);
- 循环语句(iteration statement);
- 跳转语句(jump statement);
- 声明语句(declaration statement);
- try 块;
- atomic 与 synchronized 块(TM TS)。










