精华内容
下载资源
问答
  • 初始化未完成

    千次阅读 2008-10-18 22:02:00
    初始化char时,可以char a=0;初始化string时,最好用""。string str="";而不能string str=0;
    初始化char时,可以char a=0;
    初始化string时,最好用""。string str="";而不能string str=0;
    展开全文
  • 这说明在spring初始化bean的时候,如果bean实现了InitializingBean接口,会自动调用afterPropertiesSet方法。 原文地址:https://www.cnblogs.com/study-everyday/p/6257127.html 问题 实现InitializingBean接...

    这里总结三种方法:

    一:InitializingBean 接口 

     

    这说明在spring初始化bean的时候,如果bean实现了InitializingBean接口,会自动调用afterPropertiesSet方法。

    原文地址:https://www.cnblogs.com/study-everyday/p/6257127.html

    问题

    实现InitializingBean接口与在配置文件中指定init-method有什么不同?

     

    InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候会执行该方法。

    测试程序如下:

    
     
    1. import org.springframework.beans.factory.InitializingBean;
    2. public class TestInitializingBean implements InitializingBean{
    3. @Override
    4. public void afterPropertiesSet() throws Exception {
    5. System.out.println("ceshi InitializingBean");
    6. }
    7. public void testInit(){
    8. System.out.println("ceshi init-method");
    9. }
    10. }

    配置文件如下:

    
     
    1. <bean id="testInitializingBean" class="com.TestInitializingBean" ></bean>

    Main主程序如下:

    
     
    1. public class Main {
    2. public static void main(String[] args){
    3. ApplicationContext context = new FileSystemXmlApplicationContext("/src/main/java/com/beans.xml");
    4. }
    5. }

    运行Main程序,打印如下结果:

    
     
    1. ceshi InitializingBean

    这说明在spring初始化bean的时候,如果bean实现了InitializingBean接口,会自动调用afterPropertiesSet方法。

    问题

    实现InitializingBean接口与在配置文件中指定init-method有什么不同?

    修改配置文件,加上init-method配置,修改如下:

    
     
    1. <bean id="testInitializingBean" class="com.TestInitializingBean" init-method="testInit"></bean>

    在配置文件中加入init-method="testInit"。

    运行Main程序,打印如下结果:

    
     
    1. ceshi InitializingBean
    2. ceshi init-method

    由结果可看出,在spring初始化bean的时候,如果该bean是实现了InitializingBean接口,并且同时在配置文件中指定了init-method,系统则是先调用afterPropertiesSet方法,然后在调用init-method中指定的方法。

    这方式在spring中是怎么实现的?

    通过查看spring的加载bean的源码类(AbstractAutowireCapableBeanFactory)可看出其中奥妙

    AbstractAutowireCapableBeanFactory类中的invokeInitMethods讲解的非常清楚,源码如下:

    
     
    1. protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd) throws Throwable {
    2. //判断该bean是否实现了实现了InitializingBean接口,如果实现了InitializingBean接口,则只掉调用bean的afterPropertiesSet方法
    3. boolean isInitializingBean = (bean instanceof InitializingBean);
    4. if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
    5. if (logger.isDebugEnabled()) {
    6. logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
    7. }
    8.  
    9. if (System.getSecurityManager() != null) {
    10. try {
    11. AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
    12. public Object run() throws Exception {
    13. //直接调用afterPropertiesSet
    14. ((InitializingBean) bean).afterPropertiesSet();
    15. return null;
    16. }
    17. },getAccessControlContext());
    18. } catch (PrivilegedActionException pae) {
    19. throw pae.getException();
    20. }
    21. }
    22. else {
    23. //直接调用afterPropertiesSet
    24. ((InitializingBean) bean).afterPropertiesSet();
    25. }
    26. }
    27. if (mbd != null) {
    28. String initMethodName = mbd.getInitMethodName();
    29. //判断是否指定了init-method方法,如果指定了init-method方法,则再调用制定的init-method
    30. if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
    31. !mbd.isExternallyManagedInitMethod(initMethodName)) {
    32. //进一步查看该方法的源码,可以发现init-method方法中指定的方法是通过反射实现
    33. invokeCustomInitMethod(beanName, bean, mbd);
    34. }
    35. }
    36. }

    总结

    1:spring为bean提供了两种初始化bean的方式,实现InitializingBean接口,实现afterPropertiesSet方法,或者在配置文件中同过init-method指定,两种方式可以同时使用

    2:实现InitializingBean接口是直接调用afterPropertiesSet方法,比通过反射调用init-method指定的方法效率相对来说要高点。但是init-method方式消除了对spring的依赖

    3:如果调用afterPropertiesSet方法时出错,则不调用init-method指定的方法。

     

    二:@PostConstruct

     

    @PostConstruct的API使用说明:

    PostConstruct 注释用于在依赖关系注入完成之后需要执行的方法上,以执行任何初始化。此方法必须在将类放入服务之前调用。支持依赖关系注入的所有类都必须支持此注释。即使类没有请求注入任何资源,用 PostConstruct 注释的方法也必须被调用。只有一个方法可以用此注释进行注释。应用 PostConstruct 注释的方法必须遵守以下所有标准:该方法不得有任何参数,除非是在 EJB 拦截器 (interceptor) 的情况下,根据 EJB 规范的定义,在这种情况下它将带有一个 InvocationContext 对象 ;该方法的返回类型必须为 void;该方法不得抛出已检查异常;应用 PostConstruct 的方法可以是 public、protected、package private 或 private;除了应用程序客户端之外,该方法不能是 static;该方法可以是 final;如果该方法抛出未检查异常,那么不得将类放入服务中,除非是能够处理异常并可从中恢复的 EJB。

    总结为一下几点:

    • 只有一个方法可以使用此注释进行注解;
    • 被注解方法不得有任何参数;
    • 被注解方法返回值为void;
    • 被注解方法不得抛出已检查异常;
    • 被注解方法需是非静态方法;
    • 此方法只会被执行一次;

    三:applicationlistener 

    什么是ApplicationContext? 它是Spring的核心,Context我们通常解释为上下文环境,但是理解成容器会更好些。 ApplicationContext则是应用的容器。Spring把Bean(object)放在容器中,需要用就通过get方法取出来。ApplicationEven:是个抽象类,里面只有一个构造函数和一个长整型的timestamp。ApplicationListener:是一个接口,里面只有一个onApplicationEvent方法。

    复制代码

    package org.springframework.context;
    public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    
        /**
         * Handle an application event.
         * @param event the event to respond to
         */
        void onApplicationEvent(E event);
    
    }

    复制代码

    所以自己的类在实现该接口的时候,要实装该方法。


    如果在上下文中部署一个实现了ApplicationListener接口的bean,那么每当在一个ApplicationEvent发布到ApplicationContext时,这个bean得到通知。其实这就是标准的Oberver设计模式。

     

    二、使用场景

    2.1、初始化处理

    在一些业务场景中,当容器初始化完成之后,需要处理一些操作,比如一些数据的加载、初始化缓存、特定任务的注册等等。一般来说一个项目启动时需要加载或者执行一些特殊的任务来初始化系统,通常的做法就是用servlet去初始化,但是servlet在使用spring bean时不能直接注入,还需要在web.xml配置,比较麻烦(见http://www.cnblogs.com/duanxz/p/3772979.html)。这个时候我们就可以使用Spring提供的ApplicationListener来进行操作。

    本文以在Spring boot下的使用为例来进行说明。首先,需要实现ApplicationListener接口并实现onApplicationEvent方法。把需要处理的操作放在onApplicationEvent中进行处理:

    然后,实例化ApplicationStartListener这个类,在Spring boot中通过一个配置类来进行实例化:

    随后,启动Spring boot服务,打印出一下内容:

    从打印的结果可以看出,ApplicationStartListener的onApplicationEvent方法在容器启动时已经被成功调用了。而此时初始化的容器为root容器。

     


    下面给出例子:
    首先创建一个ApplicationEvent实现类:

    复制代码

    import org.springframework.context.ApplicationEvent;  
      
    public class EmailEvent extends ApplicationEvent {  
        /** 
         * <p>Description:</p> 
         */  
        private static final long serialVersionUID = 1L;  
        public String address;    
        public String text;  
          
        public EmailEvent(Object source) {  
            super(source);  
        }  
          
        public EmailEvent(Object source, String address, String text) {  
            super(source);  
            this.address = address;  
            this.text = text;  
        }  
          
        public void print(){  
            System.out.println("hello spring event!");  
        }  
      
    }  

    复制代码

    给出监听器:

    复制代码

    import org.springframework.context.ApplicationEvent;  
    import org.springframework.context.ApplicationListener;  
      
    public class EmailListener implements ApplicationListener {  
      
        public void onApplicationEvent(ApplicationEvent  event) {  
            if(event instanceof EmailEvent){  
                EmailEvent emailEvent = (EmailEvent)event;  
                emailEvent.print();  
                System.out.println("the source is:"+emailEvent.getSource());  
                System.out.println("the address is:"+emailEvent.address);  
                System.out.println("the email's context is:"+emailEvent.text);  
            }  
              
        }  
      
    }  

    复制代码

    applicationContext.xml文件配置:  

    <bean id="emailListener" class="com.spring.event.EmailListener"></bean>  

    测试类:  

    复制代码

    import org.springframework.context.ApplicationContext;  
    import org.springframework.context.support.ClassPathXmlApplicationContext;  
    public class Test {  
        public static void main(String[] args) {  
            ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");  
              
            //HelloBean hello = (HelloBean) context.getBean("helloBean");  
            //hello.setApplicationContext(context);  
            EmailEvent event = new EmailEvent("hello","boylmx@163.com","this is a email text!");  
            context.publishEvent(event);  
            //System.out.println();  
        }  
    }  

    复制代码

    测试结果:
    hello spring event!
    the source is:hello
    the address is:boylmx@163.com
    the email's context is:this is a email text!

     

     

    -------------------------------------------------------------------------------------------------------------------------

    当spring 容器初始化完成后执行某个方法 防止onApplicationEvent方法被执行两次

    在做web项目开发中,尤其是企业级应用开发的时候,往往会在工程启动的时候做许多的前置检查。

      比如检查是否使用了我们组禁止使用的Mysql的group_concat函数,如果使用了项目就不能启动,并指出哪个文件的xml文件使用了这个函数。

    而在Spring的web项目中,我们可以介入Spring的启动过程。我们希望在Spring容器将所有的Bean都初始化完成之后,做一些操作,这个时候我们就可以实现一个接口:

    复制代码

    package com.yk.test.executor.processor
    public class InstantiationTracingBeanPostProcessor implements ApplicationListener<ContextRefreshedEvent> {
         @Override
         public void onApplicationEvent(ContextRefreshedEvent event) {
           //需要执行的逻辑代码,当spring容器初始化完成后就会执行该方法。
      }
     }

    复制代码

    同时在Spring的配置文件中,添加注入:

    <bean class="com.yk.test.executor.processor.InstantiationTracingBeanPostProcessor"/>

    但是这个时候,会存在一个问题,在web 项目中(spring mvc),系统会存在两个容器,一个是root application context ,另一个就是我们自己的 projectName-servlet  context(作为root application context的子容器)。

    这种情况下,就会造成onApplicationEvent方法被执行两次。为了避免上面提到的问题,我们可以只在root application context初始化完成后调用逻辑代码,其他的容器的初始化完成,则不做任何处理,修改后代码

    如下:

    @Override
       public void onApplicationEvent(ContextRefreshedEvent event) {
           if(event.getApplicationContext().getParent() == null){//root application context 没有parent,他就是老大.
                //需要执行的逻辑代码,当spring容器初始化完成后就会执行该方法。
           }
       }

     

    Spring 的事件传播机制 是基于观察者模式(Observer)实现的,它可以将 Spring Bean 的改变定义为事件 ApplicationEvent,通过 ApplicationListener 监听 ApplicationEvent 事件,一旦Spring Bean 使用 ApplicationContext.publishEvent( ApplicationEvent event )发布事件后,Spring 容器会通知注册在 bean.xml 中所有 ApplicationListener 接口的实现类,最后 ApplicationListener 接口实现类判断是否响应刚发布出来的 ApplicationEvent 事件。

    所以,要使用 Spring 事件传播机制需要以下四点:

    1. 建立事件类,继承 ApplicationEvent 父类

    2. 建立监听类,实现 ApplicationListener 接口

    3. 在配置文件 bean.xml 中注册写好的所有 事件类 和 监听类

    4. 需要发布事件的类 要实现 ApplicationContextAware 接口,并获取 ApplicationContext 参数

    随后便可以开始使用 Spring 事件传播机制为我们服务:(为了讲解流程的连贯性,续以上步骤来测试)

    4.1 在自己编写的需要发布事件的 Action 类中实例化 1 中编写好的事件类,并使用 ApplicationContext.publishEvent 发布事件

    5. 通过 Spring 调用 Action 方法,观察输出结果(本文使用 Junit 测试)

     

    以下为1-5步骤的源码:

    1. 建立事件类 ActionEvent.java 

     

    [java] view plain copy

     

    1. public class ActionEvent extends ApplicationEvent{  
    2.   
    3.     public ActionEvent(Object source) {  
    4.         super(source);  
    5.         System.out.println("This is ActionEvent");  
    6.     }  
    7. }  

     

    2. 建立监听类 ActionListener1.java、ActionListener2.java

     

     

    [java] view plain copy

     

    1. public class ActionListener1 implements ApplicationListener {  
    2.   
    3.     public void onApplicationEvent(ApplicationEvent event) {  
    4.         if(event instanceof ActionEvent){  
    5.             System.out.println("ActionListener1: "+event.toString());  
    6.         }  
    7.     }  
    8.   
    9. }  

    [java] view plain copy

     

    1. public class ActionListener2 implements ApplicationListener {  
    2.   
    3.     public void onApplicationEvent(ApplicationEvent event) {  
    4.         if(event instanceof ActionEvent){  
    5.             System.out.println("ActionListener2: "+event.toString());  
    6.         }  
    7.     }  
    8.   
    9. }  

     

    3. 在 bean.xml 中注册事件类和监听类

     

    [java] view plain copy

     

    1. <bean id="loginaction" class="com.ayali.action.LoginAction"/>  
    2. <bean id="listener1" class="com.ayali.action.ActionListener1"/>  
    3. <bean id="listener2" class="com.ayali.action.ActionListener2"/>  

     

    4. 编写 需要发布事件的 loginAction.java

     

     

    [java] view plain copy

     

    1. public class LoginAction implements ApplicationContextAware{  
    2.   
    3.     private ApplicationContext applicationContext;  
    4.       
    5.     public void setApplicationContext(ApplicationContext applicationContext)  
    6.             throws BeansException {  
    7.         this.applicationContext = applicationContext;  
    8.     }  
    9.       
    10.     public void login(String username, String password){  
    11.         ActionEvent event = new ActionEvent(username);  
    12.         this.applicationContext.publishEvent(event);  
    13.     }  
    14.   
    15. }  

     

    5. 编写测试方法

     

     

    [java] view plain copy

     

    1. public void testActionListener(){  
    2.     ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");  
    3.     LoginAction loginAction = (LoginAction) ctx.getBean("loginaction");  
    4.     loginAction.login("jack", "123");  
    5. }  

     

    输出结果为:

      1. This is ActionEvent  
      2. ActionListener1:com.ayali.action.ActionEvent[source=jack]  
      3. ActionListener2:com.ayali.action.ActionEvent[source=jack]  
      4.  

     

     


     

    展开全文
  • 一、背景知识在做WEB项目时,经常在项目第一次启动时利用WEB容器的监听、Servlet加载初始化等切入点为数据库准备数据,这些初始化数据是系统开始运行前必须的数据,例如权限组、系统选项、默认管理员等等。...

    一、背景知识及需求

    在做WEB项目时,经常在项目第一次启动时利用WEB容器的监听、Servlet加载初始化等切入点为数据库准备数据,这些初始化数据是系统开始运行前必须的数据,例如权限组、系统选项、默认管理员等等。而项目采用了Spring依赖注入来管理对象,而servlet并不受Spring的管理。若此时在servlet中注入Spring管理的对象,则无法使用,如下:

    public class InitServlet extends HttpServlet {
    
        @Autowired
        private IProductService productService;
        @Autowired
        private IUserService userService;
    ......
    }

    这个时候是无法使用上述中的两个service的,因为InitServlet不受Spring容器管理。虽然可以用getBean的方式手动获取service,但是违反了使用Spring的初衷。


    该篇文章也在之前【Spring实战】系列的基础上进行优化和深入分析,本篇就是在更换了hsqldb数据库并初始化了商品、普通用户和管理员用户需求时产生的。


    二、Spring提供的解决方案

    1、InitializingBean

    直接上代码

    /**
     * Created by Administrator on 2017/6/15.
     * spring容器启动后,初始化数据(产生一个默认商品、普通用户和管理员用户)
     */
    @Component
    public class InitServlet implements InitializingBean {
    
        @Autowired
        private IProductService productService;
        @Autowired
        private IUserService userService;
    
        @Override
        public void afterPropertiesSet() throws Exception {
            //库中没有商品则声称一个
            List<Product> products = productService.getProductList();
            if (null == products || products.isEmpty()){
                Product product = new Product();
                product.setProductName("Mango");
                product.setQuantity(100);
                product.setUnit("个");
                product.setUnitPrice(100);
                productService.saveProduct(product);
            }
    
            //库中没有用户则添加普通用户和管理员用户
            List<MangoUser> mangoUsers = userService.getUserList();
            if(null == mangoUsers || mangoUsers.isEmpty()){
                MangoUser mangoUser = new MangoUser();
                mangoUser.setUserName("mango");
                mangoUser.setPassword(StringUtil.md5("123456"));
                mangoUser.setRole("ROLE_USER");
                userService.saveUser(mangoUser);
    
                MangoUser mangoUser1 = new MangoUser();
                mangoUser1.setUserName("manager");
                mangoUser1.setPassword(StringUtil.md5("123456"));
                mangoUser1.setRole("ROLE_MANAGER");
                userService.saveUser(mangoUser1);
            }
        }
    
    }

    若采用XML来配置Bean的话,可以指定属性init-method。


    2、ApplicationListener

    //交给Spring管理,如果不是自动扫描加载bean的方式,则在xml里配一个即可
    @Component
    public class InitData implements ApplicationListener<ContextRefreshedEvent> {
    
        @Autowired
        private IProductService productService;
        @Autowired
        private IUserService userService;
    
        @Override
        public void onApplicationEvent(ContextRefreshedEvent event) {
            if (event.getApplicationContext().getParent() == null) {
                //库中没有商品则声称一个
                List<Product> products = productService.getProductList();
                if (null == products || products.isEmpty()){
                    Product product = new Product();
                    product.setProductName("Mango");
                    product.setQuantity(100);
                    product.setUnit("个");
                    product.setUnitPrice(100);
                    productService.saveProduct(product);
                }
    
                //库中没有用户则添加普通用户和管理员用户
                List<MangoUser> mangoUsers = userService.getUserList();
                if(null == mangoUsers || mangoUsers.isEmpty()){
                    MangoUser mangoUser = new MangoUser();
                    mangoUser.setUserName("mango");
                    mangoUser.setPassword(StringUtil.md5("123456"));
                    mangoUser.setRole("ROLE_USER");
                    userService.saveUser(mangoUser);
    
                    MangoUser mangoUser1 = new MangoUser();
                    mangoUser1.setUserName("manager");
                    mangoUser1.setPassword(StringUtil.md5("123456"));
                    mangoUser1.setRole("ROLE_MANAGER");
                    userService.saveUser(mangoUser1);
                }
            }
    
        }
    }

    注意是监听的ContextRefreshedEvent事件。

    在web 项目中(spring mvc),系统会存在两个容器,一个是root application context ,另一个就是我们自己的 projectName-servlet context(作为root application context的子容器)。这种情况下,就会造成onApplicationEvent方法被执行两次。为了避免上面提到的问题,我们可以只在root application context初始化完成后调用逻辑代码,其他的容器的初始化完成,则不做任何处理。

    event.getApplicationContext().getParent() == null


    3、@PostConstruct

    /**
     * Created by Administrator on 2017/6/15.
     * spring容器启动后,初始化数据(产生一个默认商品、普通用户和管理员用户)
     */
    @Component
    public class InitMango{
    
        @Autowired
        private IProductService productService;
        @Autowired
        private IUserService userService;
    
        @PostConstruct
        public void init() {
            //库中没有商品则声称一个
            List<Product> products = productService.getProductList();
            if (null == products || products.isEmpty()){
                Product product = new Product();
                product.setProductName("Mango");
                product.setQuantity(100);
                product.setUnit("个");
                product.setUnitPrice(100);
                productService.saveProduct(product);
            }
    
            //库中没有用户则添加普通用户和管理员用户
            List<MangoUser> mangoUsers = userService.getUserList();
            if(null == mangoUsers || mangoUsers.isEmpty()){
                MangoUser mangoUser = new MangoUser();
                mangoUser.setUserName("mango");
                mangoUser.setPassword(StringUtil.md5("123456"));
                mangoUser.setRole("ROLE_USER");
                userService.saveUser(mangoUser);
    
                MangoUser mangoUser1 = new MangoUser();
                mangoUser1.setUserName("manager");
                mangoUser1.setPassword(StringUtil.md5("123456"));
                mangoUser1.setRole("ROLE_MANAGER");
                userService.saveUser(mangoUser1);
            }
        }
    
    }



    下篇文章会分析其原理和源码实现。


    三、代码托管

    https://github.com/honghailiang/SpringMango


    四、实现原理

    其实现原理在【Spring实战】Spring注解工作原理源码解析中均能找到答案,简单说明下:

    1)在bean创建的过程中,初始化时会先调用@PostConstruct注解标注的方法,而后调用实现InitializingBean接口的afterPropertiesSet方法

    2)在finishRefresh()会分发事件,

    // Publish the final event.
    		publishEvent(new ContextRefreshedEvent(this));
    关心ContextRefreshedEvent事件的bean中的onApplicationEvent方法会被调用

    3)建议使用@PostConstruct注解,减少Spring的侵入性以及耦合性

    
    
    
    展开全文
  • C语言未初始化的局部变量是多少?

    千次阅读 2020-07-17 17:41:15
    C语言中,未初始化的局部变量到底是多少? 答案往往是: 与编译器有关。 可能但不保证初始化为0。 确定。 总之,全部都是些一本正经的形而上答案,这很令人讨厌。 但凡一些人给你滔滔不绝地扯编译器,C库,...

    C语言中,未初始化的局部变量到底是多少?

    答案往往是:

    • 与编译器有关。
    • 可能但不保证初始化为0。
    • 未确定。

    总之,全部都是些一本正经的形而上答案,这很令人讨厌。

    但凡一些人给你滔滔不绝地扯编译器,C库,处理器体系结构却给不出一个实际场景复现问题的时候,这人大概率在扯淡。

    又是周五回家时,大巴车上作短文一篇。

    其实,这个问题本身就是错误的问法,说全了能讲10万字,我们只要能在特定场景下确定其特定行为就OK了,当然,这就需要设计一个比较OK的实验。

    在演示一个实际代码行为之前,先给出一个知识, CPU不认识变量,更无法识别变量的名字,CPU只会从特定的内存位置取值或者将值存到特定的内存位置,因此当问一个变量的值是多少的时候,必须要知道这个变量对应的值被保存在什么地方。

    来看下面的代码:

    #include <stdio.h>
    
    void func1()
    {
    	int a;
    	printf("func1:%d\n", a);
    	a = 12345;
    }
    
    void func2()
    {
    	int b;
    	printf("func2:%d\n", b);
    }
    
    void func4()
    {
    	int d;
    	printf("func3:%d\n", d);
    }
    
    void func3()
    {
    	int c;
    	printf("func3:%d\n", c);
    	c = 54321;
    	func4();
    }
    
    void test_call()
    {
    	func3();
    }
    
    int main(int argc, char **argv)
    {
    	func1();
    	func2();
    
    	test_call();
    }
    

    我们有func1~func4一共4个函数,其内部均有一个未初始化的局部变量,它们的值到底是多少呢?

    对于这种局部变量,它们的值取决于:

    • 变量在栈中的位置。
    • 变量对应的栈位置在 之前 有没有被store过。

    可以看到,上述第一点标记了一个内存位置,第二点则是代码的行为,也就是说,只要有代码去store对应的位置, 且后续的代码没有reset该位置的值的话,该位置就会保留着原先被store后的值。

    验证非常简单,试一下就知道了:

    [root@localhost test]# ./a.out
    func1:0
    func2:12345
    func3:0
    func3:0
    

    按照函数调用栈帧的变化,func1的局部变量a和func2的局部变量b显然是位于同一个位置的,在func1被调用时,这是一块新的内存(可能在进入main之前有栈帧到达过这个位置),a的值取决于调入内存该位置的页面对应偏移的初始值,这取决于操作系统:

    • 操作系统在分配给程序页面时可能会将页面clear为零页。

    栈的分配不会涉及C库,这里显然并不涉及C库的行为,但类似malloc分配的内存则涉及C库了。

    打印结果,a的值为0,我们认为操作系统返回给了应用程序零页。接下来在func1中将其赋值12345之后函数返回,接下来调用func2的时候,在之前func1已经退出的栈帧位置重建栈帧,对应位置依然还是12345。

    我没有看到func1的ret操作后面有stack清0的代码指令。效率考虑,也不该有这样的指令。

    再看test_call函数,很明显,func3和func4调用使用的并不是同一个栈帧,因此即便是在func3中对c赋值了54321,也不会影响在其栈帧之上的func4的栈帧对应位置的值d。因此c和d的初始值均保持为0。

    那么,初始化一个局部变量和不初始化一个局部变量,在指令层面上,区别在哪里呢?

    很简单,亲眼看一下就知道,先看未初始化局部变量的func1:

    // int a;
    00000000004005ad <func1>:
      4005ad:   55                      push   %rbp
      4005ae:   48 89 e5                mov    %rsp,%rbp
      4005b1:   48 83 ec 10             sub    $0x10,%rsp
      4005b5:   8b 45 fc                mov    -0x4(%rbp),%eax
      4005b8:   89 c6                   mov    %eax,%esi
      4005ba:   bf 90 07 40 00          mov    $0x400790,%edi
      4005bf:   b8 00 00 00 00          mov    $0x0,%eax
      4005c4:   e8 b7 fe ff ff          callq  400480 <printf@plt>
      4005c9:   c7 45 fc 39 30 00 00    movl   $0x3039,-0x4(%rbp)
      4005d0:   c9                      leaveq
      4005d1:   c3                      retq
    

    再看初始化局部变量a为2222的版本:

    // int a = 2222;
    00000000004005ad <func1>:
      4005ad:   55                      push   %rbp
      4005ae:   48 89 e5                mov    %rsp,%rbp
      4005b1:   48 83 ec 10             sub    $0x10,%rsp
      4005b5:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)
      4005bc:   8b 45 fc                mov    -0x4(%rbp),%eax
      4005bf:   89 c6                   mov    %eax,%esi
      4005c1:   bf 90 07 40 00          mov    $0x400790,%edi
      4005c6:   b8 00 00 00 00          mov    $0x0,%eax
      4005cb:   e8 b0 fe ff ff          callq  400480 <printf@plt>
      4005d0:   c7 45 fc 39 30 00 00    movl   $0x3039,-0x4(%rbp)
      4005d7:   c9                      leaveq
      4005d8:   c3                      retq
    

    仅仅差了一条指令:

      4005b5:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)
    

    初始化的操作是依靠实实在在的指令完成的。


    总结一句, 函数返回在pop出当前栈帧的时候,并不会清理它遗留在栈帧里的数据,下个函数调用再次重用到该栈帧的内存时,未初始化的局部变量将会被遗留数据影响,从而变得不确定!

    所以,记得初始化你的局部变量。如果你不这样做,上帝终究会将你经理了的。


    浙江温州皮鞋湿,下雨进水不会胖。

    展开全文
  • c++中变量未初始化的隐患

    千次阅读 2018-03-06 15:42:08
    变量未初始化是C++编程中最为常见和易犯的错误之一。但是对于全局变量,我们可以不进行初始化,这个变量会默认的用零进行初始化,但是这通常不是一个好的习惯。我们今天先不探讨全局变量还是静态全局变量。那么对于...
  • C规定,未初始化变量的初值为0,这个清0的操作是由启动代码完成的,还有已初始化变量的初值的设置,也是由启动代码完成的。 为了启动代码的简单化,编译链接器会把已初始化的变量放在同一个段:.data,这个段的映像...
  • 变量未初始化是C++编程中最为常见和易犯的错误之一。但是对于全局变量,我们可以不进行初始化,这个变量会默认的用零进行初始化,但是这通常不是一个好的习惯。我们今天先不探讨全局变量还是静态全局变量。那么对于...
  • Android 判断 activity 初始化是否完成

    千次阅读 2018-08-28 10:31:34
    handler.removeMessages(333); handler.sendEmptyMessageDelayed(333,500); ...= PlayVideoActivity.this.getWindow().getDecorView().getWindowToken()) { ... Log.e("初始化未完成", "333"); } } } };
  • 数据库完全初始化解决办法

    千次阅读 2019-05-13 16:20:07
    数据库完全初始化解决办法数据库完全初始化现象原因解决办法拓展java.lang.NullPointerExceptionViews.size()>0 数据库完全初始化 现象 原因 数据中的默认视图被删除造成的 解决办法 恢复默认视图即可 ...
  • 基于最新Spring 5.x,详细介绍了Spring MVC 初始化流程的源码,主要包括DispatcherServlet与MVC子容器的初始化,以及各种MVC组件的初始化
  • Java类的初始化、变量的初始化

    万次阅读 2018-06-02 15:21:23
    Java类的初始化、变量的初始化知识点Java常量, final 修饰,值被设定后不能再被修改静态变量里, static 修饰,顾名思义,无须创建对象,便可在内存中申请一个存储空间进行存储成员变量, 也称实例变量,它随着当前...
  • 关于未初始化全局变量

    千次阅读 2015-03-07 15:25:03
    前几天发现未初始化全局变量一些特性,后来在一篇博客上发现有人说过这个问题 这是原博文地址 blog.csdn.net/liuqiaoyu080512/article/details/8455652 然后结合原博文,自己又做了几个实验 以下算是自己实验一遍...
  • 随波逐流,我也装上了win7 7100版,是远景的7100RC完美典藏版,但装上之后锐捷的3.63认证就让我吃了苦头,老是提示:“未能找到网卡或系统还未完成网卡初始化操作,请稍后再尝试认证”根据网上传说的改兼容性的方法操作...
  • C++认识初始化

    千次阅读 2015-08-29 18:32:55
    初始化是程序设计中一项重要的...使用未初始化的变量(或内存区域)是程序产生bug的重要原因之一。正确理解和使用初始化操作,要弄清以下几个问题。1.什么初始化在给初始化下定义前。先弄清楚两个概念。申明与定义。
  • 直接初始化与复制初始化

    千次阅读 2013-07-19 11:21:06
    这是C++中所支持的两种初始化方式。  复制初始化使用=符号,而直接初始化初始化式放在圆括号中。  (1)对于一般的内建类型,这两种初始化基本上没有区别。  int a(5);//直接初始化  int a=5;//复制初始化 ...
  • java中出现小程序查看器 启动:未初始化小程序的解决方法
  • 深入理解Java对象的创建过程:类的初始化与实例化

    万次阅读 多人点赞 2017-05-18 14:17:45
    在实例化一个对象时,JVM首先会检查相关类型是否已经加载并初始化,如果没有,则JVM立即进行加载并调用类构造器完成类的初始化。在类初始化过程中或初始化完毕后,根据具体情况才会去对类进行实例化。本文试图对JVM...
  • C++基础 内置类型和类类型的默认初始化和值初始化写在前面的话如果定义变量时候没有指定初值,则变量被默认初始化。 但是默认初始化还有另外一个含义,在定义内置类型时由于定义变量的位置不同,有可能会不发生初始...
  • 单例模式中静态变量初始化与不初始化什么区别?  public class Singleton {  private static Singleton obj = new Singleton();  public static int counter1;  public static int counter2 = 0;  ...
  • 一、我的问题是关于初始化C++类成员的。我见过许多这样的代码: CSomeClass::CSomeClass() { x=0; y=1; } 而在别的什么地方则写成下面的样子: CSomeClass::CSomeClass() : x(0), y(1) { } 我的...
  • idea初始化git 初始化git仓库

    千次阅读 2018-08-29 11:56:19
    git status 查看变化 主干发布 分支开发  git 初始化 和git的分支完成了      
  • 构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式。例如: class CExample { public: int a; float b; //构造函数初始化列表 CExample(): a(0),b...
  • 函数初始化

    千次阅读 2019-06-12 23:34:14
    然后执行构造函数,此时a=110,b未初始化。输出3; a=110,b=0. 然后st成员变量初始化结束,执行下一个static。输出1. 继续初始化下一个static 变量b 最后调用magimaFunction 输出4. 在进行static MagimaTest st ...
  • 最佳实践--Spring容器初始化完成之后执行某个方法
  • RT-Thread 自动初始化详解

    千次阅读 多人点赞 2019-06-28 15:34:09
    1.1、一般情况的初始化调用 1.2、使用自动初始化后 二、引入 三、自动初始化原理 3.1、6个自动初始化宏的定义 3.2、自动初始化过程 3.2.1、两个函数的实现 3.2.2、划分 3.2.3、示例 一、前言 在学RT-...
  • Qt中判断对象是否初始化结束

    千次阅读 2018-12-04 18:13:51
    在基于Qt SDK的C/C++编程中,在对象初始化未结束即构造函数未退出之前,无法通过emit发送任何信号。编译及运行都不会报错,但是实际上信号没有发送。因此要知道对象是否初始化结束不能通过简单的emit来完成。 我们...
  • 基本数据类型数值6类 (long/int/short/...然后为什么 Java 语言要这么规定呢?有什么内部机理吗?可能的原因如下,当我们新建一个对象时,Java会在Heap中申请一块内存区域用以存放类的数据。而成员变量就是类的数...
  • kubernetes之初始化容器

    千次阅读 2018-07-18 13:03:17
    初始化容器是什么? 在kubernetes中,一个pod可以包含多个容器,其中的init container,顾名思义主要负责初始化工作,一个pod也可以包含多个init container。后文统一用"初始化容器"表示"init ...
  • 【UCOSIII】UCOSIII的初始化和启动

    千次阅读 2018-06-22 18:32:26
    在使用UCOSIII之前我们必须先初始化UCOSIII,函数OSInit()用来完成UCOSIII的初始化,而且OSInit()必须先于其他UCOSIII函数调用,包括OSStart()。 一般UCOSIII的main函数遵循以下的格式编写: int main(void) { ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 506,244
精华内容 202,497
关键字:

初始化未完成是什么意思