数组、指针练习题及解析(含笔试题目讲解)(二)

本文涉及的产品
云解析 DNS,旗舰版 1个月
云解析DNS,个人版 1个月
全局流量管理 GTM,标准版 1个月
简介: 数组、指针练习题及解析(含笔试题目讲解)(二)

笔试题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);//0x00 00 00 04    0x02 00 00 00
  return 0;
}

程序分析:

这道题在小端存储的计算机上运行的前提下进行讲解。大小端介绍详见前几期博客的讲解连接如下:数据在内存中的存储_清水加冰的博客-CSDN博客

已知计算机为小端存储,那数组a在内存中存储情况就如下图所示:

1转化为16进制就是0x00 00 00 01,在小端机器中存储方式如上图所示。

ptr1为(int*)(&a+1)

&a为int (*)[4]类型,&a+1也是int (*)[4]类型,(int*)(&a+1)就是将&a+1的类型强制类型转换为int*类型(指针类型不同,+1-1所跳过的空间大小不同)。

那ptr1[-1]就等价于:*(ptr1-1),那ptr1-1所指向位置也就是元素4,如下图:

在解引用输出就是0x4(%x输出16进制)。

ptr2 = (int *)((int)a + 1),a是数组首元素地址,将a强制类型转换为int类型,我们先假设a的地址为0x0014ff20,转换为整形,还是0x0014ff20,只不过这里的16进制表示的是数字,那(int)a + 1也就是正常的整数相加,(int)a + 1=0x0014ff21,再将(int)a + 1转换为int*类型,那此时(int)a + 1,也就是a数组首元素地址跳过了一个字节的空间。如下图所示:

然后输出解引用的值,(int)a + 1从整形又被转换为int*类型的指针,整形指针解引用需要4个字节的空间,于是就从(int)a + 1指向的位置向后找4个字节空间的数据,转换为整形。

即00 00 00 02,又因为存储时是小端存储,所以取出时的顺序应该是:02 00 00 00,转换16进制也是0x02 00 00 00

笔试题4

#include <stdio.h>
int main()
{
  int a[3][2] = { (0, 1), (2, 3), (4, 5) };
  int *p;
  p = a[0];
  printf( "%d", p[0]);//1
return 0;
}

分析:

a是一个二维数组,进行了初始化,但它实质上只初始化了三个值,即1,3,5。这里需要注意,初始化使用的是逗号表达式,如果想一行一行初始化应该用{0,1}。所以这里有坑需要看仔细。

 

整形指针变量p=a[0],一般情况下,我们可以把a[0]理解为二维数组的第一行,但这里注意p是一个整形指针,并且赋值的时候没有&a[0],这里的a[0]就相当于是第一行数组的数组名,指向的是数组a[0][0]的地址。p[0]等价于*(p+0),也就是*p,所以结果是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]);//0xFFFFFFFC     -4
  return 0;
}

分析:

p是一个int(*)[4];类型的指针,p = a,a是一个5行5列的二维数组,那a就是第一行的地址,a是int(*)[5]类型,在赋值时会发生类型转换。我们注意,a走一步是5个整数空间,p走一步是4个整形空间。

我们来看一下p[4][2]的位置,a[4][2]的位置。

它们之间相差了4个字节。我们知道数组在存储地址时是从低地址到高地址,那我们就可以知道&p[4][2] - &a[4][2]以%d的形式输出就是一个-4。

负4的二进制序列为:

原码:10000000000000000000000000000100

反码:11111111111111111111111111111011

补码:1111 1111 1111 1111 1111 1111 1111 1100

当我们以%p(输出地址)的形式打印时,计算机就会将补码当作地址输出

1111 1111 1111 1111 1111 1111 1111 1100

  F      F       F      F      F      F     F      C

也就是0xFFFFFFFC。

笔试题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));//10    5
  return 0;
}

分析:

aa是一个二维数组,&aa取出的是二维数组的地址,&aa+1跳过的是整个二维数组。aa是二维数组首元素的地址,aa+1跳过的是5个整形空间。并且它们都被强制类型转换为int*类型。

ptr1 - 1和ptr2-1也就是ptr1和ptr2向前跳过4个字节(一个整形空间),所以*(ptr1 - 1), *(ptr2 - 1)也就是10和5。

笔试题7

#include <stdio.h>
int main()
{
char *a[] = {"work","at","alibaba"};
char**pa = a;
pa++;
printf("%s\n", *pa);//at
return 0;
}

分析:

单个字符串时我们可以这样写:char *p=“abcdef”,那当多个字符串呢?

上述题目中就是一个字符指针数组。

a是一个字符指针数组,a[0]存放的是w的地址,a[1]存放的是a(at)的地址,a[2]存放的是a(alibaba)的地址。

