C语言进阶知识(六)

指针进阶

8.指针和数组典型题目解析

当我们学习了关于数组和指针的进阶知识后,我们需要做一些题目来进行巩固,今天的内容是对大家对前文内容掌握程度的一个考察,大家可以先自己练习,再看解析解答。

(一)数组习题

再看这组代码之前,我们先来复习一下之前所学的知识。

(1)数组名一般情况代表首元素地址,有两种特殊情况除外,大家还记得吗?(进阶知识三中有详细叙述,大家忘了可以再回顾一下概念。)在这里我进行简单叙述:一是sizeof(arr),此时arr代表整个数组,一是&a,代表整个数组的地址。

(2)地址一般占4/8个字节,具体根据平台产生不同答案。

(3)sizeof()是一种单目操作符,sizeof操作符以字节形式给出了其操作数的存储大小。操作数的存储大小由操作数的类型决定。

接下来我们看第一段代码:

int main()
{
	int a[] = { 1,2,3,4 };
	printf("%d\n", sizeof(a));
	printf("%d\n", sizeof(a + 0));
	printf("%d\n", sizeof(*a));
	printf("%d\n", sizeof(a + 1));
	printf("%d\n", sizeof(a[1]));
	printf("%d\n", sizeof(&a));
	printf("%d\n", sizeof(*&a));
	printf("%d\n", sizeof(&a + 1));
	printf("%d\n", sizeof(&a[0]));
	printf("%d\n", sizeof(&a[0] + 1));
	return 0;
}

 大家可以先根据自己的理解,对上述代码进行判断。以下是我给出的详细解释:

int main()
{
	int a[] = { 1,2,3,4 };
	printf("%d\n", sizeof(a));//sizeof(a)代表整个数组,int类型4个元素,所以为16。
	printf("%d\n", sizeof(a + 0));//首元素地址+0还是地址,所以为8
	printf("%d\n", sizeof(*a));//解引用得到元素,所以为4
	printf("%d\n", sizeof(a + 1));//为a[1]元素地址,所以为8
	printf("%d\n", sizeof(a[1]));//int型,所以为4
	printf("%d\n", sizeof(&a));//&a为整个数组的地址,还是地址,所以为8
	printf("%d\n", sizeof(*&a));//对&a(整个数组地址)解引用,得到的是整个数组,所以为16
	printf("%d\n", sizeof(&a + 1));//整个元素地址+1,依旧为地址,所以为8
	printf("%d\n", sizeof(&a[0]));//首元素地址,依旧为地址,所以为8
	printf("%d\n", sizeof(&a[0] + 1));//a[1]元素地址,所以为8
	return 0;
}

大家理解了吗?有问题欢迎大家在评论区询问,现在给大家一段测试代码,看看你能答对了吗?


int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", sizeof(arr));
	printf("%d\n", sizeof(arr + 0));
	printf("%d\n", sizeof(*arr));
	printf("%d\n", sizeof(arr[1]));
	printf("%d\n", sizeof(&arr));
	printf("%d\n", sizeof(&arr + 1));
	printf("%d\n", sizeof(&arr[0] + 1));
	return 0;
}

现在我们需要再了解一些前提知识,让我们能更好的理解第二段代码。

(1)strlen函数:计算的是字符串str的长度,从字符的首地址开始遍历,以 '\0' 为结束标志,然后将计算的长度返回,计算的长度并不包含'\0'。这里有一个重点,strlen函数传参的参数是指针!

size_t  strlen (const char* str);

接下来我们看第二段代码:

int main()
{
	//字符数组
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", strlen(arr));
	printf("%d\n", strlen(arr + 0));
	printf("%d\n", strlen(*arr));
	printf("%d\n", strlen(arr[1]));
	printf("%d\n", strlen(&arr));
	printf("%d\n", strlen(&arr + 1));
	printf("%d\n", strlen(&arr[0] + 1));
    return 0;
}

大家有想法吗?接下来给出来解释:

int main()
{
	//字符数组
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", strlen(arr));//随机值
	printf("%d\n", strlen(arr + 0));//随机值
	printf("%d\n", strlen(*arr));//err 错误写法
	printf("%d\n", strlen(arr[1]));//err 错误写法
	printf("%d\n", strlen(&arr));//随机值
	printf("%d\n", strlen(&arr + 1));//随机值
	printf("%d\n", strlen(&arr[0] + 1));//随机值
	return 0;
}

