什么是指针
我们口头中的指针又称为指针变量,指针变量存储的是地址。
在深入了解指针前首先要认识两个符号
*(解引用操作符)和&(取地址操作符)
*(解引用操作符)常常使用在指针变量之前,表示取得指针指向的值
&(取地址操作符)取某变量的地址
下面举一个例子
int main() {
int num = 0;
int* a = #
//我写的时候经常忘加&
return 0;
}
&num表示取得num变量的地址
int * a 表示声明一个名称为a的int * 类型的指针变量
int * a = & num 表示将num 的地址赋予a 这个指针变量
可以通过监视来验证我们的想法
a是表明&num的变量,那&a呢?
&a是二级指针,二级指针指的是存储一级指针变量的地址的变量。也就是通常所说的“指针的指针”
int main() {
int num = 0;
int* a = #
int** pa = &a;
return 0;
}
验证
指针与数组
数组名即为指针
一维数组
int arr[5] = { 1,2,3,4,5 };
一维数组名(arr) 表示 首元素
在一般情况下, 一维数组名(arr) 的地址表示首元素的地址。
但是在sizeof(数组名)和 & 数组名 两种情况下数组名表示整个数组的地址
例题
int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));
//4*4=16
printf("%d\n",sizeof(a+0));
//表示首元素大小 4
printf("%d\n",sizeof(*a));
//*a对数组名进行解引用 得到一个int型数据(1) 4
printf("%d\n",sizeof(a+1));
//a+1表示第二个元素(2) 4
printf("%d\n",sizeof(a[1]));
//a[1]表示第二个元素(2) 4
printf("%d\n",sizeof(&a));
//&a表示取出整个数组的地址 还是个地址 地址大小为4或8
printf("%d\n",sizeof(*&a));
//*&同时出现等于什么都没变 同sizeof(a) 大小为16
printf("%d\n",sizeof(&a+1));
//跳过整个数组 来到下一个同等大小的数组的首元素位置 16
printf("%d\n",sizeof(&a[0]));
//加了&就是地址 地址统统为4或8个字节!
printf("%d\n",sizeof(&a[0]+1));
//地址+1=移动一个int型的位置 来到(2)的位置 sizeof(int)=4
二维数组
int d[3][2] = { {1,2},{3,4},{5,6} };
二维数组存储的逻辑结构
二维数组的物理结构则是连续的空间
二维数组的数组名(d)是首元素的地址,二维数组的首元素是第一行(一维数组)
在本题中 d [ 0 ] 则表示一维数组的数组名,即一维数组首元素 d [ 0 ] [ 0 ]
例题(博主以前经常写错)
int a[3][4] = {0};
printf("%d\n",sizeof(a));
//3*4*4=48
printf("%d\n",sizeof(a[0][0]));
//首元素 4
printf("%d\n",sizeof(a[0]));
//4+4+4+4=16
//a[0]是第一行这个一维数组的数组名,单独放在sizeof内部,a[0]表示第一个整个这个一维数组
//sizeof(a[0])计算的就是第一行的大小
printf("%d\n",sizeof(a[0]+1));
//a[0]并没有单独放在sizeof内部,也没取地址,a[0]就表示首元素的地址
//就是第一行这个一维数组的第一个元素的地址,a[0] + 1就是第一行第二个元素的地址
printf("%d\n",sizeof(*(a[0]+1)));
//a[0] + 1就是第一行第二个元素的地址
//*(a[0] + 1))就是第一行第二个元素
printf("%d\n",sizeof(a+1));
//4或8
//这个题我不知道错了多少遍了已经
//a虽然是二维数组的地址,但是并没有单独放在sizeof内部,也没取地址
//a表示首元素的地址,二维数组的首元素是它的第一行,a就是第一行的地址
//a+1就是跳过第一行,表示第二行的地址
printf("%d\n",sizeof(*(a+1)));
//16
//*(a + 1)是对第二行地址的解引用,拿到的是第二行
//*(a+1)-->a[1]
//sizeof(*(a+1))-->sizeof(a[1])
printf("%d\n",sizeof(&a[0]+1));
//4或8
//第二行的地址
printf("%d\n",sizeof(*(&a[0]+1)));
//16
//第二行的大小
printf("%d\n",sizeof(*a));
//16
//a表示首元素的地址,就是第一行的地址
//*a就是对第一行地址的解引用,拿到的就是第一行
printf("%d\n",sizeof(a[3]));
//16
//第4行的大小
//虽然越界了但是sizeof可不管内容
指针的类型
比较普通的类型
char ch = 'a';
char* b = &ch;
int num = 0;
int* a = #
double dou = 3.14;
double* c = &dou;
直接在类型后添上一个*就是一个类型。
进阶版
指针数组
指针数组首先得是一个数组,数组里每个元素都是一个指针
int * a1;
int * a2;
int * a3;
int * a4;
int * arr[4]={a1,a2,a3,a4};
arr首先于 [ ] 结合,成为一个有四个指针变量的数组
其实,对于指针数组,去掉数组名和后面的方括号,剩下的就是数组中存储元素的类型
数组指针
数组指针,首先是个指针,指针指向一个数组
int arr1[4] ={1,2,3,4};
int (*p) [4] = arr1 ;
必须将 * 与 p 用小括号括起来
必须将 * 与 p 用小括号括起来
( * p ) 其实就== arr
( * p )是arr 的引用
函数指针
int Add(int x, int y) {
return x + y;
}
int (*padd)(int, int) = Add;
函数指针数组
int (*pf[5]) (int, int) = { 0,Add,Sub ,Div ,Sum };
回调函数
什么是回调函数?回调函数就是通过函数指针调用的函数,比如上面的pf指针,它调用的Add,Sub ,Div ,Sum被称为回调函数
计算器实例
第一种方式,函数指针传参
在使用时直接调用calc函数即可
//写一个计算器
int Add(int x, int y) {
return x + y;
}
int Sub(int x, int y) {
return x - y;
}
int Div(int x, int y) {
return x / y;
}
int Sum(int x, int y) {
return x * y;
}
//第一种方式,函数指针传参
void calc(int(*pf)(int, int)) {
int x=0;
int y=0;
printf("please inter two numbers");
scanf("%d %d", &x, &y);
int num=pf(x, y);
printf("%d", num);
}
第二种:函数指针数组
int (*pf[5]) (int, int) = { 0,Add,Sub ,Div ,Sum };
int input = 0;
do {
printf("please make operation \n");
scanf("%d", &input);
if (input == 0)
{
//退出计算器
printf("thank you !!!\n" );
break;
}
else if (input > 0 && input < 5) {
//开始计算,首先输入待计算的数字
int x = 0;
int y = 0;
printf("please inter two numbers\n");
scanf("%d %d", &x, &y);
//调用函数
int res=pf[input](x, y);
printf("%d\n", res);
}
else {
printf("please enter right number\n");
}
} while (input);
qsort实例
指针的运算
+- 移动的距离和指针解引用后表示的数据类型有关
普通指针+-数字
int b =1;
int * pb = &b;
pb+1 == ???
pb + 1 表示移动sizeof(int ) 大小的步长
在本题中移动四个字节的大小
若pb=0x00000001
pb+1=0x00000005
char c= ' c ';
char * pc = &c;
pc+1 == ???
若pc=0x00000001
pc+1=0x00000002
数组名+-数字
这里注意,只要不是sizeof(数组名)或&数组名,其他情况下数组名==首元素