首页 > 编程语言 >面试必会(嵌入式)-C++面试高频(一)

面试必会(嵌入式)-C++面试高频(一)

时间:2024-12-12 22:31:51浏览次数:14  
标签:函数 int C++ 嵌入式 面试 内存 new class 指针

目录

1.new和malloc的区别(使用和原理)⭐

new的定义:

malloc的定义:

new与malloc的区别:(简单理解)

new与malloc使用区别

2.struct和class的区别⭐

3.char和int之间的转换

4.什么是野指针和悬挂指针⭐

5.NULL和nullptr区别⭐

6.指针常量和常量指针有何区别⭐

7.虚拟内存和物理内存的区别⭐

8.重载、重写和隐藏的区别⭐

9.简述面向对象的三大特性

10.什么是多态?


1.new和malloc的区别(使用和原理)⭐

new的定义:

new是C++的关键字,用于动态分配内存并创建对象。它可以根据类型自动计算所需内存空间,并调用对象的构造函数进行初始化。在使用new分配内存后,需要使用delete来释放这些内存空间,以防止内存泄漏。

malloc的定义:

malloc是C语言的库函数,用于动态分配一块指定大小的内存块,并返回其地址。需要注意的是,使用malloc分配内存后,需要使用free来释放这些内存空间,以防止内存泄漏。

new与malloc的区别:(简单理解)

语法:new是C++的关键字,而malloc是C语言的库函数。

类型安全:new操作符会根据类型自动计算所需内存大小,并进行类型匹配,返回的是对象类型指针;而malloc需要手动计算内存大小,并使用强制类型转换,返回的是void指针。

构造函数与析构函数的调用:new会自动调用对象的构造函数进行初始化,而malloc不会调用构造函数,得到的内存空间内容是未初始化的。

内存泄漏的检测:new可以通过异常机制检测内存分配失败,而malloc在分配失败时返回NULL,需要手动检查。

重载和自定义类型:new操作符可以重载,并能够与自定义类型的构造函数和析构函数配合使用;而malloc是库函数,不会调用自定义类型的构造和析构函数

new与malloc使用区别

#include <iostream>
#include <cstdlib>

int main() {
    // 使用new进行动态内存分配和释放
    int* newPtr = new int(10);
    std::cout << "Value allocated with new: " << *newPtr << std::endl;
    delete newPtr;

    // 使用malloc进行内存分配和释放
    int* mallocPtr = (int*)malloc(sizeof(int));
    if (mallocPtr != nullptr) {
        *mallocPtr = 20;
        std::cout << "Value allocated with malloc: " << *mallocPtr << std::endl;
        free(mallocPtr);
    }

    return 0;
}

2.struct和class的区别⭐

  1. 默认访问权限:struct中的成员默认为公共(public),而class中的成员默认为私有(private)。
  2. 默认继承方式:struct中的继承方式默认为公共(public),class中的继承方式默认为私有(private)。
  3. 使用习惯:struct适合用于简单的数据结构,class适合用于复杂的数据类型和实现面向对象编程。
  4. 成员变量和成员函数:struct中的成员变量和成员函数默认为公共,而class中的成员变量和成员函数默认为私有。
  5. 访问控制:struct中的成员在外部可直接访问,而class中的成员需要使用公共的成员函数来访问。
  6. 默认的构造函数和析构函数:class中会自动生成默认的构造函数和析构函数,而struct中不会。

3.char和int之间的转换

  1. 将char转换为int:可以直接将char类型的变量赋值给int类型的变量,将字符对应的ASCII码值赋给int变量。
char c = 'A';
int i = c;  // 将字符'A'的ASCII码值赋给i

2.将int转换为char:可以使用强制类型转换 (static_cast<char>) 将int类型的变量转换为char类型的变量,该方法只会截取int变量的低位字节作为字符。

int i = 65;
char c = static_cast<char>(i);  // 将整数65转换为对应的字符'A'

需要注意的是,对于转换为char的int值,如果超出了char类型的范围(-128至127),将会发生溢出,只保留最低位字节的值。

4.什么是野指针和悬挂指针⭐

野指针(Dangling Pointer):指的是没有初始化过的指针,它指向的地址是未知的、不确定的、随机的。

产生野指针的原因主要是指针未初始化,防止的措施就是指针初始化(包括及时初始化或置空)。

举例:

