[Cherno C++ 笔记 P11~P20]判断,循环,指针,引用,类
系列博客
[Cherno C++ 笔记 P1~P10]安装、链接器、变量、函数、头文件
[Cherno C++ 笔记 P11~P20]判断,循环,指针,引用,类
[Cherno C++ 笔记 P21~P30]static,枚举,构造函数,析构函数,继承,虚函数,接口,可见性
[Cherno C++ 笔记 P31~P40]数组、字符串、CONST、mutable、成员初始化列表、三元操作符、创建初始化对象、new关键字、隐式转换与explicit
[Cherno C++ 笔记 P41~P50]运算符重载、this、生存期、智能指针、复制与拷贝构造函数、箭头操作符、动态数组、std::vector、静态链接、动态库
前言
这个系列的视频需要一些基础,最好是学过C。
视频链接
P11 如何在Visual Studio中调试代码
P12 C++条件与分支(if语句)
P13 Visual Studio的最佳设置
P14 C++循环(for 、while)
P15 C++控制流语句(continue, break, return)
P16 C++指针
P17 C++引用
P18 C++类
P19 C++类与结构体对比
P20 如何写一个C++类
P11 如何在Visual Studio中调试代码
略
P12 C++条件与分支(if语句)
if else
让我们写一个简单的if else
#include<iostream>
int main()
{
int a = 5;
if (a < 5)
{
std::cout << a << std::endl;
}
else
{
std::cout << a << std::endl;
}
std::cin.get();
}
汇编中的if else
让我们先来看一下比较语句所产生的汇编
当然,我们可以通过 逐语句 运行来查看这些语句的执行过程
我们也可以比较<、>、==、>=、<=之间的差别
接下来我们可以来看看if else产生的汇编
大括号之间的东西我们大可不必关心,如果不在大括号中写点什么的话,这里的if else是不会产生汇编语句的。
我们需要关心的是其中的跳转语句。
可以看到,如果比较不成功的话,我们会直接跳转到else大括号中的第一行0FB1ED8.这里else并没有产生什么汇编语句,产生汇编语句的是else大括号中的代码。
如果比较成功,在运行完if下的语句后,我们会看到有一个jmp跳转,跳过else中的内容。
我们知道bool中0为False,其他为True,所以我们可以这么写。
je的功能是当值为0时进行跳转。
这样我们就来根据一个指针是否为空来进行相应的操作
可以看到,同样产生了je指令。
P13 Visual Studio的最佳设置
略
P14 C++循环(for 、while)
for,while,do while
让我们简单的来写一下这几种循环
#include<iostream>
int main()
{
int a = 0;
for (int i = 0; i < 5; i++)
{
a++;
}
while (a<10)
{
a++;
}
do
{
a++;
} while (a<20);
std::cout << a << std::endl;
std::cin.get();
}
汇编中的循环
我们可以通过逐行执行来观察,for的执行顺序是 E7C >E83>E8E>E92,在这里执行判断,如果比较成功,接着往下>E94>E97>E9A>E9D>E85>E88>E8B,在这里进行+1的操作后,接着往下>E8E…实现循环。
我们再来比较一下while 和 do while
我们可以看到这些循环实现原理都是一样的。
这几种循环可以相互替换,如何使用看个人习惯。
我个人的习惯是:有明确循环次数时使用for,没有明确循环次数时使用while
P15 C++控制流语句(continue, break, return)
我们来简单写一下
#include<iostream>
int func(int a)
{
for (int i = 0; i < 10; i++)
{
if (a++ % 3 == 0)
{
std::cout << a << std::endl;
}
else
{
continue;
}
if (a++ < 10)
{
break;
}
}
return a;
}
int main()
{
int x = 0;
std::cout << func(x) << std::endl;
std::cin.get();
}
汇编中的break,continue,return
我们来看一下主要部分
for (int i = 0; i < 10; i++)
00F65F95 mov dword ptr [ebp-8],0
00F65F9C jmp __$EncStackInitStart+2Bh (0F65FA7h)
00F65F9E mov eax,dword ptr [ebp-8]
00F65FA1 add eax,1
00F65FA4 mov dword ptr [ebp-8],eax
00F65FA7 cmp dword ptr [ebp-8],0Ah
00F65FAB jge __$EncStackInitStart+0EEh (0F6606Ah)
{
if (a++ % 3 == 0)
00F65FB1 mov eax,dword ptr [a]
00F65FB4 cdq
00F65FB5 mov ecx,3
00F65FBA idiv eax,ecx
00F65FBC mov dword ptr [ebp-0D0h],edx
00F65FC2 mov edx,dword ptr [a]
00F65FC5 add edx,1
00F65FC8 mov dword ptr [a],edx
00F65FCB cmp dword ptr [ebp-0D0h],0
00F65FD2 jne __$EncStackInitStart+64h (0F65FE0h)
00F65FD4 mov dword ptr [ebp-0D4h],1
00F65FDE jmp __$EncStackInitStart+6Eh (0F65FEAh)
00F65FE0 mov dword ptr [ebp-0D4h],0
00F65FEA cmp dword ptr [ebp-0D4h],0
00F65FF1 je __$EncStackInitStart+0A8h (0F66024h)
{
std::cout << a << std::endl;
00F65FF3 mov esi,esp
00F65FF5 push offset std::endl<char,std::char_traits<char> > (0F6103Ch)
00F65FFA mov edi,esp
00F65FFC mov eax,dword ptr [a]
00F65FFF push eax
00F66000 mov ecx,dword ptr [__imp_std::cout (0F6D0DCh)]
00F66006 call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0F6D0E4h)]
00F6600C cmp edi,esp
00F6600E call __RTC_CheckEsp (0F61294h)
00F66013 mov ecx,eax
00F66015 call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0F6D0A0h)]
00F6601B cmp esi,esp
00F6601D call __RTC_CheckEsp (0F61294h)
}
00F66022 jmp __$EncStackInitStart+0ADh (0F66029h)
else
{
continue;
00F66024 jmp __$EncStackInitStart+22h (0F65F9Eh)
}
if (a++ < 10)
00F66029 mov eax,dword ptr [a]
00F6602C mov dword ptr [ebp-0D0h],eax
00F66032 mov ecx,dword ptr [a]
00F66035 add ecx,1
00F66038 mov dword ptr [a],ecx
00F6603B cmp dword ptr [ebp-0D0h],0Ah
00F66042 jge __$EncStackInitStart+0D4h (0F66050h)
00F66044 mov dword ptr [ebp-0D4h],1
00F6604E jmp __$EncStackInitStart+0DEh (0F6605Ah)
00F66050 mov dword ptr [ebp-0D4h],0
00F6605A cmp dword ptr [ebp-0D4h],0
00F66061 je __$EncStackInitStart+0E9h (0F66065h)
{
break;
00F66063 jmp __$EncStackInitStart+0EEh (0F6606Ah)
}
}
00F66065 jmp __$EncStackInitStart+22h (0F65F9Eh)
return a;
00F6606A mov eax,dword ptr [a]
}
00F6606D pop edi
00F6606E pop esi
00F6606F pop ebx
00F66070 add esp,0D4h
00F66076 cmp ebp,esp
00F66078 call __RTC_CheckEsp (0F61294h)
00F6607D mov esp,ebp
00F6607F pop ebp
00F66080 ret
这样我们可以清楚的看到break、continue、return的工作原理。
P16 C++指针
什么是指针
指针是一个整数,一种存储内存地址的数字。
所有类型的指针都是保存内存地址的整数。
所有类型的指针都是保存内存地址的整数。
所有类型的指针都是保存内存地址的整数。
我们来创建一个空指针瞅一瞅
void* ptr = 0;
,这是一个空指针,它没有任何类型。当然,我们也可以用NULL来创建,
这样我们就创建了一个没有用的空指针。
让我们给它一个值
int a = 5;
void* ptr = &a;
我们给他打个断点来看看
ptr的值是以十六进制显示出来的一个整数,这个指针现在保存的就是a的内存地址
让我们在内存中看一看
我们把ptr的值复制过去看看
可以看到这里的值为5
我们把指针的类型改成long long
int a = 5;
long long* ptr = (long long*)&a;
我们可以看到,这里的值仍然是5
P17 C++引用
int a = 5;
int& ref = a;
ref这个变量实际上并不存在,它可以看作是变量a的别名。对ref进行操作实际上是对a操作。
当设置完成以后,不能再改变它引用的东西。
P18 C++类
一个简单的类
class Player
{
public:
int x, y;
int speed;
void Move(int xa,int ya)
{
x += xa * speed;
y += ya * speed;
}
};
类允许我们将变量分组到一个类型中,并为这些变量添加功能。
类并没有为变量提供什么新功能,不能用一般代码完成的工作用类同样无法完成。类只是提高了我们维护代码的效率。
P19 C++类与结构体对比
class 和 struct
class Player
{
int x, y;
};
struct MyStruct
{
int x, y;
};
结构体和类基本上没有什么区别,只有一个关于可见度的小区别,class中默认为private,而struct中默认为public。
C++中存在结构体的原因是为了与C保持向后兼容性。
P20 如何写一个C++类
略