C语言——指针题目

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

代码解析:

#include <stdio.h>
int main()
{
    int a[5] = { 1, 2, 3, 4, 5 };
    int* ptr = (int*)(&a + 1);
    //&a取出的是整个数组,&a+1既跳过了整个数组
    printf("%d,%d", *(a + 1), *(ptr - 1));//2,5
    //*(a+1)就是第二个元素2,*(ptr-1)就是第5个元素
    return 0;
}

🔒第二题

#include <stdio.h>
//涉及到结构体内存对齐问题,结构体大小为20个字节
struct Test
{
  int Num;
  char* pcName;
  short sDate;
  char cha[2];
  short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
int main()
{
    printf("%p\n", p + 0x1);
    printf("%p\n", (unsigned long)p + 0x1);
    printf("%p\n", (unsigned int*)p + 0x1);
    return 0;
}

代码解析:

#include <stdio.h>
//涉及到结构体内存对齐问题,结构体大小为20个字节
struct Test
{
  int Num;
  char* pcName;
  short sDate;
  char cha[2];
  short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
int main()
{
    printf("%p\n", p + 0x1);//0x10014
    //p是结构体,结构体大小为20个字节,对于16进制来说,20相当于14
    //所以结果为0x100014
    printf("%p\n", (unsigned long)p + 0x1);//0x100001
    //p被强制转换成为了unsigned long类型,结果为0x100001
    printf("%p\n", (unsigned int*)p + 0x1);//0x100004
    //无符号整型指针+1跳过一个整型变量
    //相当于+4
    return 0;
}

我们不妨来试一试运行的结果:深度还原成题目

🔒第三题

#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;
}

代码解析:

#include <stdio.h>
int main()
{
      int a[4] = { 1, 2, 3, 4 };
      //假设以小端存储方式
      //01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00
      int* ptr1 = (int*)(&a + 1);
      //&a+1相当于跳过了整个数组,到达了4之后的位置,然后强制转化成int*
      //ptr1也指向那里
      int* ptr2 = (int*)((int)a + 1);
      //这里的a被强制转化成int型,整型+1就是+1,差了一个1就1个字节
      //然后又被强转为int*,所以ptr2指向第一个元素的第二个字节位置处,与第一个元素差一个字节
      //ptr2是一个整型指针
      printf("%x,%x", ptr1[-1], *ptr2);//4,2000
      //ptar[-1]可以理解为*(ptr1+(-1)),又ptr1是整型指针4个字节,-1跳到0x 00 00 00 04
      //对ptr2解引用后向后访问4个字节,又以%x打印,所以是0x 02 00 00 00
      return 0;
}

方便理解,画个图:

可能有人不信,看一下运行结果:

🔒第四题

#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;
}

代码解析:

仔细看数组,里面的内容是()而不是{},这说明了这是一个逗号表达式,所以相当于数组里面放了1,3,5

所以实际上二维数组存储的元素是:

#include <stdio.h>
int main()
{
    int a[3][2] = { (0, 1), (2, 3), (4, 5) };
    int* p;
    p = a[0];
    //a[0]代表首元素地址,就是1的地址
    printf("%d", p[0]);//1
    //p[0]可以看作*(p+0)就是1
    return 0;
}

运行一下结果:

这道题本身并不难,比较坑,我们要知道逗号表达式,知道实际上二维数组存放的元素是什么,这是解题的关键之处

🔒第五题

#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;
}

代码解析:

为了方便大家的理解,画了个图:

对于a[4][2]👇:

对于p👇:

p被赋值为a,a是数组名首元素地址,就是a[0],但是a[0]是5个元素地址,但是p却是4个元素地址。类型存在差异.我们画图来理解p是怎么一回事:

p[4][2]:(就是黄色区域)

回到题目,指针-指针得到的是元素的个数,然后呢?一张图解决这道题:

我们不妨来看看运行结果:

🔒第六题

#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;
}

通过上面的练习之后,如果你真的彻底理解完之后,这道题就比较容易理解了:

对于二维数组:

1 2 3 4 5

6 7 8 9 10

为了方便理解,还是画图:

对于ptr1来说👇:

对于ptr2👇:

#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));//10,5
    return 0;
}

测试运行结果:

🔒第七题

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

这道题画图比较好理解:

对于a来说👉:

对于pa来说👉:

pa++就是指向下一位,指向at的位置,所以打印出来的结果为at

