大家好啊,因为上个星期生病的原因,没有及时更新,本期这就补上。
本期主要内容是深入解析指针,让大家能够进一步了解指针,希望对各位小伙伴有所帮助!
- 前言
- 一、指针的基本概念
- 二、字符指针
- 三、指针数组
- 四、数组指针
- 小知识点——数组名与&数组名
- 数组指针的使用
- 小结
- 五、 数组参数与指针参数
- 1、一维数组传参
- 补充知识点
- 2、二维数组传参
- 重要知识点
- 1、void pri(int *p)
- 2、void pri(int **p)
- 六、函数指针
- 1、概念
- 2、代码
- 补充
- 3、两道例题
- 例题1
- 例题2
- 4、计算器
- 七、函数指针数组
- 1、概念
- 2、代码
- 3、小例子
- 4、转移表
- 八、指向函数指针数组的指针
- 九、回调函数
- 1、概念
- 2、qsort函数
- A、冒泡排序优化
- B、qsort函数(重点)
- void*指针
- qsort排序整型数据类型
- qsort函数排序结构体数据类型
- qsort实现冒泡函数
- 小结
- 十、指针数组笔试题+讲解
- 第一组
- 第二组
- 第三组(陷阱多)
- 补充知识点
- 第四组
- 补充知识点
- 第五组
- 补充知识点
- 第六组
- 第七组
- 第八组
- 补充知识点
- 第九组
- 第十组
- 第十一组
- 第十二组
- 第十三组
- 第十四组
- 第十五组
- 第十六组
- 总结
前言
C语言里面的指针可以说是非常重要的了,不管是以后学习数据结构还是什么其他内容,指针是重中之重, 我本期除了会讲解指针的内容以外,还会有一套指针笔试题的讲解,话不多说,咋们进入正片。
一、指针的基本概念
1、指针其实本质上叫做指针变量,只不过我们为了更方便解读就
口语化式的叫做指针
2、指针在32位平台占4个字节内存大小,在64位平台占8个字节
内存大小
二、字符指针
字符指针的写法为:char *
常用的字符指针写法是:
char ch = 'a';
char* p = &ch;
*p = 'b';
printf("%cn", ch);
还有一种写法是:
const char ch = "abcdef"; char* p = &ch;
可别小看这两行代码,里面蕴藏着许多内容:
1、这里可不是把abcdef全部放到指针p里面去了,而是把这个定义的字符串首元素a的地址赋给了指针p
当有了首元素地址之后,依次向后面打印就可以打印出整个字符串了;
2、这个字符指针的方法使用的时候,字符串“abcdef”是不可以被改变的:
这里大家可以看到’a’没有被改为‘w’,并且程序还挂了
这是为什么呢?
是因为,这里的“abcdef”是一个常量字符串,常量字符串是不可被修改的。p的权限变大了,因为p没有被修饰,所以p敢改这个字符串,但是程序会死掉,并且出现错误,所以我们才在前面加上一个const,来表明这里的常量字符串,不能被修改。
## 小例题
char arr1[] = "abcdef";
char arr2[] = "abcdef";
char *p1 = "abcdef";
char *p2 = "abcdef";
if (arr1 == arr2)
printf("yesn");
else
printf("non");
if (*p1 == *p2)
printf("yesn");
else
printf("non");
我直接公布答案了哦!
是不是有一部分小伙伴们做错了呢?
接下来我们来分析分析:
其实很好理解,就是:字符串相同,因为数组不同,所以两个字符串地址不同;而两个指针是指向同一个字符串的,所以两个指针地址相同
三、指针数组
指针数组的本质是一个数组,数组内部存放着指针
它的写法是:
int* arr1[10];//整型指针数组(也叫一级整型指针数组) int* *arr2[10];//二级整型指针数组 char* arr3[10];//一级字符指针数组 char* *arr4[10];//二级字符指针数组
四、数组指针
数组指针本质上是一个指针,这个指针指向一个数组
结合上面的指针数组一起来看看:
int arr[5] = {1,2,3,4,5};
int(*p)[5] = &arr;
int arr[10] = { 0 };
int (*p)[10] = &arr;
char* arr2[10] = { 0 };
char* (*pa)[10] = &arr2;
以上就是数组指针与指针数组的区别,大家可千万不敢搞混了!!!
小知识点——数组名与&数组名
1、一般情况下数组名就是首元素地址
2、在sizeof和&arr的情况下,arr表示整个数组
大家看看两段代码:
大家可能会疑惑,接下来我为大家进行讲解:
大家只需要记住数组名代表整个数组的情况即可,后面我们有一组列题来专门讲这个数组名知识点的
数组指针的使用
#includevoid pri1(int arr[3][5], int row, int col) { int i = 0; for (i = 0; i < row; i++) { int j = 0; for (j = 0; j < col; j++) { printf("%d ", arr[i][j]); } printf("n"); } } void pri2(int(*p)[5], int row, int col) { int i = 0; int j = 0; for (i = 0; i < row; i++) { for (j = 0; j < col; j++) { printf("%d ", p[i][j]); } printf("n"); } } int main() { int arr[3][5] = { 1,2,3,4,5,2,3,4,5,6,3,4,5,6,7 }; pri1(arr, 3, 5); pri2(arr, 3, 5); return 0; }
大家可以先看看这两段函数代码,我接下来为大家讲讲:
int arr[3][5] = { 1,2,3,4,5,2,3,4,5,6,3,4,5,6,7 };
printf("%pn", arr);
printf("%pn", arr + 1);
可以看到二维数组加1,地址加了20个字节,也就是5个元素,正好就是二维数组的一整行元素
也就是说:
int arr[3][5] = { 1,2,3,4,5,2,3,4,5,6,3,4,5,6,7 };
pri1(arr, 3, 5);
pri2(arr, 3, 5);
小结
我们来总结一下几个知识点
#includeint main() { int a[5]; int* b[5]; int(*c)[5]; int(*d[10])[5]; return 0; }
举个例子:
五、 数组参数与指针参数 1、一维数组传参
让我们看一组代码:
#includevoid test(int arr[]) {} void test(int arr[10]) {} void test(int *arr) {} void test2(int *arr2[]) {} void test2(int *arr2[20]) {} void test2(int **arr2) {} int main() { int arr[10] = { 0 }; int* arr2[20] = { 0 }; test(arr); test2(arr2); return 0; }
大家知道答案吗?我直接公布答案了:
答案就是全部正确,大家可能知道1,2,4,5是怎么来的,那大家知道为什么3和4也是正确的吗?
请各位小伙伴们听我详细分解:
补充知识点
int *p1//一级指针 int **p2//二级指针 int***p3//三级指针 . . . 以此类推
二级指针是用来存放一级指针地址的;三级指针是用来存放二级指针地址的,也是以此类推
2、二维数组传参
#includevoid test(int arr[3][5]]) {} void test(int arr[][5]]) {} void test(int arr[][]) {} void test(int *arr) {} void test(int *arr[]) {} void test(int* arr[5]) {} void test(int(*arr)[5]) {} void test(int **arr) {} int main() { int arr[3][5] = { 0 }; test(arr); }
大家知道这些函数接收参数是正确的吗?
为什么是以上的答案呢?我们一起来分析分析:
注意:上图内容很重要,小伙伴们一定要熟记于心!!!
当函数参数是一个指针的时候,实参会是什么数据呢?
1、int arr[10]={0};
pri(arr);
可以是一个整型数组数组名
2、int a=10;
pri(&a);
可以是一个整型变量的地址
3、int *p=&a;
pri§;
可以是一个整型指针
只要是传参的值本质上是一个整型指针就行
2、void pri(int **p)
通过上面的例题我们就可以得知:只要传参本质是一个二级指针就行
1、int n = 10;int* p = &n; int** p1 = &p; pri(p1); pri(&p);
2、指针数组的数组名也可以
int*parr[10];
pri(parr)
六、函数指针 1、概念
函数指针是一个指针,该指针指向一个函数,也就是指向函数的指针
2、代码int(*p1)[10];//这个是整型数组指针 char(*p1)[10];//这个是字符数组指针 int*(*p1)[10];//这个是一个指向存放10个整型指针数组的指针 //重点来了 int (*p2)(int ,int)//这个就是函数指针
#includeint add(int x, int y) { return x + y; } void pri(int(*add)(int, int)) { int a = 5; int b = 5; int ret = add(a, b); printf("%dn", ret); } int main() { pri(add); return 0; }
上面就是函数指针的基础用法。
我们来仔细分析:
以上就是函数指针的大体模板,只不过参数部分可以变得很复杂,小伙伴们一定要用心观察。
函数名本质上并不是函数的地址。函数名可以当做函数地址来使用,这是因为发生了隐式转换,我们以后会讲到隐式转换,我们可以拿着用。
3、两道例题 例题1
( * ( void ( * ) ( ) ) 0 )( )
例题2哎呀!这是一个什么题啊,很多小伙伴直接懵逼了,别着急,我们一步步来:
void ( * arr ( int , void ( * ) ( int ) ) ) ( int )
可能又有一部分人不理解这行代码了,我们继续:
以上代码有过多重复部分,我们可以进行简化:
void ( * arr ( int , void ( * ) ( int ) ) ) ( int ) typedef void(*p)(int); p arr(int, p);
这里我们用到了typedef重定义,可能有很多人问了,为什么不是【typedef void(*)(int) p】这样把p定义在最后呢?因为C语言的语法不是这样定义的。【typedef void(p)(int)】就相当于把【typedef void()(int)】重定义为了p。
4、计算器
函数指针有什么用呢?
接下来我们看看函数指针能怎么使用:
首先我们来看看最基础的一个计算器的实现:
#define _CRT_SECURE_NO_WARNINGS #includevoid menu() { printf("*****0.exit*****n"); printf("*****1.add *****n"); printf("*****2.sub *****n"); printf("*****3.mul *****n"); printf("*****4.div *****n"); } int add(int i, int j) { return i + j; } int sub(int i, int j) { return i - j; } int mul(int i, int j) { return i * j; } int div(int i, int j) { return i / j; } int main() { int n = 0; int i = 0; int j = 0; int ret = 0; do { menu(); printf("请选择:"); scanf("%d", &n); switch (n) { case 0: printf("退出程序n"); break; case 1: printf("请输入两个操作数:"); scanf("%d %d", &i, &j); ret = add(i, j); printf("%dn", ret); break; case 2: printf("请输入两个操作数:"); scanf("%d %d", &i, &j); ret = sub(i, j); printf("%dn", ret); break; case 3: printf("请输入两个操作数:"); scanf("%d %d", &i, &j); ret = mul(i, j); printf("%dn", ret); break; case 4: printf("请输入两个操作数:"); scanf("%d %d", &i, &j); ret = div(i, j); printf("%dn", ret); break; default: printf("选择错误,请重新选择n"); break; } } while (n); return 0; }
可以看到一个简单的计算器已经实现了,但是有很多代码重复了:
能不能更简单一点呢?
答案是可以的!
#includevoid menu() { printf("*****0.exit*****n"); printf("*****1.add *****n"); printf("*****2.sub *****n"); printf("*****3.mul *****n"); printf("*****4.div *****n"); } int add(int i, int j) { return i + j; } int sub(int i, int j) { return i - j; } int mul(int i, int j) { return i * j; } int div(int i, int j) { return i / j; } void calc(int(*p)(int ,int ))//函数指针(计算器) { int i = 0; int j = 0; int ret = 0; printf("请输入两个操作数:"); scanf("%d %d", &i, &j); ret = p(i, j); printf("%dn", ret); } int main() { int n = 0; do { menu(); printf("请选择:"); scanf("%d", &n); switch (n) { case 0: printf("退出程序n"); break; case 1: calc(add); break; case 2: calc(sub); break; case 3: calc(mul); break; case 4: calc(div); break; default: printf("选择错误,请重新选择n"); break; } } while (n); return 0; }
可以看出代码变得更简单了,函数变得不那么冗余了。当然还可以让这个代码变得更简洁,我们下面将
讲
七、函数指针数组 1、概念
函数指针数组是一个数组,数组里面的元素都是函数指针。所以函数指针数组是一个存放函数指针的数组。
2、代码
int( * p [10] )( )
3、小例子
#includevoid menu() { printf("*****0.exit*****n"); printf("*****1.add *****n"); printf("*****2.sub *****n"); printf("*****3.mul *****n"); printf("*****4.div *****n"); } int add(int i, int j) { return i + j; } int sub(int i, int j) { return i - j; } int mul(int i, int j) { return i * j; } int div(int i, int j) { return i / j; } int main() { int(*p[4])(int, int) = { add,sub,mul,div }; int i = 0; for (i = 0; i < 4; i++) { int ret = p[i](8, 4); printf("%dn", ret); } return 0; }
可以看到将函数指针放到数组里面是很便捷的,接下来运用一下函数指针数组
上面我们做了一个简单的计算器,但是当计算器需要计算各种方法的时候(比如说取余,异或,按位,移位)难道要一个个的调用函数指针吗?
其实不用那么麻烦
#includevoid menu() { printf("*****0.exit*****n"); printf("*****1.add *****n"); printf("*****2.sub *****n"); printf("*****3.mul *****n"); printf("*****4.div *****n"); printf("*****5.qy *****n"); printf("*****6.awy *****n"); printf("*****7.awh *****n"); printf("*****8.yh *****n"); printf("*****9.ljy *****n"); printf("*****10.ljh*****n"); printf("*****11.zy *****n"); printf("*****12.yy *****n"); } int add(int i, int j) { return i + j; } int sub(int i, int j) { return i - j; } int mul(int i, int j) { return i * j; } int div(int i, int j) { return i / j; } int qy(int i, int j) { return i % j; } int awy(int i, int j) { return i & j; } int awh(int i, int j) { return i | j; } int yh(int i, int j) { return i ^ j; } int ljy(int i, int j) { return i && j; } int ljh(int i, int j) { return i || j; } int zy(int i, int j) { return i << j; } int yy(int i, int j) { return i >> j; } void calc(int (*pfarr)(int,int)) { int n = 0; int i = 0; int j = 0; int ret = 0; printf("请输入两个操作数:"); scanf("%d %d", &i, &j); ret = pfarr(i, j); printf("%dn", ret); } int main() { int n = 0; int(*pfarr[])(int, int) = { 0,add,sub,mul,div,qy,awy,awh,yh,ljy,ljh,zy,yy }; do { menu(); printf("请选择:"); scanf("%d", &n); if (n == 0) { printf("退出程序n"); } else if (n >= 1 && n <= 12) { calc(pfarr[n]); } else { printf("选择错误n"); } } while (n); return 0; }
可以看到函数指针数组的使用很方便
八、指向函数指针数组的指针
很多人看到这个名字就已经晕了,其实我们仔细分析会发现不难理解
int(*p[10])() int ( * ( * parr ) [5] )( ) = &p
我们只需要了解指向函数指针数组的指针是什么样子就行,不用深究。当然,相信各位小伙伴们已经发现了,这个东西又是一个指针,我们又可以放到一个数组里面,像这样一直套娃下去…
九、回调函数 1、概念
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
2、qsort函数 A、冒泡排序优化
#includevoid bubble(int* arr, int sz) { int i = 0; int j = 0; for (i = 0; i < sz - 1; i++)//一共要排序多少躺 { int flag = 1;//假设原来的数组内容已经是正确的顺序了 for (j = 0; j < sz - 1 - i; j++)//一趟排序是怎么样的 { if (arr[j] > arr[j + 1]) { int tmp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = tmp; flag = 0;//如果flag等于0那么就表示数组内容顺序有问题,已经排序完一次了 } } if (flag == 1)//如果顺序没有问题就退出 { break; } } } int main() { int arr[10] = { 9,8,7,6,5,4,3,2,1,0 }; int sz = sizeof(arr) / sizeof(arr[0]); bubble(arr, sz); int i = 0; for (i = 0; i < sz; i++) { printf("%d ", arr[i]); } return 0; }
上面的冒泡排序我们已经就行优化了,但是不管再怎么优化也只能排列整型数据,能排浮点型、字符、结构体类型的数据吗?
显而易见是不能的,那么就要用到我们的qsort函数了
qsort函数就是快速排序
上图中是qsort函数的具体内容,需要引头文件stdlib.h
qsort函数4个内容:
1、排序内容的起始地址;
2、待排序的元素个数;
3、待排序元素字节大小;
4、排序的方法
注:qsort默认是排升序的
下面我们会用qsort来排序整型数据和结构体数据。
void*指针
qsort排序整型数据类型void*是无具体类型的指针,可以接受任意类型的地址
void*是无具体类型的指针,所以不能解引用操作和+-整数的操作
#include#include int cmp(const void* e1, const void* e2) { return *(int*)e1 - *(int*)e2; } int main() { int arr[10] = { 9,8,7,6,5,4,3,2,1,0 }; int sz = sizeof(arr) / sizeof(arr[0]); qsort(arr, sz, sizeof(arr[0]), cmp); int i = 0; for (i = 0; i < sz; i++) { printf("%d ", arr[i]); } return 0; }
可以看出用了qsort函数之后结果不仅正确了,而且还节约时间,空间,可以说是非常方便了。
qsort函数排序结构体数据类型
#include#include #include struct stu { char name[20]; int age; }; int cmp_by_name(const void* e1,const void* e2) { return strcmp(((struct stu*)e1)->name, ((struct stu*)e2)->name); } int cmp_by_age(const void* e1, const void* e2) { return ((struct stu*)e1)->age - ((struct stu*)e2)->age; } void test2() { struct stu s[] = { {"zhangsan",15}, {"lisi",30} ,{"wangwu",20} }; int sz = sizeof(s) / sizeof(s[0]); qsort(s, sz, sizeof(s[0]), cmp_by_name); qsort(s, sz, sizeof(s[0]), cmp_by_age); } int main() { test2();//排序结构体数据 return 0; }
我们来看看结果:
名字排序:
年龄排序:
可以看到qsort把结构体的数据已经排序出来了,由此可见qsort的强大之处。
qsort实现冒泡函数
#includevoid swap(char* b1, char* b2, int width) { int i = 0; for (i = 0; i < width; i++) { char tmp = *b1; *b1 = *b2; *b2 = tmp; b1++; b2++; } } void bubble(void*base,int sz,int width,int (*cmp)(const void* e1,const void* e2)) { int i = 0; int j = 0; int flag = 1;//假设原来的数组内容已经是正确的顺序了 for (i = 0; i < sz - 1; i++) { for (j = 0; j < sz - 1 - i; j++) { //if (arr[j] > arr[j + 1]) if (cmp((char*)base + j * width, (char*)base + (j + 1) * width)>0) { swap((char*)base + j * width, (char*)base + (j + 1) * width, width); flag = 0;//如果flag等于0那么就表示数组内容顺序有问题,已经排序完一次了 } } if (flag == 1)//如果顺序没有问题就退出 { break; } } } int cmp(const void* e1, const void* e2) { return *(int*)e1 - *(int*)e2; } void test3() { int arr[10] = { 9,8,7,6,5,4,3,2,1,0 }; int sz = sizeof(arr) / sizeof(arr[0]); bubble(arr, sz, sizeof(arr[0]), cmp); int i = 0; for (i = 0; i < sz; i++) { printf("%d ", arr[i]); } } int main() { test3(); return 0; }
小结
到目前为止,我们的指针进阶的内容基本上完成了,但是可没有结束哦!下面有几组笔试指针题目,有许多小陷阱,欢迎大家挑战哦!我也会为大家进行讲解的!
十、指针数组笔试题+讲解
我们不多说废话,直接题目+答案+讲解
第一组#includeint main() { int a[] = { 1,2,3,4 }; printf("%dn", sizeof(a)); printf("%dn", sizeof(a+0)); printf("%dn", sizeof(*a)); printf("%dn", sizeof(a+1)); printf("%dn", sizeof(a[1])); printf("%dn", sizeof(&a)); printf("%dn", sizeof(*&a)); printf("%dn", sizeof(&a+1)); printf("%dn", sizeof(&a[0])); printf("%dn", sizeof(&a[0]+1)); return 0; }
第二组
#includeint main() { char a[] = { 'a','b','c','d','e','f' }; printf("%dn", sizeof(a)); printf("%dn", sizeof(a + 0)); printf("%dn", sizeof(*a)); printf("%dn", sizeof(a + 1)); printf("%dn", sizeof(a[1])); printf("%dn", sizeof(&a)); printf("%dn", sizeof(*&a)); printf("%dn", sizeof(&a + 1)); printf("%dn", sizeof(&a[0])); printf("%dn", sizeof(&a[0] + 1)); return 0; }
第三组(陷阱多) 补充知识点
strlen遇到 才停止, 不是字符串的内容, 只是结束标志
#includeint main() { char a[] = { 'a','b','c','d','e','f' }; printf("%dn", strlen(a)); printf("%dn", strlen(a + 0)); printf("%dn", strlen(*a)); printf("%dn", strlen(a[1])); printf("%dn", strlen(&a)); printf("%dn", strlen(&a + 1)); printf("%dn", strlen(&a[0] + 1)); return 0; }
这一组题有许多陷阱,不能直接编译出结果,我为大家一一讲解:
可以看到还是有很多陷阱的,小伙伴们以后别像小编一样粗心大意哦!
第四组 补充知识点
sizeof计算字符串的时候会把 算进去,因为 也占了空间位置
#includeint main() { char a[] = "abcdef"; printf("%dn", sizeof(a)); printf("%dn", sizeof(a + 0)); printf("%dn", sizeof(*a)); printf("%dn", sizeof(a[1])); printf("%dn", sizeof(&a)); printf("%dn", sizeof(&a + 1)); printf("%dn", sizeof(&a[0] + 1)); return 0; }
第五组 补充知识点
char a[]=“”;[]未表明个数,双引号定义字符串的时候会默认带上 。前提是方括号【】内的数字也就是空间足够大
#includeint main() { char a[] = "abcdef"; printf("%dn", strlen(a)); printf("%dn", strlen(a + 0)); printf("%dn", strlen(*a)); printf("%dn", strlen(a[1])); printf("%dn", strlen(&a)); printf("%dn", strlen(&a + 1)); printf("%dn", strlen(&a[0] + 1)); return 0; }
这一组代码也有陷阱,没有具体答案哦:
其实只要找到双引号自带 就很简单啦!
第六组
#includeint main() { char* a = "abcdef"; printf("%dn", sizeof(a)); printf("%dn", sizeof(a + 1)); printf("%dn", sizeof(*a)); printf("%dn", sizeof(a[0])); printf("%dn", sizeof(&a)); printf("%dn", sizeof(&a + 1)); printf("%dn", sizeof(&a[0] + 1)); return 0; } }
第七组
#includeint main() { char a[] = "abcdef"; printf("%dn", strlen(a)); printf("%dn", strlen(a + 1)); printf("%dn", strlen(*a)); printf("%dn", strlen(a[0])); printf("%dn", strlen(&a)); printf("%dn", strlen(&a + 1)); printf("%dn", strlen(&a[0] + 1)); return 0; }
直接分析:
第八组 补充知识点
我们之前说过二维数组数组名代表的就是第一行数组的地址
#includeint main() { int a[3][4] = { 0 }; printf("%dn", sizeof(a)); printf("%dn", sizeof(a[0][0])); printf("%dn", sizeof(a[0])); printf("%dn", sizeof(a[0]+1)); printf("%dn", sizeof(*(a[0] + 1))); printf("%dn", sizeof(a+1)); printf("%dn", sizeof(*(a + 1))); printf("%dn", sizeof(&a[0]+1)); printf("%dn", sizeof(*(&a[0] + 1))); printf("%dn", sizeof(*a)); printf("%dn", sizeof(a[3])); return 0; }
第九组
#includeint main() { int a[5] = { 1,2,3,4,5 }; int* ptr = (int*)(&a + 1); printf("%d,%d", *(a + 1), *(ptr - 1)); return 0; }
画个图就解决了:
第十组
#includestruct stu { int num; char* a; short date; char b[2]; short arr[4]; }*p; int main() { p = (struct stu*)0x100000; printf("%pn", p + 0x1); printf("%pn", (unsigned long)p + 0x1); printf("%pn", (unsigned int*)p + 0x1); return 0; }
第十一组
#includeint main() { int a[4] = { 1,2,3,4 }; int* p1 = (int*)(&a + 1); int* p2 = (int*)((int)a + 1); printf("%x,%x", p1[-1], *p2); return 0; }
第十二组
#includeint main() { int a[3][2] = { (0,1),(2,3),(4,5) }; int* p; p = a[0]; printf("%dn", p[0]); return 0; }
这个题其实很简单:
第十三组
#includeint main() { int a[5][5]; int(*p)[4]; p = a; printf("%p,%dn",&p[4][2]-&a[4][2], &p[4][2] - &a[4][2]); return 0; }
第十四组
#includeint main() { int a[2][5] = { 1,2,3,4,5,6,7,8,9,10 }; int* arr1 = (int*)(&a + 1); int* arr2 = (int*)(*(a + 1)); printf("%d,%dn", *(arr1 - 1), *(arr2 - 1)); return 0; }
第十五组
#includeint main() { char* a[] = { "work","at","alibaba" }; char** pa = a; pa++; printf("%sn", *pa); return 0; }
第十六组
#includeint main() { char* c[] = { "ENTER","NEW","POINT","FIRST" }; char** cp[] = { c + 3,c + 2,c + 1,c }; char*** cpp = cp; printf("%sn", **++cpp); printf("%sn", *--*++cpp+3); printf("%sn", *cpp[-2]+3); printf("%sn", cpp[-1][-1]+1); return 0; }
这一题小编在本子上做了,有点乱,大家可以自己做做这道题:
总结
本章花费了小编好几天时间,干货还是有的,希望能够给各位小伙伴带来新的收获,同时希望各位观众能够点点赞,最后一道题可能一些观众不理解,如果不理解的话可以私信小编,小编为为你详细解答的,希望各位小伙伴能够与小编一起进步,我们下期见啦!



