指针
指针变量, 简称指针, 用来存放其它变量的内存地址. 通过指针, 可以直接访问系统内存, 从而提高程序执行效率。
定义¶
类型标识符 * 指针变量名
- 类型标识符: 表示该指针可指向的对象的数据类型, 即该指针能存放哪类数据的地址.
- 星号和指针变量名之间可以没有空格
初始化¶
指针可以通过取地址运算符 &
获取变量的地址进行初始化:
int num = 10;
int *ptr = # // 正确:指向已存在的变量
int *ptr2 = NULL; // 安全初始化:空指针
// int *ptr3; // 危险:野指针(随机地址)
通过解引用运算符 *
可以访问指针指向的内存中的值:
指针运算¶
操作 | 说明 | 示例 |
---|---|---|
指针加减 | 按数据类型大小移动地址(ptr + n → 地址增加 n*sizeof(类型) ) |
int arr[3]; int *p = arr; p++; |
指针比较 | 比较两个指针的地址位置 | if (p1 < p2) {...} |
指针相减 | 计算两个指针之间的元素个数 | int diff = p2 - p1; |
- 指针加上或减去一个整型数值 k,等效于获得一个新指针,该指针指向原来的元素之后或之 前的第 k 个元素
- 指针加上或减去一个整型数值 k,实际移动距离(字节数)与其数据类型有关: 假定 px 是 int 型(占 4 个字节), 则 px+3 与 px 相差 12 个字节
int x[5] = {0,1,2,3,4};
int * px = &x[0]; // px 指向 x[0]
cout << *px << endl; // 输出 x[0] 的值
cout << *(px+1) << endl; // 输出的 x[1] 值
px = px + 2; // px 改为指向 x[2]
空指针¶
空指针(NULL
)是一个特殊的指针值,表示不指向任何有效的内存地址:
野指针¶
未初始化的指针称为野指针,访问野指针可能导致程序崩溃:
悬挂指针¶
悬挂指针,也叫做悬空指针,指的是释放指针指向的动态内存后的指针。
void 类型的指针¶
void * 指针名
- void 类型的指针可以存储任何类型的对象的地址;
- 不允许直接使用 void 指针访问其目标对象;
- 必须通过显式类型转换, 才可以访问 void 类型指针的目标对象
int x;
int * px;
void * pv;
pv = &x; // OK, void 型指针指向整型变量
px = (int *)pv; // OK, 使用 void 型指针时需要强制类型转换
指向常量的指针¶
const 类型标识符 * 指针名
- 指向常量的指针必须用 const 声明;
- 这里的 const 限定了指针所指对象的属性, 即目标对象是常量, 而不是指针是常量;
- 允许把非 const 对象的地址赋给指向常量的指针, 即指向常量的指针也可以指向普通变量;
- 不允许使用指向常量的指针来修改其目标对象的值, 即使它所指向的对象不是常量
const int a = 3;
int b = 5;
const int * cpa = &a; // OK
*cpa = 5; // ERROR
cpa = &b; // OK
*cpa = 9; // ERROR
b = 9; // OK
常量指针¶
常量指针, 简称常指针: 指针本身的值不能修改。
类型标识符 * const 指针名
数组名即指针常量¶
^f2f980
数组名就是一个指向数组首元素的指针常量。
-
数组名称本身是一个常量指针,不能被修改。
-
数组名称可以隐式转换为指向数组第一个元素的指针。
分析下面三种写法,有什么不同?
int b=500;
const int *a= &b; //[1] const 在 *左边, 修饰指针指向的变量
int const* a= &b; //[2] , *a=3 非法
int* const a= &b; //[3] , const 在*右边,修饰指针本身,a++错误
这三个声明中的区别在于 const
关键字的位置不同,分别表示不同的含义:
const int *a
:这表示a
是一个指向常量整数的指针,即指针a
指向的内容不能通过a
修改。但是,指针a
本身是可以改变的,可以指向不同的地址。int const* a
:与第一个声明相同,这也表示a
是一个指向常量整数的指针。const
关键字可以位于int
前面或后面,对声明的含义没有影响。int* const a
:这表示a
是一个指向整数的常量指针,即指针a
本身是常量,它指向的地址不能通过a
修改。但是,指针a
指向的内容可以通过a
修改。
综上所述,这三种声明的含义分别是:
const int *a
或int const* a
:指向常量整数的指针。int* const a
:指向整数的常量指针。
指向常量的常指针¶
const 类型标识符 * const 指针名
指针本身的值不能修改, 也不能通过该指针修改其目标对象的值。
指针数组¶
指针数组指的是由指针变量组成的数组。
类型标识符 * 指针数组名[n]
数组指针¶
数组指针 是一种特殊的指针,用于指向数组的起始地址。数组指针 是一个变量,用于存储数组的起始地址。数组指针可以被修改,可以指向不同的数组或数组中的不同位置。注意它与[[指针#^f2f980|数组名称]]不是一个概念。
int arr[] = {1, 2, 3, 4, 5}; // 定义一个数组
int *ptr = arr; // 定义一个数组指针,指向数组的第一个元素
// 修改数组指针
ptr += 2; // 指针指向数组的第三个元素
printf("Modified pointer: %p\n", (void *)ptr); // 输出修改后的指针值
printf("Value at ptr[0]: %d\n", ptr[0]); // 输出指针指向的第三个元素
int arr[5] = {1, 2, 3, 4, 5};
int (*ptr)[5] = &arr; // ptr 指向数组 arr
函数指针¶
在C语言中,函数指针 是一种特殊的指针,用于存储函数的内存地址。函数指针可以像一般函数一样,用于调用函数、传递参数。
返回类型 (*指针名)(参数类型列表);
int add(int a, int b) {
return a + b;
}
int (*func_ptr)(int, int) = add; // func_ptr 指向 add 函数
int result = func_ptr(3, 5); // 通过函数指针调用函数,结果为 8
// 创建一个函数指针数组,用于存储多个函数的地址
int subtract(int a, int b) {
return a - b;
}
int multiply(int a, int b) {
return a * b;
}
// 声明一个函数指针数组
int (*func_ptrs[3])(int, int) = {add, subtract, multiply};
// 使用函数指针数组调用不同的函数
for (int i = 0; i < 3; i++) {
printf("Result: %d\n", func_ptrs[i](10, 5));
}
另外一个示例:
typedef void(func_t)(); // 函数类型
typedef void(*func_ptr_t)(); // 函数指针类型
void test() { // 函数名就是一个函数指针
printf("%s\n", __func__);
}
int main(int argc, char* argv[])
{
func_t* func = test; // 声明一个函数指针
func_ptr_t func2 = test; // 函数指针
void (*func3)(); // 声明 包含函数原型的函数指针变量
func3 = test;
func();
func2();
func3();
return EXIT_SUCCESS;
}