一:指针的引入
- 指针==地址
- 变量访问的两种方式-----变量名与地址
- int a = 10;(类型,变量名,内存地址,值)
- 通过地址也能访问,&取地址运算符,*将地址内的值读出运算符
二:指针变量
- 指针变量==存放地址的变量
#include <stdio.h>
int main()
{
int a = 10;
int *p; //这里的*是一个标识符,告诉系统我是一个指针变量,用来保存别人地址的,和下方的运算符不同
p = &a;
printf("地址访问:%d\n",*(&a)); //取值运算符,它把后面跟的内存地址里的数据取出来
printf("指针变量的方式访问:%d\n",*p); //与上句效果相同
return 0;
}
- *的标识作用:只在指针变量定义或声明的时候
- *的运算作用:使用指针变量,取数值
- 指针变量的类型问题:需要指定相同的类型
- int 与 char 在取地址时,都能访问到指针的地址,但是取数据时,跨度不一样,导致数值不会一样
#include <stdio.h>
int main()
{
int a = 0x1234;
int *p;
char *c;
p = &a;
c = &a;
printf("地址访问:%d\n",*(&a));
printf("指针变量的方式访问:%d\n",*p);
printf("a的地址:%p,a=%x\n",p,*p);
printf("a的地址:%p,a=%x\n",c,*c);
return 0;
}
三:使用指针的场景
- 场景1:
- 在进行函数封装后,变量传递的方式只是值,且函数重新开辟了空间,函数里对值的操作都是在函数所开辟的那一片空间里,不会影响main函数里的变量变化,不能有效的操作值
- 所以可以将地址传过去,让指针间接修改数值
- 场景2:
- 可以指向固定的内存地址:
-
volatile unsigned int *p = (volatile unsigned int *)0x....;
四:定义指针变量指向数据
- p = &a[0]; //数组的首地址就是首个元素的地址
- p = a; //数组名就是数组的首地址
- 两个式子等价
- 指针偏移后需要回到首地址
一些用法
- 指针也可以像数组一样,使用下标法访问元素
int a[] = {1,2,3}; int *p = a; printf("a[0] = %d\n",p[0]);
- 数组名也可以像指针一样拿来加
int a[3] = {1,2,3}; int *p = a; for(int i = 0;i<3;i++){ printf("&d ",*(a+i)); }
- 一般来说,指针和数组是可以混用的,但是还有些区别
- a++不可实现:一个是常量指针,一个是指针变量,数组的地址是固定的,不能再进行自增
- sizeof:sizeof(数组)是算出整个数组大小;sizeof(指针)是算出Windows下给指针分配的地址空间(8个字节)
五:二维数组的地址
首地址表示:
- 数组名
- 首个元素的地址
二维数组,因数组名表示首地址,所以数组a[3][4]
- 父数组:a --->行
- 子数组:a[0],a[1],a[2] --->列
- a+1:偏移到下一行,取决于有多少行
- a[0]+1:a[0]、*a表示名字也表示子数组的首地址,加1就是往右偏转一位
- a[0] == &a[0][0] 名字等同于地址
- a[0]+1 是第0列第1行的地址 == *(a+0)+1,而第0个数组的第1个元素表示方式是a[0][1]
表示形式 | 含义 |
---|---|
a | 二维数组名,指向一维数组a[0],即0行首地址 |
a[0],*(a+0),*a | 0行0列元素地址 |
a+1,&a[1] | 1行首地址 |
a[1],*(a+1) | 1行0列元素a[1][0]的地址 |
a[1]+2,*(a+1)+2,&a[1][2] | 1行2列元素a[1][2]的地址 |
*(a[1]+2),*(*(a+1)+2),a[1][2] | 1行2列元素a[1][2]的值 |