02 C++语言基础
一、数据类型
unsigned, signed, short, long被称为修饰符
1.1 bool类型
bool类型,true时存放1,false时存放0
1.2 字符与字符串
C++中没有字符串常量,一般利用字符型数组来存储,也可引入string类。 注意:单引号表示字符,双引号表示字符串
char str1 = 'a'; //char只有一个长度,存储一个字符
char str2[] = "abcd"; //实际存储内容为['a','b','c','d','\0']
char str3[5] = "abcd"; //和str2等价,但不得使用str3[4]这种长度不足的写法
- 拥有众多的成员函数,对字符串进行操作,同名函数有多个重载
- 有多种构造函数,赋值方法,可通过length, size函数求长度
- 对于操作字符串,有+,append运算符进行字符串连接;有compare比较字符串;有求子串、查找子串的方法
1.3 浮点型float和double
double的精度高于float,float有6,7位有效位,double有15,16位 float占据32位内存,double占用64位内存 浮点数的比较:谨慎直接用等号 具体参见上一章:01计算机语言绪论 4.2节
1.4 不同类型数值运算
1.5 数组
数组是具有一定顺序关系的若干对象的组合体,每个元素有n个下标的称为n维数组
1.5.1 数组的声明
数据类型 标识符[常量表达式1][常量表达式2]...
- 除了void类型,其他基本数据类型都可以声明数组
- 标识符为数组的名称
- 常量表达式必须为正整数
int a [2][3],访问最后的值是a[1][2],因为下标从0开始。
1.5.2 数组的初始化
int a[3] = {1,1,1} //a[0]a[1]a[2]的值都是1
int a [] = {1,1,1} //与上一句等价
int b[5]={1,2,3} //合法,b[0]b[1]b[2]被赋值为1,2,3,b[4],b[5]默认赋值为0
int b[5] //合法,但是数组的初始值不确定,不一定为0
int c[][5] = {1,1,1,1,1} //合法,会自动判断第一个常量表达式的值
const d[5] = {1,2,3} //声明为常量的数组,必须给定初值
1.5.3 数组传参
使用数组元素作为实参,与其他情况无区别。使用数组名作为实参,传递的是地址,也就是引用传递,会直接操作原地址数据。
二、常量与变量
2.1 常量
分类:整型常量,实型常量,布尔常量,字符常量,字符串常量 利用const关键字定义,常量不会被更新 Differences between using const and #define:
The #define directive is a preprocessor directive; the preprocessor replaces those macros by their body before the compiler even sees it. Think of it as an automatic search and replace of your source code. A const variable declaration declares an actual variable in the language, which you can use... well, like a real variable: take its address, pass it around, use it, cast/convert it, etc. There's about no difference on performance.
const会被作为变量处理,#define是直接替换程序内的关键字,所以const会更为可靠 const默认为文件内局部变量,跨文件使用需要声明extern const
2.2 变量
2.2.1 声明和定义
C++中,大多数情况中声明变量就完成了定义
不同于python,类型和名称都要声明。
合法的变量名标识符:
- 以大写字母,小写字母和下划线或数字0-9开始
- 不能是关键字或操作符(否则引发重载)
2.2.2 变量的初始化:
C++中定义一个变量时,必须先对其进行初始化
//整型变量初始化
int a = 0;int a(0);//前两种使用条件宽松
int a = {0};int a{0};//后两种被称为列表初始化,使用条件严格,不允许信息丢失
int a{1.41}; //错误,无法执行
int a = 1.41 //可执行,初始化a=1
三、运算符
3.1 算数运算符
运算符 | 描述 | 实例 |
---|---|---|
+ | 把两个操作数相加 | A + B 将得到 30 |
- | 从第一个操作数中减去第二个操作数 | A - B 将得到 -10 |
* | 把两个操作数相乘 | A * B 将得到 200 |
/ | 分子除以分母 | B / A 将得到 2 |
% | 取模运算符,整除后的余数 | B % A 将得到 0 |
++ | 自增运算符,整数值增加 1 | A++ 将得到 11 |
-- | 自减运算符,整数值减少 1 | A-- 将得到 9 |
++a和a++的区别:先自增再操作还是先操作再自增
3.2 关系运算符
运算符 | 描述 | 实例 |
---|---|---|
== | 检查两个操作数的值是否相等,如果相等则条件为真。 | (A == B) 不为真。 |
!= | 检查两个操作数的值是否相等,如果不相等则条件为真。 | (A != B) 为真。 |
> | 检查左操作数的值是否大于右操作数的值,如果是则条件为真。 | (A > B) 不为真。 |
< | 检查左操作数的值是否小于右操作数的值,如果是则条件为真。 | (A < B) 为真。 |
>= | 检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。 | (A >= B) 不为真。 |
<= | 检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。 | (A <= B) 为真。 |
3.3 逻辑运算符
运算符 | 描述 | 实例 |
---|---|---|
&& | 称为逻辑与运算符。如果两个操作数都 true,则条件为 true。 | (A && B) 为 false。 |
|| | 称为逻辑或运算符。如果两个操作数中有任意一个 true,则条件为 true。 | (A || B) 为 true。 |
! | 称为逻辑非运算符。用来逆转操作数的逻辑状态,如果条件为 true 则逻辑非运算符将使其为 false。 | !(A && B) 为 true。 |
&&和||的短路特性:
1. &&第一个表达式的值为false时,其余表达式不再运行
2. ||第一个表达式的值为true时,其余表达式不再运行
3.4 位运算符(按位进行操作)
运算符 | 描述 | 实例 |
---|---|---|
& | 按位与操作,按二进制位进行"与"运算。运算规则: | |
0&0=0; 0&1=0; 1&0=0; 1&1=1; | (A & B) 将得到 12,即为 0000 1100 | |
| | 按位或运算符,按二进制位进行"或"运算。运算规则: | |
0|0=0; 0|1=1; 1|0=1; 1|1=1; | (A | B) 将得到 61,即为 0011 1101 | |
^ | 异或运算符,按二进制位进行"异或"运算。运算规则: | |
0^0=0; 0^1=1; 1^0=1; 1^1=0; | (A ^ B) 将得到 49,即为 0011 0001 | |
~ | 取反运算符,按二进制位进行"取反"运算。运算规则: | |
~1=-2; ~0=-1; | (~A ) 将得到 -61,即为 1100 0011,一个有符号二进制数的补码形式。 | |
<< | 二进制左移运算符。将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。 | A << 2 将得到 240,即为 1111 0000 |
>> | 二进制右移运算符。将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。 | A >> 2 将得到 15,即为 0000 1111 |
3.5 运算符优先级与结合性:
3.6 混合运算时的数据类型转换
二元运算符一般要求两个操作数的类型一致
3.6.1 隐式转换:
原则:向精度更高的,长度更长的转化
特点:转换是安全的,过程中数据的精度没有损失
例:两数中一个是long double则另一个也转换为long double
3.6.2 赋值转换:
当进行赋值运算时,右值会被自动转化为左值的类型
例:整型i = 浮点型m,则i只取m的整数部分,小数部分精度丢失
3.6.3 显式转换:
特点:可能是不安全的,将高类型数据向低类型转换时,精度会丢失
关键字 | 说明 |
---|---|
static_cast | 用于良性转换,一般不会导致意外发生,风险很低。 |
const_cast | 用于 const 与非 const、volatile 与非 volatile 之间的转换。 |
reinterpret_cast | 高度危险的转换,这种转换仅仅是对二进制位的重新解释,不会借助已有的转换规则对数据进行调整,但是可以实现最灵活的 C++ 类型转换。 |
dynamic_cast | 借助 RTTI,用于类型安全的向下转型(Downcasting)。 |
语法static_cast<newType>(data)
四、选择结构,循环结构
4.1 选择结构switch
#include <iostream>
using namespace std;
int main() {
int day;
cin >> day;
switch (day) {
case 0:
cout << "Sunday" << endl;
break;
case 1:
cout << "Monday" << endl;
break;
default:
cout << "I'm Lazy" << endl;
break;
}
return 0;
}
4.2 循环结构do while
#include <iostream>
using namespace std;
int main() {
int i = 0;
do{
i++;
} while (i < 10);
cout<<i << endl;
return 0;
}
// 输出10,说明在判断i<10 false之前,就已经完成了i++
4.3 循环结构 for
初始语句中声明的变量,只在循环内部有效,所以建议在初始语句中定义控制变量
循环逻辑:初始语句只执行一次,而表达式2(第三个表达式)则是每次执行完语句才执行
五、类型别名与类型推断
5.1 类型别名:
typedef定义数据类型的别名
typedef与#define的区别- typedef只能定义数据类型的别名,而#define可以为数值定义别名(定义1为ONE)
- typedef由编译器执行解释,#define是由预编译器进行处理的
5.2 auto类型和decltype类型
auto是一种数据类型,由编译器自动分析判断使用什么类型的数据
decltype将一种未知的数据类型赋予给其他变量