# 冷门知识

记录一下自己在学习和面试时让我头脑模糊的知识。

### 结构体对齐

有点无聊的知识，不过笔试常考。要想算一个结构体有多少字节数，有几个要点：

1. 数据成员对齐规则：结构(struct)(或联合(union))的数据成员，第一个数据成员放在offset为0的地方，每个数据成员存储的起始位置要从该成员(每个成员本身)大小的整数倍开始(比如int在32位机为４字节,则要从４的整数倍地址开始存储)。
2. 结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储.(struct a里存有struct b,b里有char,int ,double等元素,那b应该从8的整数倍开始存储.)
3. 收尾工作:结构体的总大小,也就是sizeof的结果,.必须是其内部最大成员的整数倍（结构体成员以最大成员为代表）.不足的要补齐.

### 各个数据类型的字节数

32,64位编译环境下的 sizeof 问题经常出现。不过，两者环境下的数据类型所占字节数都是一样的，

```cpp
char:1字节；
 
short:2字节；
 
int:4字节；
 
long long:8字节；
 
float:4字节；
 
double:8字节；
 
long double:8字节；
```

不同的是：指针类型和 long

* 32位：指针类型占4个字节，long 占 4个字节。
* 64位：指针类型占8个字节，long 占 8个字节。

### 静态变量只会初始化一次

```cpp
void foo() {
    static char *p = new char[128];
}
```

在多次调用 foo 函数时，p 只会被初始化一次，不是全局变量那样可以随意初始化。可以反向理解为 static 语句只会被执行一次，执行完之后随机删除。

### C++ 命名规则

合法标识符规则如下：

1. 由数字，字母，下划线组成；
2. 不能由数字开头。即第一个字符只能是字母或下划线。
3. 区分大小写。
4. 不可与系统关键字相同。

### const 限定符

* \_之前，则const限定的是\_ptr而不限定ptr。也就是说，ptr可以改变其所指向的对象，但不能通过该指针修改其所指向对象的值。
* 若const限定符在\_之后，则const限定的是ptr而不限定\_ptr。也就是说，ptr不可以改变其所指向的对象，但能通过该指针修改其所指向对象的值。
* 若在\_之前有const限定符且在\_之后也有const限定符，则ptr与\*ptr都被限定。也就是说，ptr既不可以改变其所指向的对象，也不能通过该指针修改其所指向对象的值。
* 另外：只要 在\*之后有 const限定符，则该指针在声明时必须初始化。

### fopen 那些模式

| Model | Action                                                                          |
| ----- | ------------------------------------------------------------------------------- |
| r     | 以只读方式打开文件，该文件必须存在。                                                              |
| r+    | 以读/写方式打开文件，该文件必须存在。                                                             |
| rb+   | 以读/写方式打开一个二进制文件，只允许读/写数据。                                                       |
| rt+   | 以读/写方式打开一个文本文件，允许读和写。                                                           |
| w     | 打开只写文件，若文件存在则长度清为 0，即该文件内容消失，若不存在则创建该文件。                                        |
| w+    | 打开可读/写文件，若文件存在则文件长度清为零，即该文件内容会消失。若文件不存在则建立该文件。                                  |
| a     | 以附加的方式打开只写文件。若文件不存在，则会建立该文件，如果文件存在，写入的数据会被加到文件尾，即文件原先的内容会被保留（EOF 符保留）。          |
| a+    | 以附加方式打开可读/写的文件。若文件不存在，则会建立该文件，如果文件存在，则写入的数据会被加到文件尾后，即文件原先的内容会被保留（原来的 EOF 符不保留）。 |
| wb    | 以只写方式打开或新建一个二进制文件，只允许写数据。                                                       |
| wb+   | 以读/写方式打开或建立一个二进制文件，允许读和写。                                                       |
| wt+   | 以读/写方式打开或建立一个文本文件，允许读写。                                                         |
| at+   | 以读/写方式打开一个文本文件，允许读或在文本末追加数据。                                                    |
| ab+   | 以读/写方式打开一个二进制文件，允许读或在文件末追加数据。                                                   |

### sizeof

sizeof 是 C 语言中的一个操作符 (operator), 不是函数调用 , 简单的说其作用就是返回一个对象或者类型所占的内存字节数。

### 对象的默认拷贝构造是浅拷贝，对于指针只会赋值，不会基于空间。

### fork 和 printf 的爱恨情仇

print输出字符串如果不是一"\n"为结尾的话，就不会马上输出，而是保存在缓存区中。fork函数在创建子进程的时候会复制缓存区的数据。

### ios

ios类派生istream类和ostream类，istream类和ostream类共同派生出iostream类，cin和cout都是iostream类的对象

### 静态链接库和动态链接库

静态链接库的优点

* 代码装载速度快，执行速度略比动态链接库快；
* 只需保证在开发者的计算机中有正确的.LIB文件，在以二进制形式发布程序时不需考虑在用户的计算机上.LIB文件是否存在及版本问题，可避免DLL地狱等问题。

