C语言数组和指针笔试题(五)(一定要看)

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

感谢各位大佬对我的支持,如果我的文章对你有用,欢迎点击以下链接

🐒🐒🐒个人主页

🥸🥸🥸C语言

🐿️🐿️🐿️C语言例题

🐣🐓🏀python

指针运算笔试题解析

题目1

#include <stdio.h>
  int main()
  {
    int a[5] = { 1, 2, 3, 4, 5 };
    int* ptr = (int*)(&a + 1);
    printf("%d,%d", *(a + 1), *(ptr - 1));
    return 0;
  }
  //程序输出的结果是什么

解析

a[5]是一个整形类型的数组,&a是取的整个数组的地址,因此&a+1就是跳过整个数组,(int*)是强制类型转换,是将&a+1强制转换成int类型的指针(&a+1也可以写成int(*)[5])

*(a+1)中的a是数组首元素的地址,a+1=&a[1],解引用结果就是a[1]=2

ptr-1这里就要好好说一下了,我们用一个图来表示

如图因为&a+1是跳过整个数组,因此ptr的地址在a[4]之后,与a[4]相差4个字节,而ptr-1是跳过1个元素,也就是往前跳过4个字节,因此ptr-1的地址其实就是&a[4],解引用之后就是a[4]=5

结果

题目2

//在X86环境下,假设结构体的大小是20个字节,程序输出的结构是什么
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;
}

解析

0x开头的是16进制,因此0x1其实就是1,p是结构体指针,因为结构体指针+1是跳过一个结构体的大小,而结构体大小是20个字节,又因为是16进制,所以结果是0x100014

,(unsigned long)p是将p强制类型转换为无符号的long,所以p+1就是0x100001

而printf(“%p\n”, (unsigned int*)p + 0x1),因为是将p转换为无符号整形指针,所以+1就是跳过4个字节,就是0x100004

因为是16进制的地址,所以需要4个字节,结果是

0x00100014

0x00100001

0x00100004

结果

题目3

#include <stdio.h>
  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;
  }

解析

int* ptr1 = (int*)(&a + 1)和第一题是一样的所以就不解释了

int* ptr2 = (int*)((int)a + 1)中(int)a是将a强制转换为整形类型,因为整形类型的运算和数学运算是差不多的,因此这里的+1其实就数学上的+1,举个例子假如a的地址是0x0012ff40,通过转换这个地址对应的数字是1244992,所以(int)a+1其实就是1244992+1=1244993,然后将这个结果强制类型转换为整形指针,所以转换之后的地址就是0x0012ff41

我们知道内存存储是分大小端的,这里VS是用小端存储

当用%x打印是02 00 00 00中2前面的0不会打印,所以结果是2000000

而*ptr打印就是4

结果

题目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]);
    return 0;
  }

解析

这是一个二维数组,但是要注意数组的元素,我们可以看到数组中用括号括起来了两个数组,这里需要用到逗号表达式, (,)其实只有最后一位有效,(0,1)就是1,因此二维数组我们可以这样写,a[3][2]={1,3,5};因为是三行二列,而{ }中只有三个元素,剩下的既全为0,a[0]在上一篇博客已经讲了,是一维数组{1,3}的数组名,因为p=a[0],所以p[0]=a[0][0],最后结果就应该是1

结果

题目5

假设环境是x86环境,程序输出的结果是什么
#include <stdio.h>
  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;
  }

解析

(p)[4]是一个数组指针,因为p=a中a是数组名表示数组首元素的地址,所以a其实就是int( * )[5],而p是int()[4]

因为p中只有4个元素,所以p+1就是跳过4个整形元素,因此绿色是p+4所包含的元素,红色就是a[4][2],对于&p[4][2] - &a[4][2]就是-4,用%p来打印的话就需要将-4的补码写出来

-4的原码:10000000 00000000 00000000 00000100

反码: 111111111 111111111 111111111 111111011

补码: 111111111 111111111 111111111 111111100

因为是打印的%p是地址,所以将补码看成16进制来打印,结果是FFFFFFFC

而用%d打印的话其实就是-4

结果

题目6

#include <stdio.h>
  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;
  }

解析

&aa+1中&aa是整个二维数组的地址,因此&aa+1是跳过整个二维数组,(int*)将&aa+1强制转换为整形类型的指针

(aa+1)中aa是第一行的地址,(aa+1)就是aa[1],aa[1]就行相当于第二行的数组名,aa[1]=&aa[1][0],这里的强制类型转换其实是多余的,因为第二行第一个元素本来就是整形,取他的地址就是整形类型的指针

所以*(ptr1-1)就是10,*(ptr2-1)就是5

结果

题目7

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

解析

这是一道阿里巴巴的笔试题

