一级指针
在我们创建变量时,内存会开辟出一个空间来存放这些变量,内存会被划分为一个个内存单元,每个内存单元的大小是一个字节,而每个内存单元都会给一个编号
也就是我们的一级指针(平时为了方便就叫做地址)
(注:%p是专门用来打印地址的)
指针变量
为了存放这些地址我们就需要创建一个专门的变量来存放地址,也就是指针变量
例如:
int a=20;
int* pa =&a;
此时pa就是指针变量,里面存放的就是a的地址在int * pa中 * 说明pa是一个指针
而int是pa这个指针指向的类型.
对于数组来说数组名就是数组首元素的地址,但有两个例外
1 sizeof(数组名), 这里的数组名表示整个数组,计算的是整个数组的大小
2 &数组名, 这里的数组名也表示整个数组,取出的是整个数组的地址
指针变量需要多大空间,取决于存放的是什么 当存放的是地址时
地址的存放需要多大空间,指针变量的大小就是多大
32位机器上存储需要32个bit位,也就是4个字节,指针变量大小就是4个字节
64位机器上存储需要64个bit位,也就是8个字节,指针变量大小就是8个字节
解引用操作符
*就是我们的解引用操作符
printf"%d\n",*pa);
*pa就是解引用操作 *pa==a
指针类型
指针类型决定了指针进行解引用操作时访问多大空间
例如:
int* 的指针解引用访问到4个字节
char* 的指针解引用访问到1个字节
- 指针类型决定了指针的步长,就是向前/向后走一步走多大距离
根据规律我们可以得出
type* p
p+i 是跳过i个type类型的数据
相当于跳过了i*sizeof(type)个字节
const修饰指针
添加了const就说明这是常属性–不能被修改
(当然不等于将被定义的变量转化成了常量,其本质是一个不可改变的常变量)
当const修饰指针时分为两种情况
- 放 * 左边 int const * p=&n; 修饰/限制的是*p不能通过p来改变p指向的对象 但p可以指向其他对象
- 放 * 右边 int * const p=&n; 修饰/限制的是p不能改变p本身的值 ‘’ 但p指向的内容可以通过p来改变
指针的运算关系
前提:要两个指针指向同一块空间
指针-指针的绝对值 得到的是指针和指针之间的元素个数
数组的地址+1 跳过的是整个数组
数组的首元素+1 跳过的是4个字节
arr[i]== * (arr+i)==*(p+i)==p[i]
野指针
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)指针变量在定义时如果未初始化,其值是随机的,指针变量的值是别的变量的地址,意味着指针指向了一个地址是不确定的变量,此时去解引用就是去访问了一个不确定的地址,所以结果是不可知的。例如:未初化,越界,空间释放
都会应发野指针
预防野指针
- 指针初始化
int* p=NULL;
NULL是一个标识符常量 内容是0 地址也是0(不可读写/使用)
*使用指针前要看是不是空指针(首先要初始化) - 小心指针越界
指针不再使用后,及时置NULL,使用前检查有效性
避免返回局部变量的地址
assert断言
assert.h头文件定义了宏 assert()由于在运行时确保程序符合条件,如果不符合,就报错终止程序,以此来达到防止野指针的情况
assert(p !=NULL)
若表达式为0,则会报错
如果已经不需要assert再进行断言,则在assert.h头文件前加上#define NDEBUG
这样就不会再运行assert()减少运行时间
- release版本里会选择性优化掉assert
指针数组
存放指针的数组
int arr1[]={1,2,3,4,5};
int arr2[]={2,3,4,5,6};
int arr3[]={3,4,5,6,7};
int* arr[3]={arr1,arr2,arr3};//指针数组
字符指针变量
const char* p=“hello world”//指向的是首字符的地址 常量字符串相同内容只会创建一份
(不允许修改)
数组指针变量—存放数组地址的指针变量
int arr[10]={0};
int (*p)[10]=&arr;//p是数组指针 (p)说明p是个指针 [10]说明指向的是数组 int说明是整型
int ()[10]是p的数组指针类型
p指向的是数组,数组10个元素,每个类型是int
数组指针通常会在二维数组中使用
在二维数组中,数组名就是第一行的地址
第一行是一个一维数组
函数指针变量—指向函数 存放函数的地址
&函数名 得到函数的地址
&函数名和函数名都表示函数的地址
函数返回类型 (*pf)(形参类型,形参类型)=&函数名 //pf是函数指针变量
(*pf)(实参,实参)或 pf (实参,实参)
typedef关键字 是用来类型重命名
typedef unsigned int uint 将unsigned int简化为uint
typedef int* uint
对于数组指针类型的简化时要将修改后的名称放在右边
对于函数指针类型同理
typedef int( parr_t)[5]
parr_t等价于int(*)[5]
函数指针数组—存放函数指针的数组
如果要把多个函数类型相同的函数指针存放到同一数组,就是函数指针数组
int (*pfarr[4])(int,int) pfarr就是函数指针数组
调用: pfarr[i]( 实参, 实参)
qsort --用来排序
qsort是库函数 可以直接用来排列数据
头文件是stdlib
qsort(等待排序数组的第一个元素,等待排序数组元素个数,等待排序数组的元素大小,指向两个元素的比较函数)
int cmp_int(const void* p1,const void* p2)//整型比较函数 升序
{
return *(int*)p1-*(int*)p2;//void*不能直接读写所以要转换成整型指针
//>0 返回 >0的数
//<0 返回 <0的数
//==0 返回 0
}
sizeof与strlen
sizeof只在乎内存空间所占大小,不在乎内容
strlen 是个库函数头文件是string.h 求字符串长度(只针对字符串)不遇到\0
就不会停止