文档版本 | 更新时间 | 更新内容 |
---|---|---|
v1.0 | 2020-09-13 | 初稿完成 |
地址就是内存单元的编号,从零开始的非负整数,CPU的地址总线宽度决定了寻址范围。
指针就是地址,地址就是指针。
存放地址/指针的变量。
通常在使用时,会将指针变量简称为指针,但是指针变量和指针并不是一个东西。
一个指针变量,无论指向的变量占多少字节,该指针变量本身占用的字节数,就是CPU地址总线的宽度。
/**
* CPU:64bit
* OS:Windows10
* IDE:Clion
* Compiler:MinGW-64
*/
#include
int main() {
printf("sizeof(void *):%d\r\n", sizeof(void *));
printf("sizeof(char *):%d\r\n", sizeof(char *));
printf("sizeof(int *):%d\r\n", sizeof(int *));
printf("sizeof(long *):%d\r\n", sizeof(long *));
printf("sizeof(float *):%d\r\n", sizeof(float *));
printf("sizeof(double *):%d\r\n", sizeof(double *));
return 0;
}
运行结果为:
sizeof(void *):8
sizeof(char *):8
sizeof(int *):8
sizeof(long *):8
sizeof(float *):8
sizeof(double *):8
① 定义一个指针:
int *p = NULL;
定义了一个变量p,p的类型是int *
类型,就是存放int型变量地址的类型。
因为指针可以访问硬件,所以指针的默认值通常赋为NULL,防止造成危险。
NULL
表示编号为0的地址空间,该地址处既不允许写,也不允许读。
② 指针赋值:
int i = 99;
p = &i;
将int型变量 i 的地址存放到指针变量p中,称为:指针变量 p 指向变量 i 。
③ 使用指针:
int j;
j = *p; //执行之后j的值为99
*p表示:以p的内容为地址的变量。
① 一维数组名:就是一个指针常量,它存放的是一维数组第一个元素的地址。
② 下标和指针的关系:p[i] = *(p+i)
③ 一个函数想要对一个数组进行处理,至少需要接收两个参数:数组第一个元素的地址、数组长度。
① 指针变量不能相加、相乘、相除
② 只有两个指针变量指向的是同一块连续内存(数组)时,才可以相减,表示两个地址之间的差值。
指针数组:是指针变量的数组,其中每个元素都是一个指针变量。
int *a[10];
数组指针:表示一个指向数组的指针变量,就是数组名。
int a[10];
int *p = a;
在定义函数的时候,函数名就是一个地址,所以可以先定义一个指针变量,用于保存某个函数的地址,这个指针变量就成为函数指针。
① 如何定义:
int (*add)(int a, int b);
② 如何使用:
int my_add(int a, int b)
{
return a+b;
}
int main()
{
add = my_add;
printf("myadd result is:%d\r\n", my_add(1,1));
printf("add result is:%d\r\n", add(1,1));
return 0;
}
运行结果为:
myadd result is:2
add result is:2
int i = 10;
① 一级指针:
int *p = &i;
理解:p是int *
类型,指针变量 p 用来存放 int
类型变量的地址。
② 二级指针:
int **q = &p;
理解:q是 int **
类型,指针变量 q 用来存放 int *
类型变量的地址。
③ 三级指针:
int ***r = &q;
理解:r是 int ***
类型,指针变量 r 用来存放 int **
类型变量的地址。
④ 使用:
printf("i = %d\r\n", ***r);
示例程序如下:
/**
* CPU:64bit
* OS:Windows10
* IDE:Clion
* Compiler:MinGW-64
*/
#include
int main() {
int i = 10;
int *p = &i;
int **q = &p;
int ***r = &q;
printf("i = %d, &i = %p\r\n", i, &i);
printf("p = %p, &p = %p, *p = %d\r\n", p, &p, *p);
printf("q = %p, &q = %p, *q = %p, **q = %d\r\n", q, &q, *q, **q);
printf("r = %p, &r = %p, *r = %p, **r = %p, ***r = %d\r\n", r, &r, *r, **r, ***r);
return 0;
}
运行结果为:
i = 10, &i = 000000000061FE1C
p = 000000000061FE1C, &p = 000000000061FE10, *p = 10
q = 000000000061FE10, &q = 000000000061FE08, *q = 000000000061FE1C, **q = 10
r = 000000000061FE08, &r = 000000000061FE00, *r = 000000000061FE10, **r = 000000000061FE1C, ***r = 10
① void指针表示无类型的指针,所以void类型的指针,可以转换为任何类型的指针。
比如设计一个通用的API函数,参数可以制定为void*,这样用户调用的时候无论传入int类型的指针,还是结构体类型的指针,都是可以的,但是这也需要用户在调用的时候,指明自己的指针是什么类型。
② 因为void*指针无类型,所以不可以进行运算,不能使用 * 运算符解引用。
以构造动态数组为例:
#define NUM 10
int *ptr = (int *)malloc(NUM * sizeof(int));
malloc函数用来向系统申请动态内存,参数为申请内存大小(单位:Byte),申请成功则返回内存地址,申请失败则返回NULL。
使用完之后通过函数free来释放:
free(ptr);
ptr = NULL;
静态数组(传统数组)的缺点有:
使用动态内存的动态数组解决了以上四个缺点: