C语言八道笔试题精讲带你掌握指针

目录
  • 题目一
  • 题目二
  • 题目三
  • 题目四
  • 题目五
  • 题目六
  • 题目七
  • 题目八

为了题目的准确性和我们一般学习过程中的习惯,这里所有的题目代码都是在 X86 环境(32 位平台)下运行的。

题目一

#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;
}
//程序的结果输出什么?

我们先研究 ptr 指针变量里面存储的是什么。&a+1 表示取出整个地址并向后跳跃一个数组类型的大小,也就是指向了数组最后一个元素 5 的后面一块数组类型大小的空间。

但是我们想要把这个地址交给指针变量 ptr 是不行的,因为类型不一样(&a+1 是一个数组指针类型,prt 是一个整形指针类型),所以我们在 (&a+1) 之前 (int*) 来强制类型转换。这样我们搞清楚了 ptr 指针变量里存的是什么。

那么现在来看输出部分。*(a+1) 是比较简单的,a 是首元素地址,a+1 是第二个元素地址,对其解引用得到第二个元素 2 ,这是毋庸置疑的答案。对于 *(ptr-1) ,我们知道 ptr 里面存放的地址位置,并且知道 ptr 是一个整形指针,ptr-1 相当于向前跳跃一个整形类型的大小,即指向数组最后一个元素 5 的位置。对其解引用就能得到元素 5 。

故最后的输出结果为: 2,5 。

题目二

#include <stdio.h>
struct Test
{
	int Num;
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}*p;
//如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
	printf("%p\n", p + 0x1);
	printf("%p\n", (unsigned long)p + 0x1);
	printf("%p\n", (unsigned int*)p + 0x1);
	return 0;
}
//程序最后输出什么?

我们注意观察结构体的定义,定义了一个结构体指针变量 p 。

p+0x1 非常简单,0x1 就是 1,只不过是以十六进制的格式书写的。这与我们经常习惯使用的指针计算一样,p 是一个结构体指针,那么 p+1 理当向后跳跃一个结构体类型的大小,即指向了结构体空间的后一个结构体空间的位置。并且我们知道 p 是一个全局变量,全局变量默认初始化为 0 ,那我们便可以知道,p=0 ,p+1=20 。而 %p 是一个十六进制的打印格式,所以结果输出:00000014 。

(unsigned long)p + 0x1 似乎更简单一些。p 类型转换成了一个长整形,那么就代表 p 不再是一个指针,它存放的 0 就是单纯意义上的数字。所以 0 + 0x1 就等于 00000001 。

(unsigned int*)p + 0x1 也非常简单。p 本是一个结构体指针,现在强转为整形指针,这就代表着 p 进行加减整数的时候步幅由 20 个字节 变成了 4 个字节。那么 p=0 ,p+1=4 是毋庸置疑的。输出的结果为:00000004 。

题目三

#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;
}
//程序的结果输出什么?

ptr1 的分析与题目一没有区别,ptr1 存放的是数组最后一个元素的后面一块数组类型大小的地址。并且 ptr1 是一个整形指针。

ptr1[-1] 可改写成 *(ptr-1) ,因为我们曾经说过,只要是数组下标的操作都是指针操作,数组下标的操作只是指针操作的简写。那么此时 ptr1[-1] 就应该指向数组最后一个元素 4 并对其解引用得到输出结果 00000004 。

(int*)((int)a+1) 如何理解呢?a 是数组名,那么它就指向了数组首元素的地址,再将其强制类型转换,那么 a 里面存放的地址就是单纯意义上的数字,我们在这个数字的基础上 +1 ,然后再强制类型转换成整形指针并存放在 ptr2 中。也就是说,a 本是指向数组首元素的地址,(int)a+1 就代表地址数 +1 ,地址数 +1 就说明如果是指针的话,它就会指向后一个字节。再强转为整形指针,那么此时 ptr2 指向的地址为:

再对其解引用就能找到红色方块那块空间的内容,因为是小端存储,那么输出的结果是:02000000 。

需要注意的是,本题使用的打印格式为 %x ,正儿八经的十六进制,会省略有效数字前的 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;
}
//程序的结果输出什么?

首先我们注意观察,二维数组 a 里面存放的是三个逗号表达式,我相信有人会在这里踩坑。即三个逗号表达式,那么二维数组 a 里面的内容应该是 1,3,5 ,0,0,0 (数组 a 有 6 个元素,内容不够后面自动补 0 )。

搞清了二维数组存放的是什么之后,我们来研究 a[0] 是什么。a[0] 是二维数组的第一个元素,这个元素是一个数组,也就是说,指针变量 p 存放的是二维数组中的第一个元素,这个元素是一个一维数组,即拿到了一个数组名。那么 p[0] 就很好理解了,p 是一个数组名,p[0] 就是数组的首元素,即 1 。故输出 1 。

