一.指针的含义
-
指针即内存地址
-
指针变量 即 存储内存地址的变量
-
内存地址
-
程序当运行起来,会被操作系统加载内存中,形成内存映像(内存空间)
-
对内存空间进行编号 (0x0 0x1 0x2 ... )
-
内存编号 (整数,%p 十六进制的整数)
-
二.取址定位符-&
-
获取变量的内存地址(内存编号)
-
可以使用%p来进行格式化输出
-
&操作数必须是标识符
-
变量名
-
数组名
-
函数名
-
scanf("%d",&num); //&num 取址
-
取址运算符得到是内存地址
三.指针变量
需要保存内存地址,需要定义指针变量
1.定义
TYPE * pname;
TYPE * 是表示一个完整的类型,即指针类型
TYPE - pname指针变量所存储了一个内存地址 TYPE表示内存地址中的数据为TYPE类型
* - 说明是指针变量
TYPE * 说明地址(指针)的类型为 TYPE *
TYPE *p1,*p2; //定义两个指针变量p1,p2
TYPE *px,py; //px类型为TYPE*, py类型为TYPE
在定义指针变量时 TYPE 至关重要,决定了解引用时
*p 能够取多少个字节的数据 sizeof(TYPE)
2.初始化
int *p = NULL; //可以 任意类型的指针变量都可以用NULL来初始
#define NULL ((void *)0x0)
int *pn; //没有初始化 野指针 野指针不能解引用
int num = 0;
pn = # //对指针变量进行赋值 pn存储了&num这个值(内存地址)
int *pm = # //定义指针变量直接初始化
3.赋值 和 解引用 赋值
int a = 10,b = 20;
int *pa = &a; //定义指针变量 存储&a
//间接修改a的值 指针的意义 不直接通过变量名修改变量的值,而是通过内存地址
*pa = 1024; //pa == &a *pa == *&a *&a = 1024 a=1024
pa = &b; //对指针变量pa进行赋值 pa存储&b
*pa = 9527; //pa指向了b pa == &b *&b = 9527 b=9527
4.解引用
*内存地址 即获取内存地址中的数据
*指针变量 指针变量的值就是内存地址
int num = 0x12345678;
char *pc = (char *)#
int *pi = #
//pc 的值 和 pi的值 相等
printf("%#X %#X\n",*pc,*pi); //对pc和pi解引用 结果不一样
//指针变量pc和pi的类型不一样 char * int *
//TYPE * p = &x; 在对p进行解引用时 获取了 sizeof(TYPE)个字节的数据
-
指针变量必须要有明确的指向,才能解引用
5.取址&和解引用*是一对可逆的过程
&*a === a
*&p === p
&arr[0] == &*(arr+0) == &*arr == arr
四.空指针/野指针/万能指针
空指针
NULL
野指针
指针指向不确定 指针未初始化
野指针不能直接解引用
万能指针(void *)
内存地址就一编号 整数
任意类型的指针都可以隐式转换为 void *
指针的通用类型
void *类型的指针变量,不能解引用
五.指针算术运算
指针 + 1 即内存偏移 向后偏移1个单位的内存
TYPE *p = &x;
p + 1 偏移了 sizeof(TYPE) 字节大小的内存
指针 - 1 向前偏移1个单位的内存
++p; //p = p + 1; p保存偏移1个单位之后的内存--p; //p = p - 1;
*p++;
两个指针可以相减(同类型) 得到偏移元素的个数
六.指针与[]闭合
ArrayName[index] == *(ArrayName+index) == *(index+ArrayName)
index[ArrayName]
下标[]运算,本质上 指针偏移解引用
数组名 本质 是 首元素的内存地址
*arr == arr[0]
*(arr+i) == arr[i]
七.二级指针
指针变量的内存地址
int num = 1024;
int *p = # //p变量 p存储内存地址 同时它自己也有内存地址
int **pp = &p; //p类型为int * &p类型为 int **
pp == &p
*pp == *&p == p == &num
**pp == *p == *&num == num
int x = 10;
*pp = &x; //改变的是啥 改变了p的值
**pp = 8527; //改变的是啥 改变了x的值
八.数组指针与指针数组
数组指针
本质是指针,指针存储数组的内存地址(指针指向数组)
int arr[5] = {1,2,3,4,5};
int (*parr)[5] = &arr; //数组指针 parr 和 &arr
int brr[3][7] = {};
int (*pbrr)[7] = brr; //数组名即首元素内存地址 &brr[0] 数组指针
int (*pcrr)[3][7] = &brr; //对二维数组取地址 数组指针 二维数组指针
指针数组
本质是数组,数组中元素的类型是指针
int a,b,c,d,e;
int *arr[5] = {&a,&b,&c,&d,&e};
九.函数指针和指针函数
函数指针
本质是指针,即指针指向函数
void func(void){
}
void (*pf)(void) = func; // &func; *func; pf就是函数指针
//pf指向的函数 返回值类型为void 且参数列表也为void
pf();
int bar(int a,int arr[],int n){}
int (*pb)(int,int *,int) = bar;
int arr[5] = {};
pb(10,arr,5); //函数指针赋值之后 可以 当作函数名一样使用
//函数指针即存储函数的内存地址 当函数指针指向函数时,函数指针可以当作函数一样使用
指针函数
本质是函数,即函数返回指针类型
int *func(void){}
void *malloc(size_t size);
十.const与指针
const char *s1; //const修饰 *s1 *s1只读 s1可以修改
--常量指针 指针指向常量
char const *s2; //const修饰 *s2
char * const s3; //const修饰 s3 s3只读 *s3可以修改
--指针常量 即指针是一个常量
const char * const s4; //第一个修饰 *s4 第二个const修饰 s4