int main() {
    int* ptr; // 未初始化的指针,成为野指针

    // 使用野指针会导致未定义的行为
    *ptr = 5; // 解引用野指针,可能导致程序崩溃

    return 0;
}

 悬挂指针(Dangling Reference):指针最初指向的内存已经被释放了的一种指针。指针指向的内存已释放,但指针的值没有被清零,对悬空指针操作的结果不可预知。

示例代码:

int* createInt() {
    int value = 5;
    int* ptr = &value;
    return ptr; // 返回指向局部变量的指针
}

int main() {
    int* danglingPtr = createInt(); // 指向已释放的内存

    // 对悬挂指针操作的结果不可预知
    int value = *danglingPtr; // 解引用悬挂指针,可能导致未定义的行为

    return 0;
}

5.NULL和nullptr区别⭐

类型不同:NULL是宏定义或整数值0,而nullptr是C++11引入的关键字,表示空指针。

安全性不同:NULL可能导致函数调用二义性问题,nullptr更安全,不会被错误解释为整型。

上下文匹配不同:NULL可以用于整型类型的上下文,nullptr只能用于指针类型的上下文。

6.指针常量和常量指针有何区别⭐

指针常量(Pointer to a constant):指针常量是指指针本身是不可更改的,即指针变量自身的值不能被修改,但指向的值可以修改。声明指针常量时,需要在指针类型前加上 const 修饰符。例如:

int* const ptr;  // ptr 是指向常量的指针,指向的值不能通过 ptr 修改,但可以通过其它方式修改

在这个例子中,ptr 是一个指向 int 类型常量的指针,它的值不能被修改,但可以通过其他途径修改指向的值。

常量指针(Constant pointer):常量指针是指指针指向的地址不能更改,即指针变量指向的地址不能被修改,但可以通过指针修改指向的值。常量指针在声明时需要使用 const 修饰符将指针指向的类型声明为常量。例如:

int x = 5;
 const int * ptr = &x;  // ptr 是常量指针,指向的地址不能通过 ptr 修改,但可以通过 *ptr 修改值
ptr是常量指针,不可以修改ptr指向地址的值,可以修改ptr 指向的地址。

总结:

  • 指针常量表示指针本身的值不能修改,但可以通过指针修改指向的值。
  • 常量指针表示指针指向的地址不能修改,但可以通过指针修改指向地址的值

7.虚拟内存和物理内存的区别

虚拟内存(Virtual Memory)和物理内存(Physical Memory)是计算机系统中存储和管理数据的两个概念:

  • 物理内存是计算机中的实际硬件内存,由RAM芯片组成。
  • 虚拟内存是对物理内存的扩展,使用磁盘空间来模拟更大的内存容量。

它们之间的区别包括:

  • 大小:物理内存的容量是固定的,而虚拟内存的大小可以超过物理内存的容量。
  • 访问速度:物理内存的访问速度较快,而虚拟内存的访问速度相对较慢,因为它需要与磁盘进行交互。
  • 地址空间:物理内存使用物理地址进行访问,而虚拟内存使用虚拟地址,通过内存管理单元(MMU)映射到物理内存。
  • 管理方式:物理内存的管理相对简单,而虚拟内存的管理涉及页表和页面置换等技术。
  • 可用空间:物理内存的可用空间有限,而虚拟内存可以提供更大的可用空间,因为它可以使用磁盘空间作为扩展。

总之,虚拟内存扩展了物理内存的容量,并提供了更灵活的内存管理机制,但付出的代价是访问速度较慢。

8.重载、重写和隐藏的区别⭐

重载(Overloading):

  • 重载是在同一个作用域内定义多个相同名称但参数列表不同的函数或方法。
  • 重载函数可以根据不同的参数数量或类型来执行不同的操作。
  • 重载通过函数名和参数列表来区分不同的函数。
举例代码:

#include <iostream>

void printNumber(int num) {
    std::cout << "Integer number: " << num << std::endl;
}

void printNumber(double num) {
    std::cout << "Floating-point number: " << num << std::endl;
}

int main() {
    printNumber(10);
    printNumber(3.14);
    return 0;
}

重写(Override):

  • 重写是指子类重新定义从父类继承的虚函数,使其具有不同的实现。
  • 重写的函数签名(函数名、参数列表和返回类型)必须与被重写函数相同。
  • 在运行时,根据具体的对象类型,调用的是子类重写的版本。
