指针必刷题(C语言指针就该这么学)【数据结构基础】【C语言指针必刷题】

news2024/12/24 10:04:33

前言:必备知识回忆

1.数组名的意义
   i.sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小

   ii.&数组名,这里的数组名表示整个数组,取出的是整个数组的地址

   iii.除此之外,所有的数组名都表示首元素的地址。

2.数组与指针的关系

对于数组和指针操作来说,a[i] 和 *(a+i) 是等效的,也就是说,a[i] 其实就是 *(a+i) 的简写形式。因此,p[0] 实际上就相当于 *(p+0),它表示 p 所指向的数组的第一个元素的值。

常见的数组和指针用法如下:

  • a[i]:表示数组 a 中的第 i 个元素的值。
  • *(a+i):表示 a+i 所指向的地址中存储的值(也就是 a 数组中第 i 个元素的值)。
  • p[i]:表示指针变量 p 所指向的数组中的第 i 个元素的值。
  • *(p+i):表示 p+i 所指向的地址中存储的值(也就是指针 p 所指向的数组中第 i 个元素的值)。

因此,p[0] 表示的就是指针变量 p 所指向的数组中的第一个元素的值,也就是 *(p+0)。

question one

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

打印结果:2    5 

 分析:&a取出的是整个数组的地址,&a+1指向了数组后面的地址,(int *)代表强制类型转换,把本身访问权限为20个字节强制转换为只能访问4个字节,因此后面的ptr-1指针指向了5的地址,解引用后得到的结果为5。对于*(a+1),a并没有与&操作符结合,也没有单独放在sizeof内部,因此只是代表首元素的地址,所以解引用后得到2。

question two

#include<stdio.h>
//由于还没学习结构体,这里告知结构体的大小是20个字节
struct Test
{
	int Num;
	char* pcName;
	short sDate;
	char chap[2];
	short sBa[4];
}*p;
//假设p 的值为0x100000。如下表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
	printf("%p\n",p + 0x1);
	printf("%p\n",(unsigned long)p + 0x1);
	printf("%p\n", (unsigned int *)p + 0x1);
}

打印结果:0x100014
                  0x100001 (在 32 位系统中)
                  0x100004

分析: 对于表达式p+0x1,指针p 加上0x1 后,会将指针向前移动 20 个字节,即一个Test 类型的大小。因此,表达式的值为 0x100014。对于表达式 (unsigned long)+0x1,指针 p 首先会强制转换为unsigned long类型,然后再加上 0x1,即将指针地址加上 1。因为unsigned long类型大小为 4 字节或 8 字节(取决于系统),所以结果可能有所不同。在 32 位系统中,该表达式的值为 0x100001。对于表达式(unsigned int *)p+ 0x1,指针 p 先转换成unsigned int *类型的指针,然后再加上 0x1,即将指针地址加上 4 个字节(因为unsigned int 类型的大小为 4 个字节)。因此,表达式的值为 0x100004

question three

#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);//注:%x是以十六进制的形式打印 
	return 0;
}

 打印结果:4,2000000

分析: ptr1[-1]可以转化为*(ptr1-1),&a+1取出的是整个数组的地址加一跳过了这个数组,前面的(int*)把这个数组指针强制类型转化为整形指针,所以ptr[-1]后退了四个字节,指针指向了4的前面,解引用得到4。                      地址a前面加上了(int)从一个指针强制转化为了整形,加1跳过了一个字节,而一个整形占四个字节,所以我们要在内存中展开再进行计算。最终得到结果0x02000000。

question four

#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

分析:这里其实存在陷阱,在初始化该二维数组时,花括号里面采用了逗号表达式进行初始化,而不是直接赋值,因此第一行代码可以转化为:int a[3][2]={ 1 , 3 , 5 };  题目中a[0]代表数组第一行的数组名,既没有&,也没有直接放在sizeof内部,因此这里就代表首元素1的地址,即把1的地址交给了指针变量p。p[0]可以转换为*(p+0),因此结果为1。

question five

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

 运行结果:FFFFFFFC,-4

 

分析:题目中把二维数组a的首元素地址放在了指针数组p中,也就是第一行的地址a[0],但是第一行如果完整存储在指针数组中需要的是int (*p)[5],虽然虽然类型有差异,但是还是可以赋过去,地址都是指向数组的起始位置。它的访问权限是16个字节,所以p+1跳过4个整形,以此类推……

p[4]可以转化为*(p+4),p[4][2]可以转化为*(*(p+4)+2).

由前面指针相减的知识可以知道,指针减指针结果为指针之间元素的个数,通过画图我们清晰的可以看到,两指针之间相差四个元素。又因为随着数组下标增长,地址由低到高变化,题目中是低地址减高地址,所以结果为负,得到-4,所以通过%d打印得到-4.    -4在内存中以补码的形式存储,二进制位表示为:11111111111111111111111111111100,以%p形式打印时为十六进制形式,转化为十六进制就是:FFFFFFFC

