C/C++基本语言相关面试题

46 篇文章 0 订阅
26 篇文章 0 订阅

1. malloc、free和new、delete的区别

语言关联:

malloc 和 free 是C语言中的标准库函数。
new 和 delete 是C++中的操作符。

类型安全:

malloc 返回的是 void* 类型,需要显式类型转换。
new 返回的是确切类型的指针,不需要类型转换,因此更类型安全。

构造和析构:

malloc 只分配内存,不调用构造函数。
new 不仅分配内存,还会调用对象的构造函数。
free 只释放内存,不调用析构函数。
delete 不仅释放内存,还会调用对象的析构函数。

错误处理:

malloc 在内存分配失败时返回 
#include <boost/beast/http.hpp>
#include <boost/beast.hpp>
#include <boost/asio.hpp>
#include <memory>
#include <shared_mutex>
#include <iostream>
// 简化作用域声明
namespace beast = boost::beast;         // from <boost/beast.hpp>
namespace http = beast::http;           // from <boost/beast/http.hpp>
namespace net = boost::asio;            // from <boost/asio.hpp>
using tcp = boost::asio::ip::tcp;       // from <boost/asio/ip/tcp.hpp>NULL。
new 在内存分配失败时抛出 std::bad_alloc 异常。

2. 引用与指针作用以及区别

指针更灵活,可以指向nullptr,可以修改指向,
指针通过某个指针变量指向一个变量,对它所指的变量进行简介的操作
指针是对象,指针有地址,可以定义指针的指针(二级指针)


引用更安全,必须在定义时初始化,且与原类型保持一致,且不分配内存。声明关系,一经声明,不可变更。
可对引用,再次引用。多次引用的结果,是某一变量具有多个别名,多个别名间是平行关系。
对引用操作就是直接对目标对象的操作
引用不是对象,没有实际的地址,不能定义引用指针,也不能定义引用的引用(二级引用:没有这种说法)

作用:
引用 : 用在传参时候,避免内存分配,以及对象数据的复制  ;  用在函数返回值,避免对象数据的复制,提高效率
指针 : 用在传参时候,避免对象数据的复制 
        基类指针可以指向基类对象,也可以指向派生类对象。这种灵活性使得可以通过同一个接口操作不同类型的对象。
        通过基类指针调用虚函数时,实际调用的函数是在运行时根据指针所指向的对象类型决定的,这就是动态绑定(dynamic binding)。通过指针调用虚函数比直接调用函数稍微慢一些
        代码复用

3. const 关键字的作用

一. 定义变量 : 
    1.c语言里 : 局部的const (分配到了栈去) 可以使用指针修改 全局的const (分配到了全局区去) 不能修改 (段错误)
    2.c++里 : 都不能修改,和符号表有关 (局部const变量在符号表中通常不会占用实际的内存地址,编译器会尽可能将其优化为立即数。全局const变量在符号表中会有一个固定的内存地址,但这个地址指向的是只读数据段。)
    作用:1. 避免修改 2. 避免次的内存分配 3. 类型检查 ,作用域检查
    
二. 修饰指针 : (前定值,后定向)  const在*前定值 在*后定向  
    1. char * const p; 
        const修饰指针p,表示指针p本身是常量,不能修改指针的指向。
        这意味着一旦p被初始化指向某个地址,就不能再指向其他地址。
        但是,指针p指向的地址的内容是可以修改的。
    2. const char * p; 
        const修饰char,表示指针p指向的地址的内容是常量,不能修改。
        这意味着不能通过指针p修改它所指向的地址的内容。
        但是,指针p本身是可以修改的,可以指向其他地址。
    3. char const * p; 
        这种写法与const char * p;是等价的,const修饰char,表示指针p指向的地址的内容是常量,不能修改。
        这意味着不能通过指针p修改它所指向的地址的内容。
        但是,指针p本身是可以修改的,可以指向其他地址。
    4. const char * const p;
        const修饰指针p和char,表示指针p本身和指针p指向的地址的内容都是常量,不能修改。
        这意味着指针p不能修改指向,也不能通过指针p修改它所指向的地址的内容。
        
三. 修饰函数参数
    可以防止函数内部修改传入的参数。
四. 修饰函数返回值
    1. 当函数返回值为基本数据类型(如int、char等)时,使用const修饰返回值通常没有实际意义,
    因为基本数据类型的返回值是按值传递的,函数返回后,返回值的副本可以被随意修改。
    2. 当函数返回值为指针或引用时,使用const修饰返回值可以防止返回值被修改。
    (c++运算符+重载时,const修饰返回值可以防止返回值被修改,避免返回的值是左值,禁止  const a + b = c 这种操作)
    
四. 修饰类成员函数
    1. 常量对象只能调用其const常成员函数。 普通对象可以调用所有成员函数。
    2. const修饰类成员函数,表示该成员函数不能修改类成员变量的值。
    3. const常成员函数只能调用const常成员函数,不能调用普通成员函数。
    (const用来函数重载,const修饰了函数,就视为重载那个函数,形成const版本)
五. 修饰类成员变量
    1. 常量成员变量必须在声明时 或 在构造函数的初始化列表中进行初始化(常用),之后不能被修改。 (不能在构造函数体中初始化常量成员变量)
    2. 如果常量成员变量是静态的,即属于类而不是对象,那么它的声明和定义方式略有不同。静态常量成员变量需要在类外部进行定义和初始化。
        class MyClass {
        public:
            static const int staticConstValue; // 静态常量成员变量声明
        };
        // 静态常量成员变量定义和初始化
        const int MyClass::staticConstValue = 10;


4. c++什么时候生成构造函数?