题目五

#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 存放了二维数组 a 的首元素地址,这就不难分析了,p 指向的数组与 a 数组的存储内容一样,但是内存分配不一样。

我们把这两个空间重叠在一起:

可以看到,&p[4][2] 与 &a[4][2] 相差了 4 个元素,但因为 &p[4][2] 的地址位置比较低,所以两个相减是一个负数,即 -4 。但我们要以 %p 的形式打印,%p 是一个无符号的数,所以我们便得知结果应该是一个非常大的十六进制数。那么 -4 的原反补为:

所以前者的输出结果为:fffffffc 。

我们知道,指针(地址)相减,得到的是相差的元素个数,上面我们也讲过。那么 %d 是一个有符号的打印格式,所以输出的结果就是 -4 。

题目六

#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;
}
//程序的结果输出什么?

*(ptr1-1) 的结果为 10 应该没什么意见吧?

*(aa+1) 可改写成 aa[1] ,也就是二维数组的第二个元素,这个元素是一个一维数组,也就是说得到了一个数组名。将这个数组名强转为整形指针存放在指针变量 ptr2 中,这时候就知道了 ptr2 的具体指向位置。(二维数组在内存空间中是连续存放的!)

*(ptr2-1) 就是将 ptr2 向前跳跃一个整形类型的大小,并解引用,即找到了元素 5 ,所以输出结果为 5 。

题目七

#include <stdio.h>
int main()
{
	char* a[] = { "work","at","alibaba" };
	char** pa = a;
	pa++;
	printf("%s\n", *pa);
	return 0;
}
//程序的结果输出什么?

指针数组 a 存放了三个字符串常量(关于字符串的常量在往期的博客中已经介绍过),使用二级指针 pa 来指向这个数组(char** pa=a;),这就表明 pa 变量存放的是 a 数组的首元素地址。pa++ 指向了数组的第二个元素,*pa 毋庸置疑得到了字符串常量 "at" 的首元素地址,再通过 %s 打印,就能的得到 at 。

题目八

#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;
}
//程序的结果输出什么?

我们先分析一下 c、cp、cpp 里面存放的是什么东西。指针数组 c 里面存放有四个字符串常量。指针数组 cp 里面存放的是四个字符串常量的地址。三级指针 cpp 存放的是 指针数组 cp 的首元素地址。那么草图可以画成:

**++cpp ,因为结合性的原因,我们要先计算 ++cpp,那么此时 cpp 指向 cp 的第二个元素的地址,然后解引用得到了 cp 中第二个元素,这个元素又是指向 c 的第三个元素,然后再解引用得到 c 的第三个元素,这个元素是字符串常量 "POINT" 的首字符地址,通过 %s 的形式打印得到 POINT 。一定要注意,++ 这个操作是 cpp 参与运算了,所以下次运算时会从计算后的位置开始。

*--*++cpp+3 ,还是先计算 ++cpp ,此时 cpp 指向 cp 的第三个元素的地址,然后解引用得到 c+1 ,然后 -- ,c+1 就变成了 c ,然后解引用得到 c 的首元素地址,在此地址的基础上 +3 ,就拿到了字符串常量 "ENTER" 的第四个字符的地址,然后通过 %s 的形式打印得到 ER 。

*cpp[-2]+3 可改写为 *(*(cpp-2))+3 ,也就是说,此时 cpp 指向了 cp 的首元素地址,解引用得到了 c+3 ,c+3 是指向 c 的第四个元素的地址,解引用便得到了 c 的第四个元素,这个元素是字符串常量 "FIRST" 的首元素地址,在此地址的基础上 +3 便得到字符串常量第四个字符的地址,%s 通过这个地址打印便得到 ST 。(此时 cpp 是没有实质运算的)

cpp[-1][-1]+1 可改写为 *(*(cpp-1)-1)+1 ,此时 cpp 指向 cp 的第二个元素的地址,解引用得到 c+2 ,然后 -1 得到 c+1 ,即指向了 c 的第二个元素的地址,解引用便得到了 c 的第二个元素,这个元素是字符串常量 "NEW" 的首元素地址,然后 +1 便指向了字符串常量的第二个字符的地址,通过 %s 形式打印便得到了 EW 。

