指针笔试题

最近学习到了指针进阶篇章,了解了多种高级指针的用法(也可能一点也不高级)。本文就对指针的常见试题做一个解析。

理论依据

1.指针的基本概念:

1. 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
2. 指针的大小是固定的4/8个字节(32位平台/64位平台)。
3. 指针是有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限。

2.数组各部分的含义:

例:int arr[2]={1,2}

arr代表数组首元素地址。即arr=&arr[0]。同时arr可以理解为一个指向此数组的指针。指针中存放着数组首元素的地址,解引用可得到首元素*arr=arr[0]。

[]为下标引用操作符,在调用阶段,下标内的数字决定了引用数组的元素编号。

arr[1]=*(arr+1)

int为数组元素的类型,决定了数组开辟的大小,也决定了arr的类型。

指针试题

1.

int main()
{
  int a[5] = { 1, 2, 3, 4, 5 };
  int *ptr = (int *)(&a + 1);
  printf( "%d,%d", *(a + 1), *(ptr - 1));
  return 0;
}
//程序的结果是什么?

分析:

程序第一行创建了5个int类型元素的数组。

第二行将&a+1,获得了跳过该数组的地址,并强制转换为int *类型。将&a+1赋给了ptr。

a+1中,a为首元素地址,由于是int类型,+1跳过四个字节,获得&a[1]。将其解引用后得到a[1]。即2。

ptr-1得到a[4]的地址&a[4]。解引用得到a[4]。即5

结论:

答案为:2,5。

2.

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;
printf("%p\n", p + 0x1);
printf("%p\n", (unsigned long)p + 0x1);
printf("%p\n", (unsigned int*)p + 0x1);
return 0;
}

分析:

%p是将数字按照16进制打印出来。

p结构体类型的指针,值为0x100000

此题考查不同类型的指针p加减整数的步长

第一行:

p是结构体类型指针,步长为20,因此p+1=0x100000+0x00014=0x10014。打印后结果为0x00010014

第二行:

将p强制转换为长整型,注意:此时p不再是指针型变量,在计算时不需考虑步长,直接计算即可。

第三行:

将p强制转换成unsigned int*类型,此时加减的步长为4。

结论:

答案为:0x00010014,0x00100001,0x00100004。

3.

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;
}

分析:

数组a在空间中的存储模型(16进制):

第二行:

&a是数组指针,&a=int (*)[4]a,&a+1跳过一个数组的长度即16个字节。再将&a+1强制转换成int*类型。

第三行:

a是数组首元素地址,原本类型为int*。现在将其强行转换成int类型,此时+1则是在原有数值+1,之后将其转换为int *类型,即转换为地址。此时指针跳过一个字节。

第四行:

ptr[-1]=*(ptr-1) 

结论:

答案为:0x00000004,0x20000000。

4.

nt 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]即二维元素第一行的首元素地址赋给p。

第四行:

p[0]=*(p+0)

结论:

答案为:1。

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;
}

分析:

a是数组首元素地址,类型为int (*)[5]。类型为int*,p是数组指针,类型为int(*)[4]。

p=a将a的值赋给p。此时a和p属于不同类型的变量,强行赋值会导致编译器警告,但仍会将a的值放到p中,使p指向a数组的首元素。

&p[4][2]=*(p+4)+2。&a[4][2]=*(a+4)+2。其中p+4得到跳过16个整形的地址。a+4跳过20个整形的地址。

*(p+4)拿到了一个一维数组首元素的地址,*(p+4)+2从得到该元素跳过两个元素的地址

 因此,*(a+4)+2-*(p+4)+2=4。

结论:

答案为:fffffffc,-4。

6.

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));
  printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1));
  return 0;
}

第二行:

&aa表示数组地址,&aa+1跳过二维数组。将其转为int*类型赋给ptr1

第三行:

aa表示数组首元素地址。aa+1=aa[1],*(aa+1)得到第二行第一列的元素地址,即6的地址。

第四行:

ptr1的类型为int*,ptr1-1跳过4个字节,得到arr[1][4]的地址,解引用后得到a[1][4]。

ptr2的类型为int*,ptr2-1跳过4个字节。

结论:

答案为:10,5。

7.

int main()
{
char *a[] = {"work","at","alibaba"};
char**pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}

第一行:

a[]为指针数组,指针类型为char*,此种定义方式相当于char* a[0]=char* p0="work"。即把w的地址放到p中.

第二行:

将数组的首元素地址放到二级指针pa中。

第三行:

由于pa的类型为char*,因此pa++增加一个字节。

第四行:

*pa得到数组第二个元素的地址。

结论:

答案为:at。

8.

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;
}

第一行:

c[]中包含四个存放字符串首字母地址的指针。

第二行:

在cp中存放数组c中的元素地址。

第三行;

在cpp中存放数组cp的首元素地址。

 第四行:

先给cpp++,得到&cp[1],之后经过两次解引用,第一次得到cp[1],即&c[2]。第二次解引用得到c[2],即指向“point"首字母的指针。经%s打印出POINT。

第五行:

先给cpp++,原先cpp指向cp[1],++后指向cp[2]。解引用后得到cp[2]。cp[2]=&c[1]。&c[1]-1得到&c[0]。解引用后得到c[0],即指向”ENTER"中E的指针。此时+3,指针指向第二个E。因此打印结果为:ER

第六行:

cpp[-2]=*(cpp-2)=cp[0]。解引用后为*cp[0]。*cp[0]=c[3]。c[3]+3得到FIRST中S的地址。

打印结果为:ST

第七行:

cpp[-1][-1]=*(*(cpp-1)-1)

cpp-1=&cp[1]

*(cpp-1)=cp[1]

cp[1]-1=&c[2]-1=&c[1]

*&c[1]=c[1]

c[1]+1得到NEWE的地址。

打印结果为:EW

结论:

答案为:POINT,ER,ST,EW

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值