前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >指针这几个没用的判空,你做了吗?

指针这几个没用的判空,你做了吗?

作者头像
程序员的园
发布于 2025-05-09 04:03:43
发布于 2025-05-09 04:03:43
3200
代码可运行
举报
运行总次数:0
代码可运行

好像智能指针出来后,裸指针变成了洪水猛兽,在项目里都敬而远之了,导致很多裸指针的知识点也就被束之高阁,但是我个人认为对于基础知识的深入了解才能走的更远,今天我也就斗胆抛砖引玉,梳理下裸指针的几个知识点,与大家共享。

1.new 完需要判空吗

我之前写的代码、同事现在写的代码、读者群内部分同学也说,都会在new 后默认加一个指针是否为空的判断——指针为空时做一些容错处理。形如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int* p = new int(42);  
if (p == nullptr) {
    // 处理内存分配失败的情况
}

int* p_array = new int[10];
if (p_array == nullptr) {
    // 处理内存分配失败的情况
}

但是,new完的指针真的有必要进行判空吗?为了探究这个问题,查看对应的源码(以MSVC为例),源码实现如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
_VCRT_EXPORT_STD _NODISCARD _Ret_notnull_ _Post_writable_byte_size_(_Size) _VCRT_ALLOCATOR
void* __CRTDECL operator new(
    size_t _Size
    );

_VCRT_EXPORT_STD _NODISCARD _Ret_notnull_ _Post_writable_byte_size_(_Size) _VCRT_ALLOCATOR
void* __CRTDECL operator new[](
    size_t _Size
    );

可以看到,new 运算符的实现中加入了修饰符_Ret_notnull_,它的作用是告诉编译器,该函数返回的指针不会为空。

如果说如上所示的源码还是比较片面,但是C++标准对于new 运算符的行为,是这样描述的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Throws an exception of a type that would match a handler of 
type std::bad_alloc on failure to allocate memory.

那么函数真的会抛出异常吗?如下的测试程序确实会抛出异常

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int main() {

    //编译器对单次分配过大内存会有编译报错,
    //所以少量多次分配,避免编译报错
    for (size_t i = 0; i <( 2<<30); i++)
    {
        auto p = new int[2<<10];
    }
    return 0;
}

问题:make_shared、make_unique的返回值需要判空吗?欢迎评论区讨论

2. 禁止new 的异常

经过如上的分析可知,new 分配失败时会抛出异常,但是异常是不可控的,作为程序员我们总是希望程序是可控的,那如何禁止new 抛出异常呢? C++标准库提供了std::nothrow 关键字,它可以关闭new 分配失败时抛出异常的机制,转而返回空指针。可按如下的方式使用该关键字:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <new>
int* p = new(std::nothrow) int(42);
if (p == nullptr) {
    // 安全处理分配失败的情况
}

在MSVC中,std::nothrow版本的new操作符实现如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
_VCRT_EXPORT_STD _NODISCARD _Ret_maybenull_ _Success_(return != NULL) _Post_writable_byte_size_(_Size) _VCRT_ALLOCATOR
void* __CRTDECL operator new(
    size_t _Size,
    ::std::nothrow_t const&
    ) noexcept;

_VCRT_EXPORT_STD _NODISCARD _Ret_maybenull_ _Success_(return != NULL) _Post_writable_byte_size_(_Size) _VCRT_ALLOCATOR
void* __CRTDECL operator new[](
    size_t _Size,
    ::std::nothrow_t const&
    ) noexcept;

可以看到,std::nothrow版本的new操作符实现中加入了修饰符_Ret_maybenull_,它的作用是告诉编译器,该函数返回的指针可能为空。

之前抛出异常的代码使用std::nothrow后,就不会抛出异常了,执行了返回值为空的逻辑,保证了程序的健壮性。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int main() {

    for (size_t i = 0; i <( 2<<30); i++)
    {
        auto p = new(std::nothrow) int[2<<10];
        if (p == nullptr) {
            printf("new failed\n");
        }
    }
    return 0;
}
//输出:new failed

3. 指定内存上构建对象

如上讲述的new操作符,都是通过operator new函数分配内存,然后调用对象的构造函数。一旦涉及到内存分配动作,便有可能出现内存分配失败的情况,无论是抛出异常还是返回空指针都不能构建出对象,都会影响预定的实现逻辑。为此,C++标准提供对应的解决办法,那就是使用placement new——在指定的内存上分配对象,而不是通过operator new函数分配内存。 具体的书写方式如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