chara[]是一个数组,数组里面的类型都是char,数组括号里面都是三个字符串,但是这三个字符串是放不下,因为这个数组里面的都指针char*,字符串是放不进去的,所以放进去的是他们的地址,比如work存放在a中的地址是w的地址,at存放的则是a地址…

而pa存放的是a的首元素地址,pa++后就是a的第二个元素如图

结果

题目8

#include <stdio.h>
  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;
  }

解析

上面是没有进行自增自减操作时每个数组元素对应的地址

char*c中存储的分别是

ENTER中E的地址

NEW中N的地址

POINT中P的地址

FIRST中F的地址

而cp中c+3就是第四个元素的地址,c+2就是第三个元素的地址…

cpp是将cp的首元素地址传给cpp,所以cpp就是c+3的地址

上图是经过自增自减操做后的结果,

printf(“%s\n”, **++cpp);对应的颜色是红

printf(“%s\n”, *-- * ++cpp + 3);对应的颜色是绿

printf(“%s\n”, *cpp[-2] + 3);对应的颜色是黑

printf(“%s\n”, cpp[-1][-1] + 1);对应的颜色是粉

我们一个一个的解析

第一次打印: **++cpp先将cpp进行自增,所以cpp中的cp从cp中第一个元素c+3变到第二个元素c+2 然后两次解引用之后就是POINT中P的地址,打印字符串就为POINT

第二次打印:由于++cpp将cpp中cp指向的地址永久变化了,所以第二次的++cpp就是从c+2变到了c+1,解引用再–,c+1就变成了c,所以c+1指向的地址就由1变成0,之后再解引用就是ENTER中E的地址,再+3后,就是箭头值的第二个E的地址,打印字符串就是ER

第三次打印:*cpp[-2]+3我们可以写成 * *(cpp-2)+3,由于上一次打印使cpp指向的c+2变成了c+1,所以这里的cpp-2就使cpp指向的第三个元素c+1变为第一个元素c+3(注意这里不是自增自减),再经过两次解引用后,就是FIRST中F的地址,+3就变成了箭头指向的S的地址,所以打印结果为ST

第四次打印cpp[-1][-1] + 1可以写成 *( *(cpp-1)-1)+1,cpp-1就是从第三个元素c+1变为第二个元素c+2,解引用再-1就是从原来指向的下标2变成指向的下标1,解引用再+1就是NEW中E的地址,打印的结果就是EW

结果

目录
相关文章
|
1月前
|
存储 NoSQL 编译器
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
指针是一个变量,它存储另一个变量的内存地址。换句话说,指针“指向”存储在内存中的某个数据。
96 3
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
|
1月前
|
存储 编译器 C语言
【C语言】指针大小知多少 ?一场探寻C语言深处的冒险 !
在C语言中,指针的大小(即指针变量占用的内存大小)是由计算机的体系结构(例如32位还是64位)和编译器决定的。
62 9
|
1月前
|
安全 程序员 C语言
【C语言】指针的爱恨纠葛:常量指针vs指向常量的指针
在C语言中,“常量指针”和“指向常量的指针”是两个重要的指针概念。它们在控制指针的行为和数据的可修改性方面发挥着关键作用。理解这两个概念有助于编写更安全、有效的代码。本文将深入探讨这两个概念,包括定义、语法、实际应用、复杂示例、最佳实践以及常见问题。
49 7
|
1月前
|
传感器 算法 安全
【C语言】两个数组比较详解
比较两个数组在C语言中有多种实现方法,选择合适的方法取决于具体的应用场景和性能要求。从逐元素比较到使用`memcmp`函数,再到指针优化,每种方法都有其优点和适用范围。在嵌入式系统中,考虑性能和资源限制尤为重要。通过合理选择和优化,可以有效提高程序的运行效率和可靠性。
121 6
|
2月前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
算法 编译器 程序员
C语言学习笔记—P11(数组<2>+图解+题例+三子棋游戏<初级>)
C语言学习笔记(数组<2>+图解+题例+三子棋游戏<初级>)
140 0
C语言学习笔记—P11(数组<2>+图解+题例+三子棋游戏<初级>)
|
存储 C语言
C语言学习笔记—P10(数组<1>+图解+题例)
C语言学习笔记(数组<1>+图解+题例)
153 0
|
C语言
C语言学习笔记——数组(二)
C语言学习笔记——数组
189 0
C语言学习笔记——数组(二)
|
C语言
C语言学习笔记——数组(一)
C语言学习笔记——数组
175 0
C语言学习笔记——数组(一)
|
机器学习/深度学习 C语言 编译器
【C语言】学习笔记4——数组
我直接把控制语句和循环跳过了。大致看了一下,讲得太繁琐了。这部分在后面用C写数据结构就可以练得很熟了。 1. 数组: 由数据类型相同得一系列元素组成。内存上是一片连续得存储单元。 2. 声明 int nums[5] // 内含5个int类型元素的数组 float ...
1008 0