面试指南:指针

目录

前言

指针基础

指针实战

数组与指针比较

sizeof、strlen与指针

指针++

函数传递指针的时候是副本

指针要分配给足够的空间

指针定义描述

this指针


前言

在我的博客C++高质量编程-CSDN博客内存管理章节里面讲了一些内存的基础知识,内存管理涉及最多的就是指针,也详细讲解了指针的原理以及平时编程使用指针犯的错误,不太熟悉的可以去看看,现在就对面试问的比较多的关于指针的问题归纳总结一下,希望对看到这篇博客的同学们有所帮助。

指针基础

1.什么是指针?请简述指针的基本概念和用途。

2.指针变量和普通变量有什么区别?如何声明和初始化指针变量?

3.指针的算术运算有哪些?请举例说明

4.什么是野指针?如何避免野指针的产生?

5.指针和引用区别?

6.什么是函数指针?请简述函数指针的概念和用途

7.如何声明和初始化函数指针?如何通过函数指针调用函数?

8.什么是多级指针?请举例说明多级指针的使用场景。

9.指针和数组有何关系?如何通过指针访问数组元素?

10.动态内存分配有哪些方式?请简述它们的区别和用途

11.如何使用new和delete操作符进行动态内存分配和释放?

12.C和C++分别怎么动态分配内存?有什么区别?

13.什么是内存泄漏?如何避免内存泄漏?

14.请解释C++中的const指针和指向cons指针的区别。

15.什么是智能指针?请简述智能指针的概含和用途。

16.如何使用std:unique_ptr、std::shared_ptr和std:weak_ptr智能指针?

指针实战

1.数组与指针比较

