
目录
// 类
class A
{
private:
    const int a;                // 常对象成员,只能在初始化列表赋值
public:
    // 构造函数
    A() { };
    A(int x) : a(x) { };        // 初始化列表
    // const可用于对重载函数的区分
    int getValue();             // 普通成员函数
    int getValue() const;       // 常成员函数,不得修改类中的任何数据成员的值
};
void function()
{
    // 对象
    A b;                        // 普通对象,可以调用全部成员函数
    const A a;                  // 常对象,只能调用常成员函数、更新常成员变量
    const A *p = &a;            // 常指针
    const A &q = a;             // 常引用
    // 指针
    char greeting[] = "Hello";
    char* p1 = greeting;                // 指针变量,指向字符数组变量
    const char* p2 = greeting;          // 指针变量,指向字符数组常量
    char* const p3 = greeting;          // 常指针,指向字符数组变量
    const char* const p4 = greeting;    // 常指针,指向字符数组常量
}
// 函数
void function1(const int Var);           // 传递过来的参数在函数内不可变
void function2(const char* Var);         // 参数指针所指内容为常量
void function3(char* const Var);         // 参数指针为常指针
void function4(const int& Var);          // 引用参数在函数内为常量
// 函数返回值
const int function5();      // 返回一个常数
const int* function6();     // 返回一个指向常量的指针变量,使用:const int *p = function6();
int* const function7();     // 返回一个指向变量的常指针,使用:int* const p = function7();
this 指针是一个隐含于每一个非静态成员函数中的特殊指针。它指向正在被该成员函数操作的那个对象。this 指针,然后调用成员函数,每次成员函数存取数据成员时,由隐含使用 this 指针。this 指针被隐含地声明为: ClassName const this`,这意味着不能给 `this` 指针赋值;在 `ClassName` 类的 `const` 成员函数中,`this` 指针的类型为:`const ClassName const,这说明不能对 this 指针所指向的这种对象是不可修改的(即不能对这种对象的数据成员进行赋值操作);this 并不是一个常规变量,而是个右值,所以不能取得 this 的地址(不能 &this)。this 指针:// 声明1(加 inline,建议使用)
inline int functionName(int first, int secend,...);
// 声明2(不加 inline)
int functionName(int first, int secend,...);
// 定义
inline int functionName(int first, int secend,...) {/****/};
// 类内定义,隐式内联
class A {
    int doA() { return 0; }         // 隐式内联
}
// 类外定义,需要显式内联
class A {
    int doA();
}
inline int A::doA() { return 0; }   // 需要显式内联
优点
缺点
Are "inline virtual" member functions ever actually "inlined"? 答案:http://www.cs.technion.ac.il/users/yechiel/c++-faq/inline-virtuals.html
inline virtual 唯一可以内联的时候是:编译器知道所调用的对象是哪个类(如 Base::who()),这只有在编译器具有实际对象而不是对象的指针或引用时才会发生。#include <iostream>  
using namespace std;
class Base
{
public:
    inline virtual void who()
    {
        cout << "I am Base\n";
    }
    virtual ~Base() {}
};
class Derived : public Base
{
public:
    inline void who()  // 不写inline时隐式内联
    {
        cout << "I am Derived\n";
    }
};
int main()
{
    // 此处的虚函数 who(),是通过类(Base)的具体对象(b)来调用的,编译期间就能确定了,所以它可以是内联的,但最终是否内联取决于编译器。 
    Base b;
    b.who();
    // 此处的虚函数是通过指针调用的,呈现多态性,需要在运行时期间才能确定,所以不能为内联。  
    Base *ptr = new Derived();
    ptr->who();
    // 因为Base有虚析构函数(virtual ~Base() {}),所以 delete 时,会先调用派生类(Derived)析构函数,再调用基类(Base)析构函数,防止内存泄漏。
    delete ptr;
    ptr = nullptr;
    system("pause");
    return 0;
} 
断言,是宏,而非函数。assert 宏的原型定义在 <assert.h>(C)、<cassert>(C++)中,其作用是如果它的条件返回错误,则终止程序执行。可以通过定义 NDEBUG 来关闭 assert,但是需要在源代码的开头,include <assert.h> 之前。
#define NDEBUG          // 加上这行,则 assert 不可用
#include <assert.h>
assert( p != NULL );    // assert 不可用
设定结构体、联合以及类成员变量以 n 字节方式对齐
#pragma pack(push)  // 保存对齐状态
#pragma pack(4)     // 设定为 4 字节对齐
struct test
{
    char m1;
    double m4;
    int m3;
};
#pragma pack(pop)   // 恢复对齐状态
Bit mode: 2;    // mode 占 2 位
类可以将其(非静态)数据成员定义为位域(bit-field),在一个位域中含有一定数量的二进制位。当一个程序需要向其他程序或硬件设备传递二进制数据时,通常会用到位域。
volatile int i = 10; 
extern "C" 修饰的变量和函数是按照 C 语言方式编译和连接的extern "C" 的作用是让 C++ 编译器将 extern "C" 声明的代码当作 C 语言代码处理,可以避免 C++ 因符号修饰导致代码不能和C语言库中的符号进行链接的问题。
#ifdef __cplusplus
extern "C" {
#endif
void *memset(void *, int, size_t);
#ifdef __cplusplus
}
#endif
// c
typedef struct Student {
    int age; 
} S;
等价于
// c
struct Student { 
    int age; 
};
typedef struct Student S;
此时 S 等价于 struct Student,但两个标识符名称空间不相同。
另外还可以定义与 struct Student 不冲突的 void Student() {}。
由于编译器定位符号的规则(搜索规则)改变,导致不同于C语言。
一、如果在类标识符空间定义了 struct Student {...};,使用 Student me; 时,编译器将搜索全局标识符表,Student 未找到,则在类标识符内搜索。
即表现为可以使用 Student 也可以使用 struct Student,如下:
// cpp
struct Student { 
    int age; 
};
void f( Student me );       // 正确,"struct" 关键字可省略
二、若定义了与 Student 同名函数之后,则 Student 只代表函数,不代表结构体,如下:
typedef struct Student { 
    int age; 
} S;
void Student() {}           // 正确,定义后 "Student" 只代表此函数
//void S() {}               // 错误,符号 "S" 已经被定义为一个 "struct Student" 的别名
int main() {
    Student(); 
    struct Student me;      // 或者 "S me";
    return 0;
}
总的来说,struct 更适合看成是一个数据结构的实现体,class 更适合看成是一个对象的实现体。
联合(union)是一种节省空间的特殊的类,一个 union 可以有多个数据成员,但是在任意时刻只有一个数据成员可以有值。当某个成员被赋值后其他成员变为未定义状态。联合有如下特点:
#include<iostream>
union UnionTest {
    UnionTest() : i(10) {};
    int i;
    double d;
};
static union {
    int i;
    double d;
};
int main() {
    UnionTest u;
    union {
        int i;
        double d;
    };
    std::cout << u.i << std::endl;  // 输出 UnionTest 联合的 10
    ::i = 20;
    std::cout << ::i << std::endl;  // 输出全局静态匿名联合的 20
    i = 30;
    std::cout << i << std::endl;    // 输出局部匿名联合的 30
    return 0;
}
C 语言实现封装、继承和多态: http://dongxicheng.org/cpp/ooc/
explicit 修饰的构造函数可用来防止隐式转换
class Test1
{
public:
    Test1(int n)            // 普通构造函数
    {
        num=n;
    }
private:
    int num;
};
class Test2
{
public:
    explicit Test2(int n)   // explicit(显式)构造函数
    {
        num=n;
    }
private:
    int num;
};
int main()
{
    Test1 t1=12;            // 隐式调用其构造函数,成功
    Test2 t2=12;            // 编译错误,不能隐式调用其构造函数
    Test2 t2(12);           // 显式调用成功
    return 0;
}
一条 using 声明 语句一次只引入命名空间的一个成员。它使得我们可以清楚知道程序中所引用的到底是哪个名字。如:
using namespace_name::name;
在 C++11 中,派生类能够重用其直接基类定义的构造函数。
class Derived : Base {
public:
    using Base::Base;
    /* ... */
};
如上 using 声明,对于基类的每个构造函数,编译器都生成一个与之对应(形参列表完全相同)的派生类构造函数。生成如下类型构造函数:
derived(parms) : base(args) { }
using 指示 使得某个特定命名空间中所有名字都可见,这样我们就无需再为它们添加任何前缀限定符了。如:
using namespace_name name;
一般说来,使用 using 命令比使用 using 编译命令更安全,这是由于它只导入了制定的名称。如果该名称与局部名称发生冲突,编译器将发出指示。using编译命令导入所有的名称,包括可能并不需要的名称。如果与局部名称发生冲突,则局部名称将覆盖名称空间版本,而编译器并不会发出警告。另外,名称空间的开放性意味着名称空间的名称可能分散在多个地方,这使得难以准确知道添加了哪些名称。
尽量少使用 using 指示
using namespace std;
应该多使用 using 声明
int x;
std::cin >> x ;
std::cout << x << std::endl;
或者
using std::cin;
using std::cout;
using std::endl;
int x;
cin >> x;
cout << x << endl;
::name):用于类型名称(类、类成员、成员函数、变量等)前,表示作用域为全局命名空间class::name):用于表示指定类型的作用域范围是具体某个类的namespace::name):用于表示指定类型的作用域范围是具体某个命名空间的int count = 0;        // 全局(::)的 count
class A {
public:
    static int count; // 类 A 的 count(A::count)
};
int main() {
    ::count = 1;      // 设置全局的 count 的值为 1
    A::count = 2;     // 设置类 A 的 count 为 2
    int count = 0;    // 局部的 count
    count = 3;        // 设置局部的 count 的值为 3
    return 0;
}
enum class open_modes { input, output, append };
enum color { red, yellow, green };
enum { floatPrec = 6, doublePrec = 10 };
decltype 关键字用于检查实体的声明类型或表达式的类型及值分类。语法:
decltype ( expression )
// 尾置返回允许我们在参数列表之后声明返回类型
template <typename It>
auto fcn(It beg, It end) -> decltype(*beg)
{
    // 处理序列
    return *beg;    // 返回序列中一个元素的引用
}
// 为了使用模板参数成员,必须用 typename
template <typename It>
auto fcn2(It beg, It end) -> typename remove_reference<decltype(*beg)>::type
{
    // 处理序列
    return *beg;    // 返回序列中一个元素的拷贝
}
常规引用,一般表示对象的身份。
右值引用就是必须绑定到右值(一个临时对象、将要销毁的对象)的引用,一般表示对象的值。
右值引用可实现转移语义(Move Sementics)和精确传递(Perfect Forwarding),它的主要目的有两个方面:
好处
用花括号初始化器列表列表初始化一个对象,其中对应构造函数接受一个 std::initializer_list 参数.
#include <iostream>
#include <vector>
#include <initializer_list>
template <class T>
struct S {
    std::vector<T> v;
    S(std::initializer_list<T> l) : v(l) {
         std::cout << "constructed with a " << l.size() << "-element list\n";
    }
    void append(std::initializer_list<T> l) {
        v.insert(v.end(), l.begin(), l.end());
    }
    std::pair<const T*, std::size_t> c_arr() const {
        return {&v[0], v.size()};  // 在 return 语句中复制列表初始化
                                   // 这不使用 std::initializer_list
    }
};
template <typename T>
void templated_fn(T) {}
int main()
{
    S<int> s = {1, 2, 3, 4, 5}; // 复制初始化
    s.append({6, 7, 8});      // 函数调用中的列表初始化
    std::cout << "The vector size is now " << s.c_arr().second << " ints:\n";
    for (auto n : s.v)
        std::cout << n << ' ';
    std::cout << '\n';
    std::cout << "Range-for over brace-init-list: \n";
    for (int x : {-1, -2, -3}) // auto 的规则令此带范围 for 工作
        std::cout << x << ' ';
    std::cout << '\n';
    auto al = {10, 11, 12};   // auto 的特殊规则
    std::cout << "The list bound to auto has size() = " << al.size() << '\n';
//    templated_fn({1, 2, 3}); // 编译错误!“ {1, 2, 3} ”不是表达式,
                             // 它无类型,故 T 无法推导
    templated_fn<std::initializer_list<int>>({1, 2, 3}); // OK
    templated_fn<std::vector<int>>({1, 2, 3});           // 也 OK
}
面向对象程序设计(Object-oriented programming,OOP)是种具有对象概念的程序编程典范,同时也是一种程序开发的抽象方针。

