<返回更多

C++表达式和语句有啥区别

2021-07-21    CPP编程
加入收藏

历史上

在最早的通用编程语言中,如FORTRAN,这种区别是非常清楚的。在FORTRAN中,一个语句是一个执行单位,是你做的一件事。它不被称为 "行 "的唯一原因是有时它跨越了很多行。一个表达式本身不能做任何事情,你必须把它分配给一个变量。在FORTRAN中表达式是一个错误,因为它不做任何事情。你必须对这个表达式做一些事情。

FORTRAN并没有我们今天所知的语法,这个语法是和Backus-Naur Form(BNF)一起发明的,是Algol-60定义的一部分。当时,语义上的区别("有一个值 "与 "做某事")被载入了语法中:一种是表达式,另一种是语句,解析器可以将它们区分开来。

后来语言的设计者模糊了这种区别:他们允许表达式做事情,也允许语句有一个值。C语言的设计者意识到,如果允许你评估一个表达式并丢弃其结果,就不会造成任何伤害。在C语言中,每一个语法表达式都可以通过在结尾处加一个分号而成为一个语句。

表达式和语句的这种模糊性出现在所有的C语言衍生语言中(C、C++、C#和JAVA),它们仍然有一些语句(如while),但几乎允许任何表达式作为语句使用。

这两个 "语法类别" 会导致重复劳动。例如,C语言有两种形式的条件,if语句和?:表达式。

有时,人们希望有这样的重复:例如,在标准的C语言中,只有语句可以声明一个新的局部变量,但这种能力非常有用,GNU C编译器提供了一个GNU扩展(语句表达式),使表达式也可以声明一个局部变量。

而其他语言的设计者不喜欢这种重复,他们很早就看到,如果表达式可以有副作用,也可以有值,那么语句和表达式之间的语法区别就不是那么有用了,所以他们把它去掉了。Haskell、Icon、Lisp和ML都是没有语句的语言,它们只有表达式。即使是类似结构的循环和条件形式也被认为是表达式,而且它们有值。

一些区别描述

#include <IOStream>
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 += 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[b]
*a
&a
a->b
a.b
a->*b
a.*b

a(...)
a, b
? :

特殊运算符

static_cast 转换一个类型为另一相关类型
dynamic_cast 在继承层级中转换
const_cast 添加或移除 cv 限定符
reinterpret_cast 转换类型到无关类型
C 风格转型 以 static_cast 、 const_cast 及 reinterpret_cast 的混合转换一个类型到另一类型
new 创建有动态存储期的对象
delete 销毁先前由 new 表达式创建的对象,并释放其所拥有的内存区域
sizeof 查询类型的大小
sizeof... 查询形参包的大小(C++11 起)
typeid 查询类型的类型信息
noexcept 查询表达式是否能抛出异常(C++11 起)
alignof 查询类型的对齐要求(C++11 起)

初等表达式:任何运算符的操作数都可以是其他的表达式或初等表达式。

初等表达式包括以下各项:

  1. 字面量(例如 2 或 "Hello, world")
  2. 标识表达式,包括经过适当声明的无限定的标识符(例如 n 或 cout)或者经过适当声明的有限定的标识符(例如 std::string::npos)
  3. lambda 表达式 (C++11)
  4. 折叠表达式 (C++17)
  5. requires 表达式 (C++20)
  6. 括号中的任何表达式也被归类为初等表达式

常量表达式:定义能在编译时求值的表达式。这种表达式能用做非类型模板实参、数组大小,并用于其他要求常量表达式的语境(上下文)

不求值表达式:运算符 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++ 程序片段。任何函数体都是语句的序列。

类型:

  1. 表达式语句(expression statement);
  2. 复合语句(compound statement);
  3. 选择语句(selection statement);
  4. 循环语句(iteration statement);
  5. 跳转语句(jump statement);
  6. 声明语句(declaration statement);
  7. try 块;
  8. atomic 与 synchronized 块(TM TS)。
声明:本站部分内容来自互联网,如有版权侵犯或其他问题请与我们联系,我们将立即删除或处理。
▍相关推荐
更多资讯 >>>