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

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

笔试题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月前
使用指针访问数组元素
【10月更文挑战第30天】使用指针访问数组元素。
41 3
|
2月前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
2月前
|
存储 C语言 计算机视觉
在C语言中指针数组和数组指针在动态内存分配中的应用
在C语言中,指针数组和数组指针均可用于动态内存分配。指针数组是数组的每个元素都是指针,可用于指向多个动态分配的内存块;数组指针则指向一个数组,可动态分配和管理大型数据结构。两者结合使用,灵活高效地管理内存。
|
2月前
|
容器
在使用指针数组进行动态内存分配时,如何避免内存泄漏
在使用指针数组进行动态内存分配时,避免内存泄漏的关键在于确保每个分配的内存块都能被正确释放。具体做法包括:1. 分配后立即检查是否成功;2. 使用完成后及时释放内存;3. 避免重复释放同一内存地址;4. 尽量使用智能指针或容器类管理内存。
|
2月前
|
存储 NoSQL 编译器
C 语言中指针数组与数组指针的辨析与应用
在C语言中,指针数组和数组指针是两个容易混淆但用途不同的概念。指针数组是一个数组,其元素是指针类型;而数组指针是指向数组的指针。两者在声明、使用及内存布局上各有特点,正确理解它们有助于更高效地编程。
|
19天前
|
存储 程序员 C++
深入解析C++中的函数指针与`typedef`的妙用
本文深入解析了C++中的函数指针及其与`typedef`的结合使用。通过图示和代码示例,详细介绍了函数指针的基本概念、声明和使用方法,并展示了如何利用`typedef`简化复杂的函数指针声明,提升代码的可读性和可维护性。
55 0
|
2月前
|
存储 人工智能 算法
数据结构实验之C 语言的函数数组指针结构体知识
本实验旨在复习C语言中的函数、数组、指针、结构体与共用体等核心概念,并通过具体编程任务加深理解。任务包括输出100以内所有素数、逆序排列一维数组、查找二维数组中的鞍点、利用指针输出二维数组元素,以及使用结构体和共用体处理教师与学生信息。每个任务不仅强化了基本语法的应用,还涉及到了算法逻辑的设计与优化。实验结果显示,学生能够有效掌握并运用这些知识完成指定任务。
61 4
|
2月前
使用指针访问数组元素
【10月更文挑战第31天】使用指针访问数组元素。
53 2
|
2月前
|
算法 索引
单链表题+数组题(快慢指针和左右指针)
单链表题+数组题(快慢指针和左右指针)
41 1
|
3月前
|
人工智能 前端开发 JavaScript
拿下奇怪的前端报错(一):报错信息是一个看不懂的数字数组Buffer(475) [Uint8Array],让AI大模型帮忙解析
本文介绍了前端开发中遇到的奇怪报错问题,特别是当错误信息不明确时的处理方法。作者分享了自己通过还原代码、试错等方式解决问题的经验,并以一个Vue3+TypeScript项目的构建失败为例,详细解析了如何从错误信息中定位问题,最终通过解读错误信息中的ASCII码找到了具体的错误文件。文章强调了基础知识的重要性,并鼓励读者遇到类似问题时不要慌张,耐心分析。