char szTest1[] = "123";
char szTest2[] = "123";
const char szTest3[] = "123";
const char szTest4[] = "123";
const char *pTest1= "123";
const char *pTest2= "123";
char *pTest3= "123";
char *pTest4= "123";
cout<<(szTest1==szTest2)<<endl;   //1 , 输出: 0
cout<<(szTest3==szTest4)<<endl;   //2 , 输出: 0
cout<<(pTest1==pTest2)<<endl;     //3 ,输出: 1
cout<<(pTest3==pTest4<endl;       //4 ,输出: 1

szTest1、szTest2、szTest3、szTest4都是数组,分配新空间,它们有各自的内存空间,这样写szTest1代表的数组的首地址,因此szTest1、szTest2、szTest3、szTest4都不相同;

pTest1、pTest2、pTest3、pTest4都是指针,指定的地址都相同,所以4个都是相同,因此输出是:0  0  1  1

2.sizeof、strlen与指针

char szTest1[] = "hello world";
const char* pTest1 = "hello world";
int len1 = sizeof(szTest1);
int len2 = strlen(szTest1);
int len3 = sizeof(pTest1);
int len4 = strlen(pTest1);
cout << len1;//输出 12
cout << len2;//输出 11
cout << len3;//输出 8
cout << len4;//输出 11

szTest1是数组,以'\0'结尾,sizeof计算内存占用空间,所以输出12,strlen计算字符串的长度,字符串以'\0'结尾结束,所以输出11;pTest1是个指向const char的指针,指针在x64下固定是8个字节,所以输出是8,strlen(pTest1)是计算pTest1指向的字符串长度,故输出11。

int calcLen(char str[])
{return (int)(sizeof(str)-1);//无论何时都是返回3
}

函数传递的是数组指针的时候就自动退化为指针了, 而指针的长度为4,减去1就是3

3.指针++

int m[5] = { 4, 12, 241, 33, 545 };  
int *ptr = (int *)(&m + 1);  
printf("%d,%d", *(m + 1), *(ptr - 1)); 

理解指针运算,”+1“就是偏移量的问题:一个类型为T的指针移动,是以sizeof(T)为单位移动的。m+1:在数组首元素地址的基础上,偏移一个sizeof(a[0])单位。因此m+1就代表数组第1个元素,为2;

&m+1:在数组首元素的基础上,偏移一个sizeof(m)单位,&m其实就是一个数组指针,类型为int[5]。因此&m+1实际上是偏移了5个元素的长度,也就是m+5;再看ptr是int类型,因此"ptr-1"就是减去sizeof(int*),即为m[4]=5;

m是数组首地址,也就是m[0]的地址,m+1是数组下一个元素的地址,即m[1];&m是对象的首地址,&m+1是下一个对象的地址,即m[5]

4.函数传递指针的时候是副本

void GetMemory(char *p)
{p=new char[100];strcpy(p,"hello world");
}
void main(void)
{char *str=NULL;GetMemory(str);cout<<str;delete []str;str=NULL;
}

初看函数逻辑,一切都是那么理所当然。仔细看下,这里的str是指char*的指针,把str指针拷贝了一份传给函数GetMemory,  GetMemory参数中的p和str不一样了,所以在GetMemory中改变p,不会影响到str,main函数中的str还是NULL,如果在GetMemory要返回new出的地址,那参数p必须为引用或双重指针,双重指针代码如下:

void GetMemory(char **p)
{*p=new char[100];strcpy(*p,"hello world");
}
void main(void)
{char *str=NULL;GetMemory(&str);cout<<str;delete []str;str=NULL;
}

也可以用引用,代码如下:

void GetMemory(char*& p)
{p=new char[100];strcpy(p,"hello world");
}
void main(void)
{char *str=NULL;GetMemory(str);cout<<str;delete []str;str=NULL;
}

5.指针要分配给足够的空间

void func()
{char b;char *p = &b;strcpy(p, "hello world");cout<<p;
}

因为 char类型的a变量只拥有了一字节的空间,但是"hello"拥有6字节的空间(包含最后的'\0'),所以程序出现问题,有可能崩溃,因为strcpy的时候有可能把func的返回地址覆盖了,函数调用返回不到上级函数,出现异常。

6.指针定义描述

int *a[10]; 

   由于符号[]的优先级高于*, 所以a优先于[]结合,a是一个数组,数组的每个元素都是指向int的指针

int (*a)[10];

   由于符号()的优先级高于[],所以a优先于*结合,a是一个指针,执行int类型的数组

int (*a)(int);

    首先a是一个指针,去掉a的()部分就是 int (int),这不就是一个函数吗,所以a是一个指向int(int)函数的指针,即函数指针。

int (*a[10])(int);

分解:1) int (int)  2) a[10]    3) *,所以a是一个大小为10的数组,数组的每个元素都是指针,该指针指向一个函数,该函数有一个整型参数并返回一个整型数。

int ((*a)[10])(int);

分解:1) int (int)  2) [10]    3) *a,所以a是一个指针,指向大小为10的数组,数组的每个元素都是函数,该函数有一个整型参数并返回一个整型数。

7.this指针

下面看一道比较经典的题目:

class MyTest {
public:void Test() {cout << "Test()" << endl;}
private:int m_a;
};int main()
{MyTest* p = nullptr;p->Test();
}

上面的代码运行会出现异常吗?答案是不会。

原因分析:1不管是C语言的函数还是C++得成员函数都有一个准确的地址   2 在main函数中p调用Test函数的时候,指针p会通过汇编寄存器ecx传给函数Test的入口参数   3 进入函数Test后这个ecx就赋值给this指针,可以通过this指针访问MyTest类中的成员变量和成员函数。

上面代码MyTest类中Test函数中,虽然this指针是空的,但是函数里面也没有访问任何成员函数和成员变量,所以不会报错。如果把题目改为如下:

class MyTest {
public:void Test() {cout << m_a << endl;}
private:int m_a;
};int main()
{MyTest* p = nullptr;p->Test();
}

运行报错,原因是Test函数中的this指针是空的,一旦访问m_a就提示访问内存报错。

如果还有不明白的同学可以去翻翻看<<深入理解C++对象模型>>,这本书把C++对象模型讲的比较深,比较透。

参考

C++高质量编程-CSDN博客

<<深入理解C++对象模型>>

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

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

相关文章