char**pa = a;,pa是一个二级指针,pa指向的是a[0],现在pa++之后,pa指向的就是a[1]。

*pa就是a[0],指向的是at中a的地址,而%s输出时需要一个字符串地址才能输出,所以输出结果是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);//POINT
printf("%s\n", *--*++cpp+3);//ER
printf("%s\n", *cpp[-2]+3);//ST
printf("%s\n", cpp[-1][-1]+1);//EW
return 0;
}

这道题于上到题目类似,但这道题难度更高一些。

分析:

我们先根据三个变量的初始化画出它们的关系图:

**++cpp,先是cpp++,此时cpp指向的就是cp[1],**就是两次解引用,第一次解引用是cp[1],再解一次引用就是C[2],C[2]是POINT中P的地址,那么输出的就是POINT

*--*++cpp+3,这个看起来很复杂,也间接的考察了优先级,先是++cpp,由于cpp经过了一次++操作,所以这里++操作之后cpp指向的是cp[2],再解引用就是cp[2],然后再对cp[2]进行--操作,这时cp[2]指向的就是C[0],解引用就是ENTER中E的地址,再+3,指向的就是第二个E,输出也就是ER

*cpp[-2]+3,cpp[-2]等价于*(cpp-2),那么*cpp[-2]+3也就可以写成**(cpp-2)+3,这里我们是先进行cpp-2操作,减2之前,cpp指向的是cp[2],cpp-2指向的就是cp[0],进行一次解引用是cp[0]也就是C+3,在解引用一次就是FIRST中F的地址,在+3,就是S的地址,那么输出就是ST

cpp[-1][-1]+1,这里我们在次转换一下,转换为*(*(cpp-1)-1)+1,首先看cpp,在上一个操作中cpp并没有进行++或--操作,所以此时cpp指向的依然是cp[2],cpp-1指向的就是cp[1],解引用就是cp[1](C+2),然后是*(cp[1]-1)+1,cp-1指向的是C[1],解引用后就是NEW中N的地址,+1指向的就是E的地址,所以最终输出是EW


总结

好了本期内容到此结束,希望这些内容能够对您的编程之路有所帮助。最后,感谢您阅读本文并参与博主提供的练习题。如果您有任何疑问或建议,请随时在评论区留言,博主将尽快回复您。感谢阅读!

相关文章
|
2天前
|
存储 算法 搜索推荐
深入解析String数组的操作与性能优化策略
深入解析String数组的操作与性能优化策略
|
1天前
|
存储 算法 搜索推荐
深入解析String数组的操作与性能优化策略
深入解析String数组的操作与性能优化策略
|
3天前
|
存储 Java 数据库
解析和使用String数组的方法
解析和使用String数组的方法
|
7天前
|
存储 JavaScript 前端开发
JavaScript——JavaScript基础:数组 | JavaScript函数:使用、作用域、函数表达式、预解析
在JavaScript中,内嵌函数可以访问定义在外层函数中的所有变量和函数,并包括其外层函数能访问的所有变量和函数。①全局变量:不在任何函数内声明的变量(显式定义)或在函数内省略var声明的变量(隐式定义)都称为全局变量,它在同一个页面文件中的所有脚本内都可以使用。函数表达式与函数声明的定义方式几乎相同,不同的是函数表达式的定义必须在调用前,而函数声明的方式则不限制声明与调用的顺序。③块级变量:ES 6提供的let关键字声明的变量称为块级变量,仅在“{}”中间有效,如if、for或while语句等。
25 0
|
15天前
|
JSON 资源调度 Kubernetes
实时计算 Flink版操作报错合集之解析JSON数组时,遇到报错,该怎么解决
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
|
19天前
|
算法
【经典LeetCode算法题目专栏分类】【第7期】快慢指针与链表
【经典LeetCode算法题目专栏分类】【第7期】快慢指针与链表
|
19天前
|
算法 容器
【经典LeetCode算法题目专栏分类】【第1期】左右双指针系列:盛最多水的容器、接雨水、回文子串、三数之和
【经典LeetCode算法题目专栏分类】【第1期】左右双指针系列:盛最多水的容器、接雨水、回文子串、三数之和
|
23天前
|
存储 SQL 算法
LeetCode 题目 117:填充每个节点的下一个右侧节点指针 II
LeetCode 题目 117:填充每个节点的下一个右侧节点指针 II
|
23天前
|
存储 SQL 算法
LeetCode 题目 116:填充每个节点的下一个右侧节点指针
LeetCode 题目 116:填充每个节点的下一个右侧节点指针
|
23天前
|
存储 算法 数据挖掘
LeetCode 题目 88:双指针\直接\递归\插入排序\归并排序 实现合并两个有序数组
LeetCode 题目 88:双指针\直接\递归\插入排序\归并排序 实现合并两个有序数组

推荐镜像

更多