数字类型
说是数字类型,其实不止int, float那些,在c++中,boo, char, wchar_t, char16_t, char32_t等等都是。
- char 八个位
- int 十六个位
- wchar_t 十六个位
- long 三十二个位
使用sizeof在x64机器上打印出(单位是byte):
- short 2
- int 4
- long 4
- char 1
似乎在x64上int和long是一样大的。
由于历史原因,在微软的机器上,由于历史愿意这样。
单精度与双精度
单精度占用4个字节,双精度占用8个字节。
单精度有:一个符号位,八个exp位,23个数字位;
双精度有:一个符号位,11个exp位,52个数字位。
unsigned和signed关键字
决定有没有符号位。
- 当你知道你的变量不可能为负值的时候,可以使用unsigned。
- 不要单纯地使用char类型来代替short。因为在不同的机器上,有些char是unsigned的,有些是signed的。如果你非要这么用,就加上unsigned或者signed。
- 能用double就用double。float经常会不够用。而且在某些机器上double还比float要快。
自动转换
bool b = 43;
int i = b; // 1
i = 3.14; // 3
double pi = i; // 3.0
unsigned char c = -1; // 255
signed char c2 = 255; // 确实不是255,但转成unsigned short之后打印出来还是255。给一个signed short赋值一个超过127的值会导致不可知后果,俗称undefined。
cout << true; //出来是1,cout对于bool变量只会输出1或者0
unsigned u = 10;
int i = -48;
cout << u + i; //32-bit的int会出来一个超大的数
两个unsigned做运算,如果出现了负数,会被变成signed int。
由于unsigned永远不可能小于0,所以拿来while(u >= 0)
做比较的话,会必然是true然后一直运行。
unsigned和signed不要做混合运算,会出事儿。
写法
010 八进制
0x10 十六进制
string写法
L`a` // wchar_t
u8"hi" //char (utf8)
list初始化
int a = {0};
long double b = 3.1111;
int c = {b}; //报错,会丢失信息
int d = b; //通过
外部变量
extern int i; // 声明了但是没有定义
int j; // 声明且定义
extern double pi = 3.14; //声明且定义
一个变量只能被定义一次,但是可以被声明多次。变量只能在某个一个文件里定义一次,但是可以在各个地方被声明。
在方法里对extern进行初始化会导致报错。错误会比如是error: pi has both extern and initializer
keywords
像是and
,or
,not
这种关键字,主要是以前qwer键盘还不流行的时候用的,因为那个时候的键盘可能没有||或者&&之类的字符。
其实我觉得这样挺lua,挺好的。
作用域
- global scope
- block scope
- nested scope
同样的变量名,外部的变量会内部的变量屏蔽。
#include<iostream>
int p = 10;
int main(){
int p = 20; // 一般不建议这么做
cout << p; // 这里的p其实是另一个东西了,外面的p被屏蔽 --> 20
cout << ::p; // --> 10;
}
复合类型
引用
int ival = 1024;
int &refval = ival; // 相当于起了个别名而已
int &refval2; // 一定要初始化,错
int &i1 = ival, i2 = ival; // i1是个引用,i2则不是
int &refVal4 = 10; // 右值一定要是一个object
double dval = 3.14;
int &refVal5 = dval; // 不行的,做不到的
一定要初始化,不存在rebind的。
理解为别名就最容易了,其它的不要多想。
指针
获得指针
int i = 10;
int *p = &i;
获得指针所指对象
*p = 100;
cout << *p;
传一个指针过去,使得可以在另一个函数内改变基础变量(aka,非instance):
#include<iostream>
using namespace std;
void changeValue(int* p){
*p = *p + 50;
}
int main(){
int i = 10;
changeValue(&i);
cout << i << endl;
cout << *&i << endl;
}
不同类型的指针不允许相互赋值
double d = 3.14;
double *p1 = &d;
int *p2 = p1; // error
int *p2 = &d; // error
int &ref = *p; // 对p所指的int进行引用
空指针
int *p = nullptr; // 自动赋值为0
int *p2 = 0; // 直接赋值为0
int i = 0;
int *pp2 = i; // 这样又不行
// 必须 #include<cstdlib>
int *p3 = NULL;
// 是否为空指针
if(p){
}
注意
很容易眼花看错以为是创建了一个值为0的int并把它的值赋给了p。对于一个指针来说,0就代表着空指针!所以没有所谓的真正的空,0即是空。
使用gcc编译的时候,会出现一个nullptr was not declared in this scope
的问题,查出来说编译的时候需要加上-std=c++11
的参数,其它的c++11特性应该也是需要这个的。
NULL需要一个cstdlib库。在编译之前,编译器会先执行preprocessor,初始化了NULL的值。NULL不属于任何命名空间,所以也不需要使用
std:NULL
的形式来访问。
最好每个指针都要初始化,起码要给个空指针。不然的话你都不知道它会指到哪里。
不像脚本语言没初始化的时候就默认为nil,c++这边的空指针,是要手写的。。。
引用与指针的区别
- 引用只可以初始化一次,而后它的值就不允许更改。可以视为是一个别名。
void*
可以指向任何object,可以等同于任何其他类型的指针。由于没有类型,所以不能实例化(?)。可以和任何类型的指针作比较。
一般单纯地用来处理内存。
一种恶心的写法
int i = 1024, *p = &i, &ref = i;
指向引用的指针
有毒。
诀窍是从右往左读。
int i = 10;
int *p;
int *&r = p;
r = &i;
*r = 100; // now the value of i is 100
Const
- 不可被改变的值。
- 一定要初始化,不然编译不过。
- 所谓的const,在编译期间就会被替换为对应的值。