# 1.3、复合类型
**复合类型**(compound type)是根据另一种类型定义的类型,C++ 有两种复合类型,分别是引用和指针。
> A**compound type**is a type that is defined in terms of another type. C++ has several compound types, two of which -- references and pointers.
## 引用
**引用**(reference)是对象的一个别名,通过定义引用可以减少值的拷贝。
> A **reference** defines an alternative name for an object, the compiler only binds the reference to its initializer instead of copying the initializer's value.
一个引用必须和它绑定的对象类型一致:
```
int &reval = 10; // error:绑定的不是一个对象
double dval = 3.14;
int &reval = dval; // error:类型不对
```
**引用通常用于**:
* 函数参数列表:C++ 支持把引用作为参数传给函数,这样可以减少函数调用过程中参数拷贝的开销;
* 函数返回值:这样做的优势在于内存中不产生返回值的副本,并且可用于左值运算;
## 指针
每一个变量都有一个内存位置,每一个内存位置都定义了可使用连字号(&)运算符访问的地址,它表示了在内存中的一个地址。
**指针**是一个变量,其值为另一个变量的地址。当一个指针指向一个地址时,程序可以通过(*)运算符访问变量。
> A **pointer** holds the address of another object.
**区别指针类型**
* `int *p[10];`:长度为10的指针数组,数组中的每个变量为指向int的指针变量;
* `int (*p)[10];`:一个数组指针,指向长度为10的int数组;
* `int *p(int);`:函数声明,函数名是p,参数是int类型的,返回值是int指针类型的;
* `int (*p)(int);`:函数指针,强调是指针,该指针指向的函数具有int类型参数,并且返回值是int类型的;
### 特殊指针
* **野指针**:指向已经被释放或回收的地址空间的指针。
* **空指针**:NULL 本质上是0,nullptr 则代表空指针。
```c++
typeid(NULL).name(); // output: l
typeid(nullptr).name(); // output: Dn
```
为了避免不必要的麻烦,尽量使用 nullptr:
```c++
void func(void* t) {
printf("func(void*)\n");
}
void func(int i) {
printf("func(int)\n");
}
int main() {
func(NULL); // 将导致编译不通过
func(nullptr); // 调用func(void*)
return 0;
}
```
**函数指针**是一个指向函数首地址的指针变量。C++ 在编译时,每一个函数都有一个入口地址,函数指针指向的就是这个入口地址。有了函数指针,可以通过指针来调用函数。
```c++
int max(const int& lhs, const int& rhs) {
return lhs > rhs ? lhs : rhs;
}
typedef int(*pf)(const int&, const int&);
int main () {
int (*f)(const int&, const int&); // 定义一个函数指针
pf p = max;
printf("%d\n", p(1, 2));
printf("%d\n", (*p)(1, 2));
return 0;
}
```
### 数组名和指针的区别
* 数组名和指针都可以通过增减偏移量来访问数组中的元素;
* 数组名不是真正意义上的指针,可以理解为常指针,所以数组名没有自增、自减等操作;
* 当数组名当做形参传递给调用函数后,就失去了原有特性,退化成一般指针,多了自增、自减操作,但sizeof运算符不能再得到原数组的大小了;
## 指针和引用区别
* 引用只是别名,不占用具体存储空间,只有声明没有定义,而指针是具体变量,需要占用存储空间;所以使用 sizeof 看一个指针的大小是 4,而引用则是被引用对象的大小;
* 引用在声明时必须初始化为另一变量,一旦出现必须为 `typename &refname = varname;`形式,而指针声明和定义可以分开,可以先只声明指针变量而不初始化,等用到时再指向具体变量;
* 引用一旦初始化之后就不可以再改变(变量可以被引用为多次,但引用只能作为一个变量引用),而指针变量可以重新指向别的变量;
* 不存在指向空值的引用,必须有具体实体,但是存在指向空值的指针;
* 指针和引用使用自增运算符的意义不一样;
- 目录
- 基础知识
- 1、变量和基础类型
- 1.1、内置类型
- 1.2、变量
- 1.3、复合类型
- 1.4、类型修饰符
- 1.5、类型处理
- 1.6、自定义结构
- 1.7、数组
- 2、表达式和语句
- 2.1、运算符
- 2.2、语句
- 3、函数
- 1、语法相关
- 2、资源管理
- 3、面向对象
- 4、模板与泛型编程
- Problem01:判断类中是否包含函数
- Problem02:解析函数的参数类型
- 5、系统库
- Problem01:多线程维护最大值
- Problem02:介绍一下strcpy、strncpy、memcpy、memmove
- Problem03:介绍一下网络编程
- Problem04:select、poll、epoll的区别
- 未整理
- Problem11:实现在main函数前、后执行的函数
- Problem12:可变参函数的实现
- Problem13:全局变量初始化顺序问题
- Problem14:介绍一下隐式转换
- Problem07:实现一个不能被拷贝的类
- Problem08:实现一个只能通过动态、静态分配的类
- 开源项目
- redis
- 第一部分 数据结构与对象
- redis 底层数据结构
- redis 对象
- taskflow
- 数据结构
- Executor
