本文是深蓝学院c++课程的个人笔记和感悟,仅供学习参考。
1. 数组
1.1 数组初始化
1.1.1 可变长度的数组?
- c++里面数组的初始化一般来说必须给定一个确切的大小,比如
int b[3]
- 如果用variable length的数组,比如
int b[x]
,原则上是不可以的。
因为系统在编译(compile)的时候没办法知道x这个变量是多少,那就没办法分配内存空间; 只有在运行(run)的时候才可能知道x是多少。
1.1.2 缺省值
- 在main函数里面的缺省值是随机的。
- 在main函数外面的全局定义的数组,则全部初始为0。
#include <iostream>
int b[3];
int main() {
int a[3];
std::cout << a[0] << std::endl;
std::cout << b[0] << std::endl;
return 0;
}
---result----
a[0]: 21919 (random)
b[0]: 0
1.1.3 聚合初始化
- 如果3个长度的数组,只给它2个数值:
int a[3] = {1,2}
。那么a[3]自动初始化为0
1.1.4 不能用auto来声明数组类型
auto a = {1,2,3}
中a的类型其实是std::initializer_list[3]
1.1.5 数组不能复制
c++不支持数组直接复制:
int a[] = {1,2,3}
int b = a; // X,错误!
问:为神马c++不支持数组的复制呢?
答:因为c++是一种注重性能的语言,复制得开辟好多空间喔~
1.1.6 字符串的初始化
char str[] = "hello" // char[6]
char str[] = "h", "e", "l", "l", "o" // char[5]
神奇!为神马第二种方式的str变量变成char[5],而第一个是char[6]呢?
原因: c/c++会在第一种初始化的字符串后面“偷偷地”加一个'\0'
字符,用来判断字符串的结束。
1.2 数组的复杂声明
1.2.1 指针的数组 和 数组的指针
int* data[3] = {&x1, &x2, &x3}; //data是一个包含三个int*指针的数组
int (*data)[3] = {x1, x2, x3}; //data是一个int[3]数组的指针
1.2.2 声明数组的引用
int (&data)[3] = {x1, x2, x3}
1.3 数组的元素访问
1.3.1 左值和右值
数组对象是一个左值
1.3.2 转换成指针类型
1.3.3 x[y] -> * ((x)+(y))
a[1]其实等于1[a]: *((1)+(a))
- 如果超出界限,就会出现
内存访问越界
,但是非常非常危险,因为会访问到越界的那个地址上的数据,而不会像python给你报错。
1.4 从数组到指针
1.4.1 数组和指针的隐式转换
- 数组和指针如果用
auto
,是会发生隐式转换的。本来右边是个数组,auto
之后就变成指针了。因为a[1] = *((1)+(a))
,数组其实也是需要基于地址的访问。 - 但是,数组和指针的内存结构其实非常不同,如上图所示:
- 数组保存的直接就是数据内容
- 指针保存的是指向的地址
extern int array[]
用extern的时候一定不能用指针哈,不然会把array前8个字节的数据(而且根据高位低位的规则,后4位放在前面)当成指针指向的地址… 完全跑飞了std::cbegin(array)
std::cend(array)
这里的c是指const
,防止不小心更内容。
1.5 数组的其他操作
1.5.1 数组所含元素的个数
sizeof(array) / sizeof(int)
std::size()
std::cbegin()
andstd::cend()
但对于std::vector来说,sizeof(vector)
只能返回一个固定值24(根据编译器而定)。因为std::vector通常由三个8 byte的Pointer(指针)组成:
- begin of vector
- end of vector
- end of reserved memory for vector (i.e. vector’s capacity)
1.5.2 遍历数组元素
- 基于元素个数
for(int i; i<std::size(array); i++)
- 基于pointer
auto ptr = std::cbegin(a)
while(ptr != std::cend(a))
{
ptr = ptr + 1;
}
- 基于range-based for循环
for(int x: a){
std::cout << x << std::endl;
}
1.6 C字符串
c字符串用strlen可以求出长度,但是一定要注意,在单个定义字符串的字符时,最后结尾要加一个\0
来结尾。不然其实程序是pointer一直在内存里面找下一个字符,直到找到\0
才会截止。
include <cstring>
char str1[] = "Hello";
char str2[] = ['H', 'e', 'l', 'l', 'o', '\0'];
auto ptr = str1;
std::cout << strlen(str1) << std::endl;
std::cout << strlen(str2) << std::endl;
std::cout << strlen(ptr) << std::endl;
1.7 多维数组
//一重大括号 vs 多重大括号
int x2[3][4] = {1,2,3,4,5,6};
int x2[3][4] = {{1,2,3,4}, {5,6}};
// (int, int, int, int), (int, int, int, int), (int, int, int, int)
// 自动补0
int x3[][3] = {1,2,3,4}
// 系统会直接给补全,初始化为X3[2][3]
int x4[][4] = {{1,2,3}, {5,6,7,8}}
// 自动会把第一排最后一个补0,{1,2,3,0}
2. Vector
2.1 Vector的易用性
- Vector可以复制,但Array不可以
- Vector可以在运行期改变元素个数,但Array不可以
相比之下,内建数组更加注重性能。
2.2 Vector的初始化
std::vector<int> a(3,1); //3个元素,每一个的数值都是1
std::vector<int> a{3,1};
std::vector<int> a = {1,2,3,4};
// {1,2,3} 表示一个std::initializer_list<T> 初始化列表
2.3 Vector的方法
std::vector<int> x1;
// Vector的基本方法
x1.size()
x1.empty()
x1.push_back(val)
x1.pop_back()
// Vector的比较
x1 == x2
x1 < x2 // Vector的比较是按照priority来:
// 先看第一位是不是相同,如果不同,那直接就用第一位的结果作为最终大小比较的结果啦!
// Vector中元素的遍历
x1[index]
x1.at(index) // 建议使用vec.at(idx),因为这样如果idx>size,系统会报错。
// 指针访问Vector
std::vector<int>* ptr = &x1;
// 指针访问Vector所包含的Method: -> 以及 .
(*ptr).size()
ptr->size()
2.4 多维Vector
int a[3][4];
//Array的a[0]和a[1]的维度都是一样的
std::vector<std::vector<int>> vec = {{1,2,3}, {4,5}};
//Vector可以不同维度的元素长度是可以不一样的
3. String
std::string x("Hello String");
std::string y("Hello");
y = y + "Wei";
//std::string是可以支持拼接的,但是内建字符串char[]是不能拼接的。