每天一杯羊奶,让身体更健康

每天一杯羊奶&#xff0c;让身体更健康 羊奶作为一种天然的健康饮品&#xff0c;越来越受到人们的关注和喜爱。它不仅口感醇厚&#xff0c;营养丰富&#xff0c;而且具有独特的保健功效。今天&#xff0c;小编羊大师带大家详细介绍一下每天喝一杯羊奶对身体的好处。 羊奶中的…

Python基础知识总结3-面向对象进阶知识

面向对象三大特征介绍 继承子类扩展父类语法格式关于构造函数&#xff1a;类成员的继承和重写查看类的继承层次结构 object根类dir() 查看对象属性重写 __str__() 方法 多重继承MRO方法解析顺序super()获得父类定义多态特殊方法和运算符重载特殊属性 对象的浅拷贝和深拷贝组合_…

如何利用MiniTab的命令行来提高数据建模效率

使用MiniTab进行数据建模时&#xff0c;如果涉及到需要多次更改数据、多次查看模型&#xff0c;感兴趣的同学可以尝试一下&#xff0c;把命令行显示出来&#xff0c;通过命令行的形式来执行&#xff0c;避免在繁多的菜单中到处查找。 操作方式如下图&#xff1a; 点击菜单“查…

junit单元测试:使用@ParameterizedTest 和 @CsvSource注解简化单元测试方法

在平常的开发工作中&#xff0c;我们经常需要写单元测试。比如&#xff0c;我们有一个校验接口&#xff0c;可能会返回多种错误信息。我们可以针对这个接口&#xff0c;写多个单元测试方法&#xff0c;然后将其场景覆盖全。那么&#xff0c;怎么才能写一个测试方法&#xff0c;…

业务项目中Echarts图表组件的封装实践方案

背景&#xff1a;如果我们的项目是一个可视化类/营销看板类/大屏展示类业务项目&#xff0c;不可避免的会使用到各种图表展示。那在一个项目中如何封装一个图表组件既能够快速复用、UI统一&#xff0c;又可以灵活扩充Echarts的各种复杂配置项配置就变得极为重要。 封装目标 符…

算法第十二天-矩形区域不超过K的最大数值和

矩形区域不超过K的最大数值和 题目要求 解题思路 来自[宫水三叶] 从题面来看显然是一道[二维前缀和]的题目。本题预处理前缀和的复杂度为O(m* n) 搜索所有子矩阵需要枚举[矩形左上角]和[矩形右下角]&#xff0c;复杂度是 O ( m 2 ∗ n 2 ) O(m^2 * n^2) O(m2∗n2)&#xff0c…

【数据库原理】(5)关系数据库的关系数据结构

关系及相关概念 在关系模型中,无论是实体还是实体之间的联系均由关系(二维表)来表示。 1.域&#xff08;Domain&#xff09; 定义&#xff1a;域是一组具有相同数据类型的值的集合。例子&#xff1a;实数集合、整数集合、英文字母集合等。 2.笛卡儿积&#xff08;Cartesian…

Spring之事务

当我们在某个方法上加了Transactional注解后&#xff0c;就表示该方法在调用时会开启Spring事务&#xff0c;而这个方法所在的类所对应的Bean对象会是该类的代理对象。 Spring事务的代理对象执行某个方法时的步骤&#xff1a; 1. 判断当前执行的方法是否存在Transactional注解…

mysql进阶-不同的count()性能

目录 一.count() 的含义 二.性能比较 三.结论 统计数据的四种count()方式包括&#xff1a;count(1) 、count(*)、count(id)、count(字段)。 假设数据库的存储引擎是InnoDB,如果是MyISAM,则表的总行数是已经存储的(没有where条件的情况下)&#xff0c;可以直接返回。 一.cou…

YOLOv5改进 | 卷积篇 | SAConv轻量化的可切换空洞卷积(附修改后的C3+Bottleneck)