面向对象特征
面向对象三大特征 —— 封装、继承、多态
| 关键字 | 当前类 | 包内 | 子孙类 | 包外 | 
|---|---|---|---|---|
| public | √ | √ | √ | √ | 
| protected | √ | √ | √ | × | 
| friendly | √ | √ | × | × | 
| private | √ | × | × | × | 
函数重载
class A
{
public:
    void do(int a);
    void do(int a, int b);
};
注意:
class Shape                     // 形状类
{
public:
    virtual double calcArea()
    {
        ...
    }
    virtual ~Shape();
};
class Circle : public Shape     // 圆形类
{
public:
    virtual double calcArea();
    ...
};
class Rect : public Shape       // 矩形类
{
public:
    virtual double calcArea();
    ...
};
int main()
{
    Shape * shape1 = new Circle(4.0);
    Shape * shape2 = new Rect(5.0, 6.0);
    shape1->calcArea();         // 调用圆形类里面的方法
    shape2->calcArea();         // 调用矩形类里面的方法
    delete shape1;
    shape1 = nullptr;
    delete shape2;
    shape2 = nullptr;
    return 0;
}
虚析构函数是为了解决基类的指针指向派生类对象,并用基类的指针删除派生类对象。
class Shape
{
public:
    Shape();                    // 构造函数不能是虚函数
    virtual double calcArea();
    virtual ~Shape();           // 虚析构函数
};
class Circle : public Shape     // 圆形类
{
public:
    virtual double calcArea();
    ...
};
int main()
{
    Shape * shape1 = new Circle(4.0);
    shape1->calcArea();    
    delete shape1;  // 因为Shape有虚析构函数,所以delete释放内存时,先调用子类析构函数,再调用基类析构函数,防止内存泄漏。
    shape1 = NULL;
    return 0;
}
纯虚函数是一种特殊的虚函数,在基类中不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给该基类的派生类去做。
virtual int A() = 0;
CSDN . C++ 中的虚函数、纯虚函数区别和联系:http://t.cn/E4WVQBI
.rodata section,见:目标文件存储结构:http://t.cn/E4WVBeF),存放虚函数指针,如果派生类实现了基类的某个虚函数,则在虚表中覆盖原本基类的那个虚函数指针,在编译时根据类的声明创建。虚继承用于解决多继承条件下的菱形继承问题(浪费存储空间、存在二义性)。
底层实现原理与编译器相关,一般通过虚基类指针和虚基类表实现,每个虚继承的子类都有一个虚基类指针(占用一个指针的存储空间,4字节)和虚基类表(不占用类对象的存储空间)(需要强调的是,虚基类依旧会在子类里面存在拷贝,只是仅仅最多存在一份而已,并不是不在子类里面了);当虚继承的子类被当做父类继承时,虚基类指针也会被继承。
实际上,vbptr 指的是虚基类表指针(virtual base table pointer),该指针指向了一个虚基类表(virtual table),虚表中记录了虚基类与本类的偏移地址;通过偏移地址,这样就找到了虚基类成员,而虚继承也不用像普通多继承那样维持着公共基类(虚基类)的两份同样的拷贝,节省了存储空间。
用于分配、释放内存
申请内存,确认是否申请成功
char *str = (char*) malloc(100);
assert(str != nullptr);
释放内存后指针置空
free(p); 
p = nullptr;
申请内存,确认是否申请成功
int main()
{
    T* t = new T();     // 先内存分配 ,再构造函数
    delete t;           // 先析构函数,再内存释放
    return 0;
}
定位 new(placement new)允许我们向 new 传递额外的参数。
new (palce_address) type
new (palce_address) type (initializers)
new (palce_address) type [size]
new (palce_address) type [size] { braced initializer list }
palce_address 是个指针initializers 提供一个(可能为空的)以逗号分隔的初始值列表Is it legal (and moral) for a member function to say delete this? 答案:http://t.cn/E4Wfcfl
合法,但:
new(不是 new[]、不是 placement new、不是栈上、不是全局、不是其他对象成员)分配的delete this 的成员函数是最后一个调用 this 的成员函数delete this 后面没有调用 this 了delete this 后没有人使用了如何定义一个只能在堆上(栈上)生成对象的类? 答案:http://t.cn/E4WfDhP
方法:将析构函数设置为私有
原因:C++ 是静态绑定语言,编译器管理栈上对象的生命周期,编译器在为类对象分配栈空间时,会先检查类的析构函数的访问性。若析构函数不可访问,则不能在栈上创建对象。
方法:将 new 和 delete 重载为私有
原因:在堆上生成对象,使用 new 关键词操作,其过程分为两阶段:第一阶段,使用 new 在堆上寻找可用内存,分配给对象;第二阶段,调用构造函数生成对象。将 new 操作设置为私有,那么第一阶段就无法完成,就不能够在堆上生成对象。
头文件:#include <memory>
std::auto_ptr<std::string> ps (new std::string(str));
多个智能指针可以共享同一个对象,对象的最末一个拥有着有责任销毁对象,并清理与该对象相关的所有资源。
weak_ptr 允许你共享但不拥有某对象,一旦最末一个拥有该对象的智能指针失去了所有权,任何 weak_ptr 都会自动成空(empty)。因此,在 default 和 copy 构造函数之外,weak_ptr 只提供 “接受一个 shared_ptr” 的构造函数。
unique_ptr 是 C++11 才开始提供的类型,是一种在异常时可以帮助避免资源泄漏的智能指针。采用独占式拥有,意味着可以确保一个对象和其相应的资源同一时间只被一个 pointer 拥有。一旦拥有着被销毁或编程 empty,或开始拥有另一个对象,先前拥有的那个对象就会被销毁,其任何相应资源亦会被释放。
被 c++11 弃用,原因是缺乏语言特性如 “针对构造和赋值” 的 std::move 语义,以及其他瑕疵。
move 语义;delete),unique_ptr 可以管理数组(析构调用 delete[] );MSDN . 强制转换运算符:http://t.cn/E4WIt5W
向上转换是一种隐式转换。
char` 到 `int 或 One_class` 到 `Unrelated_class 之类的转换,但其本身并不安全)try {  
    Circle& ref_circle = dynamic_cast<Circle&>(ref_shape);   
}  
catch (bad_cast b) {  
    cout << "Caught: " << b.what();  
} 
typeinfoclass Flyable                       // 能飞的
{
public:
    virtual void takeoff() = 0;     // 起飞
    virtual void land() = 0;        // 降落
};
class Bird : public Flyable         // 鸟
{
public:
    void foraging() {...}           // 觅食
    virtual void takeoff() {...}
    virtual void land() {...}
};
class Plane : public Flyable        // 飞机
{
public:
    void carry() {...}              // 运输
    virtual void take off() {...}
    virtual void land() {...}
};
class type_info
{
public:
    const char* name() const;
    bool operator == (const type_info & rhs) const;
    bool operator != (const type_info & rhs) const;
    int before(const type_info & rhs) const;
    virtual ~type_info();
private:
    ...
};
class doSomething(Flyable *obj)                 // 做些事情
{
    obj->takeoff();
    cout << typeid(*obj).name() << endl;        // 输出传入对象类型("class Bird" or "class Plane")
    if(typeid(*obj) == typeid(Bird))            // 判断对象类型
    {
        Bird *bird = dynamic_cast<Bird *>(obj); // 对象转化
        bird->foraging();
    }
    obj->land();
};
const、enum、inline 替换 #define)operator= 返回一个 reference to *this (用于连锁赋值)operator= 中处理 “自我赋值”new 中使用 [] 则 delete [],new 中不使用 [] 则 delete)(T)expression、T(expression);新式:const_cast(expression)、dynamic_cast(expression)、reinterpret_cast(expression)、static_cast(expression)、;尽量避免转型、注重效率避免 dynamic_casts、尽量设计成无需转型、可把转型封装成函数、宁可用新式转型)tr1::function 成员变量替换 virtual 函数,将继承体系内的 virtual 函数替换为另一个继承体系内的 virtual 函数)this-> 指涉 base class templates 内的成员名称,或藉由一个明白写出的 “base class 资格修饰符” 完成)英文:Google C++ Style Guide :http://t.cn/RqhluJP 中文:C++ 风格指南:http://t.cn/ELDTnur Google C++ Style Guide 图