举例代码:

#include <iostream>

class Base {
public:
    virtual void sayHello() {
        std::cout << "Hello from Base class!" << std::endl;
    }
};

class Derived : public Base {
public:
    void sayHello() override {  // 使用 override 关键字表明重写了父类的函数
        std::cout << "Hello from Derived class!" << std::endl;
    }
};

int main() {
    Base* basePtr = new Derived();
    basePtr->sayHello();  // Output: "Hello from Derived class!"
    delete basePtr;
    return 0;
}

隐藏(Hiding):

  • 隐藏是指在派生类中定义与父类具有相同名称的成员函数,使其隐藏父类中的同名函数。
  • 隐藏函数与父类的函数没有多态性,只有通过对象的实际类型调用时才会调用相应的函数。
举例代码:

#include <iostream>

class Base {
public:
    void sayHello() {
        std::cout << "Hello from Base class!" << std::endl;
    }
};

class Derived : public Base {
public:
    void sayHello() {
        std::cout << "Hello from Derived class!" << std::endl;
    }
};

int main() {
    Base baseObj;
    Derived derivedObj;
    
    baseObj.sayHello();    // Output: "Hello from Base class!"
    derivedObj.sayHello(); // Output: "Hello from Derived class!"
    
    Base* basePtr = new Derived();
    basePtr->sayHello();   // Output: "Hello from Base class!"
    
    delete basePtr;
    return 0;
}

9.简述面向对象的三大特性

面向对象编程(OOP)的三大特性是封装、继承和多态。下面对每个特性进行简要说明:

封装(Encapsulation):

  • 封装是将数据和操作封装在一个单元(类)中的机制。
  • 通过封装,实现类的成员变量和成员函数作为一个整体进行管理和操作。
  • 封装隐藏了数据的具体实现细节,只暴露出必要的接口,提供了更好的安全性和可维护性。
  • 通过访问修饰符(公有、私有、保护),控制对类的成员的访问权限。

继承(Inheritance):

  • 继承是通过创建派生类来扩展和重用已有类的机制。
  • 基类(父类)是已经定义的类,派生类(子类)继承了基类的属性和方法。
  • 子类可以自定义新的属性和方法,也可以覆盖或扩展继承的父类的属性和方法。
  • 继承实现了类与类之间的关系,实现了代码的重用和扩展。

多态(Polymorphism):

  • 多态是指同一个接口可以由不同的对象以不同的方式进行实现和响应的能力。
  • 多态允许使用基类的指针或引用来引用派生类的对象,实现了多种形态的使用。
  • 编译时多态使用函数重载和运算符重载;运行时多态通过虚函数实现(动态绑定)。
#include <iostream>
using namespace std;

class Shape {
public:
    // 声明虚函数,用于计算形状的面积
    virtual double area() const {
        return 0;
    }
};

class Circle : public Shape {
private:
    double radius;
public:
    Circle(double r) : radius(r) {}
    // 重写(覆盖)父类的虚函数,按照圆的面积公式计算面积
    double area() const override {
        return 3.14 * radius * radius;
    }
};

class Rectangle : public Shape {
private:
    double length;
    double width;
public:
    Rectangle(double l, double w) : length(l), width(w) {}
    // 重写(覆盖)父类的虚函数,按照矩形的面积公式计算面积
    double area() const override {
        return length * width;
    }
};

void printArea(const Shape& shape) {
    cout << "该形状的面积是: " << shape.area() << endl;
}

int main() {
    Circle circle(5);
    Rectangle rectangle(4, 6);

    printArea(circle);
    printArea(rectangle);

    return 0;
}
  • 多态提高了代码的灵活性和可扩展性,使得程序更具有可读性和可维护性。

10.什么是多态?

利用虚函数,基类指针指向基类对象时就使用基类的成员(包括成员函数和成员变量),指向派生类对象时就使用派生类的成员。 基类指针可以按照基类的方式来做事,也可以按照派生类的方式来做事,它有多种形态,或者说有多种表现方式,我们将这种现象称为多态(Polymorphism)。

代码举例:

#include <iostream>

class Base {
public:
    virtual void print() {
        std::cout << "This is the Base class" << std::endl;
    }
};

class Derived : public Base {
public:
    void print() override {
        std::cout << "This is the Derived class" << std::endl;
    }
};

