知识点整理
概念
指针:是一个变量,其值为地址,必须要为其初始化
声明指针或者不在使用后都要将其置为0 (NULL)
野指针:未初始化的指针
悬空指针:指针最初指向的内存已经被释放的一种指针
指针在32位机器上 占4个字节 64位占8个字节
1.指针基本使用
&号 用来取一个对象的地址
*号 一个间接寻址符,用于访问指针所指向的地址的值。
int *p;
int a = 200;
p = &a; //指针初始化 p地址指向a
*p = 100;
printf("a = %d \n",a);
printf("*p = %d\n",*p);
a=300;
printf("a = %d \n",a);
printf("*p = %d\n",*p);
printf("p的地址:[%x] a的地址[%x]\n",p,&a);
输出:
a=100
*p=100
a=300
*p=300
2.*p++ (*p)++ *++p ++*p 区别
*p++ :先取指针p指向的值,p在自身+1
(p)++ :先取p指向的值,在将该值+1 (不是将p的地址+1,是将p加1)
++p : 先将指针p的地址+1,在取该p的值
++*p : 先取p指向的值,在将该值+1
例如:p1 p2 p3 p4 都指向数组attr
int attr[] = {3,10,15,20,25,40};
int *p1= attr;
int *p2= attr;
int *p3= attr;
int *p4= attr; //printf计算从右向左 ←
printf("*p1++: %d %d\n",*p1++,*p1); //*p1=3 *p1 p1++ 打印 3 3
printf("(*p2)++ : %d %d\n",(*p2)++,*p2);//*p2=3 *p2=*p2+1 4
printf("*++p3 : %d %d\n",*++p3,*p3); //*p3=4 p3++ *p3=10
printf("++*p4 : %d %d\n",++*p4,*p4); //*p4=4 *p4=*p4+1 5
printf("attr[0] : %d\n",attr[0]);//5
printf("attr地址:%x p1[%x] p2[%x] p3[%x] p4[%x]\n",attr,p1,p2,p3,p4);
//attr地址:61fdd0 p1[61fdd4] p2[61fdd0] p3[61fdd4] p4[61fdd0]
attr是数组attr首个元素的地址,int占4字节,每次地址+1,都加4字节
在次举例:
int array[]={1,2,3,4,5,6};
int *ptr_1= (int *)(&array+1);
printf("array[%x] array+1[%x] *(array+1)[%d] \n &array[%x] &array+1[%x] *(&array+1)[%d]\n ptr_1[%x] ptr_1-1[%x] *(ptr_1-1)[%d]\n",
array,array+1,*(array+1),&array,&array+1,*(&array+1),ptr_1,ptr_1-1,*(ptr_1-1));
//array :数组的首个元素的地址
//array+1 :数组首个元素地址+1 array[1] 的地址
//&array :数组首元素地址
//&array+1:数组首元素地址+1,地址:array+array*sizeof(int)*6
//ptr_1 :数组的首元素+1的地址
//ptr-1 -1:数组的首元素+1的地址 - sizeof(int)
打印:
array[61fd70] array+1[61fd74] *(array+1)[2]
&array[61fd70] &array+1[61fd88] *(&array+1)[6421896]
ptr_1[61fd88] ptr_1-1[61fd84] *(ptr_1-1)[6]
3指针常量 常量指针 指向常量常量指针
int *p : 整型指针 表示指向整形的指针
int const *p : 常量指针 表示指针指向的值不可修改
int * const p : 指针常量 表示指针指向的地址不可修改
const int * const p 指向常量的指针常量 表示值和地址都不可变
int a11 = 200;
int a12 = 300;
int *p11 = 100; //(1)整形指针
int * const ptr11 = &a11; //(2)指针常量
//ptr11 = a12; 错误指针指向的地址不可改变
int const *ptr12 = &a12; //(3)常量指针
//*ptr12 = 100; 错误指针指向的值不可改变
3 野指针使用注意
int *pointt;
*pointt=100; //不允许 这样做
4通用指针
例1;
int com = 100;
int *p_com = &com;
void *comm = p;// 把一个整形指针转化成通用指针,它的指向地址不会改变,但是类型会丢失
int *common = (int *)comm;
printf("common :[%d] \n",*common);
例2:
typedef struct
{
int num;
char buff[1024];
}MSG;
void funcomon(void *point) {
MSG *msg = (MSG *)point;
if(msg!=NULL) {
printf("MSG [%d] [%s]\n",msg->num,msg->buff);
msg->num = 111111;
memcpy(msg->buff,"abcd",10);
}
}
5 指针的算术运算(指针步长)
++ - -
// int 型指针步长sizeof(int) 4
int a = 100;
int *p = &a;
printf("p [%p]\n",p);
printf("p+1 [%p]\n",p+1);
printf("p+2 [%p]\n",p+2);
printf("p+3 [%p]\n",p+3);
// char 型指针步长 sizeof(char) 1
char c = 'a';
char *pc = &c;
printf("pc [%p]\n",pc);
printf("pc+1 [%p]\n",pc+1);
printf("pc+2 [%p]\n",pc+2);
printf("pc+3 [%p]\n",pc+3);
//void 型指针步长1
int v = 100;
void *vv =(void *) &v;
printf("vv [%p]\n",vv); //void类型 步长为1
printf("vv+1 [%p]\n",vv+1);
printf("vv+2 [%p]\n",vv+2);
printf("vv+3 [%p]\n",vv+3);
6 指针与数组
6.1指针与一维数组
例如:
int a[3];
a &a a+1 &a+1
a :代表数组首个元素的地址 = &a[0]
a+1 :代表数组首个元素的地址+1,即为&a[1]的 地址
&a :代表的是数组首地址
&a+1:代表数组首地址+1,即&a[0] 的地址 + 40
例1:
int array[] = {1,2,3,4,5,6}; //数组名就是数组的起始地址,数组名就是一个常量指针
int *ptr_array = array;
//打印地址
printf("address: [%x] [%x] [%x] [%x] \n",array+3,&array[3],ptr_array+3,&ptr_array[3]); //代表同一个地址,第3个元素的地址
//打印值
ptr_array[1] = 100;
printf("value: [%d] [%d] [%d] [%d] [%d] [%d]\n",array[1],*(array+1),ptr_array[1],*(ptr_array+1),*&array[1],*&ptr_array[1]);//代表同一个值
//地址打印
printf("array[%x] &array+1 [%x] &ptr_array+1 [%x] \n",array,&array+1,ptr_array+1);
//array:首地址 &array+1:取首地址+1:+1是加整个数组+24 ptr_array+1 :表示首地址+四个字节
输出:
address: [61fdfc] [61fdfc] [61fdfc] [61fdfc]
value: [100] [100] [100] [100] [100] [100]
array[61fdf0] array+1[61fdf4] &array+1 [61fe08] &ptr_array+1 [61fdf4]
例2
int array[]={1,2,3,4,5,6};
int *ptr_1= (int *)(&array+1);
printf("array[%x] array+1[%x] *(array+1)[%d] \n &array[%x] &array+1[%x] *(&array+1)[%d]\n ptr_1[%x] ptr_1-1[%x] *(ptr_1-1)[%d]\n",array,array+1,*(array+1),&array,&array+1,*(&array+1),ptr_1,ptr_1-1,*(ptr_1-1));
//array :数组的首个元素的地址
//array+1 :数组首个元素地址+1 array[1] 的地址
//&array :数组首元素地址
//&array+1:数组首元素地址+1,地址:array+array*sizeof(int)*6
//ptr_1 :数组的首元素+1的地址
//ptr-1 -1:数组的首元素+1的地址 - sizeof(int)
打印:
array[61fd70] array+1[61fd74] *(array+1)[2]
&array[61fd70] &array+1[61fd88] *(&array+1)[6421896]
ptr_1[61fd88] ptr_1-1[61fd84] *(ptr_1-1)[6]
6.1.1 一维数组函数传参
一般的函数传参分为传值和传址,传值意味着对实参没有影响,仅仅是对拷贝进行操作;传址意味着可以访问实参指针所指向的值,也可以对其进行操作。
数组传参比较有趣的地方在于既是传址也是传值。传址是传递数组的地址或首元素的地址,可以通过该指针对数组进行间接访问,也修改数组值;传值是因为,对形参指针的操作并不影响实参指针。
2种方式:
void pointOneArg1(int *attr)
void pointOneArg2(int attr[])
void pointOneArg1(int *attr) {
printf("pointOneArg [%d]\n",sizeof(attr));
*attr = 100;
}
void pointOneArg2(int attr[]) {
printf("pointOneArg1 [%d]\n",sizeof(attr));
attr[5]=111;
}
int ptr_arg[10] = {1,2,3,4,5,6,7,8,9};
for (size_t i = 0; i < sizeof(ptr_arg)/sizeof(ptr_arg[0]); i++){
printf("[%d] ",ptr_arg[i]);
}
printf("\n");
pointOneArg1(ptr_arg);
for (size_t i = 0; i < sizeof(ptr_arg)/sizeof(ptr_arg[0]); i++){
printf("[%d] ",ptr_arg[i]);
}
printf("\n");
pointOneArg2(ptr_arg);
for (size_t i = 0; i < sizeof(ptr_arg)/sizeof(ptr_arg[0]); i++){
printf("[%d] ",ptr_arg[i]);
}
printf("\n");
//打印:
[1] [2] [3] [4] [5] [6] [7] [8] [9] [0]
pointOneArg [8]
[100] [2] [3] [4] [5] [6] [7] [8] [9] [0]
pointOneArg1 [8]
[100] [2] [3] [4] [5] [111] [7] [8] [9] [0]
6.2 指针与二维数组
int a[6][10];
1:访问地址
a:第一行元素的地址
a+n:表示第n+1行元素地址
*a和a[0]:表示第一行第一个元素的地址,&a[0][0]表示第一行第一个元素的地址,因此a=&a[0]=&(&a[0][0]),a表示地址的地址
*(a+n)+n和a[n]+n: 表示第n+1行第n+1个元素地址
2:访问元素
a[n][n]和*(*(a+n)+n) :访问第n+1行第n+1个元素,可以用
int array2[][3]={1,2,3,4,5,6,7,8,9};
//array2 行地址 array2+1 表示第1行的地址
//array2[0] *array2 表示第0行第0个元素的地址
//array2[0]+1 *(array2)+1 表示0行第1个元素的地址
//array2[1][1] *(*(array2+1)+1) 1行1列的值
printf("[%x] [%x]\n",array2,array2+1); //表示0行地址 1行地址
printf("[%x] [%x]\n",array2[0],array2[1]);//0行0列地址 1行0列地址
printf("[%x] [%x]\n",array2[0]+1,*(array2+1)+1);//同上
printf("[%d] [%d]\n",array2[1][1],*(*(array2+1)+1));
输出:
[61fdd0] [61fddc]
[61fdd0] [61fddc]
[61fdd4] [61fde0]
[5] [5]
6.2.1 二维数组函数传参
二维数组int a[2][3]中a是一个指向整型数组的指针,又名数组指针。
int a[2][2];
int(*p)[2] = a;
多维数组进行函数传参时,下面两种形式任选一种即可
void func(int (*p)[2]); 数组指针(行指针)
void func(int p[][2]);
int array_2[][3]={{1,2,3},{11,22,33},{111,222,333}};
//地址打印
printf("行地址 [%x] [%x]\n",array_2,array_2+1);//表示行地址
printf("行的0元素地址 [%x] [%x]\n",array_2[0],array_2[1]);
printf("行的0 ,1元素地址 [%x] [%x]\n",*(array_2),*(array_2+1));
printf("1行1个元素地址 [%x]\n",*(array_2+1)+1);
//打印:
行地址 [61fd80] [61fd8c]
行的0元素地址 [61fd80] [61fd8c]
行的0 ,1元素地址 [61fd80] [61fd8c]
1行1个元素地址 [61fd90]
7 数组指针与指针数组
概念:
指针数组
int *a[10] : 本质上就是数组元素是是个int型指针的一维数组,先找到声明符a,然后向右看,有[ ]说明a是个数组,再向左看,是int *,说明数组中的每个元素是int *。所以这是一个存放int指针的数组。
数组指针
int (a)[10] : a是指针,指向一个数组。此数组有10个int型元素,先找到声明符a,被括号括着,先看括号内的(优先级高),然后向右看,没有,向左看,是,说明a是个指针,什么指针?再看括号外面的,先向右看,有[ ] 是个数组,说明a是个指向数组的指针,再向左看,是int,说明数组的每个元素是int。所以,这是一个指向存放int的数组的指针。
7.1 数组指针
数组指针练习
数组指针本身就是二维指针,表示的是行指针, 指向一维二维数组的行指针。
数组指针指向一维时
int a[]={1,2,3};
int (*ptr)[3] = &a; //行指针地址
数组指针指向二维时
int a[][3] ={1,2,3,4,5,6,7,8,9,10};
int (*ptr)[3];
ptr=a; //行指针地址
//数组指针 与一维数组
int array_1[]= {1,2,3,4,5,6,7,8,9,10};
int (*ptr_1)[10]=&array_1; //必须要取地址,取的是首地址
printf("[%d] [%d] [%d] [%d] [%d]\n",ptr_1[0][0],ptr_1[0][1],ptr_1[0][2],ptr_1[0][3],ptr_1[0][4]);
//数组指针与二维数组
int array1[][3]={1,2,3,4,5,6,7,8,9};//定义 x 行 3 列的数组
int (*ptr)[3]=array1;
//打印值
printf("%d %d %d %d\n",array1[0][0],ptr[0][0],*(*(array1+1)+1),*(*(ptr+1)+1));//打印0行0列 1行1列的值
ptr++;//行地址+1
printf(" %d \n",*(*(ptr)));//表示1行0列的值 4
输出:
[1] [2] [3] [4] [5]
1 1 5 5
4
7.2指针数组(元素指针)
指针数组与一维数组
int array2[3] = {12,3};
int *ptr[2];
ptr[0]=array2;
指针数组与二维数组
int *ptr_22[3];
int array22[][2] ={1,2,3,4,5,6};
for (size_t i = 0; i < sizeof(array22)/sizeof(array22[0]); i++)
ptr_22[i] = array22[i];//将i行第0个元素的地址赋给指针数组
//指针数组与一维数组(指针数组指向一维数组的,元素指针)
int array2[] = {1,2,3,4,5,6,7};
int *ptr_2[2];
ptr_2[0] = array2; //首行第一个元素的地址
printf("%d %d\n",ptr_2[0][0],ptr_2[0][1]);
//指针数组与二维数组(指针数组 指向二维数组的元素指针)
int *ptr_22[3];
int array22[][2] ={1,2,3,4,5,6};
for (size_t i = 0; i < sizeof(array22)/sizeof(array22[0]); i++){
ptr_22[i] = array22[i];//将i行第0个元素的地址赋给指针数组
}
for (size_t i = 0; i < 3; i++)
{
for (size_t j = 0; j < 2; j++)
{
printf("[%d]",ptr_22[i][j]);
}
}
输出:
1 2
[1][2][3][4][5][6]
8.指针的指针
一般用作函数参数
例如:
void fun(char *str,int *indx,char c,char **strr) {//strr n会把修改后的值带回
int i=0;
while (*str!=NULL)
{
if (*str == c)
{
*strr = str;
*indx = i;
break;
}
i++;
str++;
}
}
int a = 100;
int *ptr = &a;
int **ptrr = &ptr;
printf("[%d] [%d] [%d]\n",a,*ptr,**ptrr);
char str[] = "hanbaf baop bmkand aob";
char *strr = NULL;
int indx;
fun(str,&indx,'k',&strr);//找到下标以k开头,在把k之后的打印出来
printf("[%d] [%s]\n",indx,strr);
输出:
[100] [100] [100]
[14] [kand aob]
9.函数指针
一般用作回调,JNI编程
在C++中,作为回调函数,必须为静态
//函数指针
void callback(int a,int b) {
printf("函数指针回调 [%d] [%d]\n",a,b);
}
typedef void (*CallBack)(int a,int b);//定义两种方式
typedef void (CallBack1)(int a,int b);
void Func(int ID,CallBack callback) {
callback(10,20);
}
Func(1,callback);