指针和数组笔试题解析(2)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 指针和数组笔试题解析(2)

四、指针笔试题

1、笔试题一

下面程序的输出结果是什么:(32位平台下)

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

2020062310470442.png

解析:

&a取出整个数组的地址,+1跳过整个数组,然后强制转化为int*类型后赋给int*的变量ptr,此时ptr指向的位置如图所示;

*(a+1):a代表首元素地址,+1指向第二个元素,解引用得到2;

*(ptr-1):ptr指针的类型是int*,由于指针类型决定指针解引用访问的字节个数,所以ptr-1指向第五个元素,解引用得到5;

2020062310470442.png

2、笔试题二

下面程序的输出结果是什么:(32位平台下)

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

2020062310470442.png

解析:

p+0x1 <==> p+1,因为此结构体的大小为20个字节,由指针类型的意义可知,这里+1跳过20个字节(一个Test结构体的大小),十进制的20等价于十六进制的14,所以 0x100000 + 0x14 = 0x100014;


p原本代表结构体Test的地址,但是这里将其强制类型转换为size_t,那么它就变为了普通的整数,整数+1就是+1,所以 0x100000 + 0x000001 = 0x100001;


p原本代表结构体Test的地址,但是这里将其强制类型转换为unsigned int*,由于指针的类型决定了指针加减整数跳过的字节个数,所以这里+1跳过4个字节(一个整形的大小),所以 0x100000 + 0x100004 = 0x100004;

3、笔试题三

在小端机器下,下面程序的输出结果是什么:(32位平台下)

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

2020062310470442.png

解析:

(int*)(&a + 1):和第一题类似,&a的地址,然后+1跳过整个数组,此时a指向数组后面紧挨的空间(如图所示),然后强制转化为int*类型后赋给int*的变量ptr1;


(int*)((int)a + 1):a代表首元素的地址,将a强转为int类型,+1之后强转赋给int*类型的的ptr2,因为这里的a被强转为整数,所以+1跳过一个字节,此时ptr2的指向如图所示;


ptr1[-1] <==> *(ptr1-1):因为ptr1是是整形指针,所以ptr1-1的指向如图,解引用访问一个整形大小,得到a[3] = 4;


*ptr2:如图,ptr2此时指向a[0]的第二个字节,因为ptr2是整形指针,所以解引用访问图中紫色区域,得到 0x02000000(数据以小端存储的模式存入,也会已小端存储的模式拿出);

2020062310470442.png

对于大小端字节序的理解\概念有问题的同学,可以看看我之前的文章:深度剖析数据在内存中的存储

4、笔试题四

下面程序的输出结果是什么:(32位平台下)

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

2020062310470442.png

解析:

这里有一个陷阱:二维数组初始化的内容是三个逗号表达式,所以其实a的初始化内容是 { 1,3,5 },其余自动初始化为0,所以p[0]为1;

5、笔试题五

下面程序的输出结果是什么:(32位平台下)

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

2020062310470442.png

解析:

p = a:a是二维数组的数组名,代表首元素的地址,而二维数组的首元素是第一行,所以a的类型是 int (*)[5],这里我们把a赋给p,p的类型是 int (*)[4],二者类型不同;


虽然p和a的类型不一样,但是由于二维数组和一维数组一样,在空间中都是连续存储的,所以这里并不会报错,只是会报一个警告;


所以&p[4][2]取出的是二维数组第18个元素的地址,&a[4][2]取出的是二维数组第22个元素的地址,地址相减得到的是相差元素的个数,即-4;


在内存中存储的是-4的补码即11111111 11111111 11111111 11111100,同时这里以%p(地址)的形式打印,由于地址是无符号数,所以直接取出-4的补码,即十六进制的FFFF FFFFC;

6、笔试题六

下面程序的输出结果是什么:(32位平台下)

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

2020062310470442.png

解析:

(int*) (&aa + 1):&aa取出整个二维数组的地址,+1跳过整个二维数组,然后强转为int*类型赋给ptr1;


(int *) (*(aa + 1)):aa代表首元素地址,二维数组首元素是第一行,+1跳过一行得到整个第二行的地址,然后解引用得到第二行,也相当于得到第二行的数组名,而数组名又代表首元素地址,所以*最终得到的是aa[1][0]的地址,之后再强转赋给ptr2;


*(ptr1 - 1), *(ptr2 - 1):ptr1 和 ptr2 都是整形指针,-1减去四个字节,ptr1指向aa[1][4],解引用得到10,ptr2指向aa[0][4],解引用得到5;