到此这篇关于C语言八道笔试题精讲带你掌握指针的文章就介绍到这了,更多相关C语言指针内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C语言简明分析指针与引用的具体用法

    目录 1.指针 2.引用 1.指针 在计算机中,数据是存放在内存单元中的,一般把内存中的一个字节称为一个内存单元.为了更方便地访问这些内存单元,可预先给内存中的所有内存单元进行地址编号,根据地址编号,可准确找到其对应的内存单元.由于每一个地址编号均对应一个内存单元,因此可以形象地说一个地址编号就指向一个内存单元.C 语言中把地址形象地称作指针. 主要就是两个运算符:&和*. & 表示求地址,*表示求地址中的值,*也可以用来定义指针(int *p表示整型指针): int a=1; int *

  • 带你分分钟玩转C语言指针

    目录 何为指针 数组指针 指针数组 字符串数组 数组指针的sao气操作 二级指针 函数指针 指针函数 文件指针 总结 何为指针 指针这玩意说白了,就是用来存储一个变量地址的东东 如图: (编辑器为vc2010) #include<stdio.h> void main(){ int a,*p; a=5; p=&a; printf("a=%d,p=%p,*p=",a,p,*p); getchar(); } 所以通过刚刚的情况,我们发现通过指针我们不仅可以获取一个变量的值

  • C语言超详细讲解宏与指针的使用

    目录 1.关于define 2.初识指针 (1)内存 (2)示例 (3)指针的使用示例 (4)指针变量的大小 1.关于define define是一个预处理指令,有两种用法,一种是用define定义常量:另外一种是define定义宏. 下面的例子为利用define定义常量 #define _CRT_SECURE_NO_WARNINGS #define MAX 1000 #include <stdio.h> int main() { printf("%d\n",MAX); r

  • C语言超详细讲解函数指针的运用

    目录 前言 计算器的例子 回调函数 转移表 前言 前面我们学习了各种各样的指针类型,有些指针可以说是稀奇百怪,特别是函数指针,有些朋友可能觉得,函数指针有些多余,调用函数为什么要用指针调用,直接调用不好吗? 接下来我们从具体的实例来回答同学们的问题,加深对函数指针的理解. 计算器的例子 接下来我们写一个简单的计算器程序,完成不同的计算功能比如加减乘除: #include <stdio.h> //相加函数 int add(int a, int b) { return a + b; } //相减函

  • C语言深入讲解指针与结构体的使用

    目录 1 啥是指针 1.1指针与指针变量 1.2总结 2 指针和指针类型 2.1指针+-整数 3 野指针 3.1 野指针的成因 1指针未初始化 2指针越界访问 3指针指向的空间释放 3.2 如何避免野指针的出现 4 二级指针 5 指针数组 6 结构体 6.1 结构的声明 6.2 结构体变量的定义和初始化 6.3 结构体的访问 6.4 结构体传参 1 啥是指针 刚刚接触指针的同学肯定会很懵逼,指针是啥啊?指南针哈哈,不和大家开玩笑,我们进行正题吧,指针是本质是就是地址,但我们要注意我们口头上常说的

  • C语言浅析指针的使用

    目录 指针 等价形式转换 函数指针 主函数 指针 指针是一个变量 (1) 作用:只能存储地址的值 (2) 大小:32位操作系统中占4字节:64位操作系统中占8字节 (3) 取地址:& (4) 运算:可进行加.减法操作 示例: 定义一个指针变量 p 初始化 赋值为 5 二级指针:指向指针的指针 int a; //一级指针变量 int *p = &a; *p = 5; //二级指针 int **k; //存储一级指针变量的地址 k = &p; 等价形式转换 *p <=> a

  • C语言简明介绍指针的使用

    目录 1. 指针类型 2. 野指针 3. 指针的运算 3.1 指针+-整数 3.2指针-指针 3.3 指针的关系运算 4. 指针数组 1. 指针类型 指针以字节为单位: 指针类型决定了解引用时能访问的空间的大小:也决定了指针的步长(指针+1走多远) 2. 野指针 指针未初始化 指针越界访问 指针指向的空间已释放 int* test() { int a = 10;//野指针 return &a; } int main(){ int* p = test(); //test函数里的a是局部变量,出函数

  • C语言清楚了解指针的使用

    目录 前言 字符指针 指针数组 数组指针 函数指针 前言 经过了指针的初步学习,我们了解了指针有以下特点: 1. 指针就是个变量,用来存放地址,地址唯一标识一块内存空间. 2. 指针的大小是固定的4/8个字节(32位平台/64位平台). 3. 指针是有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限. 4. 指针的运算. 今天将要从各种不同的指针的角度切入,使得我们对指针的了解更加深入. 字符指针 我们现在已经学会下面的这种指针的使用了: int main() { char

  • C语言八道笔试题精讲带你掌握指针

    目录 题目一 题目二 题目三 题目四 题目五 题目六 题目七 题目八 为了题目的准确性和我们一般学习过程中的习惯,这里所有的题目代码都是在 X86 环境(32 位平台)下运行的. 题目一 #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; } /

  • C语言全面细致精讲操作符的使用

    目录 前言 一.算术操作符 二.移位操作符 1.左移操作符 2.右移操作符 三.位操作符 四. 赋值操作符 复合赋值符 五.单目操作符 单目操作符介绍 1.sizeof 和 数组 2.++和–运算符 六.关系操作符 七.逻辑操作符 八.条件操作符 九.逗号表达式 十.下标引用与函数调用和结构成员 1. []下标引用操作符 2. ()函数调用操作符 3. 访问一个结构的成员 前言 本次文章我们要讲各种操作符的介绍,内容非常详细,大家拭目以待哦! 首先介绍操作符的种类 操作符分类: 算术操作符 移位

  • Java 八道经典面试题之链表题

    目录 第一题 移除链表元素 第二题 反转链表 第三题 链表的中心结点 第四题 倒数第k个结点 第五题 合并两个有序链表 第六题 链表分割 第七题 判断是否回文 第八题 相交链表 第一题 移除链表元素 给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 . 输入:head = [1,2,6,3,4,5,6], val = 6 输出:[1,2,3,4,5] 这道题还是比较简单的我们需要让删除的节点的前一个结点指向删

  • C语言字符串函数与内存函数精讲

    目录 strlen strcpy strcat strcmp strncpy strncat strncmp strstr strtok strerror tolower\toupper memcpy memmove memcmp memset strlen 获取字符串长度. strlen - size_t strlen( const char *string ); 1.字符串以’\0’作为结束标志,strlen函数返回的是在字符串中’\0’前面出现的字符个数(不包含’\0’). 2.参数指向的

  • C语言全面细致精讲关键字的使用

    目录 1.switch 深入理解 2.如何正确的使用 case 3.循环语句while for do while深度讲解 4.continue的作用是什么 5.goto真的没人用了吗 6.void 到底是何方妖怪 1.switch 深入理解 学习过C语言的小伙伴可能知道,switch 也是选择结构的一种,是具有判定能力的语法结构,那么他们都必须具备:判定+分支功能! 我们知道 if 可以搭配 else if 或 else 来实现分支功能,那么我们 switch 如何实现分支功能呢?这样,我们先来

  • C语言常见的指针笔试题解析

    目录 笔试题1 笔试题2 笔试题3 笔试题4 笔试题5 笔试题6 笔试题7 笔试题8 在我们学习指针之后,应该在实际应用中去理解和掌握它,毕竟实践才是检验真理的唯一标准,我们以后在找工作的过程中免不了会遇到与指针相关的试题,本篇文章可以帮助我们提前了解一些常见的指针考点.在学习这篇文章之前可以根据需要对指针进行简要复习. 注:本篇文章所有代码均在X86环境下运行. 笔试题1 #include <stdio.h> int main() { int a[5] = { 1,2,3,4,5 }; in

  • C语言数组和指针的问题一道非常值得深思的笔试题

    最近笔试就遇到下面这道题,谁都不敢说自己的C/C++能有多精通,当然,工作一久,很多老毛病也就容易犯了,所以说,理论是真的很重要的,下面这道题,说实话还是挺基础的,虽然当时笔试被我给猜对了,但还是要深究一下具体的转换细节. 如题: #include <stdio.h> int main(void) { char *str[] = {"ab","cd","ef","gh","ij","k

  • 深入解答关于Python的11道基本面试题

    前言 本文给大家深入的解答了关于Python的11道基本面试题,通过这些面试题大家能对python进一步的了解和学习,下面话不多说,来看看详细的介绍吧. 一.单引号,双引号,三引号的区别 分别阐述3种引号用的场景和区别 1),单引号和双引号主要用来表示字符串 比如: 单引号:'python' 双引号:"python" 2).三引号 三单引号:'''python ''',也可以表示字符串一般用来输入多行文本,或者用于大段的注释 三双引号:"""python&

  • 2021最新Android笔试题总结美团Android岗职能要求

    目录 Android开发面试的几部分 1.基础知识 Java部分: Android部分: 数据结构与算法: 计算机基础: 设计模式: 开源项目: 重点项目经历 技术以外的东西 自我驱动和追求 沟通和协作 我的面经总结 Android Java 计算机网络 数据结构及算法 题外话 优秀的战士需要出色的剑才能战斗.同样,在现代IT中,每个编码人员都需要最好的Android开发人员工具来提高他们的技能和效率.在Android应用程序开发这个残酷的竞争行业中,只有优秀的开发人员才能生存下去.您需要向客户

  • JS经典正则表达式笔试题汇总

    本文实例总结了JS经典正则表达式笔试题.分享给大家供大家参考,具体如下: 一.复习字符串的传统操作 如何获取一个字符串中的数字字符,并按数组形式输出,如 dgfhfgh254bhku289fgdhdy675gfh 输出[254,289,675] 分析:循环用charAt()的方法获取到每一个子字符串,判断他是不是在0~9之间,是就把他扔到准备好的数组里 var str="dgfhfgh254bhku289fgdhdy675gfh"; findNum(str); function fin

随机推荐