int main() {
    Base* basePtr;
    
    Base baseObj;
    Derived derivedObj;
    
    basePtr = &baseObj;
    basePtr->print();  // 此时使用基类的成员函数来打印消息
    
    basePtr = &derivedObj;
    basePtr->print();  // 此时使用派生类的成员函数来打印消息
    
    return 0;
}

标签:函数,int,C++,嵌入式,面试,内存,new,class,指针
From: https://blog.csdn.net/m0_67794241/article/details/144433808

相关文章

  • C++构造函数和析构函数
    目录1构造函数1.1什么是构造函数?1.2无参构造函数1.3带参数构造函数2析构函数2.1什么是析构函数?1构造函数1.1什么是构造函数?类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。构造,那构造的是什么呢?构造成员变量的初始化值,内存空间等构造......
  • 搞定leetcode面试经典150题之链表
    系列博客目录文章目录系列博客目录理论知识双向链表例题206.反转链表27.回文链表141.环形链表21.合并有序链表2.两数相加19.删除链表的倒数第N个结点138.随机链表的复制理论知识链表是数据结构中一种非常常见且基础的结构,在Java中,链表被广泛应用于解决动态......
  • 12C++循环结构-for循环(2)——教学
    一、循环变量为字符型试编一程序,按字典顺序输出26个字母。流程图:思考:先顺序输出26个小写英文字母,再逆序输出26个大写英文字母。循环可以是递增型循环,也可以是递减型循环。二、打擂台-for语句的另一种形式问题:试编一程序,输入10个数,输出其中最大的数。以前学过,输入三个数求......
  • C++ debug
    C++debug在C++中,查看程序的调用栈(CallStack)通常用于调试崩溃、性能问题或逻辑错误等场景。以下是几种常用的方法来查看调用栈:1.使用GDB调试器查看调用栈GDB(GNUDebugger)是Linux上非常流行的调试工具,可以用来查看C++程序的调用栈。示例:假设有以下C++程序:#includ......
  • 面试必会(嵌入式)-C语言面试高频(内存管理)
    1.(内存)堆和栈的区别⭐堆栈空间分配不同:栈由操作系统自动进行分配和释放,用于存放函数的参数值、局部变量的值等,具有高效性。堆:一般由程序员手动进行分配和释放,效率比栈低很多。data数据区:存放全局变量,静态变量。堆栈缓存方式不同:栈使用一级缓存,存储在处理器核心中,调用完......
  • 中高级运维工程师运维面试题(二)之NGINX
    这里写目录标题前言基础知识1.什么是NGINX?2.NGINX的基本架构是怎样的?3.如何配置NGINX以支持HTTPS?高级配置4.如何配置NGINX的负载均衡?5.如何实现URL重写和重定向?6.如何配置NGINX的缓存?性能优化7.如何优化NGINX的并发性能?安全性8.如何保护NGINX......
  • [C++] 继承详解
    目录前言演示用编译器及其标准DevC++6.7.5Redpanda C++14                           先 赞 后 看  养  成 习 惯  正文1、继承的概念与意义2、继承的使用 2.1继承的定义及语法2......
  • C++_运算符重载
    filesystemc++11在CMakeList.txtfind_package(BoostCOMPONENTSsystemfilesystemregexREQUIRED)include_directories(${Boost_INCLUDE_DIRS})target_link_libraries(projectname${Boost_LIBRARIES})程序#include<boost/filesystem.hpp>......
  • C++ Boost库 tuple元组
    元组boost::tuple是Boost库中提供的允许程序员创建固定大小的元组,这些元组可以包含不同类型的元素。元组是一个数据结构,它可以存储多个值,这些值可以是不同类型的。boost::tuple是C++标准库中std::tuple的前身,后者在C++11标准中被引入。特点固定大小:一旦创建,boost::tuple的大小......
  • 蓝桥杯嵌入式模板创建(STM32 CubeMx简单使用教程)
    蓝桥杯嵌入式新板模板创建&简单经验分享补充在最前:以下原文是22年还未毕业时写的,仅在把板子二手卖给别人的时候给别人分享了这份笔记。那时经验不多,现在也由于工作使用的芯片不同已很久没有使用CubeMX了,因此文章可能有很多错漏之处,欢迎在评论区指出。备注在前:uint8_t即un......