question six

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

 运行结果:10,5

分析:&arr取出的是整个数组的地址,加一就是跳过了整个数组,通过(int *)强制类型转换,使其只能访问四个字节,所以*(ptr1-1)结果为10。        *(aa+1)可以看作aa[1],也就是第二行数组的数组名,数组名既没有直接&,又没有放在sizeof内部,所以代表数组首元素的地址,就算没有前面的强制类型转换,其也是(int *)类型,所以*(ptr2-1)最终结果为5。

question seven

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

运行结果:at

 

分析:数组a是一个指针数组,数组的每个元素都是char* ,这里pa = a ;把数组a首元素的地址(即“work”的首字母w)放在了二级指针pa中,pa++后,指针指向了数组a的第二个元素(即“at”的首字母a)的地址,最后通过解引用pa,找到了a的地址,当使用 printf("%s", *pa) 打印 *pa 的内容时,它会从a的地址开始输出字符,直到遇到字符串结束符\0。因此最终结果为“at”。

question eight

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

 运行结果:POINT
                   ER
                   ST
                   EW

 

分析:解决这道题的方法就是画图,通过画图梳理表达式之间的关系,关系理清了,注意不要混淆,答案也就出来了。 ++操作符的优先级比*高,所以++cpp后,cpp就指向cp的第二个元素通过两次解引用,得到单词“POINT”的首字母P的地址,通过打印可以直接打印出来POINT,第一问得解。                      第二问:++cpp后,cpp指向了数组cp的第三个元素,通过解引用找到了指向字符"NEW"首字母的指针,又通过--操作,该指针又指向了"ENTER"首字符的地址,解引用后得到了“ENTER”首字符的地址,然后又+3,指针往后跳了三个字符,得到打印结果:ER。                        后面两问也是同样的道理,依次计算即可。


通过这部分的指针练习,你对指针的理解肯定更加深刻了,快去做个思维导图总结一下指针的内容吧,指针是数据结构的重要基础,一定不能草草放过去~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1317811.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于urllib库的网页数据爬取

实验名称&#xff1a; 基于urllib库的网页数据爬取 实验目的及要求&#xff1a; 【实验目的】 通过本实验了解和掌握urllib库。 【实验要求】 1. 使用urllib库爬取百度搜索页面。 2. 使用urllib库获取百度搜索的关键字搜索结果&#xff08;关键字任选&#xff09;。 实验原理及…

电子元器件介绍——二极管(四)

电子元器件介绍 文章目录 电子元器件介绍前言一、二极管的基础知识二、二极管的分类三、二极管的应用总结 前言 这一节我们看一下二极管。 一、二极管的基础知识 PN结&#xff1a;是指一块半导体单晶&#xff0c;其中一部分是P型区&#xff0c;其余部分是N型区。 在电场作用…

C++ Qt开发:自定义Dialog对话框组件

Qt 是一个跨平台C图形界面开发库&#xff0c;利用Qt可以快速开发跨平台窗体应用程序&#xff0c;在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置&#xff0c;实现图形化开发极大的方便了开发效率&#xff0c;本章将重点介绍自定义Dialog组件的常用方法及灵活运用。 在…

基于linux系统的Tomcat+Mysql+Jdk环境搭建(四)linux安装Mysql

1.切换到你需要安装mysql的路径 cd /root/usr/ 2.在线安装 安装网上的安装方式都有很多&#xff0c;可以自己百度一下 我们这里是自己搭建测试环境&#xff0c;可以直接选择在线安装&#xff0c;命令如下&#xff1a;yum install mysql-server&#xff0c; 但是我失败了 ┭┮…

在排序数组中查找元素的第一个和最后一个位置(Java详解)

一、题目描述 给你一个按照非递减顺序排列的整数数组 nums&#xff0c;和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。 如果数组中不存在目标值 target&#xff0c;返回 [-1, -1]。 你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。 示…

verilog语法进阶,时钟原语

概述&#xff1a; 内容 1. 时钟缓冲 2. 输入时钟缓冲 3. ODDR2作为输出时钟缓冲 1. 输入时钟缓冲 BUFGP verilog c代码&#xff0c;clk作为触发器的边沿触发&#xff0c;会自动将clk综合成时钟信号。 module primitive1(input clk,input a,output reg y); always (posed…

深度剖析JavaScript中冒泡和捕获机制、事件代理

JS事件传播的两种机制包括冒泡和捕获&#xff0c;下面将具体剖析它们之间本质的区别。 事件冒泡: 先触发子元素的事件&#xff0c;再触发父元素的事件。 创建一个 ul label 和 li label, 分别绑定一个父id 和 子 id, 再通过创建 script&#xff0c;去绑定各自的点击事件。 <…

Threejs利用着色器编写动态飞线特效

