速通C语言第八.五站 指针进阶题目练习

本篇博客通过一系列的C语言编程题目,详细解释了sizeof运算符和strlen函数在处理数组和指针时的不同行为,涵盖了整型数组、字符数组、二维数组以及字符指针的用法。内容包括数组大小的计算、指针地址的增减、字符串长度的确定等知识点,旨在帮助读者巩固C语言中关于指针和数组的知识。
摘要由CSDN通过智能技术生成

系列文章目录

  速通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

感谢佬们支持!

文章目录

  • 系列文章目录
  • 前言
  • 一、整形数组の题目
  • 二、字符数组の题目(一)
  •        1.sizeof
  •        2.strlen
  • 三、字符数组の题目(二)
  •        1.sizeof
  •        2 .strlen
  • 四、字符数组の题目(三)
  •        1.sizeof
  •        2 .strlen
  • 五、二维数组の题目
  •      sizeof的补充
  • 六、指针练习题
  •       1
  •       2
  •       3
  •       4
  •       5
  •       6
  •       7
  • 总结


前言

  上篇博客我们学了指针的进阶,也就是天花板,这篇博客我们来给到大家一些练习

以巩固


一、整形数组の题目

先给一个整形数组

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哦。

每日gitee侠:今天你交gitee了嘛

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值