cpp学习-第四章-表达式

核心简记

  1. 内置类型的运算符操作由语言定义
  2. 重载运算符的操作对象类型与返回值类型可以自己定义,但是运算对象的个数、优先级、结合律无法改变
  3. 表达式的求值结果分为左值与右值,记住左身份右内容就可以了
  4. 优先级与结合律只是规定了当前表达式对象的求值顺序。但是运算对象的求值顺序是没有指定的。比如(i++)*(i–)的结果是不定义的,因为*的左右对象求值顺序并不指定,只要求在*之前求出俩运算对象的值即可。规定求值顺序的有:&&||?: 从编译过程来看的话就是左右子树的求值顺序不定,但保证在该节点求值时子树节点处理完,翻译时为直线翻译但顺序不定,此外给予优化以方便。从一个表达式整体来看,其用到的对象只要保证表达式求值时前求好值即可,顺序都不定。优先级和结合性是编译时概念,与求值顺序独立,后者是运行时概念。
  5. 数学规定(m/n)*n+m%n=m因此m/n符号不同为负,m%n符号与m相同。且整数商一律向0取整。
  6. 逻辑运算规定先求左侧的值再求右侧的值,为短路求值。具体实现为翻译时采用用条件跳转优化。
  7. 测试非布尔值的布尔就直接写在条件里,否则非布尔值会使布尔向上提升。
  8. 列表初始化或列表赋值的要求:使用参数为std::initializer_list的构造函数或赋值运算符,或者是这个类满足 aggregate class 的条件。对于类来说,赋值运算的细节由定义定。
  9. ++–前置与后置:前置为先改变对象再返回改变后的对象,后置为改变对象再返回改变前的值的副本。与赋值操作使用要小心,因为赋值左右的求值顺序不定。
  10. 变量是一个具名的、可操作的存储空间,有其数据类型。数据类型决定变量的空间及操作。点运算符针对变量,箭头针对指针变量。变量在编译原理下就是分配的一块地址中的内容。*是取这个内容作为地址再将地址所在内容作为对象。.是寻找该内容中的某对象或操作。这些语法大多是给编译器认识的,具体到汇编层面只有地址:值,寄存器。针对这些地址、值做操作。
  11. 条件运算符的俩个表达式只会求值一个。
  12. 位运算符没有规定符号位,因此不用于有符号类型。且小整形会被提升。
  13. sizeof有俩种:sizeof (type)求类型的对象所占大小与 sizeof exp求表达式结果类型的大小,表达式并不求值。对引用为被引用的对象所占大小,指针为指针本身大小,解指针为指针指向对象所占大小,对数组为整个数组所占大小(数组这里含义特殊,不当指针了),string与vector只返回固定部分的大小。返回结果为size_t类型的常量表达式-编译时确定。
  14. 算数类型之间的隐式转化被设计的尽可能少损失精度,发生于:表达式类型统一,条件中化为布尔,赋值中左变右,小整数会被提升为int,有符号与无符号混合化为无符号
  15. 大多数数组的表达式中,数组自动转化为指向数组首元素的指针。例外是:decltype、取地址(所以&a返回的不是指针地址而是数组地址)、sizeof、typeid。
  16. 类类型的转化由类类型自己定义,从类转化出去或是别的转化过来都可以定义。
  17. 显示转化见下
  18. 优先级表见下

显示转化

显示转化采用cast-name(exp)的形式,type是转化目标类型,exp是表达式。cast-name分为:

  1. static_cast:具有明确定义的类型转化,不包含底层const
  2. dynamic_cast:运行时类型识别
  3. reinterpret_cast:底层重新解释,如指针化为int
  4. const_cast:用于改变运算对象的底层const

旧式转化(type) exp;根据type类型不同,旧式可化为其中之一,

c++运算符优先级表

https://zh.cppreference.com/w/cpp/language/operator_precedence
插入图

反汇编

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include<iostream>
int main(int argc, char const *argv[])
{
char arry[10]={0};
int sizeint=sizeof(arry);
void *ls=arry;
int a=18;
int b=a/2;
b=a%3;
b=a++;
b=++a;
b=a*10;
b=a>>1;
b=a<<1;
b=a?a:2;
return 0;
}
/*
relase模式下对除法有十分复杂的优化,运用数学转化为乘法与位运算
*/

x86-64

指令简记

cdq:把EAX的符号位复制到EDX的每一个 bit 上

DIV SRC:
  字节操作:16位被除数在AX,8位除数为源操作数,结果的8位商在AL中,8位余数在AH中。表示为
  (AL)<-(AX)/(SRC) 的商
   (AH) <-(AX)/(SRC) 的余数

  字操作:32位被除数放在DX,AX中。其中DX为高位字,16位除数为源操作数,结果的16位端在AX中,16位余数在DX中。表示为
  (AX)<-(DX,AX)/(SRC) 的商
  (DX)<-(DX,AX)/(SRC) 的余数

  双字操作:64位被除数在EDX,EAX中,其中EDX为高位双字,32位除数为源操作数,结果的32位商在EAX中,32位余数在EDX中,表示为
  (EAX)<-(EDX,EAX)/(SRC) 的商
  (EDX)<-(EDX,EAX)/(SRC) 的余数。

DIV均为无符号数,IDIV为符号数

IMUL r/m ;单操作数
如果参数是 r8/m8, 将把 AL 做乘数, 结果放在 AX
如果参数是 r16/m16, 将把 AX 做乘数, 结果放在 EAX
如果参数是 r32/m32, 将把 EAX 做乘数, 结果放在 EDX:EAX
IMUL r16/r32, r16/r32/m16/m32/i ;双操作数, (1)(2) -> (1)
IMUL r16/r32, r16/r32/m16/m32, i ;三操作数, (2)
(3) -> (1)
同样I为有符号

