# 冷门知识

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

### 结构体对齐

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

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的好处就是不会引起交叉编译

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

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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://wandersofb.gitbook.io/blog/c++/ji-ben-yu-fa/leng-men-zhi-shi.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
