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

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 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


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

热门文章

最新文章

推荐镜像

更多