1. 空的类定义不会生成构造函数
2. 如果一个类没有显式定义任何构造函数,编译器会自动生成一个默认构造函数。
3. 如果一个类定义了一个带有默认参数的构造函数,编译器不会自动生成默认构造函数。但是,如果该构造函数的所有参数都有默认值,那么它可以被视为默认构造函数。

1. 类a内数据成员是对象b ,而b提供了默认构造函数,为了让b的构造函数
2. 类A的基类提供了默认构造函数,A必须生成构造函数
3. 类内定义了虚函数 为了实现多态机制,需要为类维护虚函数表 ,类所有对象保存一个指向虚函数表的指针,这将在构造函数初始化;

5. extern 关键字作用

extern关键字主要用于声明变量或函数是定义在另一个文件中,或者在同一个文件的其他位置。它告诉编译器该变量或函数在其他地方有定义。

extern的主要作用是管理跨文件的符号可见性,是在多文件程序中引用全局变量和函数的关键工具。

extern常用于在一个源文件中引用另一个源文件中定义的全局变量。

对于函数,extern通常是隐式的,因为默认情况下,函数的声明和定义都具有extern链接特性。因此,我们一般不需要显式地使用extern,但是在某些情况下,可以使用它来强调函数的外部链接。

c++调用 c 

// C++文件
extern "C" {
    void cFunction();  // 声明一个C风格的函数
}


6. c_c++中强制类型转换使用场景

staic_cast : 用于基本数据类型之间的转换,如int转float,double转int等
    空指针转换为目标类型的空指针
    将非const转换为const
    不安全的向下转型

const_cast : 用于去除常量性,如将const int转为int  去掉指针引用的const;目的用于修改指针引用的权限

reinterpret_cast : 用于指针之间的转换,如将int*转为char*等
    改变引用,指针的类型 转换为整形  , 将整形转换引用或指针 
    (指针,整形,引用,函数指针,成员指针)
    无安全检查
    
dynamic_cast : 用于类之间的转换,如基类转派生类等
    向下转型,运行时进行检测的类型转换,  引用 转换失败会抛出bad_cast异常
    作用于指针引用 
                基类指针 指向派生类对象

7. 如何避免野指针?

1. 初始化指针:在声明指针变量时,尽量立即对其进行初始化。如果暂时没有合适的初始值,可以将其初始化为NULL或nullptr(在C++11及以后)。
2. 使用指针操作对象之前,检查是否为指针对象分配内存
3. 及时释放内存:在使用动态内存分配函数(如malloc、calloc、realloc、new等)分配内存后,如果不再需要这些内存,应及时使用free或delete释放内存,并将指针设置为NULL或nullptr。
4. 在C种需要对空间数据置空 memset
5. 避免重复释放:确保每个动态分配的内存只被释放一次,重复释放同一块内存会导致未定义行为。
6. 使用智能指针:在C++中,可以使用智能指针(如std::unique_ptr、std::shared_ptr)来自动管理内存,避免手动管理内存带来的风险。
7. 避免返回局部变量的地址:函数内部声明的局部变量在函数返回后会被销毁,因此不要返回这些变量的地址。

8. static关键字的作用

static关键字修饰的东西 : 变量 函数 
        
1. static 修饰的东西在函数体中时,其作用范围只存在此函数体中,其他函数不能访问到
2. static 修饰的东西在模块内(cpp/h) ,
    在cpp源文件声明,作用范围是此源文件,其他模块源文件声明相同名称的static不冲突;
    声明在头文件(.h),其他包含 此头文件的都可以访问statc修饰的东西
    
3. static在类中 : 
    以下都为整个类所有:(属于类而不是类的实例)
    (1),修饰成员变量  所有对象共享同一个静态成员变量。
    (2),修饰成员函数 (不接收this指针)  静态成员函数不能访问非静态成员变量和非静态成员函数。
    

9. c++ 什么是深拷贝?什么是浅拷贝?

浅拷贝:
浅拷贝在复制对象时,只复制对象中的基本数据类型成员和指针值,而不复制指针指向的内存内容。这意味着复制后的对象中的指针将与原对象共享同一块内存。
如果某个对象被析构,而该对象的指针指向的内存也被释放,那么其他共享同一内存的对象可能会访问已释放的内存,导致未定义行为甚至程序崩溃。这种情况称为“双重释放”(double free)。
在C++中,如果没有为类定义自定义的拷贝构造函数,编译器提供的默认拷贝构造函数就是浅拷贝。

深拷贝:
深拷贝不仅复制对象中的基本数据类型成员,还会为指针指向的内存区域分配新内存,并复制内存中的实际内容。这样,复制后的对象拥有独立的内存,不与原对象共享。
需要自定义拷贝构造函数和赋值操作符,以实现深拷贝逻辑,对每个指针成员做内存分配和内容复制。
避免了浅拷贝中的重复释放问题,因为每个对象拥有自己的内存副本。

10. 简述strcpy、sprintf与memcpy的区别?

他们都能实现字符串拷贝功能
strcpy 将源字符串复制到目标字符串中,只适用于以\0结尾的字符串,复制时会自动添加终止符\0。
目标字符串必须有足够的空间来容纳源字符串及其终止符。

sprintf 将格式化的数据写入字符串中。类似于printf,但输出到字符串而不是标准输出。
支持多种格式化选项,如整数、浮点数、字符串等。 目标字符串必须有足够的空间来容纳格式化后的数据及其终止符。

memcpy 复制源内存块到目标内存块中。适用于任意类型的数据,不仅仅是字符串。 
不会自动添加终止符\0。 目标内存块必须有足够的空间来容纳源内存块。
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

可能只会写BUG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值