char buffer[sizeof(int)];
int* p = new (buffer) int(123);  // 在指定内存上构造对象
//some code
p->~int();  // 显式析构

注意:

  • 预分配的内存必须足够大,以容纳对象的大小。在两个字节的内存上分配一个int对象是不合法的。
  • new操作符不会分配内存,只负责调用构造函数。所以指针用完后,不能用delete操作符销毁对象,而是应该手动调用析构函数。
  • 预分配的内存需要手动释放,否则会导致内存泄漏。
  • placement new 是构建内存池、自定义容器、或管理对象生命周期粒度精细化的重要技术,但也带来了较高的责任。其使用场景需极度谨慎,并确保对象构造与析构始终成对出现。

4. delete 指针需要判空吗

new 完判空一样,好像delete指针时,大家也会默认加一个判空,避免空指针的问题,但是这真的有必要吗? 其实也是完全没有必要的,C++ 明确规定,对空指针使用 delete 操作是安全的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int* p = nullptr;
delete p;  // 无操作,不会导致崩溃
p=nullptr; // 一定要置空

我们知道了delete空指针是安全的,所以需要注意,delete完指针后,一定要将指针制空,避免出现double free的问题——多次删除同一非空指针(悬空指针)是有问题的

问题:空的shared_ptr、unique_ptr在reset前需要判空吗?

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-05-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员的园 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【C++内存管理】—— 策略、陷阱及应对之道
在前面的学习中,我们已经掌握了C语言的动态内存管理,包括 malloc,realloc,calloc,free 等用于动态开辟和释放内存的函数,忘记了?没关系,点击一键复习 C语言动态内存管理
换一颗红豆
2025/02/16
1300
【C++内存管理】—— 策略、陷阱及应对之道
C++ 内存管理(一)
在编程时可以通过上图的几种方法直接或间接地操作内存。下面将介绍四种C++内存操作方法:
公众号guangcity
2019/09/20
1.6K0
C++ 内存管理(一)
从入门到精通C++(动态内存管理)
在C语言中用malloc和realloc还有colloc,来进行动态内存管理,三个函数的用处分别为: malloc:开辟一个新的空间,不对空间进行初始化和任何操作 **realloc:realloc() 函数用于重新分配之前通过 malloc()、calloc() 或 realloc() 分配的内存块的大小。它允许你在运行时改变内存块的大小。具体来说,realloc() 可以用来扩大或缩小内存块的大小,注意如果想扩容的空间还没有开辟空间,那么realloc的用法就等价于malloc。 calloc:calloc和malloc类似,但是calloc比malloc多一个步骤,就是初始化。
用户11305458
2024/10/09
2260
从入门到精通C++(动态内存管理)
“new出对象“原理的深层解密
讲解C++中有关new的知识,与malloc进行对比,以及深入探索new的实现原理.
初阶牛
2023/10/14
2820
“new出对象“原理的深层解密
C++从入门到精通——C++动态内存管理
C++动态内存管理涉及使用new和delete操作符来动态分配和释放堆内存。new用于在堆上分配内存并初始化对象,delete用于释放先前分配的内存。此外,C++还提供了智能指针如std::unique_ptr和std::shared_ptr来自动管理内存,以避免内存泄漏和悬挂指针。这些智能指针在超出作用域时会自动删除其所指向的对象。
鲜于言悠
2024/04/22
3050
C++从入门到精通——C++动态内存管理
C++内存分配失败的那些事儿
C++作为一门低级语言,直接操作内存是其核心特性之一。然而,在进行动态内存分配时,分配失败的问题始终存在。内存分配失败可能由多种原因引起,如内存耗尽或程序的内存限制等。对于内存分配失败,C++提供了两种常见的处理方式:抛出异常(std::bad_alloc)和返回空指针。如何选择合适的处理机制,取决于应用程序的需求以及对错误的容忍度。
程序员的园
2024/12/19
3640
C++内存分配失败的那些事儿
细说new与malloc的10点区别
前言 几个星期前去面试C++研发的实习岗位,面试官问了个问题: new与malloc有什么区别? 这是个老生常谈的问题。当时我回答new从自由存储区上分配内存,malloc从堆上分配内存;new/delete会调用构造函数/析构函数对对象进行初始化与销毁;operator new/delete可以进行重载;然后强行分析了一下自由存储区与堆的区别。回来后感觉这个问题其实回答得不怎么好,因为关于new与malloc的区别实际上很多。面试期间刚好是刚期末考完,之后是几个课设没时间去整理。今天花了点时间整理下
Tencent JCoder
2018/07/02
1.6K0
MSVC std::unique_ptr 源码解析
std::unique_ptr 是 c++ 11 添加的智能指针之一,是裸指针的封装,我们可以直接使用裸指针来构造 std::unique_ptr:
Kindem
2022/08/12
1.7K0
[C++] 深度剖析C_C++内存管理机制
定位new表达式语法:void* operator new(size_t, void* place) noexcept { return place; }
DevKevin
2024/07/25
1300
[C++] 深度剖析C_C++内存管理机制
【C++】Chapter02 内存管理
在以往的C语言中,我们一般使用malloc、calloc、realloc来进行内存管理;在C++中,虽然也可以继续兼容使用,但是难免会觉得用起来过于复杂和繁琐,所以C++中一般使用以下的两个操作符进行动态内存管理。
Skrrapper
2025/03/25
730
【C++】Chapter02 内存管理
探索C++内存管理
在 C 语言中,我们使用malloc、calloc等函数动态申请内存,再用free函数释放,这种方式需要开发者手动管理内存生命周期,稍有不慎就容易出现内存泄漏或非法访问的问题。而 C++ 在继承 C 语言内存管理方式的同时,引入了new和delete操作符,它们能自动调用对象的构造函数和析构函数,简化了内存管理流程。
秋邱
2025/05/11
810
探索C++内存管理
C/C++中内存管理
在C语言中,动态内存管理是通过一组库函数来实现的,主要包括 malloc、calloc、realloc 和 free。这些函数都定义在 <stdlib.h> 头文件中,用来分配和释放动态内存。下面详细解释它们的功能和使用方式:
用户11286421
2024/09/23
1010
C++初阶:C/C++内存管理、new与delete详解
在C++中,new和delete是用于动态内存管理的运算符,它们提供了对malloc、calloc、realloc和free等C语言内存管理函数的更高级的封装和功能。
是Nero哦
2024/01/30
3670
C++初阶:C/C++内存管理、new与delete详解
【C++】第六节—内存管理
现在我们必须要清楚,写一个程序,程序里面的变量都放在我们刚才介绍的那个区域呢?见下题目(面试题,面试会考这样的题,那可得好好学清楚了)
云边有个稻草人
2025/03/31
940
【C++】第六节—内存管理
C/C++内存管理
选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区) globalVar在哪里?____ staticGlobalVar在哪里?____ staticVar在哪里?____ localVar在哪里?____ num1 在哪里?____ char2在哪里?____ *char2在哪里?____ pChar3在哪里?____ *pChar3在哪里?____ ptr1在哪里?____ *ptr1在哪里?____ 是不是有点乱,看一下图解吧。
小志biubiu
2025/02/27
1080
C/C++内存管理
【C/C++】图文题目吃透内存管理
学习目标:了解C/C++内存的分段情况,C++内容管理方式、operator new与operator delete函数 、new和delete的实现原理、定位new的表达式、最后介绍相关面试题的解析
平凡的人1
2022/11/15
1.1K0
【C/C++】图文题目吃透内存管理
C++内存管理:深入理解与高效实践
在C++编程的世界里,内存管理是一项至关重要的技能。它不仅是性能优化的关键,更是确保程序稳定性和安全性的基石。C++赋予程序员直接操作内存的能力,这既是一种强大的武器,也可能成为潜在的陷阱。因此,掌握有效的内存管理策略,对于每一位C++开发者来说,都是通往高手之路的必经之路。
suye
2025/05/27
1150
【C++】拿下! C++中的内存管理
其中内存管理可能占有一定原因,只有我们打好内存管理的基础才能为大家做出贡献,那不然就只能赶快跑路了。 首先我们就要了解内存分布的情况是什么样的。
叫我龙翔
2024/02/29
2350
【C++】拿下! C++中的内存管理
【C++】语言深处的“精灵”:探索内存的奥妙
C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因 此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。
用户11456817
2025/05/13
700
【C++】语言深处的“精灵”:探索内存的奥妙
C++内存管理深度总结(近万字详解!)
在C语言中,动态内存管理主要通过malloc、calloc、realloc和free这四个函数进行。以下是一个简化的代码示例,展示了如何在C语言中使用这些函数来动态分配、使用和释放内存:
suye
2024/10/16
2690
相关推荐
【C++内存管理】—— 策略、陷阱及应对之道
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验