
指针
1. 指针的定义
指针:是在许多编程语言中用来存储内存地址的变量。指针变量的值直接指向(points to)存在该地址的对象的值。所指向的可以是计算机内存中的另一个值,或者在某些情况下,是内存映射计算机硬件的值。【来源-维基百科】
指针定义方式:数据类型 *指针变量
下面是有关的符号
*
:指针标记(间接访问操作符);
&
:地址运算符;
//exapmle
int num = 10;
int *p = #
2.指针的类型和操作
指针类型
指针类型就是该指针所指向的==内存中保存的数据类型==
下面是类型的定义:
例如整型指针就为int *p;
字符指针就为char *c;
指针的大小:指针的大小在32位平台式4个字节,在64位平台式8个字节
指针操作
指针解引用*
可以读取或修改指针所指向的内存地址里的变量 / 数据值
指针+-整数
代表指针在内存中前后移动一定数量的元素单元
int类型的指针就移动4字节,char类型的指针移动1字节,取决于类型大小
下面是一个结构体的案例
struct test{
char c;
int a;
}; //此结构体为8字节 大小取决于最大类型
int main()
{
struct test n1;
struct test* p = &n1;
printf("结构体大小%d\n", sizeof(n1)); // 结构体大小8
//将结构体地址的指针++,目前指向n1的位置
printf("结构体指针地址%p\n", p); // 结构体指针地址0061FEA0
p++;
printf("结构体指针地址%p\n", p); // 结构体指针地址0061FEA8
return 0;
}
[!Note]
注意野指针问题,未初始化的指针就是野指针,也可以是指向未初始化的地址。
如何规避野指针:
- 指针初始化
- 小心指针越界
- 指针指向空间释放,即置为空NULL
3.指针和数组
指针与数组的关系:
数组名 = 数组首元素地址 = 指针 = 数组第一个字节地址,下面的代码==p等价于arr[0]==
int arr[] = { 1, 2 };
int *p = arr;
通过指针访问数组元素可以用使用[]
,也可以使用*
example:p代用上面的指针,这里可以访问数组第二个元素
p[1]
或者*(p+1)
4.指针运算
指针相减运算p1-p2
- 计算两个指针之间相差的偏移量或元素个数
- 偏移量表示两个指针所指向内存地址之间的距离,(即两个指针指向的地址之间的内存区域大小)
指针比较运算
使用关系元素符< > <= >= == !=
来比较指针,反映指针所指向地址的前后关系
5.二级指针
即指向指针的指针(保存指针的地址)
example:
int num = 10;
int *ptr = #
int **pptr = &ptr;
printf("%d", **(pptr))
可以通过二级指针访问一级指针然后访问变量值
6.字符指针
即字符类型的指针
char str[] = "hello world";
char *p = str;
char c = 'a';
char *p2 = &c;
常量指针
指针指向的字符串为常量,不可修改,以下两种都不可修改
const char *p1 = "hello world";
char *p2 = "hello world";
const char *p3 = "hello world";
常量指针注意的是==当有几个指针定义为同样的常量时==,==都指向同一块地址==,上面代码p1和p2还有p3指向的都是同一个地址,这是由于c/c++会把字符串常量存储到内存的常量区,并且常量区的数据也是程序运行结束后系统才会释放。
但是字符数组定义的字符串若是局部变量则开辟在栈区,并指向不同的内存块地址
常量指针和指针常量
常量指针:指向的内容不可以通过该指针改变,但是指针本身可以指向其他地址,用别的指针指向这块内存时仍能修改其值
const char * ptr
指针常量:指针本身只能指向一个地址,不能指向其他地址,但指向的内容能够通过该指针修改
char * const ptr
7.指针数组
存放指针的数组,下面是一级指针数组和二级指针数组
example:
int *ptr[10]; // 存放10个int类型指针的数组
int *pptr = ptr; // 存放上面10个int类型指针的数组的地址
int num = 10;
ptr[0] = #
//此时可以通过二级指针来解引用一级指针所指向的地址的值
//当pptr解引用时还是一个指针,所以需要再解引用
printf("%d", **(pptr))
数组指针:指向整个数组的指针,而不是指向某一单个元素
其定义形式:数据类型 (*指针名)[数组长度]
下面是其定义的例子
int arr[5] = {1, 2, 3, 4, 5};
int (*p)[5] = &arr; // 指向包含5个整型元素的数组的地址
[!note]
但是函数形参以此方式接收会退化为指向单一元素地址
8.函数指针
函数指针的定义:
根据函数的返回值,参数来定义
返回值类型 (*函数指针变量名) (参数类型列表) = 函数名/函数地址
example:
void fun(int num1, int num2);
void (*p)(int, int) = fun;
函数在内存的位置为代码区(也可以叫文本区)程序指令,以及函数体都存放在此,但是运行时是在栈区运行
函数指针的使用
下面是一个使用函数指针来调用函数的例子
example:
void fun(num1, num2)
{
printf("%d\n", num1 + num2);
}
int main()
{
void (*p)(int, int) = fun;
int num1 = 10, num2 = 20;
p(num1, num2);
}
指针函数
即返回值为指针的函数
下面的例子为返回全局变量的地址
int num = 1;
int* func()
{
num = 2;
return # //返回全局变量地址
}
int main()
{
int *p = func();
printf("%d", *p);
}
9.回调函数
回调函数的声明
格式:typedef 返回值类型 (*回调函数类型)(参数列表)
即先需要声明类型,然后才能在函数内使用函数作为形参
下面写一个计算两个整型的和的函数并调用自己写的打印函数。
example:
//定义回调函数类型
typedef void (*PrintFunc)(int num);
//声明回调函数
void myPrint(int num)
{
printf("%d", num);
}
//函数参数可以随意取函数名,都是调用传进来的回调函数
void sumNum(PrintFunc printNum, int num1, int num2)
{
int sum = num1 + num2;
//调用回调函数打印
printNum(sum);
}
int main()
{
int num1 = 10;
int num2 = 20;
sumNum(myPrint, num1, num2);
return 0;
}
- 感谢你赐予我前进的力量