分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
.text:0000000000000600 sub_600 proc near ; DATA XREF: start+2E↑o
.text:0000000000000600
.text:0000000000000600 var_54 = dword ptr -54h
.text:0000000000000600 var_50 = dword ptr -50h
.text:0000000000000600 var_4C = dword ptr -4Ch
.text:0000000000000600 var_48 = qword ptr -48h
.text:0000000000000600 var_40 = dword ptr -40h
.text:0000000000000600 var_3C = dword ptr -3Ch
.text:0000000000000600 var_38 = qword ptr -38h
.text:0000000000000600 var_2C = dword ptr -2Ch
.text:0000000000000600 var_28 = qword ptr -28h
.text:0000000000000600 var_1C = dword ptr -1Ch
.text:0000000000000600 var_18 = dword ptr -18h
.text:0000000000000600 s = byte ptr -12h
.text:0000000000000600 var_8 = qword ptr -8
.text:0000000000000600
.text:0000000000000600 ; __unwind {
.text:0000000000000600 push rbp
.text:0000000000000601 mov rbp, rsp
.text:0000000000000604 sub rsp, 60h
.text:0000000000000608 ; 6: v2 = __readfsqword(0x28u);
//有的常数被分配在栈中了...毕竟debug版...
.text:0000000000000608 mov eax, 3
.text:000000000000060D mov ecx, 2
.text:0000000000000612 lea rdx, [rbp+s]
.text:0000000000000616 xor r8d, r8d
.text:0000000000000619 mov r9d, 0Ah
.text:000000000000061F mov r10d, r9d
.text:0000000000000622 mov r11, fs:28h
.text:000000000000062B mov [rbp+var_8], r11
.text:000000000000062F ; 7: memset(&s, 0, 0xAuLL);
.text:000000000000062F mov [rbp+var_18], 0
.text:0000000000000636 mov [rbp+var_1C], edi
.text:0000000000000639 mov [rbp+var_28], rsi
.text:000000000000063D mov rsi, rdx
.text:0000000000000640 mov rdi, rsi ; s
.text:0000000000000643 mov esi, r8d ; c
//var_48为数组的指针,数组地址为s
.text:0000000000000646 mov [rbp+var_48], rdx
.text:000000000000064A mov rdx, r10 ; n
.text:000000000000064D mov [rbp+var_4C], ecx
.text:0000000000000650 mov [rbp+var_50], eax
.text:0000000000000653 call _memset
//int sizeint=sizeof(arry);sizeof为编译时计算
.text:0000000000000658 mov [rbp+var_2C], 0Ah
//void *ls=arry;
.text:000000000000065F mov rdx, [rbp+var_48]
.text:0000000000000663 mov [rbp+var_38], rdx
//int a=18;
.text:0000000000000667 mov [rbp+var_3C], 12h
//int b=a/2;
.text:000000000000066E mov eax, [rbp+var_3C]
.text:0000000000000671 cdq //扩展EDX为符号位,因为64位EDX也参与运算。
.text:0000000000000672 mov ecx, [rbp+var_4C]
.text:0000000000000675 idiv ecx
.text:0000000000000677 mov [rbp+var_40], eax
// b=a%3;
.text:000000000000067A mov eax, [rbp+var_3C]
.text:000000000000067D cdq
.text:000000000000067E mov esi, [rbp+var_50]
.text:0000000000000681 idiv esi
.text:0000000000000683 mov [rbp+var_40], edx
//b=a++;
.text:0000000000000686 mov edx, [rbp+var_3C]
.text:0000000000000689 mov r8d, edx //拷贝a
.text:000000000000068C add r8d, 1
.text:0000000000000690 mov [rbp+var_3C], r8d //a放加后的
.text:0000000000000694 mov [rbp+var_40], edx //b放加前的
//b=++a;
.text:0000000000000697 mov edx, [rbp+var_3C]
.text:000000000000069A add edx, 1//不拷贝
.text:000000000000069D mov [rbp+var_3C], edx
.text:00000000000006A0 mov [rbp+var_40], edx //a,b都放加后的
// b=a*10;
.text:00000000000006A3 imul edx, [rbp+var_3C], 0Ah
.text:00000000000006A7 mov [rbp+var_40], edx
//b=a>>1;
.text:00000000000006AA mov edx, [rbp+var_3C]
.text:00000000000006AD sar edx, 1
.text:00000000000006B0 mov [rbp+var_40], edx
// b=a<<1;
.text:00000000000006B3 mov edx, [rbp+var_3C]
.text:00000000000006B6 shl edx, 1
.text:00000000000006B9 mov [rbp+var_40], edx
//b=a?a:2;
.text:00000000000006BC cmp [rbp+var_3C], 0
.text:00000000000006C0 jz loc_6D1 //false
.text:00000000000006C6 mov eax, [rbp+var_3C]
.text:00000000000006C9 mov [rbp+var_54], eax
.text:00000000000006CC jmp loc_6DE
.text:00000000000006D1 ; ---------------------------------------------------------------------------
.text:00000000000006D1
.text:00000000000006D1 loc_6D1: ; CODE XREF: sub_600+C0↑j
.text:00000000000006D1 mov eax, 2
.text:00000000000006D6 mov [rbp+var_54], eax
.text:00000000000006D9 jmp $+5
.text:00000000000006DE ; ---------------------------------------------------------------------------
.text:00000000000006DE ; 8: result = 20LL;
.text:00000000000006DE
.text:00000000000006DE loc_6DE: ; CODE XREF: sub_600+CC↑j
.text:00000000000006DE ; sub_600+D9↑j
.text:00000000000006DE mov eax, [rbp+var_54]
.text:00000000000006E1 ; 9: if ( __readfsqword(0x28u) == v2 )
.text:00000000000006E1 mov [rbp+var_40], eax
//栈保护
.text:00000000000006E4 mov rcx, fs:28h
.text:00000000000006ED mov rdx, [rbp+var_8]
.text:00000000000006F1 cmp rcx, rdx
.text:00000000000006F4 jnz loc_702
.text:00000000000006FA ; 10: result = 0LL;
.text:00000000000006FA xor eax, eax
.text:00000000000006FC ; 11: return result;
.text:00000000000006FC add rsp, 60h
.text:0000000000000700 pop rbp
.text:0000000000000701 retn //这里没有压栈参因此不用它恢复
.text:0000000000000702 ; ---------------------------------------------------------------------------
.text:0000000000000702
.text:0000000000000702 loc_702: ; CODE XREF: sub_600+F4↑j
.text:0000000000000702 call ___stack_chk_fail
.text:0000000000000702 ; } // starts at 600
.text:0000000000000702 sub_600 endp

