B站于仕琪老师课程学习笔记
Gitbub:https://github.com/ShiqiYu/CPP
2.1 Integer numbers
变量初始化
对于基本数据类型而言,声明时赋值(int n =10
)和声明后赋值(int n; n=10
)的意思时相同的,但是对于符合数据类型(如class、结构体等)初始化和赋值这两种操作可能具有不同的行为,原因是初始化函数和赋值函数可能有所不同。
初始化还有一些新的方法,如下,其中第二个时C++11标准的新初始化方法:
int n (10); // 比 = 新一些的标准
int n {10}; // initialize, C++11 standard
变量未初始化
对于没有初始化的变量,C++标准没有给定一个默认值,C++编译器也不会提出提示,会得到一个随机数,具体时多少取决于编译器和计算机架构等。
未初始化的结果就是同一个程序在不同的时间和不同的平台上出现不同的错误,给调试带来巨大困难。
Java编译器在遇到为初始化的变量的时候就会报错。
溢出(Overflow)
溢出同样也不会报错。
C和C++标准没有指定int
的宽度,它在大多数平台上是32位的,int
相当于signed int
。int
的范围是 [-231, 231-1]。最大值231(2,147,483,648)并不是一个难以达到的大数字。
其他整数类型及其长度(sizeof())
不同长度的整数:short int (short)、long int (long)、long long int (long)
Java中int
的长度是固定为32bit的,但是在C++中int
及其拓展都没有固定的位数,具体如下:
C++实现所选择采用的基础类型的大小统称为“数据模型”
sizeof()
运算符可以一字节(Bytes)为单位返回数据的宽度。(1Byte = 8 bit)
//size.cpp
#include <iostream>
using namespace std;
int main()
{
int i = 0;
short s = 0;
cout << "sizeof(int)=" << sizeof(int) << endl;
cout << "sizeof(i)=" << sizeof(i) << endl;
cout << "sizeof(short)=" << sizeof(s) << endl;
cout << "sizeof(long)=" << sizeof(long) << endl;
cout << "sizeof(size_t)=" << sizeof(size_t) << endl;
return 0;
}
上述程序的运算结果如下:
sizeof(int) = 4
sizeof(i) = 4
sizeof(short) = 2
sizeof(long) = 8
sizeof(size_t) = 8
这里需要注意到是,sizeof()
在语法上看上去很像一个函数,但是其实是运算符,并不是函数。因为函数不能将数据类型作为参数。
2.2 more integer types
char
char
虽然用来表示字符,但是本质上是一个8bit的整数,所以也有有符号和无符号之说,只写一个char
是不确定其是有符号还是无符号,具体视平台而定。(int 默认就是有符号int。)
char
其实是用字符的编码来表示字符(英文字符就用ASCII码),char
对于英语字符来说足够宽,但对于中文、日语、韩语和其他一些字符来说就不够宽了,所以char16_t
和char32_t
已被引入c++ 11,其范围分别为16位和32位。
#include<iostream>
using namespace std;
int main()
{
char c1 ='C';
char c2 = 80;
char c3 = 0x50;
char16_t c4 = u'晋';
char32_t c5 = U'晋';
cout << c1 << " " << c2 << " " << c3 << endl;
cout << +c1 << " " << +c2 << " " << +c3 << endl;
cout << c4 << endl;
cout << c5 << endl;
return 0;
}
+c1
不会改变c1
的值,但是会使其由字符输出,变为整数输出。
编译执行之后如下:
这里需要注意一下编码格式,使用GBK格式编写的代码在执行的时候就会报错,改用UTF-8格式写代码就没有问题。
bool
C++的关键字,并不是C的关键字。
bool
的宽度是 1Byte(8 bit),而不是 1 bit。(因为计算机的最小单位是 Byte)(sizeof()得到的结果是1)
bool并不是C的关键字,要想在C中使用,有以下两种方法:
- 使用
typedef
定义
typedef char bool; // 给char类型一起个别名
#define true 1
#define false 0
- c99之后,
stdbool.h
定义了上述过程,使用的时候,直接包含头文件即可
size_t
也是一个整数,用于表示内存的大小、元素的个数、下标等。
对于过去的数据长度(或者说内存大小),32bit 的int
能够表示4Gb,是足够用的。但是随着内存的不断增大,int已经不够用了,这个时候当然可以用long
、long long
等。
( GB → MB → KB → B(Byte),换算单位是210,1Byte = 8bit, 1GB = 230 bit )
C++为此定义了一个统一的标准,使用size_t
来表示。
理论上可以表示任何大小,也就是说使用size_t来表示内存大小,不会出现问题,当前系统支持多少位,它就会使用多少位。
固定宽度的整数类型(C++11)
相同整数类型的不同宽度可能会导致程序难以移植到不同的平台。自c++ 11以来,在头文件<cstdint>
中引入了一些固定宽度的整数类型,包括int8_t,uint8_t,int16_t,uint16_t,int32_t,uint32_t,int64_t,uint64_t
等。
该头文件中还定义了一些宏,来表示这些数据类型能够表示的数据大小的范围,如INT_MAX ,INT_MIN , INT8_MAX , UINT8_MAX
等。
选择合适的数据类型
感觉上,数据类型的宽度越大越好,但是更大意味着消耗更多的内存空间,有时候还会降低计算速度。如下
该照片的有6270*3780个像素点,每个像素点是RGB三种颜色,所以总共需要76,240,800个数据保存,如果使用char(Byte)来存储,只需要76M Bytes的空间,使用int要变大4倍,long就是8倍。
补充: 计算机保存图片的方式
2.3 floating point numbers
先看一个例子:
//float.cpp
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
float f1 = 1.2f;
float f2 = f1 * 1000000000000000;
cout << std::fixed << std::setprecision(15) << f1 << endl; // 格式化输出
cout << std::fixed << std::setprecision(1) << f2 << endl;
return 0;
}
程序执行结果如下:
为什么不是准确的1.2呢?
[0,1]之间的实数(浮点数、小数)是无穷多的,32 bits最多只能表示232个,我们不可能能用有限的位数取表达无限的小数,这里就使用采样的方式来表示浮点数。
对于1.2,计算机能偶提供的是1.200000047683716… 为什么是这样的?看上去不整齐?这是因为显示的是10进制,用二进制表示的时候是很整齐的。
我们可能认为计算机总是准确的。但事实并非如此。浮点运算总是会带来一些小错误。这些错误是无法消除的。我们能做的就是管理他们,不让他们出问题。(比如在计算矩阵的秩的时候,误差累计到一定程度就会出错,或者计算不出来。)
浮点数的格式
基数 (红色)+ 缩放(绿色)
绿色部分是8bit有符号数,减去-127,其实表示的就是在二进制转十进制的时候按照无符号数处理,然后再减去127就可以了。
对于上例:(-1)0 x (2)-3 x 1.25 = 0.15625 ; 1.0100000000000000000000B =1.25D
我们不能用有限长度的二进制向量表示无限的数。只有0到1.0之间的有限数字可以用浮点数表示。其余部分在浮点数空间中消失。根据浮点方程,任何二进制代码的组合都不能表示精确的“1.2”,在它的空间中只有近似的“1.200000047683716”。
浮点数 vs. 整数
精度(Precision)
//precision.cpp
#include <iostream>
using namespace std;
int main()
{
float f1 = 23400000000;
float f2 = f1 + 10;
cout.setf(ios_base::fixed, ios_base::floatfield); // fixed-point 格式化输出
cout << "f1 = " << f1 << endl;
cout << "f2 = " << f2 << endl;
cout << "f1 - f2 = " << f1 - f2 << endl;
cout << "(f1 - f2 == 0) = " << (f1 - f2 == 0) << endl;
return 0;
}
运行结果如下:
为什么 f1 和 f2 是相等的?
因为精度问题,f1+10之后值仍然距离当前采样点最近,所以就使用了当前采样点,可能+100或者更大才会变为下一个值。
这个时候,如果是银行系统,一个人有很多钱,当他存了1块钱的时候,可能就存不上去了,这个时候该怎么办?
float
和double
等都会存在这个问题,不同之处就是采样不同,+10对于float
加不上去,但是double
可以加上去。
对于浮点数而言,一般不会直接使用 ==
来比较两个数值的大小,而是用插值的形式,如下:
if(f1 == f2 ) // bad
if( fabs(f1-f2) < FLT_EPSILON ) // good
inf and nan
Example:
//nan.cpp
#include <iostream>
using namespace std;
int main()
{
float f1 = 2.0f / 0.0f;
float f2 = 0.0f / 0.0f;
cout << f1 << endl;
cout << f2 << endl;
return 0;
}
运行结果如下:
2.4 Arithmetic Operators
算数运算的原理很简单,但是计算机实现较为复杂。
Constant numbers(常数)
95(默认十进制)的不同进制表示方法:
95 // decimal 十进制
0137 // octal 八进制 64*1+8*3+7=95
0x5F // hexadecimal 十六进制 16*5+F=80+15=95
使用不同整数类型表示95:
95 // int
95u // unsigned int
95l // long
95ul // unsigned long
95lu // unsigned long
浮点数的表示:
3.14159 // 3.14159
6.02e23 // 6.02 x 10^23
1.6e-19 // 1.6 x 10^-19
3.0 // 3.0
不同数据类型表示浮点数:
默认是double
类型
6.02e13f // float
6.02e13 // double
6.02e13L // long double
在给float
类型的数据赋值的时候,最好带上f
Const 类型限定符
变量或者对象不能修改,必须在定义的时候初始化。
const float pi = 3.1415926f;
pi += 1; //error!
auto(c++)
auto
并不是具体的数据类型,而是placeholder type specifier,可以是任意类型,具体类型取决于初始化的值。
auto a = 2; // type of a is int
auto bc = 2.3; // type of b is double
auto c; // valid in C, but not in C++
auto d = a * 1.2; // type of d is double
auto声明的变量在初始化的时候确定数据类型,之后不可修改,如下:
auto a = 2; // type if a is int
a = 2.3
a已经是int
类型了,2.3会先被去掉小数位,然后赋值(assgin)给a
。
Arithmetic Operators
算数运算符如下表:
Operator name | Syntax |
---|---|
unary plus | +a |
unary minus(取负数) | -a |
addition | a + b |
subtraction | a - b |
multiplication | a * b |
division | a / b |
modulo(取余) | a % b |
bitwise NOT | ~a |
bitwise AND | a & b |
bitwise OR | a | b |
bitwise XOR | a ^ b |
bitwise left shift | a << b |
bitwise right shift | a >> b |
Assignment Operators
Assignment expression | Equivalent expression |
---|---|
a = b | |
a += b | a = a + b |
a -= b | a = a - b |
a *= b | a = a * b |
a /= b | a = a / b |
a %= b | a = a % b |
a &= b | a = a & b |
a |= b | a = a | b |
a ^= b | a = a ^ b |
a <<= b | a = a << b |
a >>= b | a = a >> b |
Data type conversions
int num_int1 = 9; // initializing an int value to num_int1
int num_int2 = 'C'; // implicit conversion(隐式类型转换)
int num_int3 = (int)'C'; // explicit conversion, C-style (显式类型转换)
int num_int4 = int('C'); // explicit conversion, function style
int num_int5 = 2.8; // implicit conversion
float num_float = 2.3; // implicit conversion from double to float
short num_short = 650000; // overflow
- 浮点数到整数类型的转换会舍弃小数位,只保留整数位,四舍五入由专门的函数
- 浮点数默认类型
double
,带有f
就是float
- 溢出的时候会保留二进制的低位。
Divisions
整数除法的结果是还是整数,然后给到 f 的这时候转换为float,所以是3.0f。
只要有一个是浮点是,计算的结果就是浮点数。
Distinct Operations for Different Types
实际上,运算只是基于int,long,float,double
这四种数据类型进行的,该四种类型之外的其他操作数都会被转换这四种,如下:
计算结果并不会溢出,就是256,因为这里将char
转换为int
取运算了。
The operands will be converted to one of the four types without losing data: int, long, float, double
具体可以参考:Implicit conversions