测试运行一下结果👉:

🔒第八题

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;
}

这道题是真的有趣,真的有趣

前面三句代码有点小乱,建议画图分析一下过程:

代码解析👇:

*++cpp:第一次,++cpp指向c+2,c+2指向point地址,第二*得到point

*–*++cpp+3:++cpp指向c+1的地址,解引用找到c+1,–就把c+1变成了c,c指向ENTER的地址,在解引用,得到ENTER,+3就是ENTER第三个位置开始,得到ER。

*cpp[-2]+3:相当于* *(cpp-2)+3.首先我们要知道经过前面两个前置++之后,cpp指向了第3个元素的地址,现在-2相当于打回原形,指向cp第一个元素的地址,第一次解引用指向c+3,在一次解引用得到FIRST,+3指向ST,所以结果为ST

cpp[-1][-1]+1:相当于*(*(cpp-1)-1)+1:上面的那次并没有自增自减,所以还是第三个元素的地址,cpp-1指向第二个元素的地址,解引用得到c+2,-1得到c+1,c+1就是NEW的地址,在解引用得到NEW,+1得到EW。

至此,这个代码的解析就到这里结束了

我们可以来测试运行一下结果:

总结

实际上,如果我们对指针的知识有了基础之后,这些就是水到渠成,这上面八道题目的练习,更是让你锦上添花,如虎添翼,训练并巩固了C指针的核心知识点,让我们对指针有了更深层次的认知。

相关文章
|
1月前
|
C语言
【c语言】指针就该这么学(1)
本文详细介绍了C语言中的指针概念及其基本操作。首先通过生活中的例子解释了指针的概念,即内存地址。接着,文章逐步讲解了指针变量的定义、取地址操作符`&`、解引用操作符`*`、指针变量的大小以及不同类型的指针变量的意义。此外,还介绍了`const`修饰符在指针中的应用,指针的运算(包括指针加减整数、指针相减和指针的大小比较),以及野指针的概念和如何规避野指针。最后,通过具体的代码示例帮助读者更好地理解和掌握指针的使用方法。
49 0
|
1月前
|
C语言
【c语言】指针就该这么学(3)
本文介绍了C语言中的函数指针、typedef关键字及函数指针数组的概念与应用。首先讲解了函数指针的创建与使用,接着通过typedef简化复杂类型定义,最后探讨了函数指针数组及其在转移表中的应用,通过实例展示了如何利用这些特性实现更简洁高效的代码。
18 2
|
1月前
|
C语言
如何避免 C 语言中的野指针问题?
在C语言中,野指针是指向未知内存地址的指针,可能引发程序崩溃或数据损坏。避免野指针的方法包括:初始化指针为NULL、使用完毕后将指针置为NULL、检查指针是否为空以及合理管理动态分配的内存。
|
1月前
|
C语言
C语言:哪些情况下会出现野指针
C语言中,野指针是指指向未知地址的指针,通常由以下情况产生:1) 指针被声明但未初始化;2) 指针指向的内存已被释放或重新分配;3) 指针指向局部变量,而该变量已超出作用域。使用野指针可能导致程序崩溃或不可预测的行为。
|
1月前
|
存储 C语言
C语言32位或64位平台下指针的大小
在32位平台上,C语言中指针的大小通常为4字节;而在64位平台上,指针的大小通常为8字节。这反映了不同平台对内存地址空间的不同处理方式。
|
1月前
|
存储 算法 C语言
C语言:什么是指针数组,它有什么用
指针数组是C语言中一种特殊的数据结构,每个元素都是一个指针。它用于存储多个内存地址,方便对多个变量或数组进行操作,常用于字符串处理、动态内存分配等场景。
|
1月前
|
存储 C语言
C语言指针与指针变量的区别指针
指针是C语言中的重要概念,用于存储内存地址。指针变量是一种特殊的变量,用于存放其他变量的内存地址,通过指针可以间接访问和修改该变量的值。指针与指针变量的主要区别在于:指针是一个泛指的概念,而指针变量是具体的实现形式。
|
1月前
|
C语言
C语言指针(3)
C语言指针(3)
14 1
|
1月前
|
C语言
C语言指针(2)
C语言指针(2)
15 1
|
1月前
|
程序员 C语言
【C语言】LeetCode(力扣)上经典题目
【C语言】LeetCode(力扣)上经典题目