嵌入式软件常用面试题汇总之 C/C++ 语言相关(2)
C/C++之指针相关的面试题汇总
对校招来说这部分考的非常频繁,应届生务必要熟悉!!
1.指针与引用的区别
- 初始化要求不同:引用在创建的同时必须初始化,就是它必须引用到一个有效的对象;而指针在定义的时候不必初始化,可以在定义后面的任何地方重新赋值。
- 可修改性不同:引用一旦被初始化为指向一个对象,它就不能被改变为另一个对象的引用;而指针在任何时候都可以改变为指向另一个对象。给引用赋值并不是改变它和原始对象的绑定关系
- 不存在NULL引用:引用不能使用指向空值的引用,它必须总是指向某个对象;而指针则可以是NULL,不需要总是指向某些对象,可以把指针指向任意的对象,所以指针更加灵活,也更容易出错。
- 测试需求区别:由于引用不会指向空值,这意味着使用引用之前不用测试它是否合法;而指针则需要经常进行测试。因此使用引用的代码效率比使用指针的要高。
- 应用的区别:如果我们想要一旦指向一个对象就不再改变指向,那么应该使用引用。如果有存在指向NULL(不指向任何对象)或在不同的时刻指向不同的对象这些可能性,应该使用指针。
总体来说,引用既具有指针的效率,又具有变量使用的方便性和直观性。
2.野指针是指什么?如何避免?
野指针就是访问了不该访问的内存或者访问了已经释放的内存,野指针的成因主要有两种:
- 指针变量没有被初始化,任何指针变量刚被创建时不会自动生成NULL指针。它的默认值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存
- 指针p被free或者delete之后,没有置为NULL,让人误以为p是一个合法的指针。free或者delete只是把指针所指的内存给释放掉,但是并没有把指针本身给干掉,用调试器跟踪可以发现p被free之后其地址仍然不变(非NULL),只是改地址对应的内存是垃圾,p成了野指针。如果此时不把p设置为NULL,会让人误以为p是一个合法的指针,如果程序比较长,我们有时记不住p所指向的内存是否已经被释放,在继续使用p之前,通常会用语句if(p!= NULL)进行防错处理,很遗憾,此时if语句起不到防错作用,因为即便p不是NULL指针,也不指向合法的内存块
我们在编写程序的时候养成良好的编程习惯就能避免野指针的出现:
- 凡定义的指针变量初始化为NULL
- 指针变量被free或delete之后将其值赋为NULL
- if(ptr == NULL) 使用之前先判断
3.写出数组指针的定义,指针数组的定义,函数指针的定义,指针函数的定义,函数指针数组的定义
- 数组指针:int (*p)[ ];(一个指向数组的指针)
- 指针数组:int *p[ ];(一个数组,数组内存放着指针)
- 函数指针:int (*p)(int);(一个指向函数地址的指针)
- 指针函数:int* p(int); (一个返回指针的函数)
- 函数指针数组:int (*p[ ])(int);(一个数组,数组内存放着指向函数地址的指针)
4.区别下函数指针和指针函数,分别怎么定义和使用?
函数指针:
- 函数指针是指向函数的指针,它存储了函数的地址。
- 声明函数指针时,需要指定函数的返回类型和参数列表,以便与函数的类型匹配。
- 通过函数指针可以调用相应的函数。
声明形式如下: returnType (*pointerName)(parameterTypes); // 声明一个函数指针,指向返回值为int,参数为两个int的函数 int (*funcPtr)(int, int); // 初始化函数指针,使其指向某个函数 funcPtr = &add; // 使用函数指针调用函数 int result = (*funcPtr)(10, 20);
指针函数:
- 是一个函数,其返回值是指针类型。
- 指针函数的声明形式与普通函数类似,只是其返回值是指针类型。
- 指针函数可以返回指向不同类型的指针,具体返回何种类型的指针取决于函数的实现。
- 使用指针函数时,需要注意返回的指针是否有效,以避免悬挂指针等问题。
// 声明一个指针函数,返回值为int* int* pointerFunction(int x) { // 函数实现 return new int(x); } // 调用指针函数 int* ptr = pointerFunction(10);
5.指针与数组的区别和联系?
指针(Pointer)和数组(Array)是 C 和 C++ 中两种不同的数据类型。
联系:
- 指针和数组都可以用来存储一组相同类型的数据。
- 指针可以指向数组的第一个元素,也可以通过指针算术运算来遍历数组的所有元素。
- 数组名本身也可以被视为指向数组第一个元素的指针,即数组名是数组首元素的地址。
- 指针和数组都支持下标运算符 [] 来访问元素。
- 对于指针来说,使用 *(ptr + i) 或 ptr[i] 都可以访问第 i 个元素。
- 对于数组来说,直接使用 array[i] 即可访问第 i 个元素。
- 在函数参数传递中,数组名会被隐式转换为指向数组首元素的指针。
- 当数组作为函数参数传递时,实际上传递的是数组首元素的地址,而不是整个数组的拷贝。
区别:
- 数据类型:
- 指针是一个变量,用来存储内存地址,可以指向任意类型的数据。
- 数组是一个集合,存储一系列相同类型的数据,其大小在编译时确定。
- 内存分配:
- 指针需要显式地进行内存分配和释放,可以使用 new 和 delete 运算符动态分配内存。
- 数组的内存分配是静态的,在编译时就已经确定了大小,并且分配的内存空间在程序运行期间不会改变。
- 语法操作:
- 指针可以进行指针算术运算,可以通过指针进行间接访问内存。
- 数组名不能进行指针算术运算,它只是一个常量指针,指向数组首元素的地址,不能被修改。
- 参数传递:
- 指针作为函数参数时,可以传递空指针,也可以修改指针指向的对象。
- 数组作为函数参数时,会被隐式地转换为指向数组首元素的指针,函数无法获取数组的大小。
6.按值传递与按地址传递的区别
按值传递参数:按值传递参数时,是将实在参数的值复制一个形式参数中,如果在调用过程中改变了形式参数的值,不会影响实在参数本身,即实在参数保持调用前的值不变。
按地址传递:按地址传递参数时,把实在参数的地址传送给被调用过程,形式参数和实在参数共用内存的同一地址。在被调用过程中,形式参数的值一旦改变,相应实参的值也跟着改变。
7.指针的强制类型转换
可以使用强制类型转换来将指针从一种类型转换为另一种类型,一般经常用的有如下的方法。
C 风格的强制类型转换:
int* ptr = new int; float* floatPtr = (float*)ptr;
reinterpret_cast:该运算符用于将指针转换为其他指针类型,通常用于不同类型之间的指针转换,例如指针和整数之间的转换。但要注意,reinterpret_cast 并不检查转换的安全性,因此可能会导致未定义的行为
int* ptr = new int; float* floatPtr = reinterpret_cast<float*>(ptr);
static_cast:用于执行具有明确定义的类型转换,通常用于基本类型之间的转换或者向上或向下的类继承关系转换。与 reinterpret_cast 相比,static_cast 更安全,因为它只能进行已定义的转换。
int num = 10; double convertedNum = static_cast<double>(num);
8.C++的智能指针是什么?
智能指针是一种 C++语言特有的指针,它是对常规指针的封装,提供了自动内存管理的功能,能够在对象不再被使用时自动释放其所占用的内存,避免了手动管理内存所带来的错误和麻烦。智能指针的设计思想是资源管理类(RAII)的一种应用,通过将对象的生命周期与智能指针的生命周期绑定,实现对对象的自动管理。
C++标准库提供了几种智能指针,其中最常用的是std::unique_ptr和std::shared_ptr。
std::unique_ptr:
- 是一种独占所有权的智能指针,它确保动态分配的内存只有一个指针可以访问,并且在指针离开作用域时自动释放其所管理的内存。
- 禁止拷贝和赋值操作,因此不支持多个指针共享同一块内存。
- 通常用于在函数中传递和返回动态分配的对象,以确保内存的正确释放。
std::shared_ptr:
- 是一种共享所有权的智能指针,允许多个指针共享动态分配的内存,内存会在所有指针都释放后才会被释放。
- 使用引用计数来跟踪内存的使用情况,当引用计数为零时,表示没有指针在管理内存,内存会被释放。
- 允许拷贝和赋值操作,因此可以多个指针共享同一块内存。
除了这两种常用的智能指针外,C++标准库还提供了std::weak_ptr,它是std::shared_ptr的弱引用,用于解决循环引用的问题。
觉得本文有用的话,给博主点个赞吧!!谢谢大家!
#嵌入式##嵌入式面试##面经##笔试##嵌入式软件技能#该专栏是我整理的一些嵌入式软件笔面试常见的题目,在有一定计算机基础上,再过一遍该专栏的内容,对应届生校招来说基本上笔面试就没什么问题了! 有任何疑问可随时与我联系,一起交流一起进步。