一、导语 动态飞线特效是可视化数据地图中常见的需求之一&#xff0c;鼠标点击的区块作为终点&#xff0c;从其他区块飞线至点击区块&#xff0c;附带颜色变换或者结合粒子动画 二、分析 利用创建3点来构成贝塞尔曲线&#xff0c;形成线段利用着色器材质来按照线段以及时间…

Linux---压缩和解压缩命令

1. 压缩格式的介绍 Linux默认支持的压缩格式: .gz.bz2.zip 说明: .gz和.bz2的压缩包需要使用tar命令来压缩和解压缩.zip的压缩包需要使用zip命令来压缩&#xff0c;使用unzip命令来解压缩 压缩目的: 节省磁盘空间 2. tar命令及选项的使用 命令说明tar压缩和解压缩命令 …

SE考研真题总结(三)

继续更新&#xff0c;今天准备连出两期该系列~ SE考研真题总结&#xff08;二&#xff09;https://blog.csdn.net/jsl123x/article/details/134857052?spm1001.2014.3001.5501 目录 一.简答题 二.代码大题 一.简答题 1.工程和科学的区别 科学是关于事物的基本原理和事实的…

代码随想录算法训练营 | day53 动态规划 1143.最长公共子序列,1035.不相交的线,53.最大子序和

刷题 1143.最长公共子序列 题目链接 | 文章讲解 | 视频讲解 题目&#xff1a;给定两个字符串 text1 和 text2&#xff0c;返回这两个字符串的最长公共子序列的长度。 一个字符串的 子序列 是指这样一个新的字符串&#xff1a;它是由原字符串在不改变字符的相对顺序的情况下…

Android动画(四)——属性动画ValueAnimator的妙用

目录 介绍 效果图 代码实现 xml文件 介绍 ValueAnimator是ObjectAnimator的父类&#xff0c;它继承自Animator。ValueAnimaotor同样提供了ofInt、ofFloat、ofObject等静态方法&#xff0c;传入的参数是动画过程的开始值、中间值、结束值来构造动画对象。可以将ValueAnimator看…

C#深拷贝效率对比

对于浅拷贝和深拷贝&#xff0c;前面的文章已经说明了。 C#浅拷贝和深拷贝数据-CSDN博客 本篇说一下&#xff0c;深拷贝的效率问题&#xff0c;效率一直是程序追求的&#xff0c;效率越高肯定越好&#xff0c;有时候功能是实现了&#xff0c;但是运行以及处理数据的效率非常低…

【算法】bfs与dfs算法解决FloodFill(洪流)问题(C++)

文章目录 1. 什么是FloodFill问题2. 用什么方法解决FloodFill问题3. 具体例题773.图像渲染200.岛屿数量695.岛屿的最大面积130.被围绕的区域 1. 什么是FloodFill问题 一般floodfill问题可以描述为&#xff1a;给定一个二维矩阵&#xff0c;其中每个元素代表一个像素点&#xf…

Python-flask 入门代码

python与pycharm安装 过程略&#xff0c;网上很多&#xff0c;记得为pycharm配置默认解释器 虚拟环境 pipenv # 全局安装虚拟环境 # 可加-U参数&#xff0c;明确全局安装&#xff0c;不加好像也可以? pip3 install pipenv #检查安装情况 pipenv --version # ---控制台输出…

跨域的解决方式(java后端)

文章目录 一、跨域介绍1、什么是跨域2、为什么会产生跨域3、禁止跨域的原因 二、简单请求和非简单请求1、简单请求1.1、什么时简单请求1.2、简单请求基础流程 2、非简单请求2.1、预检请求2.2、预检请求的回应2.3、浏览器的正常请求和回应 3、自定义跨域过滤器 三、解决方式1、C…

Java基础语法之抽象类和接口

抽象类 什么是抽象类 并不是所有的类都是用来描述对象的&#xff0c;这样的类就是抽象类 例如&#xff0c;矩形&#xff0c;三角形都是图形&#xff0c;但图形类无法去描述具体图形&#xff0c;所以它的draw方法无法具体实现&#xff0c;这个方法就可以没设计成抽象方法&…

003 Windows用户与组管理

Windows用户管理 一、用户账户 1、什么是用户账户 不同用户身份拥有不同的权限每个用户包含了一个名称和一个密码每个用户账户具有唯一的安全标识符查看系统中的用户 net user 安全标识符&#xff08;SID&#xff09; whoami /user 使用注册表查看 打开注册表命令regedi…

Sentinel使用详解

组件简介 Sentinel是阿里开源的一套用于服务容错的综合性解决方案。它以流量为切入点&#xff0c;从流量控制、熔断降级、系统负载保护等多个维度来保护服务的稳定性。Sentinel承接了阿里巴巴近10年的双十一大促流量的核心场景&#xff0c;例如秒杀、消息削峰填谷、集群流量控…

Java集合-12

Map的接口特点 key重复时会进行替换 package com.edu.map;import java.util.HashMap; import java.util.Map; import java.util.Set;SuppressWarnings({"all"}) public class Demo01 {public static void main(String[] args) {Map map new HashMap<>();map.…