当前位置: 首页 > news >正文

[C/C++安全编程]_[中级]_[如何避免出现野指针]

场景

  1. Rust里不会出现野指针的情况,那么在C++里能避免吗?

说明

  1. 野指针是指指向无效内存地址的指针,访问它会导致未定义行为,可能引发程序崩溃、数据损坏或安全漏洞。它是 C/C++ 等手动内存管理语言中的常见错误,而 Rust 通过编译期检查几乎彻底消除了这一问题。

  2. 很遗憾,在C++里类成员指针变量是不会自动初始化的,它的指针地址是随机的,可能为0,可能为无效值。 而全部变量,C++都会默认初始化,局部变量没初始化就调用的话就会出现编译警告。没搞懂C++标准为什么单单留着类成员变量不自动初始化的的问题。

  3. C++11开始,可以使用新语法给成员变量在声明的时候直接赋值初始化。 这是开发自己手动做的工作,编译器不会代办。这种新语法还是减少了很多野指针的问题,比构造函数初始化列表方便多了。

class A
{
public:void* handle_ = NULL;int percent_ = 0;int64_t size_ = 0;
};
  1. C结构体,扁平数据结构可以用{0}赋值初始化。

  2. 总结下,C++的变量初始化规则:

  • 全局变量,在头文件里声明的或者在.cpp文件里声明的都会被编译器初始化,原始类型是0,指针类型是NULL

  • 类成员变量,静态和非静态的成员都不会被编译器初始化。非静态成员可以在声明时就手动赋值初始化,而静态非const成员必须在类外定义再次赋值初始化。

  • 局部非静态变量未初始化不能使用,会有编译警告。

  • 局部静态变量会被编译器自动初始化。

  • 非指针类对象不需要赋值初始化,因为它会调用构造函数自动初始化。如果是类成员变量,那么在创建类实例的时候会自动初始化。如果是局部变量,那么也会在声明时自动调用构造函数初始化。

例子

test-variable-init.cpp

#include <iostream>#include <memory>
#include <string>
#include <vector>
#include <stdint.h>
#include <assert.h>
#include "test-variable-init.h"using namespace std;class A;// 在头文件里声明了的全局变量,默认初始化.普通类型是0,指针类型是NULL;
int gLang;void* gWin;// 只在.cpp里声明的变量,默认初始化.普通类型是0,指针类型是NULL;int gAdd;static long gCount;A* gA;struct RGB
{int r;int g;int b;
};class A
{
public:void* handle_ = NULL;int percent_ = 0;int64_t size_ = 0;RGB rgb_ = {0};string str_;// 没有初始化string *str2_;private:static const bool bOk_ = false; // 可以直接赋值初始化static string *str3_; // 类静态成员非const,不能直接赋值初始化。
};string* A::str3_ = nullptr;#define NATIVE_FREE(a,name) shared_ptr<void> a##name(a,[](void* data){ free(data); cout << "call free" << endl; })void TestVariableInit()
{assert(gLang == 0);assert(gWin == NULL);assert(gAdd == 0);assert(gCount == 0);assert(gA == NULL);// 方法的static变量,默认初始化.static int bRun;assert(bRun == 0);// 方法的非static变量,UB(未定义行为),需要手动初始化。int fNumber = 0;  // 如果不手动初始化,编译错误,使用了未初始化的局部变量。cout << "fNumber: " << fNumber << endl;int* fDay = NULL; // 如果不初始化,编译错误,使用了未初始化的局部变量。//*fDay = 10; //RGB rgb2; // 如果不初始化,编译错误,使用了未初始化的局部变量。//cout << "rgb2 r: " << rgb2.r << " g: " << rgb2.g << " b: " << rgb2.b << endl;RGB rgb = {0};cout << "rgb r: " << rgb.r << " g: " << rgb.g << " b: " << rgb.b << endl;// RGB *pRgb; // `pRgb` 如果不初始化,编译错误,使用了未初始化的局部变量。RGB* pRgb = (RGB*)malloc(sizeof(RGB));memset(pRgb, 0, sizeof(RGB));cout << "pRgb: " << pRgb->r << endl;NATIVE_FREE(pRgb, pRgb);A a;cout << "a.size: " << a.size_ << endl;cout << "a.rgb r: " << a.rgb_.r << endl;// 未初始化,也不会编译报错。cout << "str2_ address: " << (int)a.str2_ << endl;
}int main()
{// 全部变量和静态变量都存储在全局的静态储存区。std::cout << "Hello World!\n";cout << "================ TestVariableInit ==============" << endl;TestVariableInit();
}

test-variable-init.h

#pragma onceextern int gLang;extern void* gWin;

输出

Hello World!
================ TestVariableInit ==============
fNumber: 0
rgb r: 0 g: 0 b: 0
pRgb: 0
a.size: 0
a.rgb r: 0
str2_ address: 0
call free

参考

  1. 如何避免出现悬垂指针
http://www.dtcms.com/a/288035.html

相关文章:

  • MySQL 写入性能优化全攻略(附 GitHub 面试题项目链接)
  • 相机参数的格式与作用
  • 大语言模型置信度增强实战指南
  • 第 3 篇:《WHERE 就是刷选项——像点外卖一样精确筛房!》
  • 【硬件】嵌入式问题
  • FPGA相关通信问题详解
  • XSS漏洞总结
  • 商业秘密视域下计算机软件的多重保护困境
  • 正点原子stm32F407学习笔记9——PWM 输出实验
  • 深度学习中的模型剪枝工具Torch-Pruning的使用
  • HAMR硬盘高温写入的可靠性问题
  • RHCE(2)
  • Qt Graphs 模块拟取代 charts 和 data visualization还有很长的路要走
  • 完美解决 Ubuntu 中自定义启动器图标重复的问题(以 MATLAB 为例)
  • 游戏开发日志
  • 操作系统-进程同步机制
  • 搭建比分网服务器怎么选数据不会卡顿?
  • SEO长尾关键词优化实战指南抢占市场
  • 基于DTLC-AEC与DTLN的轻量级实时语音增强系统设计与实现
  • 你的网站正在被Google最新算法惩罚吗?
  • SpringJDBC源码初探-JdbcTemplate类
  • xss的利用
  • 博图SCL语言中常用运算符使用详解及实战案例(下)
  • 抖音回应:没有自建外卖,就是在团购的基础上增加的配送功能
  • 前端开发技巧:浏览器模拟弱网络环境
  • Streamlit 官翻 4 - 快速参考、知识库 Quick Reference
  • 电脑windows系统深度维护指南
  • 网络包从客户端发出到服务端接收的过程
  • 初识C++——开启新旅途
  • 【每日算法】专题十五_BFS 解决 FloodFill 算法