指针进阶
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了;
今天的题目就是这么多,欢迎大家在评论区讨论!