图片来源于:CSDN . 一张图总结Google C++编程规范(Google C++ Style Guide)
STL 方法含义索引:http://t.cn/E4WMXXs
容器的详细说明:http://t.cn/E4WMXXs
| 容器 | 底层数据结构 | 时间复杂度 | 有无序 | 可不可重复 | 其他 | 
|---|---|---|---|---|---|
| array | 数组 | 随机读改 O(1) | 无序 | 可重复 | 支持快速随机访问 | 
| vector | 数组 | 随机读改、尾部插入、尾部删除 O(1)头部插入、头部删除 O(n) | 无序 | 可重复 | 支持快速随机访问 | 
| list | 双向链表 | 插入、删除 O(1)随机读改 O(n) | 无序 | 可重复 | 支持快速增删 | 
| deque | 双端队列 | 头尾插入、头尾删除 O(1) | 无序 | 可重复 | 一个中央控制器 + 多个缓冲区,支持首尾快速增删,支持随机访问 | 
| stack | deque / list | 顶部插入、顶部删除 O(1) | 无序 | 可重复 | deque 或 list 封闭头端开口,不用 vector 的原因应该是容量大小有限制,扩容耗时 | 
| queue | deque / list | 尾部插入、头部删除 O(1) | 无序 | 可重复 | deque 或 list 封闭头端开口,不用 vector 的原因应该是容量大小有限制,扩容耗时 | 
| priority_queue | vector + max-heap | 插入、删除 O(log2n) | 有序 | 可重复 | vector容器+heap处理规则 | 
| set | 红黑树 | 插入、删除、查找 O(log2n) | 有序 | 不可重复 | |
| multiset | 红黑树 | 插入、删除、查找 O(log2n) | 有序 | 可重复 | |
| map | 红黑树 | 插入、删除、查找 O(log2n) | 有序 | 不可重复 | |
| multimap | 红黑树 | 插入、删除、查找 O(log2n) | 有序 | 可重复 | |
| hash_set | 哈希表 | 插入、删除、查找 O(1) 最差 O(n) | 无序 | 不可重复 | |
| hash_multiset | 哈希表 | 插入、删除、查找 O(1) 最差 O(n) | 无序 | 可重复 | |
| hash_map | 哈希表 | 插入、删除、查找 O(1) 最差 O(n) | 无序 | 不可重复 | |
| hash_multimap | 哈希表 | 插入、删除、查找 O(1) 最差 O(n) | 无序 | 可重复 | 
http://t.cn/aEv0DV
| 算法 | 底层算法 | 时间复杂度 | 可不可重复 | 
|---|---|---|---|
| find | 顺序查找 | O(n) | 可重复 | 
| sort | 内省排序 | O(n*log2n) | 可重复 | 
SqStack.cpp:http://t.cn/E4WxO0b
typedef struct {
    ElemType *elem;
    int top;
    int size;
    int increment;
} SqSrack;
typedef struct {
    ElemType * elem;
    int front;
    int rear;
    int maxSize;
}SqQueue;
SqQueue.rear++
SqQueue.rear = (SqQueue.rear + 1) % SqQueue.maxSize
SqList.cpp
顺序表数据结构和图片
typedef struct {
    ElemType *elem;
    int length;
    int size;
    int increment;
} SqList;
LinkList.cpp
LinkList_with_head.cpp
typedef struct LNode {
    ElemType data;
    struct LNode *next;
} LNode, *LinkList; 
HashTable.cpp
哈希函数:H(key): K -> D , key ∈ K
typedef char KeyType;
typedef struct {
    KeyType key;
}RcdType;
typedef struct {
    RcdType *rcd;
    int size;
    int count;
    bool *tag;
}HashTable;
函数直接或间接地调用自身
// 广义表的头尾链表存储表示
typedef enum {ATOM, LIST} ElemTag;
// ATOM==0:原子,LIST==1:子表
typedef struct GLNode {
    ElemTag tag;
    // 公共部分,用于区分原子结点和表结点
    union {
        // 原子结点和表结点的联合部分
        AtomType atom;
        // atom 是原子结点的值域,AtomType 由用户定义
        struct {
            struct GLNode *hp, *tp;
        } ptr;
        // ptr 是表结点的指针域,prt.hp 和 ptr.tp 分别指向表头和表尾
    } a;
} *GList, GLNode;
// 广义表的扩展线性链表存储表示
typedef enum {ATOM, LIST} ElemTag;
// ATOM==0:原子,LIST==1:子表
typedef struct GLNode1 {
    ElemTag tag;
    // 公共部分,用于区分原子结点和表结点
    union {
        // 原子结点和表结点的联合部分
        AtomType atom; // 原子结点的值域
        struct GLNode1 *hp; // 表结点的表头指针
    } a;
    struct GLNode1 *tp;
    // 相当于线性链表的 next,指向下一个元素结点
} *GList1, GLNode1;
BinaryTree.cpp
typedef struct BiTNode
{
    TElemType data;
    struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;
一种不相交的子集所构成的集合 S = {S1, S2, …, Sn}
F(n)=F(n-1)+F(n-2)+1 (1 是根节点,F(n-1) 是左子树的节点数量,F(n-2) 是右子树的节点数量)平衡二叉树插入新结点导致失衡的子树
调整:

B 树(B-tree)、B+ 树(B+-tree)
对于在内部节点的数据,可直接得到,不必根据叶子节点来定位。
B 树、B+ 树区别来自:differences-between-b-trees-and-b-trees、B树和B+树的区别: http://t.cn/RrBAaZa http://t.cn/E4WJhmZ

八叉树(octree),或称八元树,是一种用于描述三维空间(划分空间)的树状数据结构。八叉树的每个节点表示一个正方体的体积元素,每个节点有八个子节点,这八个子节点所表示的体积元素加在一起就等于父节点的体积。一般中心点作为节点的分叉中心。
http://t.cn/E4WJUGz
| 排序算法 | 平均时间复杂度 | 最差时间复杂度 | 空间复杂度 | 数据对象稳定性 | 
|---|---|---|---|---|
| 冒泡排序 | O(n2) | O(n2) | O(1) | 稳定 | 
| 选择排序 | O(n2) | O(n2) | O(1) | 数组不稳定、链表稳定 | 
| 插入排序 | O(n2) | O(n2) | O(1) | 稳定 | 
| 快速排序 | O(n*log2n) | O(n2) | O(log2n) | 不稳定 | 
| 堆排序 | O(n*log2n) | O(n*log2n) | O(1) | 不稳定 | 
| 归并排序 | O(n*log2n) | O(n*log2n) | O(n) | 稳定 | 
| 希尔排序 | O(n*log2n) | O(n2) | O(1) | 不稳定 | 
| 计数排序 | O(n+m) | O(n+m) | O(n+m) | 稳定 | 
| 桶排序 | O(n) | O(n) | O(m) | 稳定 | 
| 基数排序 | O(k*n) | O(n2) | 稳定 | 
均按从小到大排列
| 查找算法 | 平均时间复杂度 | 空间复杂度 | 查找条件 | 
|---|---|---|---|
| 顺序查找 | O(n) | O(1) | 无序或有序 | 
| 二分查找(折半查找) | O(log2n) | O(1) | 有序 | 
| 插值查找 | O(log2(log2n)) | O(1) | 有序 | 
| 斐波那契查找 | O(log2n) | O(1) | 有序 | 
| 哈希查找 | O(1) | O(n) | 无序或有序 | 
| 二叉查找树(二叉搜索树查找) | O(log2n) | ||
| 红黑树 | O(log2n) | ||
| 2-3树 | O(log2n - log3n) | ||
| B树/B+树 | O(log2n) | 
| 图搜索算法 | 数据结构 | 遍历时间复杂度 | 空间复杂度 | 
|---|---|---|---|
| BFS广度优先搜索 | 邻接矩阵邻接链表 | O(|v|2)O(|v|+|E|) | O(|v|2)O(|v|+|E|) | 
| DFS深度优先搜索 | 邻接矩阵邻接链表 | O(|v|2)O(|v|+|E|) | O(|v|2)O(|v|+|E|) | 
| 算法 | 思想 | 应用 | 
|---|---|---|
| 分治法 | 把一个复杂的问题分成两个或更多的相同或相似的子问题,直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并 | 循环赛日程安排问题、排序算法(快速排序、归并排序) | 
| 动态规划 | 通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法,适用于有重叠子问题和最优子结构性质的问题 | 背包问题、斐波那契数列 | 
| 贪心法 | 一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是最好或最优的算法 | 旅行推销员问题(最短路径问题)、最小生成树、哈夫曼编码 | 
对于有线程系统:
对于无线程系统:
线程间的通信目的主要是用于线程同步,所以线程没有像进程通信中的用于数据交换的通信机制
进程之间的通信方式以及优缺点来源于:进程线程面试题总结
| 对比维度 | 多进程 | 多线程 | 总结 | 
|---|---|---|---|
| 数据共享、同步 | 数据共享复杂,需要用 IPC;数据是分开的,同步简单 | 因为共享进程数据,数据共享简单,但也是因为这个原因导致同步复杂 | 各有优势 | 
| 内存、CPU | 占用内存多,切换复杂,CPU 利用率低 | 占用内存少,切换简单,CPU 利用率高 | 线程占优 | 
| 创建销毁、切换 | 创建销毁、切换复杂,速度慢 | 创建销毁、切换简单,速度很快 | 线程占优 | 
| 编程、调试 | 编程简单,调试简单 | 编程复杂,调试复杂 | 进程占优 | 
| 可靠性 | 进程间不会互相影响 | 一个线程挂掉将导致整个进程挂掉 | 进程占优 | 
| 分布式 | 适应于多核、多机分布式;如果一台机器不够,扩展到多台机器比较简单 | 适应于多核分布式 | 进程占优 | 
| 优劣 | 多进程 | 多线程 | 
|---|---|---|
| 优点 | 编程、调试简单,可靠性较高 | 创建、销毁、切换速度快,内存、资源占用小 | 
| 缺点 | 创建、销毁、切换速度慢,内存、资源占用大 | 编程、调试复杂,可靠性较差 | 
多进程与多线程间的对比、优劣与选择来自:多线程还是多进程的选择及区别
在现代操作系统里,同一时间可能有多个内核执行流在执行,因此内核其实象多进程多线程编程一样也需要一些同步机制来同步各执行单元对共享数据的访问。尤其是在多处理器系统上,更需要一些同步机制来同步不同处理器上的执行单元对共享的数据的访问。
来自:Linux 内核的同步机制,第 1 部分、Linux 内核的同步机制,第 2 部分
主机字节序又叫 CPU 字节序,其不是由操作系统决定的,而是由 CPU 指令集架构决定的。主机字节序分为两种:
32 位整数 0x12345678 是从起始位置为 0x00 的地址开始存放,则:
| 内存地址 | 0x00 | 0x01 | 0x02 | 0x03 | 
|---|---|---|---|---|
| 大端 | 12 | 34 | 56 | 78 | 
| 小端 | 78 | 56 | 34 | 12 | 
大端序 小端序
可以这样判断自己 CPU 字节序是大端还是小端:
#include <iostream>
using namespace std;
int main()
{
    int i = 0x12345678;
    if (*((char*)&i) == 0x12)
        cout << "大端" << endl;
    else    
        cout << "小端" << endl;
    return 0;
}
网络字节顺序是 TCP/IP 中规定好的一种数据表示格式,它与具体的 CPU 类型、操作系统等无关,从而可以保重数据在不同主机之间传输时能够被正确解释。
网络字节顺序采用:大端(Big Endian)排列方式。
在地址映射过程中,若在页面中发现所要访问的页面不在内存中,则产生缺页中断。当发生缺页中断时,如果操作系统内存中没有空闲页面,则操作系统必须在内存选择一个页面将其移出内存,以便为即将调入的页面让出空间。而用来选择淘汰哪一页的规则叫做页面置换算法。
全局:
局部:
计算机经网络体系结构:
计算机经网络体系结构
| 分层 | 作用 | 协议 | 
|---|---|---|
| 物理层 | 通过媒介传输比特,确定机械及电气规范(比特 Bit) | RJ45、CLOCK、IEEE802.3(中继器,集线器) | 
| 数据链路层 | 将比特组装成帧和点到点的传递(帧 Frame) | PPP、FR、HDLC、VLAN、MAC(网桥,交换机) | 
| 网络层 | 负责数据包从源到宿的传递和网际互连(包 Packet) | IP、ICMP、ARP、RARP、OSPF、IPX、RIP、IGRP(路由器) | 
| 运输层 | 提供端到端的可靠报文传递和错误恢复( 段Segment) | TCP、UDP、SPX | 
| 会话层 | 建立、管理和终止会话(会话协议数据单元 SPDU) | NFS、SQL、NETBIOS、RPC | 
| 表示层 | 对数据进行翻译、加密和压缩(表示协议数据单元 PPDU) | JPEG、MPEG、ASII | 
| 应用层 | 允许访问OSI环境的手段(应用协议数据单元 APDU) | FTP、DNS、Telnet、SMTP、HTTP、WWW、NFS | 
通道:
通道复用技术:
主要信道:
三个基本问题:
SOH - 数据部分 - EOT点对点协议(Point-to-Point Protocol):
广播通信:
IP 地址分类:
IP 地址 ::= {<网络号>,<主机号>}| IP 地址类别 | 网络号 | 网络范围 | 主机号 | IP 地址范围 | 
|---|---|---|---|---|
| A 类 | 8bit,第一位固定为 0 | 0 —— 127 | 24bit | 1.0.0.0 —— 127.255.255.255 | 
| B 类 | 16bit,前两位固定为 10 | 128.0 —— 191.255 | 16bit | 128.0.0.0 —— 191.255.255.255 | 
| C 类 | 24bit,前三位固定为 110 | 192.0.0 —— 223.255.255 | 8bit | 192.0.0.0 —— 223.255.255.255 | 
| D 类 | 前四位固定为 1110,后面为多播地址 | |||
| E 类 | 前五位固定为 11110,后面保留为今后所用 | 
应用:
TTL(Time To Live,生存时间)该字段指定 IP 包被路由器丢弃之前允许通过的最大网段数量
0.0.0.0, Netmask: 0.0.0.0)指向自治系统的出口。根据应用和执行的不同,路由表可能含有如下附加信息:
协议:
端口:
| 应用程序 | FTP | TELNET | SMTP | DNS | TFTP | HTTP | HTTPS | SNMP | 
|---|---|---|---|---|---|---|---|---|
| 端口号 | 21 | 23 | 25 | 53 | 69 | 80 | 443 | 161 | 
受限于公众号文章字数限制,后续部分请看【今天的第二篇推文】,
特征:
TCP 如何保证可靠传输:
TCP 首部
TCP:状态控制码(Code,Control Flag),占 6 比特,含义如下:
URG=1 时,表明紧急指针字段有效,代表该封包为紧急封包。它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据), 且上图中的 Urgent Pointer 字段也会被启用。ACK=1 时确认号字段才有效,代表这个封包为确认封包。当 ACK=0 时,确认号无效。RST=1 时,表明 TCP 连接中出现严重差错(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立运输连接。FIN=1 时,表明此报文段的发送端的数据已发送完毕,并要求释放运输连接。特征:
TCP 是一个基于字节流的传输服务(UDP 基于报文的),“流” 意味着 TCP 所传输的数据是没有边界的。所以可能会出现两个数据包黏在一起的情况。
\r\n 标记。FTP 协议正是这么做的。但问题在于如果数据正文中也含有 \r\n,则会误判为消息的边界。流量控制(flow control)就是让发送方的发送速率不要太快,要让接收方来得及接收。
拥塞控制就是防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载。
因为 TCP 三次握手建立连接、四次挥手释放连接很重要,所以附上《计算机网络(第 7 版)-谢希仁》书中对此章的详细描述:https://github.com/huihut/interview/blob/master/images/TCP-transport-connection-management.png
UDP 报文
【TCP 建立连接全过程解释】
【答案一】因为信道不可靠,而 TCP 想在不可靠信道上建立可靠地传输,那么三次通信是理论上的最小值。(而 UDP 则不需建立可靠传输,因此 UDP 不需要三次握手。)
Google Groups . TCP 建立连接为什么是三次握手?{技术}{网络通信}
【答案二】因为双方都需要确认对方收到了自己发送的序列号,确认过程最少要进行三次通信。
知乎 . TCP 为什么是三次握手,而不是两次或四次?
【答案三】为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。
《计算机网络(第 7 版)-谢希仁》
【TCP 释放连接全过程解释】
【问题一】TCP 为什么要进行四次挥手?/ 为什么 TCP 建立连接需要三次,而释放连接则需要四次?
【答案一】因为 TCP 是全双工模式,客户端请求关闭连接后,客户端向服务端的连接关闭(一二次挥手),服务端继续传输之前没传完的数据给客户端(数据传输),服务端向客户端的连接关闭(三四次挥手)。所以 TCP 释放连接时服务器的 ACK 和 FIN 是分开发送的(中间隔着数据传输),而 TCP 建立连接时服务器的 ACK 和 SYN 是一起发送的(第二次握手),所以 TCP 建立连接需要三次,而释放连接则需要四次。
【问题二】为什么 TCP 连接时可以 ACK 和 SYN 一起发送,而释放时则 ACK 和 FIN 分开发送呢?(ACK 和 FIN 分开是指第二次和第三次挥手)
【答案二】因为客户端请求释放时,服务器可能还有数据需要传输给客户端,因此服务端要先响应客户端 FIN 请求(服务端发送 ACK),然后数据传输,传输完成后,服务端再提出 FIN 请求(服务端发送 FIN);而连接时则没有中间的数据传输,因此连接时可以 ACK 和 SYN 一起发送。
【问题三】为什么客户端释放最后需要 TIME-WAIT 等待 2MSL 呢?
【答案三】
域名:
域名 ::= {<三级域名>.<二级域名>.<顶级域名>},如:blog.huihut.com标准格式:
协议类型:[//服务器地址[:端口号]][/资源层级UNIX文件路径]文件名[?查询][#片段ID]完整格式:
协议类型:[//[访问资源需要的凭证信息@]服务器地址[:端口号]][/资源层级UNIX文件路径]文件名[?查询][#片段ID]其中【访问凭证信息@;:端口号;?查询;#片段ID】都属于选填项 如:
https://github.com/huihut/interview#cc
HTTP(HyperText Transfer Protocol,超文本传输协议)是一种用于分布式、协作式和超媒体信息系统的应用层协议。HTTP 是万维网的数据通信的基础。
请求方法
| 方法 | 意义 | 
|---|---|
| OPTIONS | 请求一些选项信息,允许客户端查看服务器的性能 | 
| GET | 请求指定的页面信息,并返回实体主体 | 
| HEAD | 类似于 get 请求,只不过返回的响应中没有具体的内容,用于获取报头 | 
| POST | 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改 | 
| PUT | 从客户端向服务器传送的数据取代指定的文档的内容 | 
| DELETE | 请求服务器删除指定的页面 | 
| TRACE | 回显服务器收到的请求,主要用于测试或诊断 | 
状态码(Status-Code)
更多状态码:菜鸟教程 . HTTP状态码
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
我们知道 TCP 建立连接要进行 “三次握手”,即交换三个分组。大致流程如下:
只有就完了三次握手,但是这个三次握手发生在 Socket 的那几个函数中呢?请看下图:

socket 中发送的 TCP 三次握手
从图中可以看出:
上面介绍了 socket 中 TCP 的三次握手建立过程,及其涉及的 socket 函数。现在我们介绍 socket 中的四次握手释放连接的过程,请看下图:

socket 中发送的 TCP 四次握手
图示过程如下:
这样每个方向上都有一个 FIN 和 ACK。
各大设计模式例子参考:CSDN专栏 . C++ 设计模式 系列博文
设计模式工程目录
单例模式例子
抽象工厂模式例子
适配器模式例子
桥接模式例子
观察者模式例子
一般应用程序内存空间有如下区域:
栈保存了一个函数调用所需要的维护信息,常被称为堆栈帧(Stack Frame)或活动记录(Activate Record),一般包含以下几方面:
堆分配算法:
典型的非法指针解引用造成的错误。当指针指向一个不允许读写的内存地址,而程序却试图利用指针来读或写该地址时,会出现这个错误。
普遍原因:
| 平台 | 可执行文件 | 目标文件 | 动态库/共享对象 | 静态库 | 
|---|---|---|---|---|
| Windows | exe | obj | dll | lib | 
| Unix/Linux | ELF、out | o | so | a | 
| Mac | Mach-O | o | dylib、tbd、framework | a、framework | 
#include、#define 等预编译指令,生成 .i 或 .ii 文件).s 文件).o 文件).out 文件)现在版本 GCC 把预编译和编译合成一步,预编译编译程序 cc1、汇编器 as、连接器 ld MSVC 编译环境,编译器 cl、连接器 link、可执行文件查看器 dumpbin
编译器编译源代码后生成的文件叫做目标文件。目标文件从结构上讲,它是已经编译后的可执行文件格式,只是还没有经过链接的过程,其中可能有些符号或有些地址还没有被调整。
可执行文件(Windows 的
.exe和 Linux 的ELF)、动态链接库(Windows 的.dll和 Linux 的.so)、静态链接库(Windows 的.lib和 Linux 的.a)都是按照可执行文件格式存储(Windows 按照 PE-COFF,Linux 按照 ELF)
.obj 格式.o 格式a.out 格式.COM 格式PE 和 ELF 都是 COFF(Common File Format)的变种
| 段 | 功能 | 
|---|---|
| File Header | 文件头,描述整个文件的文件属性(包括文件是否可执行、是静态链接或动态连接及入口地址、目标硬件、目标操作系统等) | 
| .text section | 代码段,执行语句编译成的机器代码 | 
| .data section | 数据段,已初始化的全局变量和局部静态变量 | 
| .bss section | BSS 段(Block Started by Symbol),未初始化的全局变量和局部静态变量(因为默认值为 0,所以只是在此预留位置,不占空间) | 
| .rodata section | 只读数据段,存放只读数据,一般是程序里面的只读变量(如 const 修饰的变量)和字符串常量 | 
| .comment section | 注释信息段,存放编译器版本信息 | 
| .note.GNU-stack section | 堆栈提示段 | 
其他段略
在链接中,目标文件之间相互拼合实际上是目标文件之间对地址的引用,即对函数和变量的地址的引用。我们将函数和变量统称为符号(Symbol),函数名或变量名就是符号名(Symbol Name)。
如下符号表(Symbol Table):
| Symbol(符号名) | Symbol Value (地址) | 
|---|---|
| main | 0x100 | 
| Add | 0x123 | 
| ... | ... | 
Linux 下的共享库就是普通的 ELF 共享对象。
共享库版本更新应该保证二进制接口 ABI(Application Binary Interface)的兼容
libname.so.x.y.z
大部分包括 Linux 在内的开源系统遵循 FHS(File Hierarchy Standard)的标准,这标准规定了系统文件如何存放,包括各个目录结构、组织和作用。
/lib:存放系统最关键和最基础的共享库,如动态链接器、C 语言运行库、数学库等/usr/lib:存放非系统运行时所需要的关键性的库,主要是开发库/usr/local/lib:存放跟操作系统本身并不十分相关的库,主要是一些第三方应用程序的库动态链接器会在
/lib、/usr/lib和由/etc/ld.so.conf配置文件指定的,目录中查找共享库
LD_LIBRARY_PATH:临时改变某个应用程序的共享库查找路径,而不会影响其他应用程序LD_PRELOAD:指定预先装载的一些共享库甚至是目标文件LD_DEBUG:打开动态链接器的调试功能创建一个名为 MySharedLib 的共享库
CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(MySharedLib)
set(CMAKE_CXX_STANDARD 11)
add_library(MySharedLib SHARED library.cpp library.h)
library.h
#ifndef MYSHAREDLIB_LIBRARY_H
#define MYSHAREDLIB_LIBRARY_H
// 打印 Hello World!
void hello();
// 使用可变模版参数求和
template <typename T>
T sum(T t)
{
    return t;
}
template <typename T, typename ...Types>
T sum(T first, Types ... rest)
{
    return first + sum<T>(rest...);
}
#endif
library.cpp
#include <iostream>
#include "library.h"
void hello() {
    std::cout << "Hello, World!" << std::endl;
}
创建一个名为 TestSharedLib 的可执行项目
CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(TestSharedLib)
# C++11 编译
set(CMAKE_CXX_STANDARD 11)
# 头文件路径
set(INC_DIR /home/xx/code/clion/MySharedLib)
# 库文件路径
set(LIB_DIR /home/xx/code/clion/MySharedLib/cmake-build-debug)
include_directories(${INC_DIR})
link_directories(${LIB_DIR})
link_libraries(MySharedLib)
add_executable(TestSharedLib main.cpp)
# 链接 MySharedLib 库
target_link_libraries(TestSharedLib MySharedLib)
main.cpp
#include <iostream>
#include "library.h"
using std::cout;
using std::endl;
int main() {
    hello();
    cout << "1 + 2 = " << sum(1,2) << endl;
    cout << "1 + 2 + 3 = " << sum(1,2,3) << endl;
    return 0;
}
执行结果
Hello, World!
1 + 2 = 3
1 + 2 + 3 = 6
/SUBSYSTEM:WINDOWS/SUBSYSTEM:CONSOLEInt WINAPI _tWinMain(
    HINSTANCE hInstanceExe,
    HINSTANCE,
    PTSTR pszCmdLine,
    int nCmdShow);
int _tmain(
    int argc,
    TCHAR *argv[],
    TCHAR *envp[]);
| 应用程序类型 | 入口点函数 | 嵌入可执行文件的启动函数 | 
|---|---|---|
| 处理ANSI字符(串)的GUI应用程序 | _tWinMain(WinMain) | WinMainCRTSartup | 
| 处理Unicode字符(串)的GUI应用程序 | _tWinMain(wWinMain) | wWinMainCRTSartup | 
| 处理ANSI字符(串)的CUI应用程序 | _tmain(Main) | mainCRTSartup | 
| 处理Unicode字符(串)的CUI应用程序 | _tmain(wMain) | wmainCRTSartup | 
| 动态链接库(Dynamic-Link Library) | DllMain | _DllMainCRTStartup | 
知识点来自《Windows核心编程(第五版)》
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    switch(fdwReason)
    {
    case DLL_PROCESS_ATTACH:
        // 第一次将一个DLL映射到进程地址空间时调用
        // The DLL is being mapped into the process' address space.
        break;
    case DLL_THREAD_ATTACH:
        // 当进程创建一个线程的时候,用于告诉DLL执行与线程相关的初始化(非主线程执行)
        // A thread is bing created.
        break;
    case DLL_THREAD_DETACH:
        // 系统调用 ExitThread 线程退出前,即将终止的线程通过告诉DLL执行与线程相关的清理
        // A thread is exiting cleanly.
        break;
    case DLL_PROCESS_DETACH:
        // 将一个DLL从进程的地址空间时调用
        // The DLL is being unmapped from the process' address space.
        break;
    }
    return (TRUE); // Used only for DLL_PROCESS_ATTACH
}
// 载入库
HMODULE WINAPI LoadLibrary(
  _In_ LPCTSTR lpFileName
);
HMODULE LoadLibraryExA(
  LPCSTR lpLibFileName,
  HANDLE hFile,
  DWORD  dwFlags
);
// 若要在通用 Windows 平台(UWP)应用中加载 Win32 DLL,需要调用 LoadPackagedLibrary,而不是 LoadLibrary 或 LoadLibraryEx
HMODULE LoadPackagedLibrary(
  LPCWSTR lpwLibFileName,
  DWORD   Reserved
);
// 卸载库
BOOL WINAPI FreeLibrary(
  _In_ HMODULE hModule
);
// 卸载库和退出线程
VOID WINAPI FreeLibraryAndExitThread(
  _In_ HMODULE hModule,
  _In_ DWORD   dwExitCode
);
FARPROC GetProcAddress(
  HMODULE hInstDll,
  PCSTR pszSymbolName  // 只能接受 ANSI 字符串,不能是 Unicode
);
在 VS 的开发人员命令提示符 使用 DumpBin.exe 可查看 DLL 库的导出段(导出的变量、函数、类名的符号)、相对虚拟地址(RVA,relative virtual address)。如:
DUMPBIN -exports D:\mydll.dll
DLL 头文件
// MyLib.h
#ifdef MYLIBAPI
// MYLIBAPI 应该在全部 DLL 源文件的 include "Mylib.h" 之前被定义
// 全部函数/变量正在被导出
#else
// 这个头文件被一个exe源代码模块包含,意味着全部函数/变量被导入
#define MYLIBAPI extern "C" __declspec(dllimport)
#endif
// 这里定义任何的数据结构和符号
// 定义导出的变量(避免导出变量)
MYLIBAPI int g_nResult;
// 定义导出函数原型
MYLIBAPI int Add(int nLeft, int nRight);
DLL 源文件
// MyLibFile1.cpp
// 包含标准Windows和C运行时头文件
#include <windows.h>
// DLL源码文件导出的函数和变量
#define MYLIBAPI extern "C" __declspec(dllexport)
// 包含导出的数据结构、符号、函数、变量
#include "MyLib.h"
// 将此DLL源代码文件的代码放在此处
int g_nResult;
int Add(int nLeft, int nRight)
{
    g_nResult = nLeft + nRight;
    return g_nResult;
}
// A simple program that uses LoadLibrary and 
// GetProcAddress to access myPuts from Myputs.dll. 
#include <windows.h> 
#include <stdio.h> 
typedef int (__cdecl *MYPROC)(LPWSTR); 
int main( void ) 
{ 
    HINSTANCE hinstLib; 
    MYPROC ProcAdd; 
    BOOL fFreeResult, fRunTimeLinkSuccess = FALSE; 
    // Get a handle to the DLL module.
    hinstLib = LoadLibrary(TEXT("MyPuts.dll")); 
    // If the handle is valid, try to get the function address.
    if (hinstLib != NULL) 
    { 
        ProcAdd = (MYPROC) GetProcAddress(hinstLib, "myPuts"); 
        // If the function address is valid, call the function.
        if (NULL != ProcAdd) 
        {
            fRunTimeLinkSuccess = TRUE;
            (ProcAdd) (L"Message sent to the DLL function\n"); 
        }
        // Free the DLL module.
        fFreeResult = FreeLibrary(hinstLib); 
    } 
    // If unable to call the DLL function, use an alternative.
    if (! fRunTimeLinkSuccess) 
        printf("Message printed from executable\n"); 
    return 0;
}
一个程序的 I/O 指代程序与外界的交互,包括文件、管程、网络、命令行、信号等。更广义地讲,I/O 指代操作系统理解为 “文件” 的事物。
_start -> __libc_start_main -> exit -> _exit
其中 main(argc, argv, __environ) 函数在 __libc_start_main 里执行。
int mainCRTStartup(void)
执行如下操作:
大致包含如下功能:
包含: