前言
上期我们介绍了strlen、sizeof的各种用法,本期带大家来学习指针类的笔试题。大家也要多多思考才行。
指针笔试题
首先我们来了解两点:
%p是打印地址
%x是以16进制形式打印
题一
#include<stdio.h>
int main()
{ //程序的结果是什么?
int a[5] = { 1, 2, 3, 4, 5 };
int *prt = (int *)(&a + 1);
printf( "%d,%d", *(a + 1), *(prt - 1));
return 0;
}
输出:2,5
分析:
我们知道 &a+1 里 &a 取出的是整个数组地址(&a的类型是 int(*)[5] ,该类型大小为5个字节长度) +1 是跳过整个数组,指向的位置如图:
前面加个 (int *) 是强制类型转换的意思,将 int( * )[5] 类型强制转换为 int 类型,那么该类型的变量加一减一所跳过长度就是4字节(上期有讲过指针变量加一减一步长)
因此,prt - 1 所指向的位置如下图:
解引用就得到 5 ,(a + 1)==>a[1] ,也就是2.
题二
我们来看一道关于结构体指针的例题:
#include<stdio.h>
struct Test
{
int Num;
char* pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//假设 p 的值为0x100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
p = (struct Test*)0x100000; //因为0x100000的类型是int型,所以要强制类型转换
printf("%p\n", p + 0x1);
printf("%p\n", (unsigned long)p + 0x1);
printf("%p\n", (unsigned int*)p + 0x1);
return 0;
}
本题考查指针加一步长多少?
输出:
分析:
p = (struct Test*)0x100000;
printf("%p\n", p + 0x1); //00100014
//指针 p 所指向的类型是 struct Test ,该类型占20字节单位,因此加一跳过20字节。
printf("%p\n", (unsigned long)p + 0x1); //00100001
//强制类型转换为 unsigned long 类型,就是一个整数,整数加一就是加一个字节
printf("%p\n", (unsigned int*)p + 0x1); //00100004
//强制类型转换为 unsigned int* 类型,p就指向一个无符号整型,占4个字节,加一就是加4个字节
题三
#include<stdio.h>
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;
}
输出:4,2000000
分析:
int a[4] = { 1, 2, 3, 4 };
int* ptr1 = (int*)(&a + 1); //&a取出整个数组地址,加一跳过整个数组,强制类型转换为int *型,prt1指针指向下图1位置
int* ptr2 = (int*)((int)a + 1); //将首元素地址强制类型转换为整型,加一跳过一个字节,再转换回来,int*类型解引用访问4个字节,见图二
printf("%x,%x", ptr1[-1], *ptr2); //prt1[-1]==>*(prt1-1),prt1-1指向下图1位置
图一:
图二:
题四
#include <stdio.h>
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) }; 数组的初始化内容有逗号表达式,实际上数组初始化的是1,3,5
//如果要将上数全部存进去,可以这样写{{0,1},{2,3},{4,5}},或者{0,1,2,3,4,5}
int* p;
p = a[0];
printf("%d", p[0]);
return 0;
}
输出:1
解析:
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int* p;
p = a[0]; //将a[0][0]存放入p中
printf("%d", p[0]);
题五
#include <stdio.h>
int main()
{
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]);
return 0;
}
输出:
解析:
p的首地址指向a的首地址,我们将数组a画出来,指针p指向的数组是4个元素,如下:
所以,p[4][2]和a[4][2]如图:
&p[4][2] - &a[4][2],指针相减结果就是两地址间的元素个数,就是4,但是它是小的减大的所以是-4,以%p打印就是FFFFFFFC,以%d打印就是-4
题六
#include <stdio.h>
int main()
{
int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int* ptr1 = (int*)(&aa + 1);
int* ptr2 = (int*)(*(aa + 1)); //aa代表首元素地址即aa[0]的地址,+1就是a[1]的地址
printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));
return 0;
}
输出:10,5
不多解释了
题七
#include<stdio.h>
int main()
{
char* a[] = { "work","at","alibaba" };
char** pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
输出:at
分析:a[0]存放‘w’的地址,a[1]存放‘a’的地址,a[2]存放‘a’的地址,如图(两个a地址不同,由首字符地址可以找到该字符串)
pa++,原来pa指向a[0],现在指向a[1],解引用得到a[1]的内容即地址,打印字符串得到 at
题八(稍难)
注意题中的++、- - 哦!
#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);
printf("%s\n", *-- * ++cpp + 3);
printf("%s\n", *cpp[-2] + 3);
printf("%s\n", cpp[-1][-1] + 1);
return 0;
}
输出:
相信大家应该都有所疑惑吧,下面我们来分析一下:
char* c[] = { "ENTER","NEW","POINT","FIRST" };
由该代码可以画出下图:
char** cp[] = { c + 3,c + 2,c + 1,c };
char*** cpp = cp;
由此又可以得到下图:
printf("%s\n", **++cpp);
cpp先++,再解引用,++就改变了cpp里存放的地址,使原本指向cp[0]的指针++变为指向cp[1],如图:
解引用得到c[2]的地址,解引用得到字符‘P’的地址,打印就是:POINT
printf("%s\n", *-- * ++cpp + 3);
cpp在原来基础上再++,如图:
cpp解引用得到cp[2],–cp[2]就是将cp[2]里的地址–,所以cp[2]里地址指向了c[0],如图。解引用得到的地址指向字符‘E’,加3个字节就指向了字符E,打印字符串为ER
printf("%s\n", *cpp[-2] + 3);
经由几次++后,cpp指向cp[2] ,cpp[-2]可以写出*(cpp-2) ,得到cp[0],也就是c[3]的地址,再解引用得到字符‘F’的地址,加3得到‘S’的地址,打印字符串得到 ST
printf("%s\n", cpp[-1][-1] + 1);
cpp[-1][-1] 可以写成*(*(cpp-1)-1) ,cpp-1得到cp[1]的地址,解引用得到c[2]的地址,再-1得到c[1]的地址,解引用得到字符‘N’的地址,加一字节,得到字符‘E’的地址,打印字符串,得到EW
好了关于指针的系列就告一段落了,相信大家应该收获满满吧,下期见了~