1、多级指针
int main() {
// 指针是用来记录内存地址的,但它本身也有内存地址。
int i = 1000;
int *ip = &i; // 取出i的内存地址ip。
printf("i的值是%d,内存地址是%p\n", i, ip);
int **ipp = &ip; // 取出内存地址ip的内存地址ipp。
printf("i的内存地址的值是%p,内存地址的内存地址是%p\n", ip, ipp);
int ***ippp = &ipp; // 取出内存地址ip的内存地址ipp的内存地址ippp。
printf("i的内存地址的内存地址的值是%p,内存地址的内存地址的内存地址是%p\n", ipp, ippp);
// int ****ippp = &ippp; // 报错,最多三级指针
int ippp_value = *ippp;
int ipp_value = **ippp;
int ip_value = ***ippp;
printf("ippp_value值是%p,ipp_value值是%p,ip_value值是%d\n", ippp_value, ipp_value, ip_value);
}
打印结果:
i的值是1000,内存地址是0x7ff7b0c9443c
i的内存地址的值是0x7ff7b0c9443c,内存地址的内存地址是0x7ff7b0c94430
i的内存地址的内存地址的值是0x7ff7b0c94430,内存地址的内存地址的内存地址是0x7ff7b0c94428
ippp_value值是0xb0c94430,ipp_value值是0xb0c9443c,ip_value值是1000
2、数组与数组指针
int main() {
int intArray[] = {1, 2, 3, 4};
for (int i = 0; i < sizeof(intArray) / sizeof(intArray[0]); ++i) {
printf(" i=%d,内存地址是=%p\n", i, &intArray[i]); // 地址连续
}
// 数组本身就是它的内存地址,它内存地址又是它第一个元素的内存地址。
printf("intArray=%p\n", intArray);
printf("&intArray=%p\n", &intArray);
printf("&intArray[0]=%p\n", &intArray[0]);
}
打印结果:
i=0,内存地址是=0x7ff7b3824420
i=1,内存地址是=0x7ff7b3824424
i=2,内存地址是=0x7ff7b3824428
i=3,内存地址是=0x7ff7b382442c
intArray=0x7ff7b3824420
&intArray=0x7ff7b3824420
&intArray[0]=0x7ff7b3824420
数组中的元素地址是连续的,每次挪动4个字节,因为是int数组。
int main() {
int intArray[] = {1, 2, 3, 4};
int *intArray_p = intArray;
printf("从intArray_p地址中取值%d\n", *intArray_p); // 只能取到第一个元素的值
// 那想获取到第二个元素的值呢,就要指针挪动
printf("从intArray_p地址中取出第二个元素的值%d\n", *++intArray_p); // 取第二个元素的值
// 如果想要取第四个元素。因为现在数组指针指向第二个元素,需要再往后移2位
intArray_p += 2;
printf("从intArray_p地址中取出第二个元素的值%d\n", *intArray_p); // 取第四个元素的值
// 将指针重新回到1
intArray_p -= 3;
printf("将指针重新回到1,value=%d\n", *intArray_p);
}
3、采用指针遍历数组
int main() {
int intArray[] = {1, 2, 3, 4};
for (int i = 0; i < sizeof(intArray) / sizeof(intArray[0]); ++i) {
printf("第%d元素的值是%d,内存地址是%p\n", i, *(intArray + i), intArray + i);
}
}
打印结果:
第0元素的值是1,内存地址是0x7ff7b5415420
第1元素的值是2,内存地址是0x7ff7b5415424
第2元素的值是3,内存地址是0x7ff7b5415428
第3元素的值是4,内存地址是0x7ff7b541542c
根据内存地址,依次输出元素的值。可以看出内存地址是连续的。间隔4个字节,一个int值占四个字节
4、循环时给数组赋值
int main() {
int intArray[4]; // 定义数组,长度4,未赋值。
int *intArrayP = intArray; // 将数组转成指针。
printf("intArray的内存地址是%p,默认元素的值是%d\n", intArrayP, *intArrayP);
for (int i = 0; i < sizeof(intArray) / sizeof(intArray[0]); ++i) {
// 第一个元素地址是intArrayP,那第二、三、四的地址,依次+1。
// 第一个元素赋值1000,第二、三、四的值也依次+1。
*(intArrayP + i) = 1000 + i;
printf("第%d个元素的值是%d\n", i,*(intArrayP + i));
}
}
打印结果:
intArray的内存地址是0x7ff7bda22420,默认元素的值是-1113447360
第0个元素的值是1000,地址是0x7ff7bda22420
第1个元素的值是1001,地址是0x7ff7bda22424
第2个元素的值是1002,地址是0x7ff7bda22428
第3个元素的值是1003,地址是0x7ff7bda2242c
地址连续,值依次增加1。
5、数组指针操作的几种方式
int main() {
int intArray[] = {1, 3, 5, 7};
int *intArrayP = &intArray;
for (int i = 0; i < sizeof(intArray) / sizeof(intArray[0]); ++i) {
printf("地址是:%p。第一种:值是%d,第二种:值是%d,第三种:值是%d\n", &intArray[i],intArrayP[i], intArray[i], *(intArrayP + i));
}
}
打印结果:
地址是:0x7ff7b5240420。第一种:值是1,第二种:值是1,第三种:值是1
地址是:0x7ff7b5240424。第一种:值是3,第二种:值是3,第三种:值是3
地址是:0x7ff7b5240428。第一种:值是5,第二种:值是5,第三种:值是5
地址是:0x7ff7b524042c。第一种:值是7,第二种:值是7,第三种:值是7
第一种:根据指针获取值;
第二种:根据数组取值;
第三种:根据内存地址获取值;
6、指针类型有何用?
不同类型的指针偏移量是不一样的。char占1个字节,short占2个字节,int、float占4个字节,double、long、char *占8个字节
7、函数指针
// 监听回调的方法
void onClick(char* msg) {
printf("%s\n", msg);
}
// 定义一个监听方法,具体实现在下面
void setOnclickListener(void (*clickCallback)(char*));
int mainT2_6() {
// 第一种写法,直接将函数地址传入。函数本身就是地址。
setOnclickListener(onClick);
// 第二种写法:和写法三类似,但写法二更标准。
// 先定义一个call函数,然后call函数的值修改成onClick()函数的值。再将call函数的地址作为参数传入。
// 这样做的好处是,避免修改onClick()函数导致全局改变
void (*call)(char*);
call = onClick;
setOnclickListener(call);
// 第三种写法:第二种写法的非标注写法,在某些编译器上有可能报错。
void (*call2)(char*) = onClick;
setOnclickListener(call2);
// 不写参数,这种简写好像也可以。
void *call3 = onClick;
setOnclickListener(call3);
}
void setOnclickListener(void (*clickCallback)(char*)) {
// 某种情况下会进行回调
clickCallback("我被点击啦...");
}