先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新大数据全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip204888 (备注大数据)
正文
(int*)(&a + 1)
中&a
取得是整个数组的地址,它的类型为int(*)[4],&a+1
跳过一个数组的大小,即跳过16个字节,再强制类型转换为(int*)
。ptr[-1]
即*(ptr-1)
,所以输出为4。
(int*)((int)a + 1)
中((int)a+1)
是将a强制类型转换为int
类型后+1,a是数组名,代表着数组首元素的地址。将数组中的元素转换成十六进制后为a[]={0x00000001,0x00000002,0x00000003,0x00000004}
,VS中采用的是小端存储,在内存中表示如下图。假设一个数的地址以十六进制表示为0x00000015,强制类型转换成整型之后为21,21+1=22,22转换成十六进制为0x00000016,所以强制类型转换成整型之后再+1相当于地址向后移动了1个字节。因此
((int)a + 1)
表示a的地址向后移动了1个字节,所以在*ptr2
中,从a的位置向后读4个字节,即00000002,因为是小端存储,在读取时就以小端的方式读,所以结果为20000000。
笔试题4
#include <stdio.h>
int main()
{
int a[3][2] = { (0,1),(2,3),(4,5) };
//逗号表达式:按照从左到右依次计算,整个表达式的结果为最后一个表达式的值。
//int a[3][2] = { 1,3,5 };
int\* p;
p = a[0];
//a[0],在二维数组中,把每一行都看成一维数组的时候,a[0]是第一行的数组名,数组名表示首元素的地址,即a[0]=&a[0][0]
printf("%d", p[0]);//输出结果为 1
//p等于a[0],p[0]等于\*(&a[0][0]+0),即数组第一个元素的值。
return 0;
}
解析:
int a[3][2] = { (0,1),(2,3),(4,5) }
中含有逗号表达式(逗号表达式:按照从左到右依次计算,整个表达式的结果为最后一个表达式的值)。计算之后得到数组为int a[3][2] = { 1,3,5 }
,p = a[0]
将a[0]赋值给整型指针变量p,在二维数组中,把每一行都看成一维数组的时候,a[0]表示第一行的数组名,数组名表示首元素的地址,即a[0]=&a[0][0]。p[0]
中p等于a[0],p[0]等于*(p+0)
等于*(&a[0][0]+0)
,即数组第一个元素的值1。
笔试题5
#include <stdio.h>
int main()
{
int arr[5][5];
int(\*p)[4];
p = arr;
printf("%p %d\n", &p[4][2] - &arr[4][2], &p[4][2] - &arr[4][2]);//输出结果为 FFFFFFFC -4
return 0;
}
解析:
本题考察的是指针-指针。int(*p)[4]
是数组指针类型,p = arr
中arr
是二维数组数组名,数组名就是首元素的地址,在二维数组中首元素地址就是第一行的地址即&arr[0]
,&arr[0]
用int(*)[5]
类型数组指针接收,将int(*)[5]
类型赋给int(*)[4]
类型,可以编译,虽然不会报错但是有警告,因为p是int(*)[4]
类型,p+1
的时候向后走4个整型,具体可见下图:
p[4][2]
可以写成*(*(p+4)+2)
,位置如上图所示,指针和指针相减的绝对值是元素之间的个数,&p[4][2] - &arr[4][2]
是低地址-高地址,得到的是-4。
-4的原码、反码和补码如下表所示:
原码、反码、补码 | 值 |
---|---|
原码 | 10000000000000000000000000000100 |
反码 | 11111111111111111111111111111011 |
补码 | 11111111111111111111111111111100 |
用%d打印的时候打印的是原码。
%p–输出地址,地址没有原码、反码和补码,在内存中存的是补码,因为打印的地址是一个明确的数-4,打印的时候就把补码当成地址打印,补码转换成十六进制就是FFFFFFFC。
笔试题6
#include <stdio.h>
int main()
{
int a[2][5] = { 1,2,3,4,5,6,7,8,9,10 };
int\* ptr1 = (int\*)(&a + 1);
int\* ptr2 = (int\*)(\*(a + 1));
printf("%d %d", \*(ptr1 - 1), \*(ptr2 - 1));//输出结果为 10 5
return 0;
}
解析:
&a+1
跳过整个二维数组,然后强制类型转换为(int*)类型,*((a+1))
中,a
是二维数组数组名,表示数组首元素的地址,在二维数组中,数组首元素的地址即第一行的地址,第一行的数组名可以用a[0]表示,*(a+1)
可以表示成*(&a[0]+1)
,&a[0]是int(*)[5]类型,+1之后到二维数组的第二行,所以(*(a+1))
就是a[1]
,a[1]就是第二行数组名,也表示第二行首元素的地址&a[1][0]
。
笔试题7
#include <stdio.h>
int main()
{
char\* a[] = { "work","at","alibaba" };
char\*\* pa = a;
pa++;
printf("%s\n", \*pa);//输出结果为 at
return 0;
}
解析:
char* a[] = { "work","at","alibaba" }
中a[]
是字符指针数组,存的是字符串首字符的地址,char** pa
表示pa指向char*类型,char** pa = a
表示pa指向a,a是数组名,表示数组首元素的地址,此时,pa指向数组中的第一个元素,pa++
表示pa跳过一个char*
类型,则pa此时指向数组中的第二个元素,*pa
取出数组中第二个元素的首地址,打印字符串。
笔试题8
#include <stdio.h>
int main()
{
char\* c[] = { "ENTER","NEW","POINT","FIRST" };
char\*\* cp[] = { c + 3,c + 2,c + 1,c };
char\*\*\* cpp = cp;
printf("%s\n", \*\*++cpp);//输出结果为POINT
printf("%s\n", \*-- \*++cpp+3);//输出结果为ER
printf("%s\n", \*cpp[-2]+3);//输出结果为ST
printf("%s\n", cpp[-1][-1]+1);//输出结果为EW
return 0;
}
解析:
c
数组中存的是字符串首字符的地址,c+1
中c
是数组名,数组名表示数组首元素的地址,因为数组c
里面的数据类型是char*
类型的,存放的是字符串首字符的地址,数组首元素就是字符的地址,地址的地址要用二级指针来接收,所以用char**
,c+1
之后指向数组c
中第二个元素的地址,c+2
指向第三个元素的地址,c+3
指向第四个元素的地址,而c
数组中的元素都是字符串首字符的地址。数组cp
中的元素类型都是char**
类型,要用char***
类型接收。
cpp
指向的类型是char**
,char*** cpp = cp
,cp
是数组名,表示数组首元素的地址,刚开始三级指针cpp
指向cp
数组中的c+3
的地址,**++cpp
中++cpp
之后指向c+2
的地址,第一次解引用*++cpp
(解引用操作得到的是指针指向地址中所存放的内容),得到的是c+2
,c+2
指向的是数组c
中第三个元素的地址,再次解引用之后得到的是c
数组中存放字符串POINT
首字符的地址,打印字符串得到POINT。
*-- *++cpp+3
中+
的优先级比++
、--
、*
的优先级低,所以+3
放在最后计算,++cpp
执行后cpp
指向cp
数组c+1
的地址,解引用得到c+1
,--(c+1)
执行后cp
数组中元素c+1
变为c
,c
指向数组c
中第一个元素的地址,解引用之后得到字符串ENTER
首字符的地址,+3向后移动3个字节,得到ER。
*cpp[-2]+3
等于*(*(cpp-2))+3
,(cpp-2)
表示指向数组cp
中c+3
的地址,但是cpp
指向的位置没有发生改变,*(cpp-2)
表示解引用之后得到c+3
,c+3
指向数组c
中第四个元素的地址,再次解引用得到的是字符串FIRST
首字符的地址,+3向后移动三个字节,打印ST。
cpp[-1][-1]+1
等于*(*(cpp-1)-1)+1
,因为cpp
指向的位置没有发生改变,所以cpp-1
指向数组cp
中c+2
的地址,*(cpp-1)
后得到c+2
,*(cpp-1)-1
等于(c+2)-1
为c+1
,c+1
指向数组c
中第二个元素的地址,*(*(cpp-1)-1)
执行后得到字符串NEW
首字符的地址,+1向后移动1个字节指向E,打印字符串为EW。
扩展:理解(*(void(*)())0)()
和void(*signal(int,void(*)(int)))(int)
。
1.void(*)()
可以看作是一个函数指针类型,假设函数指针是pf
那它可以写成void(*pf)()
,()里放个类型表示要做强制类型转换,0是int
类型,表示要将0强制转换成为函数指针类型,要把0当作是一个函数的地址(没有参数,返回类型为void
的函数)。*(void(*)())0
表示对0进行解引用,找到0地址处的函数,(*(void(*)())0)()
表示调用0地址处的函数,函数无参。去掉最左边的*也可以调用。
2.void(*signal(int,void(*)(int)))(int)
中signal
是函数名,signal
函数有两个参数,一个是int
,一个是函数指针,,该函数指针指向的函数的参数是int
类型,返回类型是void
,同时signal
函数的返回类型是函数指针,该函数指针指向的函数的参数是int
,返回类型是void
。
void(*signal(int,void(*)(int)))(int)
可以简化,typedef void(*pf_t)(int)
,此时pf_t
代表的就是void(*)(int)
;pf_t
要写在里面,语法规定;此时pf_t signal(int,pf_t)
就等价于void(*signal(int,void(*)(int)))(int)
。
typedef void(*)(int) pf_t
这样定义 void(*)(int)
是错误的,void(*)(int) signal(int ,void(*)(int))
这样写是错误的。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注大数据)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注大数据)
[外链图片转存中…(img-wJwwDrIH-1713339952831)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!