7、笔试题七

下面程序的输出结果是什么:(32位平台下)

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

2020062310470442.png

解析:

如图:2020062310470442.png

a是一个指针数组,其中a[0]存放的是字符’w’的地址,a[1]存放的是字符’a’的地址,a[2]存放的是字符’a’的地址;


pa = a:a代表首元素的地址,而首元素是字符’w’的地址,所以pa是一个二级指针,指向a[0],pa++指向a[1];


*pa:对pa解引用得到a[1],a[1]中存放的是字符’a’的地址,以字符串的形式打印得到"at";

8、笔试题八

下面程序的输出结果是什么:(32位平台下)

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

2020062310470442.png

解析:

各变量的指向关系如图:

2020062310470442.png

**++cpp:++cpp指向cp[1],解引用找到cp[1],再解引用找到字符’P’的地址,以字符串的形式打印得到’POINT’,此时由于自增操作符的副作用,cpp指向cp[1];

20200623104134875.png

*-- * ++cpp + 3:++cpp指向cp[2],解引用找到cp[2]中的内容c+1,前置–使得cp[2]中的内容变为c,此时cp[2]不再指向字符’N’的地址,而是指向字符’E’的地址;解引用找到字符’E’的地址,+3找到字符串’ENTER’中第二个’E’的地址,以字符串的形式打印得到’ER’;此时由于自增操作符的副作用导致cpp指向cp[2],cp[2]指向字符’E’;

2020062310470442.png

*cpp[-2] + 3 <==> *(*(cpp-2)) + 3:cpp-2指向cp[0],解引用找到cp[0]中的内容c+3,再解引用找到字符’F’的地址,+3找到字符串’FIRST’中字符’S’的地址,以字符串的形式打印得到’ST’;此时因为cpp-2并没有副作用,所以cpp仍然指向cp[2];

20200623104134875.png

cpp[-1][-1] + 1 <==> *(*(cpp - 1) - 1) + 1:cpp-1指向cp[1],解引用找到cp[1]中的内容c+2,再-1变为c+1,解引用找到c+1处的内容,即字符’N’,+1找到字符串"NEW"中的字符’EW’;此时cpp仍然指向cp[2],cp[1]中的内容仍然为c+2;

2020062310470442.png


相关文章
|
15天前
使用指针访问数组元素
【10月更文挑战第30天】使用指针访问数组元素。
30 3
|
14天前
使用指针访问数组元素
【10月更文挑战第31天】使用指针访问数组元素。
28 2
|
23天前
|
算法 索引
单链表题+数组题(快慢指针和左右指针)
单链表题+数组题(快慢指针和左右指针)
28 1
|
1月前
|
人工智能 前端开发 JavaScript
拿下奇怪的前端报错(一):报错信息是一个看不懂的数字数组Buffer(475) [Uint8Array],让AI大模型帮忙解析
本文介绍了前端开发中遇到的奇怪报错问题,特别是当错误信息不明确时的处理方法。作者分享了自己通过还原代码、试错等方式解决问题的经验,并以一个Vue3+TypeScript项目的构建失败为例,详细解析了如何从错误信息中定位问题,最终通过解读错误信息中的ASCII码找到了具体的错误文件。文章强调了基础知识的重要性,并鼓励读者遇到类似问题时不要慌张,耐心分析。
|
1月前
|
存储
如何使用指针数组来实现动态二维数组
指针数组可以用来实现动态二维数组。首先,定义一个指向指针的指针变量,并使用 `malloc` 为它分配内存,然后为每个子数组分配内存。通过这种方式,可以灵活地创建和管理不同大小的二维数组。
|
1月前
|
存储
如何通过指针数组来实现二维数组?
介绍了二维数组和指针数组的概念及其区别,详细讲解了如何使用指针数组模拟二维数组,包括定义与分配内存、访问和赋值元素、以及正确释放内存的步骤,适用于需要动态处理二维数据的场景。
|
1月前
|
存储 算法 C语言
C语言:什么是指针数组,它有什么用
指针数组是C语言中一种特殊的数据结构,每个元素都是一个指针。它用于存储多个内存地址,方便对多个变量或数组进行操作,常用于字符串处理、动态内存分配等场景。
|
1月前
魔法指针 之 二级指针 指针数组
魔法指针 之 二级指针 指针数组
19 1
|
1月前
|
存储
一篇文章了解区分指针数组,数组指针,函数指针,链表。
一篇文章了解区分指针数组,数组指针,函数指针,链表。
18 0

推荐镜像

更多