一、本文介绍 本文给大家带来的改进机制是可切换的空洞卷积(Switchable Atrous Convolution, SAC)是一种创新的卷积网络机制,专为增强物体检测和分割任务中的特征提取而设计。SAC的核心思想是在相同的输入特征上应用不同的空洞率进行卷积,并通过特别设计的开关函数来融合这…

2023-应用开发中遇到的问题与解决方案

随着科技的不断发展&#xff0c;应用开发已经成为了当今社会中不可或缺的一部分。无论是手机应用、网页应用还是桌面应用&#xff0c;它们都为我们的生活带来了极大的便利。然而&#xff0c;在应用开发的过程中&#xff0c;我们往往会遇到各种各样的问题。作者将针对应用开发过…

2024.1.5 Hadoop各组件工作原理,面试题

目录 1 . 简述下分布式和集群的区别 2. Hadoop的三大组件是什么? 3. 请简述hive元数据服务配置的三种模式? 4. 数据库与数据仓库的区别? 5. 简述下数据仓库经典三层架构? 6. 请简述内部表和外部表的区别? 7. 简述Hive的特点,以及Hive 和RDBMS有什么异同 8. hive中无…

平铺式桌面环境体验之I3WM

平铺式桌面环境体验之I3WM 一说到Linux桌面&#xff0c;大家常用的桌面环境可能是 GNOME、KDE或者Xfce&#xff08;窗口可层叠&#xff09;&#xff0c;因为这几个流行的桌面环境更容易上手&#xff0c;更适合从Windows向Linux过渡。 但今天&#xff0c;我们来体验一种平铺式…

【SpringBoot】分组校验和自定义校验(注解)

分组检验 参数校验时&#xff0c;我们希望不同的方法参数校验规则不同&#xff0c;这时就需要分组校验。 public class Category {NotNull(groups Update.class)private Integer id;//主键IDNotEmptyprivate String categoryName;//分类名称// NotEmpty(groups {Add.class,…

java基础之Java8新特性-Stream(流)

简介 流&#xff08;Stream&#xff09;是 Java 8 引入的一种处理集合数据的抽象概念&#xff0c;它提供了一种更简洁、更灵活的方式来操作和处理集合数据。流可以看作是一系列元素的管道&#xff0c;可以对这些元素进行筛选、转换、排序、归约等操作&#xff0c;实现各种数据…

剑指offer题解合集——Week3day3

文章目录 剑指offerWeek3周三&#xff1a;树的子结构AC代码思路&#xff1a; 剑指offerWeek3 周三&#xff1a;树的子结构 题目链接&#xff1a;树的子结构 输入两棵二叉树 A&#xff0c;B&#xff0c;判断 B 是不是 A的子结构。我们规定空树不是任何树的子结构。数据范围 每…

【人工智能】深入了解人工智能的核心算法与应用实践

人工智能 学习AI要看的第一本书人工智能应当以人为本人工智能&#xff08;第3版&#xff09;通晓六点&#xff0c;明白人工智能是怎么回事基本概念和历史基础知识基于知识的系统高级专题现在和未来安全和编程 人工智能已经是基础学科 学习AI要看的第一本书 人工智能知识对于当…

Unix操作系统的前世今生

Unix是一种多用户、多任务操作系统&#xff0c;最初由AT&T贝尔实验室的肯汤普逊&#xff08;Ken Thompson&#xff09;和丹尼斯里奇&#xff08;Dennis Ritchie&#xff09;等人开发于上世纪70年代初。它被设计成一种通用的操作系统&#xff0c;支持跨多种硬件平台&#xf…

C++入门(详细解读,建议收藏)

&#x1f6a9;C是什么?&#x1f6a9;⛲&#x1f31f;⚡&#x1f966;&#x1f4ac; C语言是结构化和模块化的语言&#xff0c;适合处理较小规模的程序。对于复杂的问题&#xff0c;规模较大的程序&#xff0c;需要高度的抽象和建模时&#xff0c;C语言则不合适。为了解决软件危…

ssm基于Java Web的怀旧唱片售卖系统论文

摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装怀旧唱片售卖系统软件来发挥其高效地信息处理的作用&#x…