精华内容
下载资源
问答
  • 空指针
    千次阅读
    2021-03-30 22:48:56

    空指针

    1. 什么是空指针

    空指针是一个特殊的指针,也是一个对任何指针类型都合法的指针,指针变量具有空指针值,表示它处于闲置状态,没有指向任何有意义的内容

    2. 空指针的表示

    标准库定义了一个与数值0等价的符号常量NULL,程序中可以写ptr = 0 或者ptr = nullptr;两种写法都是表示ptr指针为空指针,C语言保证这个值不会是任何对象的地址

    3. 什么时候使用空指针

    如果定义指针时,未将指针指向特定的对象,应该将指针置空; int * ptr = NULL;

    使用free或者delete指针后,应该将指针置空,避免野指针的产生;delete *ptr ; ptr = NULL;

    4. 代码示例

    #include <stdio.h>
    int main(){
        int n = 3;
        int * int_ptr = NULL;
        void * void_ptr = NULL;
        void_ptr = &n;
        int_ptr = (int *)void_ptr;
        printf("%d\n", *(int_ptr));
        return 0;
    }
    
    更多相关内容
  • Java中的空指针异常

    千次阅读 多人点赞 2021-11-25 14:56:31
    1.1 异常的分类1.2 空指针异常引入1.3 空指针异常说明二、Java中的null三、空指针产生的常见情况及避免办法3.1 字符串常量未初始化,比较时导致空指针异常3.2 接口类型的对象没有使用具体的类进行初始化导致空指针...

    一、什么是空指针异常?

    1.1 异常的分类

    在这里插入图片描述
    NullPointerException是RuntimeException的一个子类,这是运行时异常,在编译时期不会触发。

    1.2 空指针异常引入

    Java是没有指针的,所以我们常说"Java 指针"就是指"Java 的引用"。空指针就是空引用,Java空指针异常就是引用本身为空,但却调用了方法,这个时候就会出现空指针异常。

    成员变量和方法是属于对象的(除去静态的),在对象中才存在相对应的成员变量和方法,然后通过对象去调用这些成员变量和方法。

    而对于空指针来说,它是不指向任何对象的,也就是没有所谓的成员变量和方法,这个时候去调用某些属性和方法时,就一定会出现空指针异常。

    1.3 空指针异常说明

    /**
     * @author QHJ
     * @date 2021/11/25  14:42
     * @description:
     */
    public class NullPointTest {
        private int a = 1;
        private int b = 2;
        public static void main(String[] args) {
            NullPointTest nullPointTest1 = new NullPointTest();
            System.out.println(nullPointTest1.a);
            System.out.println(nullPointTest1.sum());
    
            NullPointTest nullPointTest2 = null;
            System.out.println(nullPointTest2.b);
            System.out.println(nullPointTest2.sum());
    
        }
        public String sum(){
            return "这是sum()方法...";
        }
    }
    

    在这里插入图片描述
    说明:对象 nullPointTest1 是通过默认的无参构造方法实例出来的 NullPointTest 对象;对象 nullPointTest2 只是声明了一个空对象,并没有指向实际的对象,它没有响应的成员变量和方法,当调用不属于它的成员变量和方法时,引发空指针异常。

    二、Java中的null

    null是Java中一个很重要的概念,null 设计的初衷是为了表示一些缺失的东西,比如缺失的用户、资源或一些其他东西。

    • 首先,null是关键字,像public、static、final。它是大小写敏感的,你不能将 null 写成 Null 或 NULL,编译器将不能识别它们然后报错。

    • null是任何引用类型的默认值,不严格的说是所有object类型的默认值。就像你创建了一个布尔类型的变量,它将 false 作为自己的默认值,Java中的任何引用变量都将 nul l作为默认值。这对所有变量都是适用的,如成员变量、局部变量、实例变量、静态变量(但当你使用一个没有初始化的局部变量,编译器会警告你)。

    • null既不是对象也不是一种类型,它仅是一种特殊的值,你可以将其赋予任何引用类型,你也可以将 null 转化成任何类型。

    • null可以赋值给引用变量,但不能将null赋给基本类型变量。例如int、double、float、boolean。编译器将会报错。

      当直接将null赋值给基本类型,会出现编译错误。但是如果将null赋值给包装类object,然后将object赋给各自的基本类型,编译器不会报,但是你将会在运行时期遇到空指针异常。这是Java中的自动拆箱导致的。

    • 任何含有null值的包装类在Java拆箱生成基本数据类型时候都会抛出一个空指针异常。
      在这里插入图片描述

    • 如果使用了带有 null 值的引用类型变量,instanceof 操作将会返回 false。

      Integer iAmNull = null;
      if(iAmNull instanceof Integer){
          System.out.println("iAmNull is instance of Integer");
      }else{
          System.out.println("iAmNull is NOT an instance of Integer");
      }
      
      结果:iAmNull is NOT an instance of Integer
      
    • 不能调用非静态方法来使用一个值为 null 的引用类型变量。但是可以调用静态方法里值为 null 的引用类型变量,因为静态方法使用静态绑定,不会抛出空指针异常。
      在这里插入图片描述

    • 可以将 null 传递给方法使用,这时方法可以接收任何引用类型,例如public void print(Object obj)可以这样调用print(null)。从编译角度来看这是可以的,但结果完全取决于方法。null 安全的方法,如在这个例子中的 print() 方法,不会抛出空指针异常,只是优雅的退出。如果业务逻辑允许的话,推荐使用 null 安全的方法。
      在这里插入图片描述

    • 可以使用 = = 或者 != 操作来比较 null 值,但是不能使用其他算法或者逻辑操作,例如小于或者大于。在Java中 null == null 将返回true。

    三、空指针产生的常见情况及避免办法

    3.1 字符串常量未初始化,比较时导致空指针异常

    • 出现空指针

      String str = null;
      // 不推荐——把变量放在常量前,当变量为空时,运行时报空指针
      if (str.equals("zhangsan")){
          System.out.println("相等");
      }else{
          System.out.println("不相等");
      }
      

      在这里插入图片描述

    • 避免空指针

      String str = null;
      // 推荐——把常量放在变量前,运行时不会报错
      if ("zhangsan".equals(str)){
          System.out.println("相等");
      }else {
          System.out.println("不相等");
      }
      

      在这里插入图片描述

    3.2 接口类型的对象没有使用具体的类进行初始化导致空指针异常

    • 出现空指针

      // list1 没有使用具体的类进行初始化,在使用时会报错
      List list1;
      // list2 没有使用具体的类进行初始化,报空指针
      List list2 = null;
      System.out.println("不能直接使用list1...");
      System.out.println("list2:" + list2.isEmpty());
      

      在这里插入图片描述

    • 避免空指针

      List list3 = new ArrayList();
      System.out.println("list3:" + list3.isEmpty());
      

      在这里插入图片描述

    3.3 参数类型为包装类型,使用时自动拆箱导致空指针异常

    • 出现空指针

      Integer a = 1;
      Integer b = null;
      sum(a, b);
      
      
      public static String sum(Integer a, Integer b){
          System.out.println(a + b);
          return "这是sum()方法...";
      }
      

      在这里插入图片描述

      注意: null 值不能转换为基本数据类型!

    • 避免空指针

      Integer a = 1;
      Integer b = null;
      // 避免空指针——及时判空	
      if (a != null && b != null){
          sum(a, b);
      }else{
          System.out.println("参数不允许为空值!");
      }
      
      
      public static String sum(Integer a, Integer b){
          System.out.println(a + b);
          return "这是sum()方法...";
      }
      

      在这里插入图片描述

    3.4 对象为空,但未判空导致空指针异常

    • 出现空指针

      UserEntity userEntity = null;
      System.out.println(userEntity.getName() + "\t" +userEntity.getAge());
      

      在这里插入图片描述

    • 避免空指针

      UserEntity userEntity = null;
      if (userEntity != null){
          System.out.println(userEntity.getName() + "\t" +userEntity.getAge());
      }else {
          System.out.println("对象不能为空哦!");
      }
      

      在这里插入图片描述

    3.5 对key、value不能为null的容器put为null的key、value值导致空指针异常

    • 出现空指针

      Map<String, String> map = new Hashtable<>();
      map.put("name:", "张三");
      map.put("age:", "23");
      map.put("nick:", null);
      map.put("", "");
      System.out.println(map);
      

      在这里插入图片描述

    • 避免空指针

      Map<String, String> map = new HashMap<>();
      map.put("name:", "张三");
      map.put("age:", "23");
      map.put("nick:", null);
      map.put("", "");
      System.out.println(map);
      

      在这里插入图片描述

    3.6 方法或者远程服务返回的list不是空而是null,没有进行判空,就直接调用该list的方法导致空指针异常

    • 出现空指针

      // 假设是通过方法或远程调用获得的list
      List list = null;
      Stream stream = list.stream().filter(s -> s.equals("zhangsan"));
      System.out.println(stream);
      

      在这里插入图片描述

    • 避免空指针

      // 假设是通过方法或远程调用获得的list
      List list = null;
      if (list != null){
          Stream stream = list.stream().filter(s -> s.equals("zhangsan"));
          System.out.println(stream);
      }else {
          System.out.println("获取的集合list不能为空!");
      }
      

      在这里插入图片描述

    四、总结和技巧

    在遇到空指针错误时,要重点关注报错发生的所在行,通过空指针异常产生的两条主要原因(变量未初始化和对象为空)诊断具体的错误,主要注意以下几点:

    1. 检查使用之前是否进行了初始化
    2. 尽量避免在函数中返回 null 值,如果必须要返回 null 值,一定要给出详细的注释信息;
    3. 外部传值时,一定要及时的进行判空处理(除非有明确的说明可以为 null );
    4. 使用 equals() 方法时,要遵循 "常量在前变量在后"的原则;
    5. 使用 valueOf() 替换toString()
    6. 通过返回空的 Collection 或 Array 来替代 null 值;
    7. 使用注解 @NotNull、@Nullable;
    8. 定义合理的默认值,遵守约定(比如设置默认值,设置是否允许为空,从而形成合理的约定);
    9. 从数据库取数据,可以约束哪些字段不能为空。

    Java中的8种数据类型,变量的值可以有其默认值,假如没有对其正常赋值,Java虚拟机是不能正确编译通过的,所以使用基本数据类型一般是不会引起空指针异常的。也就是说,在实际开发中大多数的空指针异常主要与对象的操作相关。

    展开全文
  • 空指针在概念上不同于未初始化的指针。空指针可以确保不指向任何对象或函数; 而未初始化指针则可能指向任何地方。
  • 空指针:针对对象类型,对象在堆中分配“房子”。空指针(null)的对象没有分配到“房子”,所以,无法使用该对象。 使用任何的对象数据时,都要进行空指针判断。 通用的方式有三种: (1)null显式判断 (2)...

    1 简介

    空指针:针对对象类型,对象在堆中分配“房子”。空指针(null)的对象没有分配到“房子”,所以,无法使用该对象。
    使用任何的对象数据时,都要进行空指针判断。
    通用的方式有三种:

    1. null显式判断
    2. Objects.isNull(Object)隐式判断
    3. CollectionUtils工具判断空指针和空数据

    2 引用类型

    2.1 null直接显式判断空指针

    /**
         * 引用类型:直接使用null判断空指针
         */
        @Test
        public void nullRawTest() {
            String var1 = null;
            if (null == var1) {
                logger.info(">>>>>>>>>>var1 is null");
            } else {
                logger.info(">>>>>>>>>>var1 is:{}", var1);
            }
        }
    

    2.2 Objects.isNull隐式判断空指针

    /**
         * 引用类型:使用Objects工具判断空指针
         */
        @Test
        public void nullUnderObjectsTest() {
            String var1 = null;
            if (Objects.isNull(var1)) {
                logger.info(">>>>>>>>>>var1 is null");
            } else {
                logger.info(">>>>>>>>>>var1 is:{}", var1);
            }
        }
    

    3 集合类型

    3.1 null直接显式判断空指针

    /**
         * 集合类型:直接使用null判断空指针
         */
        @Test
        public void nullCollectionUnderRawTest() {
            List<String> list1 = null;
            if (null == list1) {
                logger.info(">>>>>>>>>>lsit1 is null");
            } else if (list1.isEmpty()) {
                logger.info(">>>>>>>>>>list1 is empty");
            } else {
                logger.info(">>>>>>>>>>list1 is:{}", list1);
            }
        }
    

    3.2 Objects.isNull隐式判断空指针

        /**
         * 集合类型:使用Objects判断空指针
         */
        @Test
        public void nullCollectionUnderObjectsTest() {
            List<String> list1 = null;
            if (Objects.isNull(list1)) {
                logger.info(">>>>>>>>>>lsit1 is null");
            } else if (list1.isEmpty()) {
                logger.info(">>>>>>>>>>list1 is empty");
            } else {
                logger.info(">>>>>>>>>>list1 is:{}", list1);
            }
        }
    

    3.3 CollectionUtils.isEmpty工具判断空指针和空

    CollectionUtils判断

    3.3.1 引入依赖

    Apache通用集合工具:4.4版本。

    <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-collections4 -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-collections4</artifactId>
        <version>4.4</version>
    </dependency>
    

    3.3.2 空指针

    /**
         * 集合类型:使用CollectionUtils判断空指针和空数据
         */
        @Test
        public void nullUnderCollectionUtilsTest() {
            List<String> list1 = null;
            if (CollectionUtils.isEmpty(list1)) {
                logger.info(">>>>>>>>>>var1 is null or empty");
            } else {
                logger.info(">>>>>>>>>>var1 is:{}", list1);
            }
        }
    

    3.3.3 空数据

    /**
         * 集合类型:使用CollectionUtils判断空指针和空数据
         */
        @Test
        public void emptyUnderCollectionUtilsTest() {
            List<String> list1 = Collections.emptyList();
            if (CollectionUtils.isEmpty(list1)) {
                logger.info(">>>>>>>>>>var1 is null or empty");
            } else {
                logger.info(">>>>>>>>>>var1 is:{}", list1);
            }
        }
    

    3 小结

    序号方法描述
    1null判断通用方法。所有对象均可以使用该方法判断空指针。
    2Objects.isNull(Object)判断通用方法。所有对象均可以使用该方法判断空指针。
    3CollectionUtils.isEmpty(Collection)判断Collection集合类型的数据判断空指针。

    4 完整样例

    package com.monkey.java_study.functiontest;
    
    import org.apache.commons.collections4.CollectionUtils;
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    import org.junit.Test;
    
    import java.util.*;
    
    /**
     * 空指针判断测试.
     *
     * @author xindaqi
     * @date 2021-12-16 9:55
     */
    public class NullPointerTest {
    
        private static final Logger logger = LogManager.getLogger(NullPointerTest.class);
    
        private static final String TEST_VALUE = "test";
    
        /**
         * 引用类型:直接使用null判断空指针
         */
        @Test
        public void nullRawTest() {
            String var1 = null;
            if (null == var1) {
                logger.info(">>>>>>>>>>var1 is null");
            } else {
                logger.info(">>>>>>>>>>var1 is:{}", var1);
            }
        }
    
        /**
         * 引用类型:使用Objects工具判断空指针
         */
        @Test
        public void nullUnderObjectsTest() {
            String var1 = null;
            if (Objects.isNull(var1)) {
                logger.info(">>>>>>>>>>var1 is null");
            } else {
                logger.info(">>>>>>>>>>var1 is:{}", var1);
            }
        }
    
        /**
         * 集合类型:直接使用null判断空指针
         */
        @Test
        public void nullCollectionUnderRawTest() {
            List<String> list1 = null;
            if (null == list1) {
                logger.info(">>>>>>>>>>list1 is null");
            } else if (list1.isEmpty()) {
                logger.info(">>>>>>>>>>list1 is empty");
            } else {
                logger.info(">>>>>>>>>>list1 is:{}", list1);
            }
        }
    
        /**
         * 集合类型:使用Objects判断空指针
         */
        @Test
        public void nullCollectionUnderObjectsTest() {
            List<String> list1 = null;
            if (Objects.isNull(list1)) {
                logger.info(">>>>>>>>>>list1 is null");
            } else if (list1.isEmpty()) {
                logger.info(">>>>>>>>>>list1 is empty");
            } else {
                logger.info(">>>>>>>>>>list1 is:{}", list1);
            }
        }
    
        /**
         * 集合类型:使用CollectionUtils判断空指针和空数据
         */
        @Test
        public void nullUnderCollectionUtilsTest() {
            List<String> list1 = null;
            if (CollectionUtils.isEmpty(list1)) {
                logger.info(">>>>>>>>>>var1 is null or empty");
            } else {
                logger.info(">>>>>>>>>>var1 is:{}", list1);
            }
        }
    
        /**
         * 集合类型:使用CollectionUtils判断空指针和空数据
         */
        @Test
        public void emptyUnderCollectionUtilsTest() {
            List<String> list1 = Collections.emptyList();
            if (CollectionUtils.isEmpty(list1)) {
                logger.info(">>>>>>>>>>var1 is null or empty");
            } else {
                logger.info(">>>>>>>>>>var1 is:{}", list1);
            }
        }
    }
    
    展开全文
  • 当C++遇到空指针异常......

    千次阅读 2022-04-21 08:54:58
    在Java语言中,有空指针异常,在编程时为了代码安全,在遇到空指针时,防止程序崩溃,会捕捉空指针异常,即NullPointerException异常类。 比如下面就是一段捕获NullPointerException异常的Java代码片段: try { 。...

    在编程实践中,空指针引起的错误屡见不鲜,指针解引用时遇到了空指针,说明程序有严重的错误,底层一般会通过某种机制通知上层模块,抛出空指针异常就是一种常见的方式。比如,在Java语言中就有空指针异常,如果程序在运行过程中,对空指针进行了访问,JVM就会抛出一个 NullPointerException 类型的异常,表示程序访问了空指针。如果程序不对这个异常进行处理,程序一般会崩溃,导致JVM进程终止。因此,在编程时为了代码安全,防止程序崩溃,并且能够从中恢复正常的话,一般会捕捉这个空指针异常并进行恢复处理。

    比如下面就是一段捕获 NullPointerException 异常的 Java 代码片段:

    try {
    	// 业务处理
    } catch (NullPointerException e) {
        e.printStackTrace();
        // 进行恢复操作
    }
    

    try语句块中的代码访问到空指针后,会抛出 NullPointerException ,随后在 catch 语句块捕获这个异常,并在语句块中进行处理。因为代码捕获了这个异常,从而避免了程序发生崩溃。

    我们知道, C++ 也支持异常,那么如果遇到了空指针,能否像 Java 那样捕获空指针异常呢?先编写一段代码测试一下:

    static void catch_null_except() {
    	int *p = nullptr;
    	try {
    		int x = 100 + *p; 
    	} catch (...) {
    		puts("catch exception!");
    	}
    }
    
    int main() {
    	catch_null_except();
    }
    

    因为p是一个空指针,对它进行解引用,肯定会导致内存违法访问。如果编译后并运行,会发现程序会崩溃:Segmentation fault (core dumped)。可见,语句块 catch (...) 并没有捕获到任何异常,由此可见,在 C++ 中是无法捕获空指针异常的!

    这是为什么呢?

    我们知道,空指针实际上指向的是虚拟内存地址为 0 的位置,它是一个特殊的位置,操作系统内核是不会为应用程序在这个 0 地址上分配物理内存页的。因此当应用进程访问这个位置时,内核不会像访问常规内存那样:发现该处地址没有分配物理页面,会产生一个缺页异常,然后异常处理程序为它分配一个物理页,并建立页表项,而是直接向进程抛出一个内存段错误的信号:SIGSEGV。我们知道,这个信号的缺省处理是终止进程并生成 coredump 文件,因此,当程序访问空指针时,内核会直接终止进程,也就是应用程序根本不会有抛出异常的机会,实际上应用程序压根就不知道它访问了空指针,因为它自己判断不了,抛异常也就无从谈起,所以尽管上述 C++ 程序使用了 catch 语句块,也没有异常可捕捉。同样,如果程序访问一个指向不属于进程地址空间的指针(也可以说是野指针,通常是编程错误造成的),它所指向的内存位置是无效线性地址,同样操作系统内核也会直接产生一个 SIGSEGV 信号,终止进程。

    我们不妨做个实验,在 Linux 环境下编写一个信号处理函数来处理 SIGSEGV 信号,并修改前面的 main 函数,看看会发生什么?代码如下:

    static void handler(int signo) {
    	std::cout << "signal no:" << signo << std::endl;
    	exit(-1);
    }
    
    int main() {
    	signal(SIGSEGV, handler);
    	catch_null_except();
    }
    

    程序运行时会输出:signal no:11,编号为11的信号正是SIGSEGV#define SIGSEGV 11。可见,尽管 C++ 无法捕获空指针异常,可以借助于信号机制来判断是否发生了空指针引起的段错误异常。

    但仅仅判断是否访问了空指针还不够,还得要想法让程序从内存违例的异常中恢复正常才有意义。

    我们先看一下常规操作是怎么规避空指针风险的,为了便于说明问题,可以设想这样一个例子,假设有一个函数,它的功能是统计一个整型指针数组的各个数组成员,并计算它们所指向的整数值的和。
    如下所示:

    int sum(int **array, size_t num) {
    	int i = -1;
    	int sum = 0;
    
    	while (++i < num) {
    		if (array[i] == NULL) {
    			continue;
    		}
    		sum += *array[i];
    	}
    
    	return sum;
    }
    
    int main() {
    	int x=1, y=2, z=3;
    	int *array[4];
    	array[0] = &x;
    	array[1] = NULL; // 空指针 
    	array[2] = &y;
    	array[3] = &z;
    	
    	int s = sum(array, 4);
    	printf("sum=%d\n", s);
    	puts("exit main");
    }
    

    sum() 的参数 array 数组,它里面存放的数据成员是整型指针,因为它是作为输入参数由外面传入进来的,不能保证里面没有空指针,为了加固程序的健壮性,一般会进行防御性编程,比如在每次解引用指针前,先判断是否是空指针,如果是,就忽略不计。

    因此,在 sum() 函数内的 while 循环中,需要每次判断从数组取得的元素是否是空指针。

    if (array[i] == NULL) {
    	continue;
    }
    

    虽然传入的数组参数包含空指针的概率极低,但为了安全起见,这个过程仍不得不进行。概率很低,但又不得不用,就像一块狗皮膏药一样贴在那儿,而且几乎就是全程在做无用功,那么,在保证代码安全的前提下,有没有方法来去掉这个发生概率很低的逻辑判断?

    如果要达到这个目的,一个是要能够检测到空指针,显然可以使用前面介绍的 SIGSEGV 信号处理机制来实现,另一个是检测到空指针之后,能让程序跳过这个指针,让程序不再访问它就行了。当然,检查空指针不能在while循环中, 如果每次循环都有额外的开销,还不如直接使用if语句判断呢!

    可见,方案的关键使用一定的方法跳过这条空指针,即如何在信号 handler() 中来通知函数 sum() 遇到了空指针,在下一次循环时跳过这个空指针?Linux 系统中为信号机制提供了一对函数:siglongjmp() 和 sigsetjmp(),它们实现了信号处理程序的流程进行非局部跳转的功能(所谓非局部跳转是指可以从一个函数内直接跳转到另一个函数的内部某处位置)。可以在检测到空指针时,使用它们让程序指令跳转到预定的目标地址,从而跳过那段访问空指针的代码,接着使用 C++ 的异常机制,通过 throw 一个异常的方式通知上层调用模块。

    修改代码如下:

    class invalid_ptr {
    	const char *message;
    public:
    	invalid_ptr(const char *msg) : message(msg) {
    	}
    	
    	const char *what() const {
    		return message;
    	}
    };
    
    static sigjmp_buf jmpbuf;
    static volatile sig_atomic_t jumpok = 0;
    
    static void handler(int signo) {
    	if (jumpok == 0) return;
    	puts("meet the invalid ptr");
    	siglongjmp(jmpbuf, 1);
    	puts("nerver print this message");
    	return;
    }
    
    int sum(int **array, size_t num) {
    	typedef void (*sighandler_t)(int);
    	sighandler_t old = signal(SIGSEGV, handler);
    
    	if (sigsetjmp(jmpbuf, 1)) {
    		puts("return to the main loop after skip the invalid pointer");
    		signal(SIGSEGV, old);
    		throw invalid_ptr("exception: dereference a invalid pointer!");
    	} else
    		jumpok = 1;
    
    	int sum = 0;
    	for (int i = 0; i < num; i++) {
    		sum += *array[i];
    	}
    
    	signal(SIGSEGV, old); // 恢复旧的处理方法
    	return sum;
    }
    
    int main() {
    	int x=1, y=2, z=3;
    	int *array[4];
    	array[0] = &x;
    	array[1] = NULL; // 空指针 
    	array[2] = &y;
    	array[3] = &z;
    	
    	try {
    		int s = sum(array, 4);
    		printf("sum=%d\n", s);
    	} catch (const invalid_ptr &ex) {
    		cout << ex.what() << endl;
    	}
    	
    	puts("exit main");
    }
    

    程序运行结果如下:

    meet the invalid ptr
    return to the main loop after skip the invalid pointer
    exception: dereference a invalid pointer!
    exit main

    该程序的关键在于siglongjmp() 和 sigsetjmp()的组合使用,sigsetjmp(jmpbuf, 1) 用来设置程序跳转的目标处,调用它时,会把此处的上下文信息保存在 jmpbuf 参数中,并返回 0,说明不是从 siglongjmp() 跳过来的。当发生 SIGSEGV 异常时,调用信号处理程序 handler,它调用 siglongjmp(jmpbuf, 1) 时不再从handler中返回,而是直接跳转到 jmpbuf 参数保存的目标地址处,也就是 sigsetjmp() 的返回处,同时让第二个参数1从 sigsetjmp() 处作为它的返回值,程序流程转到此处继续运行,因为返回值不为 0,说明是从 handle r跳过来的,即发生了 SIGSEGV 异常,抛出 invalid_ptr 异常。

    上述例子虽然程序遇到异常没有让程序崩溃,只是粗暴的放弃,比较生硬,并没有任何恢复的逻辑操作。有没有更好的方案呢?比如检测到空指针之后,不是直接抛异常,而是能让程序跳过这个指针,从下一个数组成员开始,显然这是比较好的一种方案,也就是能从异常中恢复正常。

    修改代码如下:

    int sum(int **array, size_t num) {
    	typedef void (*sighandler_t)(int);
    	sighandler_t old = signal(SIGSEGV, handler);
    
    	volatile int i=-1;
    	volatile int sum = 0;
    
    	if (sigsetjmp(jmpbuf, 1))
    		puts("return to the main loop after skip the invalid pointer");
    	else
    		jumpok = 1;
    
    	while (++i < num) {
    		sum += *array[i];
    	}
    
    	signal(SIGSEGV, old); // 恢复旧的处理方法
    	return sum;
    }
    
    int main() {
    	int x=1, y=2;
    	int *array[4];
    	array[0] = &x;
    	array[1] = NULL; // 空指针 
    	array[2] = &y;
    	array[3] = (int *)(0x12345678); // 模拟一个野指针 
    	
    	int s = sum(array, 4);
    	printf("sum=%d\n", s);
    	puts("exit main");
    }
    

    编译并执行这段代码,输出的 log 如下:

    meet the invalid ptr
    return to the main loop after skip the invalid pointer
    meet the invalid ptr
    return to the main loop after skip the invalid pointer
    sum=3
    exit main

    根据前面的分析,当发生了 SIGSEGV 异常之后,程序流程跳转到 sigsetjmp() 的返回位置,然后继续执行,此时,数组索引再次加1之后,刚好跳过了空指针的位置,也就忽略了此处的空指针。

    在main()函数中,还专门模拟了一个“野指针”,即array数组中的第四项:array[3] = (int *)(0x12345678); 加上第二项空指针array[1] = NULL;,程序运行时会发生两次内存段错误,一次空指针,一次野指针,经过信号处理程序接收 SIGSEGV 信号和 siglongjmp 跳转,可以跳过这两个错误项。从输出的 log 中也可以看出,程序先后两次遇到了无效指针的错误,都被程序正确处理了,两个有效的数组元素参与了计算,计算结果 sum=3,正好是这两个元素所指整数的和。可见,该方案同第一个方案相比,它的功能还有所增强,可以判断出某种形式的野指针,并忽略它;同第二个方案相比,遇到错误不是粗暴地把结果丢弃,而是忽略无效的数据,显然这样更符合函数的目的,达到了第一种方案的目的。其次,不依赖于特定语言的异常机制,像C语言也可以使用此方案,调用者对发生异常是无感的,不像前面,还得要使用 try…catch 来捕捉。

    程序的核心功能是 while 循环块,代码非常利索,没有了判断空指针的逻辑,去掉了那块狗皮膏药,也没有增加任何开销。当然准备和收尾工作还是有开销的,但它们只执行一次,而开销最大的核心功能部分降低了的开销,但前面的方案,不管有没有空指针,每次循环时都有进行一次空指针判断的开销。如果不发生内存段错误事件,每次循环操作它都会痛痛快快地执行完,毫不拖泥带水,中间没有任何逻辑判断,也就没有跳转指令来打断指令流水线,只有当发生了内存段错误(当然概率很低),才会有额外的开销。如果发生了内存段错误,会触发信号的处理机制,并通过 siglongjmp 跳转到一个正确的位置继续执行,相当于程序在运行过程中发生异常后跑飞了,siglongjmp 又把它拉回到正常的 sigsetjmp 轨道上,避免了程序崩溃。

    当然,为了说明问题,这个例子中有许多指针在循环中遍历,如果只有一个指针被访问,直接使用 if 语句来判断空指针显然是最简单的方案。何况这个例子使用了 sigsetjmp 和 siglongjmp,让程序从一个函数的内部直接跳转到另一个函数的内部,有点黑科技的味道,违反了结构化编程,代码让人不易理解,而且容易出现 bug。大家可能也注意到了它的局部变量 i 和 sum 都使用volatile修饰了,使用voaltile修饰局部变量在一定程度上有性能损失,如果不加以修饰,编译时如果打开优化选项,如-O2,这些局部变量可能会优化掉,替换为寄存器,当使用 longjmp 跳转时,会用 jmpbuf 里面存放的寄存初始值来设置这些寄存器,导致程序状态不一致。其次,移植性也不好,在Windows、Linix、Unix平台的信号处理机制有一些差异性。因此,并不提倡使用,在本例中,使用逻辑判断空指针是最简单也最容易理解的方法,当然,如果能够保证程序没有错误,在某些应用场合也不失为一种优雅的解决方案。…

    展开全文
  • 空指针和野指针的区别

    多人点赞 2022-05-14 15:52:18
    主要介绍:什么是空指针,什么是野指针,野指针和空指针的区别,导致野指针出现的情况,野指针的危害有那些,该如何避免野指针的出现。 文章目录 前言 空指针 野指针 指针未初始化 指针越界访问 指针指向的空间...
  • Java空指针异常和解决办法

    千次阅读 2022-04-19 20:37:21
    目录问题描述:尝试1:尝试2:使用Optional防止出现空指针异常引用 问题描述: public class Main20220419 { public static void main(String[] args) { String id = null; Item item = ItemService.getItemById...
  • 【C语言基础】野指针与空指针

    千次阅读 多人点赞 2021-11-16 21:12:43
    全网最接地气的C语言野指针介绍,此处对于野指针与空指针知识点做一些简要的介绍,作者实属初学,写博客也是作者学习的一个过程,难免文章中有内容理解不到位或者有不当之处,还请朋友们不吝指正,希望大家多多给予...
  • 空指针就是空引用,java空指针异常就是引用本身为空,却调用了方法,这个时候就会出现空指针异常。可以理解,成员变量和方法是属于对象的(除去静态),在对象中才存在相对应的成员变量和方法,然后通过对象去调用这些...
  • C语言空指针NULL以及void指针

    千次阅读 2021-05-23 12:49:56
    空指针NULL在C语言中,如果一个指针不指向任何数据,我们就称之为空指针,用NULL表示。例如:int *p = NULL;注意区分大小写,null 没有任何特殊含义,只是一个普通的标识符。NULL 是一个宏定义,在stdio.h被定义为:...
  • 1 场景复现 实体列表,通过stream获取数据,findFirst后,直接使用get获取数据,抛出空指针异常,复现代码如下: @Test public void streamGetUnsafeTest() { List<UserEntity> userEntityList = new ArrayList();...
  • Android 防止空指针异常

    千次阅读 2019-07-29 10:23:40
    文章目录空指针异常防止空指针异常Java 注解Java 8 中的 Optional 类型Kotlin总结 空指针异常 先复习下异常。 异常分为 Exception 和 Error,Exception 和 Error 类都继承自Throwable类。 Exception(程序可恢复)...
  • C语言 野指针和空指针

    千次阅读 2021-09-26 10:01:02
    文章目录空指针问题思考:案例探索:不允许向NULL和非法地址拷贝内存野指针什么情况下会导致野指针?指针变量未初始化指针释放后未置空指针操作超越变量作用域如何规避野指针初始化时置 NULL释放时 置 NULL 空指针 ...
  • 空指针和野指针

    千次阅读 2018-05-02 15:54:35
    空指针常量0、0L、'\0'、3 - 3、0 * 17 (它们都是“integer constant expression”)以及 (void*)0 等都是空指针常量。至于系统选取哪种形式作为空指针常量使用,则是实现相关的。一般的 C 系统选择 (void*)0 或者...
  • c++空指针

    千次阅读 2021-04-04 17:47:39
    空指针不指向任何对象,在试图使用一个指针之前代码可以首先检查它是否为空。以下列出几个生成空指针的方法: (1)int* p1 = nullptr;这是c++11新引入的方法,nullptr是一个特殊的字面值,它可以被转换成任何其它...
  • 什么是空指针?如何从根源避免空指针NullPointException 什么是空指针异常(java.lang.NullPointException)? public class NullPointerException extends RuntimeException 当应用程序试图在需要对象的地方使用 ...
  • foreach循环报NPE空指针异常

    千次阅读 2021-03-01 07:59:20
    前言最近debug时忽然发现,如果一个集合赋值为null,那么对该集合进行foreach循环(也叫增强for循环)时,会报NPE(即空指针异常NullPointerException)。代码如下:final List list = null;// final List list = new ...
  • ​ 不管大大小小的项目,只要一个不注意,就会有java.lang.NullPointerException的异常抛出来,自己在测试的时候还好,但是线上项目一旦空指针切没有处理,那就问题大了。所以我们单测的开发好的需求的时候尽量吧...
  • EasyExcel报错空指针异常

    千次阅读 2021-08-19 18:58:44
    解决EasyExcel对表格的读报错空指针异常 前言:最近我们老大叫我们学习对Excel的导入和导出,我就着手开始研究如何实现效果了,故此我开始学习了 EasyExcel 突然遇到BUG了 出现一个空指针异常; 我返回官方文档 看到...
  • 空指针异常主要原因以及解决方案

    万次阅读 2020-03-23 00:14:44
    空指针异常产生的主要原因如下: (1)当一个对象不存在时又调用其方法会产生异常obj.method() // obj对象不存在 (2)当访问或修改一个对象不存在的字段时会产生异常obj.method() // method方法不存在 (3)字符串...
  • 关于java的空指针报错: 最常见的空指针报错是因为在一个类的对象指针创建之后再调用它,没有给他分配空间,直接调用这个对象的方法或者数据,当然这个说起来还是很简单的,大部分的人都不会犯这样的错误,但是在...
  • 最近在review代码的时候发现,使用了空指针调用成员函数,并且成员函数内部有使用到成员变量,居然没有出错。很是奇怪,就用一篇博客把关于空指针调用成员函数相关的内容总结起来。 本文实例源码github地址:...
  • import org.springframework.... } } 如果有报ApplicationContext空指针,则可能原因是没加载之前就往下走了,要在要 使用的类 前面加 @DependsOn("springContextUtils") ApplicationContext之getBean方法详解 转自:...
  • 解决VS2019取消对空指针的引用的警告问题,以及空指针报错问题 取消对空指针的引用警告 这个警告一般是在我们动态开辟了一块内存,并用指针指向这块新开辟的内存时出现,目前我遇到的都是这样,解决办法是在后面加一...
  • springboot中测试类使用@Autowired空指针处理方案
  • SpringBoot空指针异常及时间格式问题

    千次阅读 2022-04-12 18:39:41
    在用Springboot和bootstrap做项目时,添加数据的时候,增加完毕后跳转失败,并报空指针异常。在添加时间数据时,又报400错误,下面是出现的问题及阶级措施。 1、空指针异常 问题描述:跳转页面出现500错误,说明逻辑...
  • 关于空指针

    千次阅读 2018-06-14 22:34:30
    臭名昭著的空指针到底是什么?... 永远也不能得到空指针, 同样对 malloc() 的成功调用也不会返回空指针, 如果失败, malloc() 的确返回空指针, 这是空指针的典型用法: 表示 "未分配" 或者...
  • 二叉树空指针个数的理解

    千次阅读 多人点赞 2020-11-08 12:58:24
    引言 二叉树在使用链式存储时,每一...空指针的个数为n+1是众所周知的事,虽然不论哪种证明方法都仅仅是恒等式变形的结果,但去分析等式两边的意义是十分有助于理解关系本质的。 几种理解 1.以节点数为切入点 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 866,326
精华内容 346,530
关键字:

空指针