系列文章目录
速通C语言系列
速通C语言第一站 一篇博客带你初识C语言 http://t.csdn.cn/N57xl
速通C语言第二站 一篇博客带你搞定分支循环 http://t.csdn.cn/Uwn7W
速通C语言第三站 一篇博客带你搞定函数 http://t.csdn.cn/bfrUM
速通C语言第四站 一篇博客带你学会数组 http://t.csdn.cn/Ol3lz
速通C语言第五站 一篇博客带你详解操作符 http://t.csdn.cn/OOUBr
速通C语言第六站 一篇博客带你掌握指针初阶 http://t.csdn.cn/7ykR0
速通C语言第七站 一篇博客带你掌握数据的存储 http://t.csdn.cn/qkerU
速通C语言第八站 一篇博客带你掌握指针进阶 http://t.csdn.cn/m95FK
感谢佬们支持!
文章目录
前言
上篇博客我们学了指针的进阶,也就是天花板,这篇博客我们来给到大家一些练习
以巩固
一、整形数组の题目
先给一个整形数组
int a[] = {1, 2, 3, 4};
1
printf("%d\n", sizeof(a));
由于sizeof(数组名)算出的是数组的大小,所以答案为16
2
printf("%d\n", sizeof(a+0));
sizeof内为 a+0,即数组名未单独放在sizeof中,所以此处a表示的为首元素的地址,所以a+0
即为a的首元素地址+0,即为1的地址,所以为4/8.
3
printf("%d\n", sizeof(*a));
*a 为对a的首元素地址解引用,a的首元素为1,即为int,所以答案为4
4
printf("%d\n", sizeof(a+1));
与第二个题类似,a+1拿到了第二个元素的地址,所以答案依然为4
5
printf("%d\n", sizeof(a[0]));
这个题没什么好说的,a[ 0 ] 为第一个元素,所以答案为4
6
printf("%d\n", sizeof(&a));
&a取出的是a(整个数组)的地址,地址大小固定为 4/8,所以答案为4/8
7
printf("%d\n", sizeof(*&a));
&a取出的是a(整个数组)的地址,所以再解引用就找到了这个数组,所以答案为4*4=16
8
printf("%d\n", sizeof(&a+1));
&a+1 意为跳过整个数组的下一块地址,由于是地址,所以答案与依然为4/8
9
printf("%d\n", sizeof(&a[0]));
显然,取出了首元素的地址,所以为4/8
二、字符数组の题目(一)
先给一个字符数组
char arr[] = { 'a','b','c','d','e','f' };
1.sizeof
1
printf("%d\n", sizeof(arr));
算出的为数组的大小,所以为1*6=6
2
printf("%d\n", sizeof(arr+0));
arr+0即为arr的首元素地址+0,即为‘a‘的地址,所以为4/8.
3
printf("%d\n", sizeof(*arr));
*arr 为对arr的首元素地址解引用,arr的首元素为‘a‘,即为char,所以答案为1
4
printf("%d\n", sizeof(arr[1]));
arr[1]即为arr的第二个元素,所以答案为1
5
printf("%d\n", sizeof(&arr));
&arr取出的是arr(整个数组)的地址,所以为4/8
6
printf("%d\n", sizeof(&arr+1));
跳过了整个数组的下一块空间,所以为4/8
7
printf("%d\n", sizeof(&arr[0]+1));
首元素的地址加1,即为第二个元素的地址,所以为4/8
2 .strlen
我们讲sizeof改成strlen
1
printf("%d\n", strlen(arr));
由于arr存的是一个一个字符,所以我们不知道 /0 在哪,所以结果为随机值
2
printf("%d\n", strlen(arr + 0));
同理,由于不知道 /0在哪,所以为随机值,由于arr 和arr【0】都是第一个元素这波2题和1题是同一个随机值
3
printf("%d\n", strlen(*arr));
由于strlen传的参数为指针,而*arr为字符,所以这波会出问题。
4
printf("%d\n", strlen(arr[1]));
与3题同理
5
printf("%d\n", strlen(&arr));
&arr虽然取出了数组的地址,但由于strlen的形参为 const char* str,所以他传过去数组的地址后也会转换为字符的地址,所以依然是从首元素开始找 /0 ,为和1题、2题一样的随机值
6
printf("%d\n", strlen(&arr + 1));
&arr+1,即跳过整个数组的下一块空间,再从那块空间开始找 \0 ,由于arr长度为6,所以这个随机值理论上为1题的随机值-6
7
printf("%d\n", strlen(&arr[0] + 1));
从第二个元素开始找 \0 ,所以依然为随机值,且由于跳过了一个元素,所以理论上为1题的随机值-1
三、字符数组の题目(二)
给第二个字符数组
char arr[] = "abcdef";
这次的字符数组我们存的是字符串,所以他会自动带上 \0
1.sizeof
1
printf("%d\n", sizeof(arr));
由于会自动带上 \0, 所以大小为7
2
printf("%d\n", sizeof(arr + 1));
arr+1即为arr的首元素地址+1,即为‘b‘的地址,所以为4/8.
3
printf("%d\n", sizeof(*arr));
解引用出来为第一个元素,所以答案为1
4
printf("%d\n", sizeof(arr[1]));
第二个元素的大小为1,所以答案为1
5
printf("%d\n", sizeof(&arr));
取出数组的地址,所以答案为4/8
6
printf("%d\n", sizeof(&arr + 1));
答案为4/8
7
答案为4/8
2 .strlen
1
printf("%d\n", strlen(arr));
由于strlen计算时 \0 不计入长度,所以答案为6
2
printf("%d\n", strlen(arr + 1));
从数组的第二个元素开始计算,所以答案为5
3
printf("%d\n", strlen(*arr));
由于解引用后为第一个元素,即为char类型,所以无法给strlen传参,所以这波会出问题
4
printf("%d\n", strlen(arr[0]));
同理于3题,这波同样会出问题
5
printf("%d\n", strlen(&arr));
&arr取出的是数组的地址,与1题一样,他从第一个元素向后计算,所以答案为6
6
printf("%d\n", strlen(&arr + 1));
跳过了这个数组的下一块空间,由于不知道 \0 在哪,所以答案为随机值
7
printf("%d\n", strlen(&arr[0] + 1));
即为从第二个元素开始计算,所以答案为5
四、字符数组の题目(三)
给第三个字符数组
char* arr = "abcdef";
1.sizeof
1
printf("%d\n", sizeof(arr));
a的地址,所以为4/8
2
printf("%d\n", sizeof(arr + 1));
arr+1,即为b的地址
3
printf("%d\n", sizeof(*arr));
解引用出来是a,a为一个字节,所以答案为1
4
printf("%d\n", sizeof(arr[0]));
在前面我们学过arr[0]等价于*(arr+0),所以答案为1
5
printf("%d\n", sizeof(&arr));
arr为指针,&arr即为二级指针,所以答案为4/8
6
printf("%d\n", sizeof(&arr + 1));
&p+1为跳过arr数组后的下一块空间,由于是地址,所以为4/8
2 .strlen
1
printf("%d\n", strlen(arr));
答案为6
2
printf("%d\n", strlen(arr + 1));
从b往后算,所以为5
3
printf("%d\n", strlen(*arr));
出错
4
printf("%d\n", strlen(arr[1]));
出错
5
printf("%d\n", strlen(&arr));
6
printf("%d\n", strlen(&arr + 1));
由于是跳过了arr数组,所以我们甚至不知道他会存什么,所以也是随机值,而且和5题的随机值没有关系
7
printf("%d\n", strlen(&arr[0] + 1));
从b往后算,所以答案为5
五、二维数组の题目
给一个二维数组
int a[3][4] = { 0 };
1
printf("%d\n", sizeof(a));
三行四列,每个元素为int,3*4*4=48
2
printf("%d\n", sizeof(a[0][0]));
a[0][0]拿到的是第一行第一列的那个元素,大小为4
3
printf("%d\n", sizeof(a[0]));
a[ 0 ]即为第一行,所以算的是第一行的大小,4*4=16
4
printf("%d\n", sizeof(a[0]+1));
我们把a【0】看作一个一维数组,由于a【0】并未单独放在sizeof种,所以这波a【0】是首元素地址,再+1即a【0】【1】的地址,所以大小为4/8
5
printf("%d\n", sizeof(*(a[0] + 1)));
解引用找到了第一行第二个元素,大小为4
6
printf("%d\n", sizeof(a+1));
a是二维数组的数组名,未单独放在sizeof中,所以代表的是首元素的地址,即为第一行的地址,再+1就找到了第二行的地址,大小为4/8
7
printf("%d\n", sizeof(*(a + 1)));
解引用找到了第二行,所以大小为4*4=16
8
printf("%d\n", sizeof(&a[0] + 1));
&a[ 0 ]即取出了第一行的地址,加1就取到了第二行的地址,大小为4/8
9
printf("%d\n", sizeof(*(&a[0] + 1)));
加上了解引用就找到了第二行,所以大小为16
10
printf("%d\n", sizeof(*a));
a为首元素的地址,即第一行的地址,解引用就可以找到第一行,所以大小为16
11***(这个太重要了,一颗心不够,double star,trible star)
printf("%d\n", sizeof(a[3]));
看起来是越界了,但是能打印出结果为16,C语言对越界的检查是一种较松的模式,只要你不访问,就不算越界(就算访问了,他对越界的检查也是抽查),所以我们可以推出他这波应该是越界了但没访问,那么为什么呢?这里我们给一波补充
补充
C语言表达式有两个属性:值属性和类型属性
比如:3是整形,5是整形,
3+5的值属性为8
类型属性为4(因为int是4个字节)
而sizeof的内部玩的其实是类型属性而不是值属性,所以他也不管算的结果,也不管你越不越界
再给一波例子
short s = 5;
int a = 4;
printf("%d\n", sizeof(s = a + 6));
printf("%d\n", s);
运行结果为:
由于内部不计算,所以s的值还是5,而 sizeof(s=a+6)打印的为s所占大小,即 2字节
六、指针练习题
1
void test1()
{
int a[5] = { 1,2,3,4,5 };
int* ptr = (int*)(&a + 1);
printf("%d %d", *(a + 1), *(ptr - 1);
}
*(a+1)好说,是数组a的第二个元素,即2
&a指向数组a的地址,+1跳过一个数组到下一块空间,前面加上(int*)表示
将其强转为int*的指针,所以 ptr - 1会找到5的地址,对其解引用为5
运行结果
2
struct Test
{
int Num;
char* pcName;
char sDate;
char cha[2];
short sBa[4];
}*p;//结构体指针
printf("%p\n", p + 0x1);
printf("%p\n", (unsigned long)p + 0x1);
printf("%p\n", (unsigned int*)p + 0x1);
p是个结构体指针,假设p为0x100000,由于我们还没学怎么求结构体,已知该结构体的大小为20.那么三个结果是多少?
p为结构体指针,+1跳过一个结构体,20的16进制为0x100014,所以第一个结果为0x100014
这次将其转换为了整形,整形加1是真的加1,所以结果为0x100001
将其转换为了无符号整形的指针,所以加1跳过4,所以结果为0x100004
3
int a[4] = { 1,2,3,4 };
int* ptr1 = (int*)(&a + 1);
int* ptr2 = (int*)((int)a + 1);
printf("%x %x", ptr1[-1], *ptr2);
ptr1显然是好说的,我们在第一个题中已经见过了,所以ptr【-1】为 4,%x意为以16进制打印,0x 00000004,输出有效数字,最后依然为4
下面这个可就有点麻烦了 我们画个图演示一下
(注:由于我们用的是VS2019,所以此处以小端存储为例)
a是数组名,也就是首元素地址,前面加(int)即将其转换为整形,后面再加一,那就是真的加1,也就是跳过一个字节。即
此时再将其转换为int*的指针后,由于int*指针一次访问4个字节,所以他会访问这4个字节
再转换为16进制,即为 0x 20 00 00 00,输出后即为 20000000
运行结果:
4
int a[3][2] = { (0,1),(2,3),(4,5) };
int* p;
p = a[0];
printf("%d\n", p[0]);
这个题首先我们要注意观察,数组a的大括号内并不是3个大括号,而是3个小括号,所以里面是3个逗号表达式,结果分别为 1、 3、 5.所以a数组的元素为
所以p拿到的是第一行的地址,而p【0】也就是第一行的第一个元素,即1,所以答案是1
5
int a[5][5];
int(*p)[4];
p = a;
printf("%p,%d\n", &p[4][2]-&a[4][2], &p[4][2] - &a[4][2]);
p=a,即把a数组的地址赋给p,但现在的问题是a是 5*5 的,而p一次只能存4个元素,即
现在我们再来找一下 a【4】【2】和 p【4】【2】
以%d打印情况下,指针-指针是两个指针间的元素个数,由于右边地址大,左边小,为-4
以%p打印情况下,-4要转化成补码
再转换为16进制,即 1111-》 F, 1100-》C,所以答案为 FFFFFFFC
6
char* a[] = { "work","at","alibaba" };
char** pa = a;
pa++;
printf("%s\n", *pa);
a是个字符指针数组,每个元素存的是每个字符串首元素的地址,即w、a、a
pa是个二级指针,指向a,如图
pa++,所以pa就指向了第二个char*,以%s打印,即打印字符串,所以打印at
7***(哎呀这个题太重要了,一颗星不够,double star,trible star)
char* c[] = { "ENTER","NEW","POINT","FIRST" };
char** cp[] = { c + 3,c + 2,c + 1,c };
char*** cpp = cp;
printf("%s\n", **++cpp);
printf("%s\n", *--*++cpp);
printf("%s\n", *cpp[-2]+3);
printf("%s\n", cpp[-1][-1] + 1);
相信很多同学一看到这个题立即推放弃学习C语言,先别急,我们先画个图
在6题的经验下,我们知道,c数组存的是E、N、P、F,而这波他的cp也是个数组,存的是
数组c的不同位置,cpp这个3级指针指向cp
第一个为**++cpp,所以指向的就是c+2,而c+2指向的为POINT,所以答案为POINT
由于第一个打印中已经++过cpp了,所以在++cpp,指向的就为c+1
由于后面的+3优先级低,所以这波会先解引用拿到c+1的地址
c+1的前面有个--,所以c+1减1变成c
c再解引用会拿到ENTER,此时我们再+3,即拿到第二个E的地址,打印字符串,所以为ER
经过前两次打印,cpp指向的位置和cp数组发生了一些变化,我们先标记出来。方便下两次打印观察
cpp【-2】 ,即cpp-2,所以此时cpp指向c+3,解引用后指向的就是FIRST,+3后就为ST
对于第四个我们可以简化为 *(*(cpp-1)-1) +1
cpp-1,就找到了c+2的位置,
解引用后再-1,就找到了N的位置,
此时再加1,就找到了E的位置,从E开始打印,就是 EW
总结
做总结,相信今天这波题做下来,大家一定会对指针有更深的理解,讲过的东西下来以后要再次看过
水平有限,还请各位大佬指正。如果觉得对你有帮助的话,还请三连关注一波。希望大家都能拿到心仪的offer哦。