Advertisement

第四章 表达式

阅读量:

第四章 表达式

字面值、变量、算术和逻辑运算式都属于表达式

4.1 基础

第一小节提到了运算符分类、类型转换、运算符重载、左右值等基本概念

C++运算符

类型转换

cppreference.com 包含关于自动类型转换的所有规定

[一般算术转换 - cppreference.com]介绍了这些常见情况下的整数到整数运算符的隐式转化规则

具体见4.11节

运算符重载

仅能实现运算对象与返回值类型的重载,并且不支持对优先级、结合律及运算对象个数的重载

见后文

左右值

Modern C++

Modern C++

左值 一般 可以代替右值使用,反过来不允许

优先级与结合律

用多了就知道结合律了,一般都带括号比较稳妥

求值顺序

只有**& &||** 、?: 四种运算符规定了求值顺序

其他运算符没有规定(有利有弊)

4.2 算术运算符

会进行一般算术转换(见上面类型转换);对象和结果都是右值

溢出属于UB;

除法向0取整;取余运算规则是m%n=m - (m/n) * n

4.3 逻辑和关系运算符

&&和||都有短路 策略:左侧为真(假)才对右侧求值

  • 练习4.10

4.4 赋值运算符

左侧必须是可修改的左值,返回类型也是左侧类型;

右侧可以是花括号(注意不要窄化int k={3.14})

优先级较低

4.5 递增递减运算符

后置版本优先级比*解引用高,因此会出现下面的简便写法

无论前置后置,尽量不要和其他运算符放在一起改变对象的值(UB)

4.6 成员访问运算符

有点和箭头;点优先级比*解引用高,注意写法

4.7 条件运算符?:

  • 练习4.21
  • 练习4.23

+比==优先级高,不能得出想要的结果

要在+后面加上括号,把后面的都括起来

4.8 位运算符

只作用于整数,类似;最好不用于无符号数

左移操作会将数值向左移动一位,并在右侧填充0;右移操作对无符号数进行右侧填充0,并对有符号数根据具体情况决定如何填充(我的运行环境是补充前导符号位)

4.9 sizeof运算符

sizeof (type)或者sizeof expr

返回值是size_t类型,不会计算表达式的值

有些特殊情况:

复制代码
 struct Empty { };

    
 struct Base {
    
     int a;
    
 };
    
 struct Derived : Base {
    
     int b;
    
 };
    
 struct Bit {
    
     unsigned bit : 1;
    
 };
    
 struct CharChar {
    
     char c;
    
     char c2;
    
 };
    
 struct CharCharInt {
    
     char c;
    
     char c2;
    
     int i;
    
 };
    
 struct IntCharChar {
    
     int i;
    
     char c;
    
     char c2;
    
 };
    
 struct CharIntChar {
    
     char c;
    
     int i;
    
     char c2;
    
 };
    
 struct CharShortChar {
    
     char c;
    
     short s;
    
     char c2;
    
 };
    
  
    
 int main()
    
 {
    
     Empty e;
    
     Derived d;
    
     Base& b = d;
    
     [[maybe_unused]] Bit bit;
    
     int a[10];
    
  
    
     auto f = [&]() { return sizeof(int[10]) == sizeof a ? throw 1 : e; };
    
     //  f(); // 返回类型是 Empty,但是必然会抛出 1
    
  
    
     auto println = [](auto rem, std::size_t size) { std::cout << rem << size << '\n'; };
    
     
    
     //sizeof结果永不为0
    
  
    
     println("1) 空类的大小:                        ", sizeof e); // 1
    
     println("2) 指针的大小:                        ", sizeof &e); // 8
    
     println("3) sizeof(Bit)    -类:               ", sizeof(Bit)); // 4
    
     println("4) sizeof(int[10])-含有10个 int 的数组:", sizeof(int[10])); // 40
    
     println("5) sizeof a       -含有10个 int 的数组:", sizeof a); // 40
    
     // 对数组运算得 数组元素总数*每个元素所占字节数
    
     println("6) 含有10个 int 的数组的长度:           ", ((sizeof a) / (sizeof *a))); // 10
    
     println("7) 含有10个 int 的数组的长度 (2):       ", ((sizeof a) / (sizeof a[0]))); // 10
    
     println("8) Derived 的大小:                    ", sizeof d); // 8
    
     println("9) 通过 Base 获取 Derived 的大小:      ", sizeof b); // 4
    
     println("A) sizeof(unsigned):                 ", sizeof(unsigned)); // 4
    
     println("B) sizeof(int):                      ", sizeof(int)); // 4
    
     println("C) sizeof(short):                    ", sizeof(short)); // 2
    
     println("D) sizeof(char):                     ", sizeof(char)); // 1
    
     // 对char求大小永远为1
    
     println("E) sizeof(CharChar):                 ", sizeof(CharChar)); // 2
    
     println("F) sizeof(CharCharInt):              ", sizeof(CharCharInt)); // 8
    
     println("G) sizeof(IntCharChar):              ", sizeof(IntCharChar)); // 8
    
     println("H) sizeof(CharIntChar):              ", sizeof(CharIntChar)); // 12
    
     println("I) sizeof(CharShortChar):            ", sizeof(CharShortChar)); // 6
    
     println("J) f() 的大小:                        ", sizeof f()); // 1
    
 }

sizeof 运算符 - cppreference.com

需要注意的是,在处理string或vector时使用sizeof函数仅能获取其在内存中的占用情况,并非直接反映数据元素的数量。具体来说,在这种情况下您将无法获得关于内存块长度、内存地址(指针)以及存储容量等其他相关信息。这些细节都是通过sizeof函数所无法捕捉到的。

  • 练习4.28

The memory address occupied by a bool type is one byte; similarly, the same applies to char types, which also use one byte; the integer type occupies four_bytes; the float and double types occupy four and eight_bytes respectively; the long and signed-long types take up eight_bytes each; the unsigned-long and __int64 types also use eight_bytes; short integers are stored in two-byte locations; a wchar_t variable takes up four-byte space; finally, long doubles require sixteen-byte allocations for storage.

  • 练习4.29

4.10 逗号运算符

返回值是最右侧表达式的值,类型同理

优先级最低(容易有题挖坑)

  • 练习4.33

somevalue ? ++x, ++y : --x, --y实际上somevalue ? ++x, ++y : --x是一个整体条件运算

4.11 类型转换

隐式的转换过程往往出现在实参与形参类型或数量不一致的情况;运算符作用于的操作数类型不符的情况;初始化值与变量声明的类型不符的情况;以及条件判断的结果并非布尔类型的情况;此外,在switch语句使用的判别对象也不是整型的情况下也会发生隐式的转换过程。这些情形主要包括以下五种常见的情形:

算术转换

常规的算术转换规则在C++语言中得到了详细说明

转换阶段为:枚举、浮点、整型

在C++编程中对整数类型的优化与改进是实现高效数据处理的关键步骤;具体而言,在C++标准库中提供了丰富的数据类型支持,并且通过隐式转换 - cppreference.com这一资源可以深入理解相关机制


当无符号类型碰到有符号类型时:

  1. 无符号更大或两者一般大:

有符号数转换为无符号数

  1. 有符号更大:

看哪个类型更大就往哪里转换

例:

其他隐式转换

将数组元素转化为指向器对象
处理指向器对象之间的数据传递
将变量或对象强制解析为布尔值
统一使用基础类型的常量进行数据转化
在类体内实现特定的自定义数据类型映射,并且仅执行一次这样的映射操作

显式转换

C++里的长这样:cast_name (expr) ——要加括号!!!

C里的长这样:(type)expr 或 type(expr)——不过会被视为使用cast

static_cast 不包含底层const的明确转换都可以用 int i, j; double slope = static_cast(j) / i; void* p = &i; double* dp = static_cast<double*>(p);
const_cast 只能用来改变底层const(指针和引用) const char* pc; char* p = const_cast<char*>(pc); auto newp = const_cast<const char*>(p);
reinterpret_cast 转换指针和引用到另一种类型(按位重新解释) int i = 7; bitset<32> a(i); cout << a << endl; // 00000000000000000000000000000111 char* p2 = reinterpret_cast<char*>(&i); std::cout << (p2[0] == '\x7' ? "本系统是小端的\n" : "本系统是大端的\n"); //低位在前为小端存储
dynamic_cast 用于安全的运行时类型识别 (RTTI) 转换,只适用于类层次结构,通常与基类指针和派生类指针一起使用 后面会介绍
  • 练习4.36
  • 练习4.37

第四章结束

略琐碎,除了左右值,其他的规则用到的时候查就可以了

全部评论 (0)

还没有任何评论哟~