动态链接库的优点

* 更加节省内存并减少页面交换；
* DLL文件与EXE文件独立，只要输出接口不变（即名称、参数、返回值类型和调用约定不变），更换DLL文件不会对EXE文件造成任何影响，因而极大地提高了可维护性和可扩展性；
* 不同编程语言编写的程序只要按照函数调用约定就可以调用同一个DLL函数；
* 适用于大规模的软件开发，使开发过程独立、耦合度小，便于不同开发者和开发组织之间进行开发和测试。

不足之处

* 使用静态链接生成的可执行文件体积较大，包含相同的公共代码，造成浪费；
* 使用动态链接库的应用程序不是自完备的，它依赖的DLL模块也要存在，如果使用载入时动态链接，程序启动时发现DLL不存在，系统将终止程序并给出错误信息。而使用运行时动态链接，系统不会终止，但由于DLL中的导出函数不可用，程序会加载失败；速度比静态链接慢。当某个模块更新后，如果新模块与旧的模块不兼容，那么那些需要该模块才能运行的软件，统统撕掉。这在早期Windows中很常见。

### 格式化输出

使用操作符对数据进行格式输出时，必须包含iomanip.h头文件

### 基类指针指向派生类

若想调用派生类虚函数，直接调用。 若想调用基类虚函数，添加域名。

### static 变量初始化（出自[此处](https://blog.csdn.net/qq_34139994/article/details/105157313))

编译时初始化：如果静态变量本身是基本数据类型(POD)，且初始化值是常量,那么这个初始化过程是在编译期间完成的。

```cpp
static int *p = new int[1024];

int x = 3;
int y = 4;
static int z = x + y;
```

加载时初始化：程序被加载时立即进行的初始化。这个初始化发生在main函数之前。即使程序任何地方都没访问过该变量, 仍然会进行初始化，因此形象地称之为"饿汉式初始化"。

1. 静态变量是一个基本数据类型，但是初始值非常量
2. 静态变量是一个类对象，这种情况下即使是使用常量初始化，也是加载时初始化，而不是编译时初始化

```cpp
static std::string str = "Hello world !";

class MyClass {
public:	
	MyClass();    
	MyClass(int a, int b)
;};

static MyClass* MyClass1 = new MyClass();
static MyClass MyClass2;
```

运行时初始化：这个初始化发生在变量第一次被引用。也就是说，从程序执行模型角度看，程序所在进程空间中，哪个线程先访问了这个变量，就是哪个线程来初始化这个变量。因此，相对于加载初始化来说，这种初始化是把真正的初始化动作推迟到第一次被访问时，因而形象地称为"懒汉式初始化"。

```cpp
int myfunc()
{     	
    static std::string msg = "hello world !";    //运行时初始化
}
```

### 拷贝构造函数和赋值运算符（出自[此处](https://blog.csdn.net/xiaozhidian/article/details/114377907)）

拷贝构造函数和赋值运算符的行为比较相似，都是将一个对象的值复制给另一个对象；但是其结果却有些不同，

拷贝构造函数使用传入对象的值生成一个新的对象的实例，而赋值运算符是将对象的值复制给一个已经存在的实例。

这种区别从两者的名字也可以很轻易的分辨出来，拷贝构造函数也是一种构造函数，那么它的功能就是创建一个新的对象实例；赋值运算符是执行某种运算，将一个对象的值复制给一个已经存在的实例。

调用的是拷贝构造函数还是赋值运算符，主要是看是否有新的对象实例产生。如果产生了新的对象实例，那调用的就是拷贝构造函数；如果没有，那就是对已有的对象赋值，调用的是赋值运算符。

<<<<<<< HEAD

### C 和 C++ 中结构体的区别

* C中的结构体没有函数 但是C++的结构体可以有函数。
* C++的结构体可以被类取代，因为类相对于结构体 具有封装性等优势。 C++中结构体与类的区别：
* 结构体中的成员访问权限不声明时候默认是 公开的；而类 是私有的

### #import 和 #include 的区别

三个文件：文件A.h 文件B.h 文件C.h 文件C.h需要引入A.h、B.h ,文件B.h需要引入文件A.h,这样就重复引用了A.h两次，使用#import可以进行优化。

* Objective-C 中 #import 和 #include 的区别： 预编译指令 Objective-C：#import C，C++：#include #import由gcc编译器支持 在 Objective-C 中，#import 被当成 #include 指令的改良版本来使用。除此之外，#import 确定一个文件只能被导入一次，这使你在递归包含中不会出现问题。 使用哪一个还是由你来决定。一般来说，在导入 Objective-C 头文件的时候使用 #import，包含 C 头文件时使用 #include。 #import比起#include的好处就是不会引起交叉编译

### 编译器自动生成的函数

空类编译器会自动生成一个默认的无参构造函数、默认的析构函数、默认的拷贝构造函数以及默认赋值函数。