sizeof是直接编译时计算的,因此编译为常数,其它都是正常汇编指令使用。注意release模式下的除法与乘法会有很大优化改动。

值得一提的是ida反编译也进行了优化…把不影响外界、输入出等的汇编代码不进行反编译。

ARM64

指令简记

UXTB:字节被无符号扩展到32 位(高24 位清0——译注)

MADD W10, W10, W8, WZR
W10=W10*W8

分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
.text:00000000000007E8 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00000000000007E8 EXPORT main
.text:00000000000007E8 main ; DATA XREF: LOAD:0000000000000460↑o
.text:00000000000007E8 ; .got:main_ptr↓o
.text:00000000000007E8
.text:00000000000007E8 var_60 = -0x60
.text:00000000000007E8 var_5C = -0x5C
.text:00000000000007E8 var_58 = -0x58
.text:00000000000007E8 var_54 = -0x54
.text:00000000000007E8 var_50 = -0x50
.text:00000000000007E8 var_44 = -0x44
.text:00000000000007E8 var_40 = -0x40
.text:00000000000007E8 var_3C = -0x3C
.text:00000000000007E8 var_38 = -0x38
.text:00000000000007E8 var_2C = -0x2C
.text:00000000000007E8 var_28 = -0x28
.text:00000000000007E8 var_1C = -0x1C
.text:00000000000007E8 var_18 = -0x18
.text:00000000000007E8 var_14 = -0x14
.text:00000000000007E8 var_8 = -8
.text:00000000000007E8 var_s0 = 0
.text:00000000000007E8
.text:00000000000007E8 ; __unwind {
.text:00000000000007E8 SUB SP, SP, #0x70
.text:00000000000007EC STP X29, X30, [SP,#0x60+var_s0]
.text:00000000000007F0 ADD X29, SP, #0x60
.text:00000000000007F4 MOV W8, #0xA
.text:00000000000007F8 MOV W9, #3
.text:00000000000007FC MOV W10, #0x12
.text:0000000000000800 SUB X11, X29, #-var_14
.text:0000000000000804 MOV W12, WZR
.text:0000000000000808 MOV X2, #0xA
.text:000000000000080C MRS X13, #3, c13, c0, #2
.text:0000000000000810 LDR X13, [X13,#0x28]
.text:0000000000000814 STUR X13, [X29,#var_8]
.text:0000000000000818 STUR WZR, [X29,#var_18]
.text:000000000000081C STUR W0, [X29,#var_1C]
.text:0000000000000820 STUR X1, [X29,#var_28]
.text:0000000000000824 MOV X1, X11
.text:0000000000000828 MOV X0, X1
.text:000000000000082C UXTB W1, W12
.text:0000000000000830 STR W10, [SP,#0x60+var_44]
.text:0000000000000834 STR X11, [SP,#0x60+var_50]
.text:0000000000000838 STR W8, [SP,#0x60+var_54]
.text:000000000000083C STR W9, [SP,#0x60+var_58]
.text:0000000000000840 BL .memset
//int sizeint=sizeof(arry);
.text:0000000000000844 LDR W8, [SP,#0x60+var_54]
.text:0000000000000848 STUR W8, [X29,#var_2C]
//void *ls=arry;
.text:000000000000084C LDR X11, [SP,#0x60+var_50]
.text:0000000000000850 STR X11, [SP,#0x60+var_38]
//int a=18;
.text:0000000000000854 LDR W9, [SP,#0x60+var_44]
.text:0000000000000858 STR W9, [SP,#0x60+var_3C]
//int b=a/2;
.text:000000000000085C LDR W10, [SP,#0x60+var_3C]
.text:0000000000000860 ADD W12, W10, #1
.text:0000000000000864 CMP W10, #0
.text:0000000000000868 CSEL W10, W12, W10, LT
.text:000000000000086C ASR W10, W10, #1
.text:0000000000000870 STR W10, [SP,#0x60+var_40]
//b=a%3;
.text:0000000000000874 LDR W10, [SP,#0x60+var_3C]
.text:0000000000000878 MOV W12, #3
.text:000000000000087C LDR W1, [SP,#0x60+var_58]
.text:0000000000000880 SDIV W14, W10, W1
.text:0000000000000884 MSUB W10, W14, W1, W10
.text:0000000000000888 STR W10, [SP,#0x60+var_40]
// b=a++;
.text:000000000000088C LDR W10, [SP,#0x60+var_3C]
.text:0000000000000890 ADD W14, W10, #1
.text:0000000000000894 STR W14, [SP,#0x60+var_3C]
.text:0000000000000898 STR W10, [SP,#0x60+var_40]
// b=++a;
.text:000000000000089C LDR W10, [SP,#0x60+var_3C]
.text:00000000000008A0 ADD W10, W10, #1
.text:00000000000008A4 STR W10, [SP,#0x60+var_3C]
.text:00000000000008A8 STR W10, [SP,#0x60+var_40]
//b=a*10
.text:00000000000008AC LDR W10, [SP,#0x60+var_3C]
.text:00000000000008B0 MADD W10, W10, W8, WZR
.text:00000000000008B4 STR W10, [SP,#0x60+var_40]
b=a>>1;
.text:00000000000008B8 LDR W10, [SP,#0x60+var_3C]
.text:00000000000008BC ASR W10, W10, #1
.text:00000000000008C0 STR W10, [SP,#0x60+var_40]
b=a<<1;
.text:00000000000008C4 LDR W10, [SP,#0x60+var_3C]
.text:00000000000008C8 LSL W10, W10, #1
.text:00000000000008CC STR W10, [SP,#0x60+var_40]
//b=a?a:2;
.text:00000000000008D0 LDR W10, [SP,#0x60+var_3C]
.text:00000000000008D4 STR W12, [SP,#0x60+var_5C]
.text:00000000000008D8 CBZ W10, loc_8E8
.text:00000000000008DC LDR W8, [SP,#0x60+var_3C]
.text:00000000000008E0 STR W8, [SP,#0x60+var_60]
.text:00000000000008E4 B loc_8F4
.text:00000000000008E8 ; ---------------------------------------------------------------------------
.text:00000000000008E8
.text:00000000000008E8 loc_8E8 ; CODE XREF: main+F0↑j
.text:00000000000008E8 MOV W8, #2
.text:00000000000008EC STR W8, [SP,#0x60+var_60]
.text:00000000000008F0 B loc_8F4
.text:00000000000008F4 ; ---------------------------------------------------------------------------
.text:00000000000008F4
.text:00000000000008F4 loc_8F4 ; CODE XREF: main+FC↑j
.text:00000000000008F4 ; main+108↑j
.text:00000000000008F4 LDR W8, [SP,#0x60+var_60]
.text:00000000000008F8 STR W8, [SP,#0x60+var_40]
.text:00000000000008FC MRS X9, #3, c13, c0, #2
.text:0000000000000900 LDR X9, [X9,#0x28]
.text:0000000000000904 LDUR X10, [X29,#var_8]
.text:0000000000000908 CMP X9, X10
.text:000000000000090C B.NE loc_924
.text:0000000000000910 MOV W8, WZR
.text:0000000000000914 MOV W0, W8
.text:0000000000000918 LDP X29, X30, [SP,#0x60+var_s0]
.text:000000000000091C ADD SP, SP, #0x70
.text:0000000000000920 RET
.text:0000000000000924 ; ---------------------------------------------------------------------------
.text:0000000000000924
.text:0000000000000924 loc_924 ; CODE XREF: main+124↑j
.text:0000000000000924 BL .__stack_chk_fail
.text:0000000000000924 ; } // starts at 7E8
.text:0000000000000924 ; End of function main