在C 语言中,对于任何类型T,都可以在T所在的内存地址处产生一个包含此对象地址的对应变量。这种变量,实际上是一种指向对象的变量,因此,这些变量称为指针。
指针是构建数据结构和操作内存的精确而高效的工具。
章节结构
- 指针基础
- 存储空间分配
- 数据集合与指针的算术运算
- 作为函数参数的指针
- 指向指针的指针
- 泛型指针与类型转换
- 函数指针
指针基础
一个指针存储数据在内存的地址而不是存储数据本身。
理解指针的最佳方法:绘制图表。
- 指针通常都是按位置用箭头一个一个链接起来,而不是在图标中画出实际的地址。
- 当指针补指向任何数据,,也就是说指针被设置成NULL时,用两条竖线来表示。
- 悬空指针:指向无效地址的指针。
可能产生悬空指针的一些错误示例:将任意的整型变量强制转换为指针变量;操作超出数组边界的指针;释放一个或多个仍被引用的指针。
存储空间分配
指针变量的大小通常与编译器的设定以及某些特定的C实现中的类型界定符有关。
- 必须记住:当声明一个指针时,仅仅只是为指针本身分配了空间,并没有为指针所引用的数据分配空间。
为数据分配空间的两种方法:
- (1)直接声明一个变量
- (2)在运行时动态的分配存储空间(例如:使用malloc或realloc)。
当声明一个变量,发生了什么
当声明一个变量时,编译器会根据变量的类型预留足够的内存空间。
变量的存储空间是系统自动分配的,但是此存储空间不会在程序的整个生命周期中永久存在。
自动变量是一种在进入或离开一个模块或函数时其存储空间能够自动分配和释放的变量
在C 语言中,当想要动态分配存储空间时,会得到一个指向一个堆存储空间的指针(第3章),此存储空间由我们自行管理,并且会一直存在,除非我们显式的将它释放。
作为函数参数的指针
函数调用时会用到指针的两个地方:
- 交换值
- 把数组传递给函数
C语言把数组名当做一个不可变的指针来使用。当向函数传递一个类型为T的数组对象时,其实就等同于向函数传递一个指向类型为T的对象的指针。
/* 函数参数的传递机制,以及函数运行机制,函数运行是另外开辟一个存储空间运行,因此如果仅仅传递值,那么只是赋值新开辟的内存地址,一系列的操作只是在新开辟的内存地址上进行。不对其他内存空间产生影响。
而按引用传递,那么传递进去的是内存地址,一系列的操作是在原来内存地址上进行的,因此会对原来的内存地址存储的内容产生影响。 */
// 正确的交换变量值得方式
void swap(int *x,int *y){
int tmp;
tmp = *x;
*x = *y;
*y = tmp;
return;
}
// 错误的交换变量值的方式
void swap_error(int x,int y){
int tmp;
tmp = x;
x = y;
y= tmp;
return;
}
在C 语言的函数调用中指针起着至关重要的作用。最重要的是,指针支持将参数作为引用传递给函数(即按引用调用)。
按引用传递参数时,当函数改变此参数时,这个被改变参数的值会一直存在,甚至函数退出后仍然存在。 当按值调用传递函数时,此时值得改变只能持续到函数返回时。
使用指针传递大容量复杂的函数参数是一种高效的方法:因为只是传递一个指针而不是一个数据的完整副本到函数中,这样可以大大地节省内存空间