大家一定很好奇,随机值是什么意思,我们在前提知识说过,strlen遇到\0才会停止,而我们数组里没有\0,那么函数就会在内存中一直找,直到找到\0,所以就是随机值。那err错误写法又是怎么回事?这是因为strlen传参是指针,而当对*arr,解引用的时候,得到的为首元素,此时已经不是一个地址了,此时传参为'a',ASCII码值为97,此时将97作为地址传参,那么strlen从97开始统计长度时候,就非法访问内存了,所以这是一个错误写法。 那我们接下来再来看一段测试代码,这一次大家根据自己的思考再想一次答案。

int main()
{
	char arr[] = "abcdef";
	printf("%d\n", strlen(arr));
	printf("%d\n", strlen(arr + 0));
	printf("%d\n", strlen(*arr));
	printf("%d\n", strlen(arr[1]));
	printf("%d\n", strlen(&arr));
	printf("%d\n", strlen(&arr + 1));
	printf("%d\n", strlen(&arr[0] + 1));
	return 0;
}

 大家这次答案对了吗?来核对一下吧!

int main()
{
	char arr[] = "abcdef";
	printf("%d\n", strlen(arr));//6
	printf("%d\n", strlen(arr + 0));//6
	printf("%d\n", strlen(*arr));//err
	printf("%d\n", strlen(arr[1]));//err
	printf("%d\n", strlen(&arr));//6
	printf("%d\n", strlen(&arr + 1));//随机值
	printf("%d\n", strlen(&arr[0] + 1));//5
	return 0;
}

(二)指针习题

习题一

int main()
{
 int a[5] = { 1, 2, 3, 4, 5 };
 int *ptr = (int *)(&a + 1);
 printf( "%d,%d", *(a + 1), *(ptr - 1));
 return 0;
}

结合我们之前学过的内容,大家自己判断一下这个题目的答案.

 

习题二

//由于还没有讲解结构体,这里告知结构体的大小是20个字节
struct Test
{
	int Num;
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}*p=(struct Test*)0x100000;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
	printf("%p\n", p + 0x1);
	printf("%p\n", (unsigned long)p + 0x1);
	printf("%p\n", (unsigned int*)p + 0x1);
	return 0;
}

这道题有一些小小的难度,有一个比较关键的点,大家想清楚就很简单。

当p为结构体指针时,此时+1,加的应该是一个结构体,也就是20个字节,将其转化为16进制,打印出来就是0x1000014.

而将p强制类型转化为整型时,那么此时+1就是真正意义上的加1,结果就为0x100001.

最后一种情况,p强制类型转化为整型指针时,此时加一相当于跳过4个字节,那么结果就是0x100004,我们来核对一下。

习题三

int main()
{
	int a[4] = { 1, 2, 3, 4 };
	int* ptr1 = (int*)(&a + 1);
	int* ptr2 = (int*)((int)a + 1);
	printf("%x,%x", ptr1[-1], *ptr2);
	return 0;
}

 我们用vs,它内存储存是小端储存,我们描述它在内存中按以下方式储存,那么&a就是整个数组的地址,&a+1应该是一个数组指针,强制转化为整型指针,此时应指向图示位置,移动了一个数组,那么当a转化为int类型时,+1就应该仅仅只走一个字节,也就是下图所指位置,所以当ptr1[-1]打印时,就相当于打印04 00 00 00,且以十六进制打印;*ptr2,应该打印00 00 00 02吗?那你就进入陷阱了,不要忘了我们是小端储存,所以应该是02 00 00 00.那么我们来看一下实际运行结果:

 

 

 习题四

int main()
{
	int a[3][2] = { (0, 1), (2, 3), (4, 5) };
	int* p;
	p = a[0];
	printf("%d", p[0]);
	return 0;
}

这个题目就更有意思了,我们一起来看一下,难道在数组中储存的是0,1,2,3, 4,5吗?那你又调入陷阱了,这是逗号表达式,最终看右边的值,所以在数组中储存应该是1,3,5 ,0,0,0;a[0]为首行元素,数组名又代表首元素地址,所以此时a[0]就是a[0][0];此时p也指向首元素地址,那么p[0]当然就是1了;

今天的题目就是这么多,欢迎大家在评论区讨论! 

  • 11
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值