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

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析DNS,个人版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 指针和数组笔试题解析(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


相关文章
|
4天前
|
存储 算法 搜索推荐
深入解析String数组的操作与性能优化策略
深入解析String数组的操作与性能优化策略
|
10天前
|
存储 C++
有关【指针运算】的经典笔试题
有关【指针运算】的经典笔试题
14 4
|
8天前
|
编译器 C语言
指针进阶(数组指针 )(C语言)
指针进阶(数组指针 )(C语言)
|
10天前
|
C语言
【C语言】:详解函数指针变量,函数指针数组及转移表
【C语言】:详解函数指针变量,函数指针数组及转移表
14 2
|
10天前
|
C语言
【C语言】:详解指针数组,数组指针及(二维)数组传参(2)
【C语言】:详解指针数组,数组指针及(二维)数组传参(2)
10 1
|
2天前
|
存储 算法 搜索推荐
深入解析String数组的操作与性能优化策略
深入解析String数组的操作与性能优化策略
|
5天前
|
存储 Java 数据库
解析和使用String数组的方法
解析和使用String数组的方法
|
7天前
|
存储 C语言
C语言中的多级指针、指针数组与数组指针
C语言中的多级指针、指针数组与数组指针
7 0
|
7天前
|
存储 C语言
C语言数组指针详解与应用
C语言数组指针详解与应用
13 0
|
9天前
|
存储 JavaScript 前端开发
JavaScript——JavaScript基础:数组 | JavaScript函数:使用、作用域、函数表达式、预解析
在JavaScript中,内嵌函数可以访问定义在外层函数中的所有变量和函数,并包括其外层函数能访问的所有变量和函数。①全局变量:不在任何函数内声明的变量(显式定义)或在函数内省略var声明的变量(隐式定义)都称为全局变量,它在同一个页面文件中的所有脚本内都可以使用。函数表达式与函数声明的定义方式几乎相同,不同的是函数表达式的定义必须在调用前,而函数声明的方式则不限制声明与调用的顺序。③块级变量:ES 6提供的let关键字声明的变量称为块级变量,仅在“{}”中间有效,如if、for或while语句等。
26 0

推荐镜像

更多