一、指针是什么
指针是内存单元的最小编号,也就是地址;通常口语中的指针指的是指针变量
二、指针和指针类型
int* ,char* ,short* ,double*,float*,结构体指针
无论是什么类型的指针变量,他在内存中的大小都是一样。如:char类型占1字节,int类型占4字节;但是char*和int*在64位操作系统中,都占8字节;
也就是说,无论是何种类型的指针,其大小是依据操作系统的环境决定。
那么所有的指针类型大小都一样,为何还要区分类型呢?
虽然所有的指针类型大小都一样,但是不同类型的指针,在解引用时,其可访问的字节数量是不一样的。如:char*类型指针其大小为4字节(32位系统),通过*解引用时,由于char只有1字节,所以只能修改一字节的数据;而int*,由于int占4字节,那么通过*解引用时,就可以修改4字节的数据。
其次,不同类型的指针,在对指针进行+-操作时,影响其步长:
int* 的指针+1,其结果地址增加了4个字节
char* 的指针+1,其结果地址增加了1个字节
三、野指针
野指针:指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
野指针问题:
1、指针未初始化
2、指针访问越界
3、指针指向的空间释放
如何避免野指针:
1、指针初始化
2、小心指针越界
3、指针指向空间及时释放
4、避免返回局部变量地址
5、指针使用前检查有效性
四、指针运算
1、指针+-整数
指针+-整数 =指针当前地址 +- (指针的类型所占的字节个数 * 整数)
比如:int* p;p++ 相当远将p的地址+上4字节,如原来的地址0x01,p++后等于0x05;
2、指针 - 指针
指针 - 指针 = 指针与指针之间元素的个数
int arr[10] = { 0 };
int ret = &arr[9] - &arr[0]; //ret = 9
指针 - 指针的前提:这两个指针必须指向同一块空间才可以相减。
3、指针的关系运算
指针与指针之间可以比较大小
C语言规定,指针在比较时,允许与数组中最后一个元素后面一个的内存地址进行比较,不允许与数组的第一个元素的前一个地址进行比较。
允许p1与p2比,不允许p1与p3比较
五、指针和数组
数组:一组相同类型元素的集合
指针:地址
六、二级指针
二级指针:存放指针地址的指针
int main() {
int a = 10;
int* pa = &a;
int** ppa = &pa; //二级指针
printf("%p\n",pa);
printf("%p ---- %d",*ppa,**ppa);
return 0;
}
int * pa = &a; // * 表示pa是指针变量,int 表示pa指向的是一个整形变量
int* * ppa = &pa //*表示ppa是指针变量,int*表示ppa指向的是整形指针
二级指针是用来存放一级指针变量的地址;
七、指针数组
存放指针的数组
int* parr[10] = {NULL};
八、数组指针
指向数组的指针:
int arr[10];
int (*p)[10] = &arr;
/*
*(*p) :表示是指针
*(*p)[]:表示这个指针指向数组
*(*p)[10]:表示这个指针指向的数组大小为10
*int (*p)[10]:表示定义了一个指向大小为10的整形数组
*int (*p)[10] = &arr;表示将arr数组的地址赋给这个数组指针。
*&arr:表示整个数组的地址
*p+1:&arr + 0x28,地址偏移40个字节
*/
数组指针的类型:int (*p)[10] = &arr,p的类型int (*)[10],去掉p就是他的类型;
int (*p)[10] = &arr;//表示数组指针,是一个指针
int* p[10] = {0};//表示指针数组,是一个数组
整形指针:指向整型的指针 int*,字符指针:指向字符型的指针:char*
int* arr[10]:arr[] 数组,int* 整形指针,所以int* arr[10]是指针数组,存放指针类的数组
int (*p)[10]:*p:指针,[10]:数组,*p[10] 指针指向[10]数组,int:整形,所以int(*p)[10]:指向int型数组[10]的指针。所以p是一个指向int [10]的数组指针。
1、数组名
数组名通常表示数组的首元素的地址;
例外:
sizeof(数组名):这里的数组名表示整个数组,计算的是整个数组的大小,单位字节
&数组名:表示的是整个数组,表示得到整个数组的地址
九、数组参数和指针参数
1、数组与指针
int arr[5]; //arr整形数组
int *parr[5]; //指针数组
int (*parr)[5]; //数组指针,指针类型:int (*)[5];
int (*parr[10])[5]; //parr[10]:存放int(*)[5] 类型的数组指针的数组,大小为10;
---------------------------------------------------------------------------------------------------------------------------------
2、一维数组传参
void test(int arr[]){}; //test(arr);实参传递一个数组,形参使用一个数组接受,形参数组可以没有大小
void test(int arr[10]){};//形参实参都是数组,且数组大小一致,可以
void test(int *arr){};//test(arr);arr表示首元素地址,使用int * 类型的指针可以接受
void test2(int *arr[20]){};//实参传递类型:指针数组;形参接受类型:指针数组,大小与实参一致,可以
void test2(int **arr){};//arr2:表示数组首元素的地址,arr2存放的是int*类型地址,int **表示首元素,可以
int main()
{
int arr[10] = {0};
//arr2 是一个指针数组,数组存放int*的指针
int* arr2[20] = {0};
test(arr);
test2(arr2);
}
3、二维数组传参
void test(int arr[3][5]){}; //形参3行5列整形数组,实参3行5列整形数组,可以
void test(int arr[][]){}; //形参可以缺少行,但是不能缺少列
void test(int arr[][5]){}; //可以
void test(int* arr){}; //二维数组的数组名表示第一行的一维数组,一维数组的地址不可以用一个一级指针接受,不可以
void test(int* arr[5]){}; //实参传递的是指针,这里形参是一个指针数组。不可以
void test(int (*arr)[5]){}; //实参传递指针,形参用数组指针接收,可以
void test(int **arr){}; //不可以,一维数组的地址,不可以用二级指针接收
int main()
{
int arr[3][5] = {0};
test(arr);
}
4、一级指针传参
void test(int* p){};//一级指针传参,实参必须是地址,却类型需要与形参类型一致
5、二级指针传参
void test(int** p){};//二级指针形参,实参需要也是二级指针
6、函数指针
指向函数的指针
void func(int x,int y)
{
printf("%d\n",x+y);
}
int main()
{
//数组指针,指向数组的指针
int arr[5] = {0};
int (*parr)[5] = &arr;
//函数指针,指向函数的指针
void (*pf)(int x,int y) = &func; //void (*pf)(int x,int y) = func;
printf("%p\n",fp);
//通过函数指针,调用函数
int ret = pf(2,3); //int ret = (*pf)(2,3);
}
函数指针的用途:回调函数
7、函数指针数组
存放函数地址的数组
int add(int x,int y)
{
int ret = x + y;
return ret;
}
int sub(int x,int y)
{
int ret = x - y;
return ret;
}
int main()
{
//定义
int (*arr[2])(int,int) = {add,sub};
//调用
int res = arr[0](2,4);
}
8、指向函数指针数组的指针
int add(int x,int y)
{
int ret = x + y;
return ret;
}
int sub(int x,int y)
{
int ret = x - y;
return ret;
}
int main()
{
//定义函数指针数组
int (*funcArr[2])(int,int) = {add,sub};
//定义指向函数指针数组的指针
int (*(*pFuncArr)[2])(int,int) = &funcArr;
}
9、回调函数
回调函数:通过函数指针调用的函数,当该指针调用了其所指向的函数时,就成为回调函数。
1、void* 指针
//库函数qsort()
void qsort(void* base, //排序的起始位置
size_t num, //排序元素的个数
size_t width, //排序元素的大小(单位字节)
int(* cmp)(const void* el1,const void* el2) //函数指针-比较函数
);
void* x;
//void* 类型的变量,不能直接通过*x解引用,void* 指针是没有具体类型的指针,void* 可以接受任意类型的指针,所以不能解引用操作,也不能进行指针运算操作
//对void* x解引用
*(int*)x = 0;
void* 类型的变量,不能直接通过*x解引用,void* 指针是没有具体类型的指针,void* 可以接受任意类型的指针,所以不能解引用操作,也不能进行指针运算操作。
void* 类型的指针,如果要使用,需要强制类型转换成目标类型才能进行解引用
2、P[0] = *(p + 0)
int arr[3][2] = {1,2,3,4,5,6};
int* p = arr[0];
printf("%d ",p[0]);
//这里的P[0] = *(p + 0),arr[0]表示第一行的首元素的地址,所以int* p = arr[0]; 等于 int* p = &arr[0][0];所以p[0] = *(&arr[0][0] + 0);那么p[0]打印的东西就为1
指针测试
int main()
{
char* c[] = {"enter","new","point","first"};
char** cp[] = {c + 3,c + 2,c + 1,c};
char*** cpp = cp;
printf("%s\n",**++cpp); //point
printf("%s\n",*-- * ++cpp + 3); //er
printf("%s\n",*cpp[-2] + 3); // st
printf("%s\n",CPP[-1][-1] + 1); //ew
}