第一天 2021-3-3 每日刷两题之一
一、C/C++编译全过程
-
预处理(Preprocessing):预处理用于将所有的#include头文件以及宏定义替换成其真正的内容。test.c->test.i
①以.c文件为单位进行,各个.c文件之间不知道彼此的存在;
②将#include的.h文件代码放入对应.c文件中,此时.h文件已经完成全部作用可以废弃(帮助理解.h文件本质,就是为了减少代码的重复编写而产生的,没有特殊功能);
③将#define宏定义的值放入代码对应位置
-
编译(Compilation):将经过预处理之后的程序转换成特定汇编代码(assembly code)的过程。test.i->test.s
①以.c文件为单位进行,各个.c文件之间不知道彼此的存在;
-
汇编(Assemble):汇编过程将上一步的汇编代码转换成机器码(machine code) test.s->test.o
①以.c文件为单位进行,各个.c文件之间不知道彼此的存在; -
链接(Linking):链接过程将多个目标文以及所需的库文件(.so等)链接成最终的可执行文件(executable file)。
二、static关键字
C/C++在头文件中使用static定义变量意味着什么
定义全局变量时/函数使用static,意味着该变量/函数的作用域只限于定义它的源文件中,其它源文件不能访问。
三、extern关键字
C/C++中extern关键字详解
extern关键字只用在变量上,代表声明一个变量而不是定义。
当在头文件中声明一个变量,必须使用static/extern,否则在将多个引用该头文件的c文件链接时,引发多重定义的错误。static:该变量只在各个c文件中单独有效
extern:该变量由所有extern共享(此时禁止在头文件中初始化)
★为什么头文件声明函数不需要static/extern关键字?
重复定义会引发错误,重复声明并不会。
函数声明与定义跟变量的声明与定义是有区别的。
函数只要未写出函数体,即为声明,不需要额外加extern关键字;
变量必须加上extern关键字才能被编译器认定为一个声明;
编译器为什么不报重定义函数错误?
因此也可以得出结论,extern关键字不会用在函数上。
★★★函数声明中的extern问题
四、STL:vector
vector<int> result={1,2,3}//可以用这种方式初始化
result.push_back(4) //插入元素
result.emplace_back(4)//相对于push_back, 但比它更有效率
vector是表示可变大小数组的序列容器。
五、STL:map&unordered_map
c++ map与unordered_map区别及使用
都负责存储key-value键值对数据
#include<map> //底层为红黑树
#include<unordered_map> //底层为哈希表
★★C++ STL unordered_map容器用法详解(成员方法表)
可以使用如下三种方式赋值
unordered_map<string,int> hashtable
hashtable["key"]=7;
hashtable.emplace("key",7);
hashtable.insert({"key",7});//使用insert需要先组合参数!
unordered_map常用查找结构
unordered_map<int, int> hashtable;
for (int i = 0; i < n; ++i) {
auto it = hashtable.find(n); //查找key=n,未找到则返回一个指向容器中最后一个键值对之后位置的迭代器
if (it != hashtable.end()) {
return "find";
}
return "not find";
}
// 使用count()代码更简洁,若存在返回1,不存在返回0
unordered_map<int, int> hashtable;
for (int i = 0; i < n; ++i) {
if (hashtable.count(n)) {
return "find";
}
return "not find";
}
// 如何使用查找到的返回值
// hashtable将返回一个迭代器,使用如下方法取出key和value
itr=hashtable.find(n);
itr->first; //返回key值
itr->seconde; //返回value值
unordered_map常用遍历结构
unordered_map<int,int> h;
for(auto iter=h.begin();iter!=h.end();iter++){
cout<<iter->second;
cout<<h[iter->first];
}
成员方法 | 功能 |
---|---|
begin() | 返回指向容器中第一个键值对的正向迭代器。 |
end() | 返回指向容器中最后一个键值对之后位置的正向迭代器。 |
cbegin() | 和 begin() 功能相同,只不过在其基础上增加了 const 属性,即该方法返回的迭代器不能用于修改容器内存储的键值对。 |
cend() | 和 end() 功能相同,只不过在其基础上,增加了 const 属性,即该方法返回的迭代器不能用于修改容器内存储的键值对。 |
empty() | 若容器为空,则返回 true;否则 false。 |
size() | 返回当前容器中存有键值对的个数。 |
max_size() | 返回容器所能容纳键值对的最大个数,不同的操作系统,其返回值亦不相同。 |
operator[key] | 该模板类中重载了 [] 运算符,其功能是可以向访问数组中元素那样,只要给定某个键值对的键 key,就可以获取该键对应的值。注意,如果当前容器中没有以 key 为键的键值对,则其会使用该键向当前容器中插入一个新键值对。 |
at(key) | 返回容器中存储的键 key 对应的值,如果 key 不存在,则会抛出 out_of_range 异常。 |
find(key) | 查找以 key 为键的键值对,如果找到,则返回一个指向该键值对的正向迭代器;反之,则返回一个指向容器中最后一个键值对之后位置的迭代器(如果 end() 方法返回的迭代器)。 |
count(key) | 在容器中查找以 key 键的键值对的个数。只会返回0或1 |
equal_range(key) | 返回一个 pair 对象,其包含 2 个迭代器,用于表明当前容器中键为 key 的键值对所在的范围。 |
emplace() | 向容器中添加新键值对,效率比 insert() 方法高。 |
emplace_hint() | 向容器中添加新键值对,效率比 insert() 方法高。 |
insert() | 向容器中添加新键值对。 |
erase() | 删除指定键值对。 |
clear() | 清空容器,即删除容器中存储的所有键值对。 |
swap() | 交换 2 个 unordered_map 容器存储的键值对,前提是必须保证这 2 个容器的类型完全相等。 |
bucket_count() | 返回当前容器底层存储键值对时,使用桶(一个线性链表代表一个桶)的数量。 |
max_bucket_count() | 返回当前系统中,unordered_map 容器底层最多可以使用多少桶。 |
bucket_size(n) | 返回第 n 个桶中存储键值对的数量。 |
bucket(key) | 返回以 key 为键的键值对所在桶的编号。 |
load_factor() | 返回 unordered_map 容器中当前的负载因子。负载因子,指的是的当前容器中存储键值对的数量(size())和使用桶数(bucket_count())的比值,即 load_factor() = size() / bucket_count()。 |
max_load_factor() | 返回或者设置当前 unordered_map 容器的负载因子。 |
rehash(n) | 将当前容器底层使用桶的数量设置为 n。 |
reserve() | 将存储桶的数量(也就是 bucket_count() 方法的返回值)设置为至少容纳count个元(不超过最大负载因子)所需的数量,并重新整理容器。 |
hash_function() | 返回当前容器使用的哈希函数对象。 |
当向unordered_map插入重复的key值时,将直接失败。
六、【题】两数之和
使用哈希表(cheat?)存储已经扫描过的数字,再次用到时很快的找到有没有符合要求的数字。
相比枚举法:
时间复杂度由O(N^2)降低为O(N)
空间复杂度由O(1)升为O(N)
借着哈希算法的无敌强行降低,牛!
七、Iterator
C++迭代器实现原理(附带了Java)
★★ C++迭代器(STL迭代器)iterator详解
vector::iterator i; //定义正向迭代器
"::"的作用:
因为iterator是定义在vector的内部类,所以需要使用::来引用
java用. cpp用::
class A{
class B{...}
..
}
A::B //cpp
A.B //java