指针的优点
- 使程序更加简洁、紧凑、高效
- 表示更加复杂的数据
- 动态内存分配
- 得到多于1个的返回值
1.概念
地址:内存中每个字节单位都有一个编号
指针:指针就是地址
指针变量:存放地址的变量
2.定义格式
存储类型 数据类型 *变量名;
int a=3;
int *p=&a;
char ch = ‘a’;
char *p = &ch;
3.指针操作符
&:取变量的地址
*:取地址里面的内容
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5dZMlvwm-1675324148118)(assets/image-20230131100811-hibc4g2.png)]
&和*是互逆的
4.初始化
指针变量使用前不仅要定义还要初始化,未经初始化的指针变量不能随便使用,可能会产生野指针。
-
将普通变量的地址赋值给指针变量
//a)已有空间 int a = 10; int *p =&a; printf("%d %d\n",a,*p);//10 10 printf("%p %p\n",&a,p);//地址 *p = 20; printf("%d %d\n",a,*p);//20 20 //b)备用指针 int *p = NULL; p = &a;
-
将数组首地址赋值给指针变量
char s[10] = "hello"; char *p = s; printf("%s %s %c\n", s, p,*p);//hello hello h
-
指针变量里面保存的地址赋值给另一个指针变量
int a =10; int * p = &a; int *q = NULL; q=p;
5.指针运算
char buf[32] = "hello";
char *p = buf;
-
算术运算
p++:指针向高地址方向移动一个数据单位,指针的指向发生变化
p–:指针向低地址方向移动一个数据单位,指针的指向发生变化
p+n: 访问了高地址方向第n个数据的地址,指针的指向没有变化
p-n: 访问了低地址方向第n个数据的地址,指针的指向没有变化
两个地址之间的差=两个地址之间相隔数据元素的个数,p-q=相隔元素的个数
//1 char *p = s; printf("%c %p %p\n",*p,p,s);//h 0xbfd8b392 0xbfd8b392 p++; printf("%c %p %p\n", *p,p,s);//e 0xbfd8b393 0xbfd8b392 int a[32]={1,2,3,4,5}; int *q=a; printf("%d %p %p\n",*q,q,a);//1 0xbfd8b310 0xbfd8b310 q++; printf("%d %p %p\n",*q,q,a);//2 0xbfd8b314 0xbfd8b310 //2 char s[10] = "hello"; char *p = s; printf("%c %p %p\n",*p,p,s);//h 0xbffeebf2 0xbffeebf2 printf("%c %p %p\n", *(p+1),p+1,s);//e 0xbffeebf3 0xbffeebf2 int a[32]={1,2,3,4,5}; int *q=a; printf("%d %p %p\n",*q,q,a);//1 0xbffeeb70 0xbffeeb70 printf("%d %p %p\n",*(q+1),q+1,a);//2 0xbffeeb74 0xbffeeb70 //3 int m = 100; double n = 200; int *p1 = NULL,*p2 = NULL; double *q1=NULL,*q2=NULL; p1 = &m; p2 = p1 + 2; q1 = &n; q2 = q1 + 2; printf("p1=%p p2=%p\n",p1,p2);//p1=0xbfa238bc p2=0xbfa238c4 printf("p2-p1=%d\n",p2-p1);//p2-p1=2 printf("q1=%p q2=%p\n",q1,q2);//q1=0xbfa238d0 q2=0xbfa238e0 printf("q2-q1=%d\n",p2-p1);//q2-q1=2
-
关系运算
< > == !=
指针之间的关系运算比较的是指针指向地址的高低
注意:指向不同数据类型的指针关系运算没有意义,指向不同区域的指针关系运算没有意义,一般在同一个数组中进行比较
6.指针的大小
int a=5;
int *p = &a;//sizeof(p)=4
char ch = 'a';
char *q = &ch;//sizeof(q)=4
int a[5]={1,2,3,4,5};
int *k = a;//sizeof(k)=4
//和地址有关
总结:
- 32位操作系统:指针大小4字节;64位操作系统:指针大小8字节
- 内存地址是固定的,但是变量的地址是不固定的,由栈区随机分配
- 指针类型根据指向空间的数据类型来确定的
7.段错误
Segmentation fault (core dumped)
-
野指针:
-
产生原因:
- 没有初始化
- 指针被free后没有置位NULL
-
解决方式:int *p =NULL;//NULL系统宏,空指针
-
-
内存泄漏,对非法空间赋值
8.指针修饰
1.const常量化
-
const int a = 10; a=20;//报错,因为a被const修饰所以不能被赋值但是可以通过指针间接更改a的值 int *p = &a; printf("%d\n",a);
-
const int *p;//指针指向的内容不能修改,但是指针的指向可以修改 int a=10; const int * p = &a; //1) *p = 20;//无法写入 printf("%d\n",*p); //2) int b=30; p=&b;//改变指向 printf("%d\n",*p);
-
int *const p;//修饰p,指针指向 不能修改,指针指向的内容可以修改 int a=10; int b=20; int * const p = &a; 1)*p = 20;//正确 2)p = &b;//报错,指针指向不能修改
2.void
void a;//不可以,不能修饰变量
void * p //任意类型的指针
(int *)p
9.大小端
大端:地地址存放高字节数据,高地址存放低字节数据
小端:地地址存放低字节数据,高地址存放高字节数据
举例:0x12345678 起始地址0x4000
0x4000 | 0x4001 | 0x4002 | 0x4003 | |
---|---|---|---|---|
大端 | 0x12 | 34 | 56 | 78 |
小端 | 0x78 | 56 | 34 | 12 |
10.二级指针
一级指针:存放变量的地址
二级指针:存放一级指针的地址
int a= 10;
int *p = &a;
int **q = &p;
//访问a的值:a *p **q
//访问a的地址:&a p *q
//访问p的地址:&p q
11.指针和数组
直接访问:按变量的地址存取变量的值(通过数组名访问)
间接访问:通过存取变量地址的变量来访问变量的值(通过指针访问)
1.指针和一维数组
int a[5]={1,12,3,4,5};//a是数组名也是首地址,地址常量
int *p=a;
int a[5]={1,12,3,4,5};
printf("%d %d\n",a[0],*a);// 1 1
printf("%d %d\n",*a,*(a+1));// 1 12
printf("%d %d\n",*a,*a+1);// 1 2
地址 | 元素 | |||||
---|---|---|---|---|---|---|
p | a | 1 | a[0] | *a | *p | p[0] |
p+1 | a+1 | 12 | a[1] | *(a+1) | *(p+1) | p[1] |
p+2 | a+2 | 3 | a[2] | *(a+2) | *(p+2) | p[2] |
p+3 | a+3 | 4 | a[3] | *(a+3) | *(p+3) | p[3] |
p+4 | a+4 | 5 | a[4] | *(a+4) | *(p+4) | p[4] |
直接访问:
间接访问:
访问数组元素a[i]的值:
直接访问:a[i] *(a+i)
间接访问:p [i] *(p+i)
访问数组元素a[i]的地址:
直接访问:&a[i] a+i
间接访问:&p[i] p+i
举例:
int a[10]={1,2,3,4,5}; int * p = a; printf("%d\n",*p++);//1 printf("%d\n",*a++);//报错,a是地址常量
*p++ :先取p的值,再对p向后移动一个单位
*(p++):先取p的值,再对p向后移动一个单位
(*p)++ :先取p的值,然后对值进行自加操作,指向不变
++*p :先取p的值,然后对值进行自加操作
++(*p) :先取p的值,然后对值进行自加操作
*++p :p自加(向后移动一个数据),然后取内容
*(++p) :p自加(向后移动一个数据),然后取内容
2.二维数组
int a[2][3]={1,2,3,4,5,6};//a:数组名,第一行的首地址,a+1:第二行的首地址
//*:表示降级,将行地址降级为列地址
//*a:第一行第一列的地址
//*a+1:第一行第二列地址
//*(a+1):第二行第一列地址
//*(a+1)+1:第二行第二列地址
int a[2][3] = {1, 12, 3, 4, 5};
printf("%p %p %p %p\n", a, a[0], a[0] + 1, a[1]);
printf("%p %p %p %p\n", a, *a, *a + 1, *(a + 1));
访问a[i][j]的地址:
a[i]+j *(a+i)+j
访问a[i][j]的值
*(a[i]+j ) *((a+i)+j)
12.数组指针
-
定义:指向数组的指针
-
格式:存储类型 数据类型 (*指针变量名) [列数]
int a[2][3]={1,2,3,4,5,6}; int (*p) [3]= a;//sizeof(p)=4 32位操作系统 //int (*)[3] 3:表示三个三个数据的运算
访问a[i][j]地址:
p[i]+j *(p+i)+j
访问a[i][j]值:
*(p[i]+j) *( *(p+i)+j)
13.指针数组
-
定义:本质是数组,数组里面存放指针
-
格式:存储类型 数据类型 *数组名 [元素个数]
int *arr[5];
-
应用实例:
-
存放普通变量地址
int a = 10,b=20,c=30; int *p[3]={&a,&b,&c}; //访问b的值? *p[1] **(p+1) //访问b的地址? p[1] *(p+1)
-
用于存放二维数组每行第一列地址
int a[2][3]={1,2,3,4,5,6}; int *p[2]={a[0],a[1]}; //访问a[1][2]的地址 p[1]+2 *(p+1)+2 //访问a[1][2]的数据 printf("%d %d\n",*(p[1]+2),*(*(p+1)+2));
-
用于存放字符串
char *p[3]={"hello","world","hqyj"}; //打印"world"? printf("%s\n",p[1]);//world printf("%s\n",*(p+1));//world //打印字符'd' printf("%c\n%c\n",*(p[1]+4),*(*(p+1)));//d d
-
命令行参数
int main(int argc, char const *argv[]){ printf("%s %s %s\n",argv[0],argv[1],argv[2]); printf("%d\n",argc); return 0; }
-