目录
10. 指针相关的笔试题及解析
10.1 笔试题1
题目:
-
#include
<stdio.h
>
-
int main()
-
{
-
int a[
5]
= {
1,
2,
3,
4,
5 };
-
int
* ptr
= (int
*)(
&a
+
1);
-
printf(
"%d,%d",
*(a
+
1),
*(ptr
-
1));
-
return
0;
-
}
-
/
/程序的结果是什么?
运行截图:
文字解析:
a是数组首元素的地址,类型为int *,数组首元素的地址+1,跳过一个整型元素,即数组第二个元素的地址,对其进行解引用就是a[1],所以输出结果为2。
&a是数组的地址,数组地址+1就是跳过一个数组大小的地址,此时类型为int (*)[5],对其进行强制类型转换后,类型变为了(int*),-1就回退一个整型元素的大小,此时指针指向的元素是5。
画图解析:
10.2 笔试题2
代码:
-
程序的结果是什么?
-
#include
<stdio.h
>
-
struct
Test
-
{
-
int Num;
-
char
* pcName;
-
short sDate;
-
char cha[
2];
-
short sBa[
4];
-
}
*p;
-
/
/假设p 的值为
0x
100000。 如下表表达式的值分别为多少?
-
/
/已知,结构体
Test类型的变量大小是
20个字节
-
int main()
-
{
-
p
=
0x
100000;
-
printf(
"%p\n", p
+
0x
1);
-
printf(
"%p\n", (unsigned long)p
+
0x
1);
-
printf(
"%p\n", (unsigned int
*)p
+
0x
1);
-
return
0;
-
}
运行截图:
代码解析:
p的类型为(struct Test*),所以+1后跳过12个字节,在进行(unsigned long)强制类型转换后,+1后跳过1个字节,在进行(unsigned int*)强制类型转换后,加1跳过一个整型的大小,即4个字节。
10.3 笔试题3
代码:
-
int main()
-
{
-
int a[
4]
= {
1,
2,
3,
4 };
-
int
*ptr
1
= (int
*)(
&a
+
1);
-
int
*ptr
2
= (int
*)((int)a
+
1);
-
printf(
"%x,%x", ptr
1[-
1],
*ptr
2);
-
return
0;
-
}
运行截图:
画图解析:
代码解析:
&a得到的是数组a的地址,+1即跳过一个数组的大小,对其进行强制类型转换后类型变成了int*,ptr[-1]=ptr+(-1),因为此时的类型是int*,所以回退的是一个整型的空间,因为其类型是int*,一次能够访问4个字节,所以对其进行输出后的结果是4。
对a进行强制类型转换后a的类型就变成了int,+1就是简单的加1,跳过一个字节,跳过一个字节在上图中体现的就是跳过两位数组,此时指向了00,因为其类型是int*,一次能够访问4个字节,所以取出的数组就是00 00 00 02,因为其是小端存储,并且是按照16进制形式进行打印,所以打印结果是20 00 00 00。
10.4 笔试题4
代码:
-
#include
<stdio.h
>
-
#include
<stdio.h
>
-
int main()
-
{
-
int a[
3][
2]
= { (
0,
1), (
2,
3), (
4,
5) };
-
int
* p;
-
p
= a[
0];
-
printf(
"%d", p[
0]);
-
return
0;
-
}
运行截图:
代码解析:
此处有一个需要注意的点,就是{}中的()中的代码是逗号表达式,即代码等价于int a[3][2] = {1,3,5},即最终数组初始化的结果就像下方画的图一样!
a[0]是第一行的数组名,而此处的数组名代表的是第一行第一个元素的地址 ,即a的地址,p[0]代表的是*(p+0),p+0之后并未发生改变,仍然是第一行第一个元素的地址,且此时的类型也是int*,对其进行解引用之后就是数组第一行第一个元素,即1。
注意:
1、()中的内容是逗号表达式!
2、p[0] = *(p+0)。
10.5 笔试题5
代码:
-
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;
-
}
运行截图:
代码解析:
10.6 笔试题6
代码:
-
#include
<stdio.h
>
-
int main()
-
{
-
int aa[
2][
5]
= {
1,
2,
3,
4,
5,
6,
7,
8,
9,
10 };
-
int
* ptr
1
= (int
*)(
&aa
+
1);
-
int
* ptr
2
= (int
*)(
*(aa
+
1));
-
printf(
"%d,%d",
*(ptr
1
-
1),
*(ptr
2
-
1));
-
return
0;
-
}
运行截图:
代码解析:
&aa+1就是跳过一个二维数组,指向10的后面,此时的类型是int (*)[2][5],强制类型转换后类型变成int*。此时使ptr1-1就是回退一个整型元素,指向元素10,解引用之后得到元素10。
aa是二维数组的数组名,数组名是数组首元素的地址,即第一行的地址,aa+1跳过一行,此时是数组第二行的地址,对其进行解引用之后就是第二行的数组名,再进行请值类型转换后类型变成了int *(当然,此处就算不进行强制类型转换类型也是int*,因为第二行数组名是一维数组,数组名代表首元素的地址,其类型自然就是int*)。此时使ptr2-1就是回退一个元素,指向元素5,解引用之后得到元素5。
10.7 笔试题7
代码:
-
#include
<stdio.h
>
-
int main()
-
{
-
char
* a[]
= {
"work",
"at",
"alibaba" };
-
char
** pa
= a;
-
pa
+
+;
-
printf(
"%s\n",
*pa);
-
return
0;
-
}
运行截图:
代码分析:
如何理解char *a = { "work","at","alibaba" };这行代码呢?
其实这行代码等价于下面的这三行代码:
a[0] = "work";
a[1] = "at";
a[2] = "alibaba";
其中a[0]、a[1]、a[2]的类型都是字符指针类型,都是存储的后面字符串常量的首地址。
a代表的是数组首元素的地址,即a[0]的地址,pa的类型是char**,即二级指针,+1后跳过一个char *类型,即pa++后pa指向的是a[1],即存储的是a[1]的地址,对其进行解引用之后得到的就是字符串常量中"at"中a的地址。
10.8 笔试题8
代码:
-
#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;
-
}
运行截图:
代码分析:
对于**++cpp,这个表达式来说,*和++优先级相同,但是结合性是自右向左,所以先进行++操作,++后cpp指向cp[1],即图中的c+2,对其进行一次解引用之后可以得到cp[1]中存放的地址,二次解引用之后可以得到c[2]中存放的即"POINT"首元素即P的地址,所以打印结果应该是POINT。
对于*--*++cpp+3这个表达式来说,,首先是cpp自增1,cpp指向了图中的c+1(前一个表达式中已经自增过一次了),此时进行解引用之后得到了c+1,然后进行自减,此时就变成了c,然后再对其进行解引用之后得到了"ENTER"中字符E的地址 ,此时再进行加3,然后得到的就是E的地址,所以输出结果为ER。
对于*cpp[-2]+3这个表达式来说,这个表达式等价于**(cpp-2)+3,首先是cpp-2,因为之前cpp指向的是图中的c+1,-2之后指向的是c+3,对其进行解引用之后得到的是c[3]的地址,然后进行解引用之后就得到了FIRST的地址,然后对其进行+3,就得到了S的地址,所以打印结果为ST。
对于cpp[-1][-1]+1这个表达式来说,这个表达式等价于*(*(cpp-1)-1)+1,首先cpp-1,从指向c+1这个位置指向 了c+2这个位置,解引用得到的就是c+2,然后进行-1,变成了了c+1,然后解引用之后得到了c[1]的内容,其实是N的地址,N的地址+1得到的就是E的地址,所以输出的结果是EW。