精华内容
下载资源
问答
  • 2019-06-07 17:04:26

    Linux I2C驱动框架

    一、几个重要的对象

    在讨论I2C驱动框架前,先讨论几个重要的概念

    1、I2C总线

    struct bus_type i2c_bus_type = {
    	.name		= "i2c",
    	.match		= i2c_device_match,
    	.probe		= i2c_device_probe,
    	.remove		= i2c_device_remove,
    	.shutdown	= i2c_device_shutdown,
    	.pm		= &i2c_device_pm_ops,
    };
    

    I2C总线对应着/bus下的一条总线,这个i2c总线结构体管理着i2c设备I2C驱动匹配删除等操作,I2C总线会调用i2c_device_match函数看I2C设备和I2C驱动是否匹配,如果匹配就调用i2c_device_probe函数,进而调用I2C驱动probe函数

    特别提示:i2c_device_match会管理I2C设备I2C总线匹配规则,这将和如何编写I2C驱动程序息息相关

    2、I2C驱动

    struct i2c_driver {
    	int (*probe)(struct i2c_client *, const struct i2c_device_id *); //probe函数
    	struct device_driver driver; //表明这是一个驱动
    	const struct i2c_device_id *id_table; //要匹配的从设备信息(名称)
    	int (*detect)(struct i2c_client *, struct i2c_board_info *); //设备探测函数
    	const unsigned short *address_list; //设备地址
    	struct list_head clients; //设备链表
    };
    

    对应的是I2C驱动程序

    3、I2C设备

    struct i2c_client {
    	unsigned short addr; //设备地址
    	char name[I2C_NAME_SIZE]; //设备名称
    	struct i2c_adapter *adapter; //设配器,值I2C控制器
    	struct i2c_driver *driver; //设备对应的驱动
    	struct device dev; //表明这是一个设备
    	int irq; //中断号
    	struct list_head detected; //节点
    };
    

    对应的是I2C设备

    4、I2C设配器

    I2C设配器是什么?

    经过上面的介绍,知道有I2C驱动I2C设备,我们需要通过I2C驱动去和I2C设备通讯,这其中就需要一个I2C设配器I2C设配器对应的就是SOC上的I2C控制器

    struct i2c_adapter {
    	unsigned int id; //设备器的编号
    	const struct i2c_algorithm *algo; //算法,发送时序
    	struct device dev; //表明这是一个设备
    };
    

    其中的i2c_algorithm是算法的意思,对应的就是如何发送I2C时序

    struct i2c_algorithm {
        /* 作为主设备时的发送函数 */
    	int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
    			   int num);
    
        /* 作为从设备时的发送函数 */
    	int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
    			   unsigned short flags, char read_write,
    			   u8 command, int size, union i2c_smbus_data *data);
    };
    

    小结

    I2C驱动有4个重要的东西,I2C总线I2C驱动I2C设备I2C设备器

    • I2C总线:维护着两个链表(I2C驱动、I2C设备),管理I2C设备I2C驱动的匹配和删除等
    • I2C驱动:对应的就是I2C设备的驱动程序
    • I2C设备:是具体硬件设备的一个抽象
    • I2C设配器:用于I2C驱动和I2C设备间的通用,是SOC上I2C控制器的一个抽象

    以注册I2C驱动为例,简单讲解I2C总线的运行机制(I2C设备道理相同)

    • 1、注册I2C驱动
    • 2、将I2C驱动添加到I2C总线的驱动链表中
    • 3、遍历I2C总线上的设备链表,根据i2c_device_match函数进行匹配,如果匹配调用i2c_device_probe函数
    • 4、i2c_device_probe函数会调用I2C驱动probe函数

    经过上面的讲解,对I2C驱动框架应该有了基本的了解了,下面通过分析内核源码来深入学习I2C驱动框架

    二、内核源码分析

    1、注册I2C驱动

    编写I2C驱动程序时,通过调用i2c_add_driver函数来注册驱动,下面来分析这个函数发生了什么

    static inline int i2c_add_driver(struct i2c_driver *driver)
    {
    	return i2c_register_driver(THIS_MODULE, driver);
    }
    
    int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
    {
        /*
        	struct bus_type i2c_bus_type = {
    			.name		= "i2c",
    			.match		= i2c_device_match,
    			.probe		= i2c_device_probe,
    			.remove		= i2c_device_remove,
    			.shutdown	= i2c_device_shutdown,
    			.pm		= &i2c_device_pm_ops,
    		};
        */
        driver->driver.bus = &i2c_bus_type; // 绑定总线
        
        driver_register(&driver->driver); // 向总线注册驱动
        
        /* 遍历I2C总线上所有设备,调用__process_new_driver函数 */
        bus_for_each_dev(&i2c_bus_type, NULL, driver, __process_new_driver);
    }
    

    上面的程序可以看到,在调用i2c_add_driver后做了三件事,第一绑定总线,要记住这个总线结构体,第二向总线注册驱动,这算是I2C驱动框架的重点,第三遍历总线的设备,调用__process_new_driver函数

    乍一看,会绝对第三件事比较重要,好像匹配规则在这里面,其实这里面有一部分匹配规则,但并不是最重要的,最重要的是在driver_register函数中

    下面将重点分析driver_register函数

    int driver_register(struct device_driver *drv)
    {
        bus_add_driver(drv); // 将驱动添加到总线上
    }
    
    int bus_add_driver(struct device_driver *drv)
    {
        driver_attach(drv);
        
        /* 将驱动添加到总线的驱动链表(bus->p->klist_drivers) */
        klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); 
    }
    

    接下来分析driver_attach(drv)这个函数,这个函数是重头

    int driver_attach(struct device_driver *drv)
    {
    	return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
    }
    

    来看看bus_for_each_dev函数做了什么

    int bus_for_each_dev(struct bus_type *bus, struct device *start,
    		     void *data, int (*fn)(struct device *, void *))
    {
        /* 遍历总线的所有设备链表(bus->p->klist_devices)的所有设备,执行fn函数 */
    	while ((dev = next_device(&i)) && !error)
    		error = fn(dev, data);
    }
    

    会到上一个函数,我们知道bus_for_each_dev对应的fn函数是__driver_attach函数,也就是说,driver_attach函数会遍历总线上的所有设备执行__driver_attach函数,接下面来分析__driver_attach函数

    static int __driver_attach(struct device *dev, void *data)
    {
        /* 判断驱动和设备是否匹配 */
    	if (!driver_match_device(drv, dev))
    		return 0;
        
        /* 如果匹配的话,调用driver_probe_device函数 */
        if (!dev->driver)
    		driver_probe_device(drv, dev);
    }
    

    首先来看一看driver_match_device函数,通过这个函数我们可以知道匹配规则

    static inline int driver_match_device(struct device_driver *drv,
    				      struct device *dev)
    {
        /* 调用总线的macth函数 */
    	return drv->bus->match ? drv->bus->match(dev, drv) : 1;
    }
    

    请不要忘了现在的总线式i2c总线,对应的结构体上面已经给出,如下

    struct bus_type i2c_bus_type = {
    	.name		= "i2c",
    	.match		= i2c_device_match,
    	.probe		= i2c_device_probe,
    	.remove		= i2c_device_remove,
    	.shutdown	= i2c_device_shutdown,
    	.pm		= &i2c_device_pm_ops,
    };
    

    drv->bus->match对应的就是i2c_bus_type.match

    下面来分析i2c_device_match函数

    static int i2c_device_match(struct device *dev, struct device_driver *drv)
    {
        struct i2c_client	*client = i2c_verify_client(dev); // 有device获得i2c_client
        struct i2c_driver	*driver = to_i2c_driver(drv); // 有device_driver获得i2c_driver
        
        /* 调用i2c_match_id进行匹配 */
        return i2c_match_id(driver->id_table, client) != NULL;
    }
    

    driver->id_table是什么,来看一看id_table的定义

    struct i2c_device_id {
    	char name[I2C_NAME_SIZE];
    };
    

    其实就是一个字符串,表示设备的名字

    i2c_driver中有一个我们自己i2c_device_id数组,用来匹配i2c设备

    来看看怎么匹配

    static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
    						const struct i2c_client *client)
    {
        /* 字符串匹配 */
    	strcmp(client->name, id->name);   
    }
    

    可以知道匹配规则是

    strcmp(i2c_client->name, i2c_driver->i2c_device_id->name); 
    

    就是匹配驱动和设备中的名字

    好了,现在弄清楚匹配规则了,下面来看一看如果匹配成功后要干嘛

    回到下面这个函数

    static int __driver_attach(struct device *dev, void *data)
    {
        /* 判断驱动和设备是否匹配 */
    	if (!driver_match_device(drv, dev))
    		return 0;
        
        /* 如果匹配的话,调用driver_probe_device函数 */
        if (!dev->driver)
    		driver_probe_device(drv, dev);
    }
    

    可以看到匹配成功后,会调用driver_probe_device函数,下面看一看这个函数

    int driver_probe_device(struct device_driver *drv, struct device *dev)
    {
    	really_probe(dev, drv);
    }
    
    static int really_probe(struct device *dev, struct device_driver *drv)
    {
        /* 调用了总线的probe函数 */
        dev->bus->probe(dev);
    }
    
    struct bus_type i2c_bus_type = {
    	.name		= "i2c",
    	.match		= i2c_device_match,
    	.probe		= i2c_device_probe,
    	.remove		= i2c_device_remove,
    	.shutdown	= i2c_device_shutdown,
    	.pm		= &i2c_device_pm_ops,
    };
    

    可以知道,总线的probe函数是i2c_device_probe

    static int i2c_device_probe(struct device *dev)
    {
        /* 调用i2c驱动的probe函数 */
        driver->probe(client, i2c_match_id(driver->id_table, client));
    }
    

    通过上面的分析,可以知道i2c总线进制,当向内核注册i2c驱动时,会将i2c驱动添加到总线的链表中,遍历总线上所有设备,通过i2c_client->name, i2c_driver->i2c_device_id->name进行字符串匹配,如果匹配,就调用驱动程序的probe函数

    继续回到上面的程序

    int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
    {
        /*
        	struct bus_type i2c_bus_type = {
    			.name		= "i2c",
    			.match		= i2c_device_match,
    			.probe		= i2c_device_probe,
    			.remove		= i2c_device_remove,
    			.shutdown	= i2c_device_shutdown,
    			.pm		= &i2c_device_pm_ops,
    		};
        */
        driver->driver.bus = &i2c_bus_type; // 绑定总线
        
        driver_register(&driver->driver); // 向总线注册驱动
        
        /* 遍历I2C总线上所有设备,调用__process_new_driver函数 */
        bus_for_each_dev(&i2c_bus_type, NULL, driver, __process_new_driver);
    }
    

    刚刚我们分析了driver_register,下面来分析一下bus_for_each_dev

    其实bus_for_each_dev蕴含着i2c总线的另外一种匹配规则,不过不经常使用

    int bus_for_each_dev(struct bus_type *bus, struct device *start,
    		     void *data, int (*fn)(struct device *, void *))
    {
        /* 遍历总线上的设备链表(bus->p->klist_devices)的所有设备,调用fn函数 */
    	while ((dev = next_device(&i)) && !error)
    		error = fn(dev, data);	
    }
    

    由上面可知,fn函数对应的是__process_new_driver函数

    static int __process_new_driver(struct device *dev, void *data)
    {
        /* data对应驱动,to_i2c_adapter(dev)对应设备对应的适配器 */
    	return i2c_do_add_adapter(data, to_i2c_adapter(dev));
    }
    
    static int i2c_do_add_adapter(struct i2c_driver *driver,
    			      struct i2c_adapter *adap)
    {
    	i2c_detect(adap, driver);
    }
    
    static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
    {
        address_list = driver->address_list; // 获取驱动指定的地址
        
        temp_client->adapter = adapter; // 绑定好设配器(I2C控制器)
        
        /* 使用该是适配器,去探测所有地址 */
        for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1)
            i2c_detect_address(temp_client, driver);
    }
    
    static int i2c_detect_address(struct i2c_client *temp_client,
    			      struct i2c_driver *driver)
    {
        i2c_check_addr_busy(adapter, addr);
            
        i2c_default_probe(adapter, addr);
        
        /* 如果能到达这里,就说明该i2c控制器对应的总线上,该地址存在,调用驱动的detect函数进一步确认 */
        driver->detect(temp_client, &info);
        
        /* 如果驱动程序确认的话,生成i2c设备 */
        client = i2c_new_device(adapter, &info);
        list_add_tail(&client->detected, &driver->clients); // 将该设备添加到驱动程序的链表中
    }
    

    以上的做法是,遍历总线上的所有设备,拿到设备对应的设备器(I2C控制器),去给总线发送驱动指定好的地址,如果地址存在的话,就调用驱动的detect函数

    为什么会有这种方式?

    如果在不知道i2c设备在哪一条总线的情况下,这种方式就发挥了作用

    上面分析了注册I2C驱动,下面来分析注册I2C设备

    2、注册I2C设备

    内核通过i2c_new_device注册i2c设备

    struct i2c_client *
    i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
    

    需要指定i2c设配器和设备信息

    i2c_adapter可以通过i2c_get_adapter来获取

    struct i2c_adapter *i2c_get_adapter(int id)
    

    i2c_get_adapter可以获取哪个i2c控制器,获取哪一个i2c控制器取决于你的i2c设备接在哪条i2c总线上

    i2c_board_info结构体描述了设备的硬件信息

    struct i2c_board_info {
    	char		type[I2C_NAME_SIZE]; // 设备名称,用于与驱动匹配
    	unsigned short	addr; // 设备地址
    	int		irq; // 中断号
    };
    

    下面来详细分析i2c_new_device函数

    struct i2c_client *
    i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
    {
        struct i2c_client	*client;
        
        client->adapter = adap; // 设定设备的设配器
        client->addr = info->addr; // 地址
        client->irq = info->irq; // 中断号
        
        client->dev.bus = &i2c_bus_type; // 绑定总线
        
        device_register(&client->dev); // 向总线注册设备
        
        return client;
    }
    

    下面分析device_register的过程

    int device_register(struct device *dev)
    {
    	device_initialize(dev);
    	return device_add(dev);
    }
    
    int device_add(struct device *dev)
    {
        bus_add_device(dev);
        
        bus_probe_device(dev);
    }
    
    int bus_add_device(struct device *dev)
    {
        /* 将设备添加到总线的设备链表中(bus->p->klist_devices) */
        klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
    }
    
    void bus_probe_device(struct device *dev)
    {
    	device_attach(dev);
    }
    
    int device_attach(struct device *dev)
    {
        bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
    }
    
    int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
    		     void *data, int (*fn)(struct device_driver *, void *))
    {
        /* 遍历总线的驱动链表上的所有驱动,调用fn函数 */
    	while ((drv = next_driver(&i)) && !error)
    		error = fn(drv, data);
    }
    

    这里的fn函数指的是__device_attach函数

    static int __device_attach(struct device_driver *drv, void *data)
    {
        /* 判断是够匹配 */
    	if (!driver_match_device(drv, dev))
    		return 0;
    
    	return driver_probe_device(drv, dev);
    }
    

    driver_match_devicedriver_probe_device函数跟上面分析的完全相同,这里不再累赘

    至此注册i2c设备已经分析完,流程为,注册i2c设备,将i2c加入总线的设备链表中,调用总线的匹配函数判断是够匹配,如果匹配,就调用驱动的probe函数

    内核还有静态一种注册i2c设备的方法

    通过i2c_register_board_info注册,起始最后还是调用了i2c_new_device,这里简单分析一下

    int __init
    i2c_register_board_info(int busnum,
    	struct i2c_board_info const *info, unsigned len)
    {
        list_add_tail(&devinfo->list, &__i2c_board_list); // 将设备信息添加到链表中
    }
    
    static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
    {
    	list_for_each_entry(devinfo, &__i2c_board_list, list) {
    		if (devinfo->busnum == adapter->nr
    				&& !i2c_new_device(adapter,
    						&devinfo->board_info))
    	}
    }
    

    3、驱动如果使用设配器给设备发送数据

    通过上面的分析,我们已经知道了i2c总线的工作机制,下面来看看当i2c设备和i2c驱动匹配之后,驱动程序要怎么去和设备通讯

    驱动程序一般调用i2c_transfer来发送信息

    int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
    

    需要指定i2c设配器

    上面说过驱动程序是 i2c设配器向设备发送数据的,那么i2c_transferv底层肯定是通过i2c_adapter发送数据的

    int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
    {
        adap->algo->master_xfer(adap, msgs, num); // 通过设配器发送数据
    }
    

    三、总结

    i2c总线维护着两个链表,一个驱动链表,一个设备链表,每当注册进一个驱动(或设备),就会将其添加到总线上相应的链表上,然后遍历总线的设备(或驱动)链表的所有设备(或驱动),通过总线的匹配函数判断是够匹配,如果匹配,则调用驱动的probe函数,然后我们就可以在probe函数注册字符设备,创建设备节点,实现fops集等等

    更多相关内容
  • 一看就懂的i++和++i详解

    万次阅读 多人点赞 2020-05-09 22:10:35
    我相信很多朋友可能之前已经百度过i++和++i的相关文章了,也做过不少的练习,觉得自己已经深刻掌握了它们之间的原理了,真的是这样的吗?来试试计算一下我下面提供的几道练习,你就会发现你又不会了!

    前言

    转载请说明出处,本文来自Android菜鸟:https://blog.csdn.net/android_cai_niao/article/details/106027313 QQ:2717521606

    我相信很多朋友可能之前已经百度过i++和++i的相关文章了,也做过不少的练习,觉得自己已经深刻掌握了它们之间的原理了,真的是这样的吗?来试试计算一下我下面提供的几道练习,你就会发现你又不会了!

    示例代码

    请先自己心算一下答案,然后找个本子记下来,然后再跟我后面的答案对比,看你能做对几道题,能做对两题以上的我喊你大哥!!

    示例1

    int i = 0;
    i = i++; 
    System.out.println("i = " + i); 
    

    示例2

    int a = 2; 
    int b = (3 * a++) + a;
    System.out.println(b);
    

    示例3

    int a = 2; 
    int b = a + (3 * a++);
    System.out.println(b);
    

    示例4

    int i = 1;
    int j = 1;
    int k = i++ + ++i + ++j + j++; 
    System.out.println(k);
    

    示例5

    int a = 0;
    int b = 0;
    a = a++;
    b = a++;
    System.out.println("a = " + a + ", b = " + b);
    

    示例答案

    示例1:0
    示例2:9
    示例3:8
    示例4:8
    示例5:a = 1, b = 0

    i++ 和 ++i原理

    i++ 即后加加,原理是:先自增,然后返回自增之前的值
    ++i 即前加加,原理是:先自增,然后返回自增之后的值
    重点:这是一般人所不知道的,记住:不论是前++还是后++,都有个共同点是先自增。
    对于++i 就不说了,大多数人都懂,而对于 i++ 的原理,我用代码模拟其原理,如下:

    int temp = i;
    i = i + 1;
    return temp;  
    

    这3句代码就是上面所说的那样:i++是先自增,然后才返回自增之前的值。

    i++字节码分析

    有很多的人写的文章上都是说i++是先返回i的值,然后再自增,这是错误,是先自增,然后再返回自增之前的值,你可能会问,这有区别吗?答:有的。只要这个没理解对,则你在计算i++的相关问题时就有可能算错。

    有的人可能又会问了,我凭什么相信你,你有什么证据证明i++是先自增,然后再返回自增之前的值吗?我还真去找过证据,我们把class的字节码搞出来,分析一下就知道了,证明如下:

    public class Test {
        void fun() {
            int i = 0;
            i = i++;
        }
    }
    
    

    如上,我们写了一个超级简单的Test类。在cmd中输入这个命令(javap -c Test.class)以查看其生成的字节码,截图如下:
    i++字节码图
    我们关注fun()方法这一段就可以了,如下:
    i++字节码图

    这就是fun()函数对应的字节码了,我们一行一行的来分析,首先我们要说两个概念,一个是变量,一个是操作栈,fun()方法中有两个变量,哎,不是只有一个变量i吗?怎么会有两个?要了解这个你需要去学习字节码的相关知识,这里我们不深究,我画图如下:
    在这里插入图片描述
    如上图,变量有两个,在位置0的变量是什么我们不要管,系统自动分配的,你要知道的是位置1的变量其实就是我们定义的变量i就行了,接下来,我们来一行行分析fun()方法对应的字节码:
    “ iconst_0 ”: i代表int类型,const代表常量,0就代表整数0,整句话的意思就是把int类型的常量0放入操作栈的栈顶中,图解如下:
    在这里插入图片描述
    “ istore_1 ”: i代表int类型,store代表存储,1代表位置为1的变量,整句话的意思就是把操作栈中栈顶的值拿走,保存到位置为1的变量上,图解如下:
    在这里插入图片描述
    “ iload_1 ”: i代表int类型,load代表加载变量的值,1代表位置为1的变量,整句话的意思就是把位置为1的变量的值加载到操作栈的栈顶中,图解如下:
    在这里插入图片描述
    “ iinc 1, 1 ” : i代表int类型,inc(increment)代表增加,这里还有两个1,前面的1代表对位置为1的变量,第2个1代表增加1,因为有i += 3这种自增操作,这种情况的话第2个数字会是3,即自增3(iinc 1, 3)。“iinc 1, 1” 整句话的意思就是把位置为1的变量的值增加1,图解如下:
    在这里插入图片描述
    注:自增操作不会改变操作栈中的值,所以变量i的值自增后变成了1,而操作栈中的值还是0。

    “ istore_1 ”: i代表int类型,store代表存储,1代表位置1的变量,整句话的意思就是:把栈顶中的值拿走,保存到位置为1的变量中,图解如下:
    在这里插入图片描述
    所以,这几行字节码合起来看,i++不就是先自增,然后才返回自增之前的值嘛!!所以大家千万别搞错顺序了。 用代码理解的话,就相当于下面的代码:

    int temp = i;
    i = i + 1;
    return temp;  
    

    最后再把++i的字节码图也贴一下,大家可以根据我上面讲解的知识分析一下,就会知道++i和i++的区别了:

    void fun() {
        int i = 0;
        i = ++i;
    }
    

    ++i字节码图

    表达式原则

    表达式有一个原则:一个变量也是表达式,多个表达式的加减法运算都是从左到右进行的

    来看一下 if 语句的其中一种结构定义:

    if (条件表达式) 语句;
    

    用这个结构写个代码,如下:

    boolean b = true;
    int i = 0;
    if(b) i++;
    

    按照上面 if 语句的结构定义,if括号中是一个表达式,但是上面代码写了一个变量b进去,这是一个变量啊,怎么也能当成一个表达式么?没错,一个变量也是表达式。

    记住这个重点:一个变量也是表达式,多个表达式的加减法运算都是从左到右进行的

    讲到这里,估计有人会对这个运算顺序和乘法这些搞混了,示例如下:

    int a = 0;
    int b = a + a * 2;
    

    如上代码,按着我的说法,一个变量也是一个表达式,“b = a + a * 2”这里a出现了两次,就是有两个a表达式,从左到右算的话先算a + a,这肯定不对啊,这不是我的意思哈,乘除法的优先级还是不能乱的,那应该先算a * 2吗?也不对,应该是这样的:因为有乘法,所以a * 2优先组成表达式,而不是a + a组成表达式,也就是说总体上可以分为两个表达式:“a” 表达式 和 “a * 2” 表达式,这两个表达式相加肯定从左到右计算嘛,先算完a表达式的结果,再算a * 2表达式的结果。你可能会想先算a和先算a * 2有区别吗?答案是:有的,看完下面 的“示例3详解” 你就清楚了。

    示例答案详解

    示例1详解

    int i = 0;
    i = i++;  
    System.out.println("i = " + i);  // 结果:0
    

    先看i++,根据原理“先自增,然后返回自增之前的值”,i 自增后,i = 1,但是接着返回自增之前的值0,此时表达式变成 i = 0,0没有赋值给 i 时 i 的值是1,但是当把0赋值给 i 时,i 的值就又变成0了。因此 i = i++ 这句代码是做无用功,因为 i 的值最终还是和原来一样。

    示例2详解

    int a = 2; 
    int b = (3 * a++) + a;
    System.out.println(b);   // 结果:9
    

    int b = (3 * a++) + a;a++后,a = 3,并返回自增之前的值2,所以此时表达式为:

    int b = (3 * 2) + a;此时a的值已经是3了,表达式又变为:

    int b = (3 * 2) + 3; 所以b = 9

    示例3详解

    int a = 2; 
    int b = a + (3 * a++);
    System.out.println(b); // 结果:8
    

    这题和示例2几乎一样啊,只是换了一下顺序而已,为什么结果就不一样了呢?这就需要用到“表达式原则 了”:一个变量也是表达式,多个表达式的加减法运算都是从左到右进行的

    int b = a + (3 * a++);按一般人的想法是先算 3 * a++,a 先自增 a=3,然后返回自增之前的值2,所以此时表达式变为:

    int b = a + (3 * 2); 此时a的值为3了,表达式又变为:

    int b = 3 + (3 * 2);结果 b = 9

    我们说一个变量也是表达式,多个表达式的加减法运算都是从左到右进行的,这个理论你可能不能深刻体会,但是如果我把代码稍微改一下你就能理解了,如下:

    int b = (a * 1) + (3 * a++) 这个代码和 int b = a + (3 * a++) 是一样的,没有区别,但是看(a *1)你就很容易的知道要先算a * 1这个表达式,表达式的结果为2。

    所以,虽然 int b = a + (3 * a++) 中前面的a只是一个变量,但他也是一个表达式,a这个表达式和(3 * a++)这个表达式进行相加,多个表达式的运算都是从左到右进行的,所以先算a这个表达式,a表达式计算结果为2,所以表达式变成:

    int b = 2 + (3 * a++) 然后是a自增并返回自增之前的值2,所以表达式又变为:

    int b = 2 + (3 * 2);所以结果为8。此时a的值为3

    示例4详解

    int i = 1;
    int j = 1;
    int k = i++ + ++i + ++j + j++;  
    System.out.println(k);  // 结果:8
    

    有了前面3条示例的详解,相信这一条大家就能自己解答了,可以先自己解答一下,看结果是不是8,不是的话,再来看我下面的讲解:

    表达式原则说多个表达式的加减法运算都是从左到右进行的,这里的表达式有:i++、++i、++j、j++,都是加法,那我们就从左到右计算这4个表达式就OK了,如下:

    1、先算i++,i++之后i的值为2,并返回++之前的值1,所以整个表达式可以变为:

       1 + ++i + ++j + j++; // 此时的i值为2

    2、再计算++i,++i之后i的值为3,并返回3,所以整个表达式可以变为:

       1 + 3 + ++j + j++; // 此时i的值为3

    3、再计算++j,++j之后j的值为2,并返回2,所以整个表达式可以变为:

       1 + 3 + 2 + j++; // 此时j的值为2

    4、再计算j++,j++之后 j的值为3,并返回2,所以整个表达式可以变为:

       1 + 3 + 2 +2; // 结果为8,此时j的值为3

    示例5详解

    int a = 0;
    int b = 0;
    a = a++;
    b = a++;
    System.out.println("a = " + a + ", b = " + b); // a = 1, b = 0
    

    到了第5题,好像已经没有难度了,大家应该都能解出来了,但是为了文章的完整性,我还是分解一下,大家应该自己先算一次,算不对再来看我的分解:

    a = a++; a++之后a的值为1,并返回0,所以a的值由1又变回了0
    b = a++; a++之后a的值为1,并返回0,0赋值给b,所以b为0,而a还是1哦!!

    总结

    • i++ 即后加加,原理是:先自增,然后返回自增之前的值
    • ++i 即前加加,原理是:先自增,然后返回自增之后的值
    • 一个变量也是表达式,多个表达式的加减法运算都是从左到右进行的
    • 真实开发中,我们不会写这些复杂的i++代码,但是为什么还要掌握这些细节呢?答:笔试,万一笔试的时候遇到这样的题目呢?回答对了就可以加分了,因为这种题很多人是答不出来的,而你回答出来了,那可是很加分的哦!
    展开全文
  • MyEclipse2017ci3破解

    千次下载 热门讨论 2017-03-09 22:05:01
    myeclipse2017ci3破解,亲测。
  • 详解i++和++i,通俗易懂

    万次阅读 多人点赞 2021-01-19 19:16:31
    之前已经百度过i++和++i的相关文章了,感觉这篇写的最好理解,和大家分享一下! 示例代码 请先自己心算一下答案,然后找个本子记下来,然后再跟我后面的答案对比,看你能做对几道题,能做对两题以上的我喊你大哥!...

    前言

    之前已经百度过i++和++i的相关文章了,感觉这篇写的最好理解,和大家分享一下!

    示例代码

    请先自己心算一下答案,然后找个本子记下来,然后再跟我后面的答案对比,看你能做对几道题,能做对两题以上的我喊你大哥!!

    示例1

    int i = 0;
    i = i++; 
    System.out.println("i = " + i); 
    
     

      示例2

      int a = 2; 
      int b = (3 * a++) + a;
      System.out.println(b);
      
       

        示例3

        int a = 2; 
        int b = a + (3 * a++);
        System.out.println(b);
        
         

          示例4

          int i = 1;
          int j = 1;
          int k = i++ + ++i + ++j + j++; 
          System.out.println(k);
          
           

            示例5

            int a = 0;
            int b = 0;
            a = a++;
            b = a++;
            System.out.println("a = " + a + ", b = " + b);
            
             

              示例答案

              示例1:0
              示例2:9
              示例3:8
              示例4:8
              示例5:a = 1, b = 0

              i++ 和 ++i原理

              i++ 即后加加,原理是:先自增,然后返回自增之前的值
              ++i 即前加加,原理是:先自增,然后返回自增之后的值
              重点:这是一般人所不知道的,记住:不论是前++还是后++,都有个共同点是先自增。
              对于++i 就不说了,大多数人都懂,而对于 i++ 的原理,我用代码模拟其原理,如下:

              int temp = i;
              i = i + 1;
              return temp;  
              
               

                这3句代码就是上面所说的那样:i++是先自增,然后才返回自增之前的值。

                i++字节码分析

                有很多的人写的文章上都是说i++是先返回i的值,然后再自增,这是错误,是先自增,然后再返回自增之前的值,你可能会问,这有区别吗?答案:有的。只要这个没理解对,则你在计算i++的相关问题时就有可能算错。

                有的人可能又会问了,我凭什么相信你,你有什么证据证明i++是先自增,然后再返回自增之前的值吗?我还真去找过证据,我们把class的字节码搞出来,分析一下就知道了,证明如下:

                public class Test {
                    void fun() {
                        int i = 0;
                        i = i++;
                    }
                }
                

                  如上,我们写了一个超级简单的Test类。在cmd中输入这个命令(javap -c Test.class)以查看其生成的字节码,截图如下:
                  i++字节码图
                  我们关注fun()方法这一段就可以了,如下:
                  i++字节码图

                  这就是fun()函数对应的字节码了,我们一行一行的来分析,首先我们要说两个概念,一个是变量,一个是操作栈,fun()方法中有两个变量,哎,不是只有一个变量i吗?怎么会有两个?要了解这个你需要去学习字节码的相关知识,这里我们不深究,我画图如下:
                  在这里插入图片描述
                  如上图,变量有两个,在位置0的变量是什么我们不要管,系统自动分配的,你要知道的是位置1的变量其实就是我们定义的变量i就行了,接下来,我们来一行行分析fun()方法对应的字节码:
                  “ iconst_0 ” i代表int类型,const代表常量,0就代表整数0,整句话的意思就是把int类型的常量0放入操作栈的栈顶中,图解如下:
                  在这里插入图片描述
                  “ istore_1 ” i代表int类型,store代表存储,1代表位置为1的变量,整句话的意思就是把操作栈中栈顶的值拿走,保存到位置为1的变量上,图解如下:
                  在这里插入图片描述
                  “ iload_1 ” i代表int类型,load代表加载变量的值,1代表位置为1的变量,整句话的意思就是把位置为1的变量的值加载到操作栈的栈顶中,图解如下:
                  在这里插入图片描述
                  “ iinc 1, 1 ” i代表int类型,inc(increment)代表增加,这里还有两个1,前面的1代表对位置为1的变量,第2个1代表增加1,因为有i += 3这种自增操作,这种情况的话第2个数字会是3,即自增3(iinc 1, 3)。“iinc 1, 1” 整句话的意思就是把位置为1的变量的值增加1,图解如下:
                  在这里插入图片描述
                  注:自增操作不会改变操作栈中的值,所以变量i的值自增后变成了1,而操作栈中的值还是0。

                  “ istore_1 ” i代表int类型,store代表存储,1代表位置1的变量,整句话的意思就是:把栈顶中的值拿走,保存到位置为1的变量中,图解如下:
                  在这里插入图片描述
                  所以,这几行字节码合起来看,i++不就是先自增,然后才返回自增之前的值嘛!!所以大家千万别搞错顺序了。 用代码理解的话,就相当于下面的代码:

                  int temp = i;
                  i = i + 1;
                  return temp;  
                  
                   

                    最后再把++i的字节码图也贴一下,大家可以根据我上面讲解的知识分析一下,就会知道++i和i++的区别了:

                    void fun() {
                        int i = 0;
                        i = ++i;
                    }
                    
                     

                      ++i字节码图

                      表达式原则

                      表达式有一个原则:一个变量也是表达式,多个表达式的加减法运算都是从左到右进行的

                      来看一下 if 语句的其中一种结构定义:

                      if (条件表达式) 语句;
                      
                       

                        用这个结构写个代码,如下:

                        boolean b = true;
                        int i = 0;
                        if(b) i++;
                        
                         

                          按照上面 if 语句的结构定义,if括号中是一个表达式,但是上面代码写了一个变量b进去,这是一个变量啊,怎么也能当成一个表达式么?没错,一个变量也是表达式。

                          记住这个重点:一个变量也是表达式,多个表达式的加减法运算都是从左到右进行的

                          讲到这里,估计有人会对这个运算顺序和乘法这些搞混了,示例如下:

                          int a = 0;
                          int b = a + a * 2;
                          
                           

                            如上代码,按着我的说法,一个变量也是一个表达式,“b = a + a * 2”这里a出现了两次,就是有两个a表达式,从左到右算的话先算a + a,这肯定不对啊,这不是我的意思哈,乘除法的优先级还是不能乱的,那应该先算a * 2吗?也不对,应该是这样的:因为有乘法,所以a * 2优先组成表达式,而不是a + a组成表达式,也就是说总体上可以分为两个表达式:“a” 表达式 和 “a * 2” 表达式,这两个表达式相加肯定从左到右计算嘛,先算完a表达式的结果,再算a * 2表达式的结果。你可能会想先算a和先算a * 2有区别吗?答案是:有的,看完下面 的“示例3详解” 你就清楚了。

                            示例答案详解

                            示例1详解

                            int i = 0;
                            i = i++;  
                            System.out.println("i = " + i);  // 结果:0
                            
                             

                              先看i++,根据原理“先自增,然后返回自增之前的值”,i 自增后,i = 1,但是接着返回自增之前的值0,此时表达式变成 i = 0,0没有赋值给 i 时 i 的值是1,但是当把0赋值给 i 时,i 的值就又变成0了。因此 i = i++ 这句代码是做无用功,因为 i 的值最终还是和原来一样。

                              示例2详解

                              int a = 2; 
                              int b = (3 * a++) + a;
                              System.out.println(b);   // 结果:9
                              
                               

                                int b = (3 * a++) + a;a++后,a = 3,并返回自增之前的值2,所以此时表达式为:

                                int b = (3 * 2) + a;此时a的值已经是3了,表达式又变为:

                                int b = (3 * 2) + 3; 所以b = 9

                                示例3详解

                                int a = 2; 
                                int b = a + (3 * a++);
                                System.out.println(b); // 结果:8
                                
                                 

                                  这题和示例2几乎一样啊,只是换了一下顺序而已,为什么结果就不一样了呢?这就需要用到“表达式原则 了”:一个变量也是表达式,多个表达式的加减法运算都是从左到右进行的

                                  int b = a + (3 * a++);按一般人的想法是先算 3 * a++,a 先自增 a=3,然后返回自增之前的值2,所以此时表达式变为:

                                  int b = a + (3 * 2); 此时a的值为3了,表达式又变为:

                                  int b = 3 + (3 * 2);结果 b = 9

                                  我们说一个变量也是表达式,多个表达式的加减法运算都是从左到右进行的,这个理论你可能不能深刻体会,但是如果我把代码稍微改一下你就能理解了,如下:

                                  int b = (a * 1) + (3 * a++) 这个代码和 int b = a + (3 * a++) 是一样的,没有区别,但是看(a *1)你就很容易的知道要先算a * 1这个表达式,表达式的结果为2。

                                  所以,虽然 int b = a + (3 * a++) 中前面的a只是一个变量,但他也是一个表达式,a这个表达式和(3 * a++)这个表达式进行相加,多个表达式的运算都是从左到右进行的,所以先算a这个表达式,a表达式计算结果为2,所以表达式变成:

                                  int b = 2 + (3 * a++) 然后是a自增并返回自增之前的值2,所以表达式又变为:

                                  int b = 2 + (3 * 2);所以结果为8。此时a的值为3

                                  示例4详解

                                  int i = 1;
                                  int j = 1;
                                  int k = i++ + ++i + ++j + j++;  
                                  System.out.println(k);  // 结果:8
                                  
                                   

                                    有了前面3条示例的详解,相信这一条大家就能自己解答了,可以先自己解答一下,看结果是不是8,不是的话,再来看我下面的讲解:

                                    表达式原则说多个表达式的加减法运算都是从左到右进行的,这里的表达式有:i++、++i、++j、j++,都是加法,那我们就从左到右计算这4个表达式就OK了,如下:

                                    1、先算i++,i++之后i的值为2,并返回++之前的值1,所以整个表达式可以变为:

                                       1 + ++i + ++j + j++; // 此时的i值为2

                                    2、再计算++i,++i之后i的值为3,并返回3,所以整个表达式可以变为:

                                       1 + 3 + ++j + j++; // 此时i的值为3

                                    3、再计算++j,++j之后j的值为2,并返回2,所以整个表达式可以变为:

                                       1 + 3 + 2 + j++; // 此时j的值为2

                                    4、再计算j++,j++之后 j的值为3,并返回2,所以整个表达式可以变为:

                                       1 + 3 + 2 +2; // 结果为8,此时j的值为3

                                    示例5详解

                                    int a = 0;
                                    int b = 0;
                                    a = a++;
                                    b = a++;
                                    System.out.println("a = " + a + ", b = " + b); // a = 1, b = 0
                                    
                                     

                                      到了第5题,好像已经没有难度了,大家应该都能解出来了,但是为了文章的完整性,我还是分解一下,大家应该自己先算一次,算不对再来看我的分解:

                                      a = a++; a++之后a的值为1,并返回0,所以a的值由1又变回了0
                                      b = a++; a++之后a的值为1,并返回0,0赋值给b,所以b为0,而a还是1哦!!

                                      总结

                                      • i++ 即后加加,原理是:先自增,然后返回自增之前的值
                                      • ++i 即前加加,原理是:先自增,然后返回自增之后的值
                                      • 一个变量也是表达式,多个表达式的加减法运算都是从左到右进行的
                                      • 真实开发中,我们不会写这些复杂的i++代码,但是为什么还要掌握这些细节呢?答:笔试,万一笔试的时候遇到这样的题目呢?回答对了就可以加分了,因为这种题很多人是答不出来的,而你回答出来了,那可是很加分的哦!

                                      转载自https://blog.csdn.net/android_cai_niao/article/details/106027313

                                      展开全文
                                    • 终于弄明白 i = i++和 i = ++i

                                      万次阅读 多人点赞 2020-06-09 15:54:57
                                      写在前面:前些天看完了JVM的内存结构,自以为自己是懂了,心里想想不就是分线程共享和...文章目录1、题目2、分析2.1、第一步2.2、第二步2.3、第三步2.4、第四步2.5、结果3、i = ++i 1、题目 package pers.mobian..

                                      写在前面:前些天看完了JVM的内存结构,自以为自己是懂了,心里想想不就是分线程共享和线程私有嘛,然后又怎么怎么分怎么怎么的嘛…


                                      直到遇到了这道题目。说句实话,曾经自己做这种运算题目,完全是靠脑子空想,然后拿上笔颤抖的写下一个自己都不知道正不正确的答案。不过过了今天,我终于能确定它的答案了。


                                      为此,我也专门写一篇博客,记录我的学习!!!




                                      1、题目

                                      package pers.mobian.questions01;
                                      
                                      public class test01 {
                                          public static void main(String[] args) {
                                              int i = 1;
                                              i = i++;
                                              int j = i++;
                                              int k = i + ++i * i++;
                                              System.out.println("i="+i);
                                              System.out.println("j="+j);
                                              System.out.println("k="+k);
                                          }
                                      }
                                      

                                      你能肯定并且准确的说出你的答案吗?





                                      2、分析

                                      在做这道题时我们的脑中要有对应的JVM内存结构。其中一个方法对应一个栈帧

                                      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7S1O4RTM-1591687148745)(01_自增变量.assets/image-20200607101447244.png)]

                                      此题目我们只需要用到栈帧里面的局部变量表和操作数栈


                                      2.1、第一步

                                      int i = 1

                                      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2xGca3Uz-1591687148747)(01_自增变量.assets/image-20200607103756406.png)]

                                      只是一个简单的赋值操作


                                      2.2、第二步

                                      i = i++

                                      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bLH5OxNN-1591687148749)(01_自增变量.assets/image-20200607104123111.png)]

                                      结果:i还是等于1


                                      2.3、第三步

                                      int j = i++

                                      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H6LTb9Ge-1591687148751)(01_自增变量.assets/image-20200607104936003.png)]

                                      结果:i在局部变量表中变成了2,操作数栈中的 i 值为1,并且将 i 的值返回给 j,即此条语句以后,i = 2,j = 1


                                      2.4、第四步

                                      int k = i + ++i * i++

                                      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EOsOaSzt-1591687148753)(01_自增变量.assets/image-20200607110626891.png)]

                                      结果:局部变量表中的i = 4,k = 11


                                      2.5、结果

                                      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6eruEoFm-1591687148754)(01_自增变量.assets/image-20200607110650949.png)]




                                      3、i = ++i

                                      按理说根据上面的分析过程,再来分析 i = ++i,就很简单了。
                                      我们的 i 变量先在局部变量表中进行自增,然后再将 i 进栈,然后再把栈中的数据返回给我们的变量 i 。

                                      public class test02 {
                                          public static void main(String[] args) {
                                              int i = 1;
                                              i = ++i;
                                              System.out.println(i); // 结果:i = 2
                                          }
                                      }
                                      


                                      最后的最后:当我和室友讨论一番之后,发现C语言的计算机制和Java的有些许出入,所以本文的思考过程和计算推导仅针对Java语言

                                      展开全文
                                    • i510400和i512400差距

                                      万次阅读 2022-02-12 18:00:58
                                      i512400的规格是6核12线程,默认主频2.5GHz,单核最大加速频率4.4GHz,全核最大加速频率4.0GHz,不支持超频,二级缓存7.5MB 三级缓存为18MB,内存支持DDR5-4800/DDR4-3200,TDP功耗为65W,内置核显型号为UHD 730。...
                                    • i2c我们用得很多,i3c又是什么?

                                      万次阅读 2020-05-04 12:06:24
                                      常见的板级间通信总线有i2c、spi、uart、i2s等,这类总线常用于ADC、DAC、传感器、触摸屏等等外设中;i2s则用于传输音频信息,在音频ADC和DAC中应用。随着物联网的应用越来越广泛,涌现各类传感器,温度、气压、心率...
                                    • i++和++i的区别

                                      万次阅读 多人点赞 2022-01-11 16:08:11
                                      1、首先,单独拿出来说,i++和++i的意思是一样的,就是i = i + 1。 2、如果当做运算符来说,就是a = i++ 和 a = ++i这样的形式,情况就不一样了。 a = i++的意思是,先把i的值赋给a,再执行i = i + 1; a = ++i是先...
                                    • I2C总线介绍

                                      万次阅读 多人点赞 2018-12-06 16:01:22
                                      1.1 I2C总线知识 1.1.1 I2C总线物理拓扑结构   I2C总线在物理连接上非常简单,分别由SDA(串行数据线)和SCL(串行时钟线)及上拉电阻组成。通信原理是通过对SCL和SDA线高低电平时序的控制,来产生I2C总线协议所...
                                    • myeclipse 2017 ci 1 windows版本 破解文件

                                      千次下载 热门讨论 2017-01-17 09:17:06
                                      微梦联科 myeclipse 2017 ci 1 windows版本 破解文件
                                    • GitLab CI介绍——入门篇

                                      万次阅读 多人点赞 2019-07-30 09:25:10
                                      本文将会对Gitlab CI进行简要介绍,包括Gitlab Runner,Gitlab CI中的相关概念以及.gitlab-ci.yml的常用配置。 那么,GitLab CI 是什么? GitLab CI 是GitLab内置的进行持续集成的工具,只需要在仓库根目录下创建....
                                    • I2C库函数

                                      千次阅读 2018-08-08 17:10:35
                                      1.void I2C_DeInit(I2C_TypeDef* I2Cx) 功能:将I2Cx外设寄存器重设为默认值 注释:该函数调用了RCC_APB1PeriphResetCmd来进行挂载于APB上外设的寄存器的复位 例如:I2C_DeInit();   2.void I2C_Init(I2C_...
                                    • I2C协议---I2C时序图解析

                                      万次阅读 多人点赞 2018-08-08 19:55:57
                                      一、I2C协议简介   I2C 通讯协议(Inter-Integrated Circuit)是由 Phiilps 公司开发的,由于它引脚...  关于I2C协议的更多内容,可阅读《I2C总线协议》,本博文主要分析I2C波形图,对于I2C的基础知识不在做介...
                                    • jdk1.8 32位官方正式版 jdk-8u91-windows-i586

                                      千次下载 热门讨论 2016-06-02 21:36:59
                                      jdk1.8 32位官方正式版 jdk-8u91-windows-i586
                                    • i++ 、 ++ii+=1、i=i+1 的问题

                                      万次阅读 多人点赞 2019-04-19 22:36:40
                                      i++ 、 ++ii+=1、i=i+1 等这类问题是也笔试必考内容之一,基础中的基础。如果不会,那就已经可以看出你个人的基础水平了。虽然看上去简单,但是不懂原理是不行的。只有了解原理才能记忆最深 i++ 和 ++i 的区别: ...
                                    • 【组件】i2c抽象/模拟i2c

                                      万次阅读 2017-12-23 09:27:26
                                      关于i2c的使用,并不陌生,C51、ARM、MSP430等,都基本集成硬件i2c,或者不集成i2c的,可以根据总线时序图使用普通IO口翻转模拟一根i2c总线。对于当下流行的stm32饱受诟病的硬件i2c,相信很多人都是使用模拟i2c。...
                                    • i51135g7和i511320h哪个好

                                      万次阅读 多人点赞 2021-10-21 10:32:23
                                      i51135g7还是i511320h这些点很重要 http://www.adiannao.cn/dy i5-1135G7 4 核 8 线程,主频 2.4GHz,睿频 4.2GHz,8MB 三级缓存。跑分方面,i5-1135G7 在 Geekbench 4 上单核最高 6046 分,多核最高 21469 分。 ...
                                    • Java i++ 与 ++i 原理

                                      万次阅读 多人点赞 2019-08-30 11:22:29
                                      记得大学期间学习C语言的时候,对于 i++ 和 ++ii-- 和 --i)的理解,一直似懂非懂的状态,因为很多人给出的解释都不够严谨,比如:i++是在操作之后增加,而++i是在操作之前增加。但是,如何确定“操作”的时间点...
                                    • jdk-6u45-linux-i586-rpm.bin

                                      千次下载 热门讨论 2014-12-28 12:40:58
                                      jdk-6u45-linux-i586-rpm.bin //linux jdk 1.6 x86
                                    • i++和++i的真正区别详解

                                      万次阅读 多人点赞 2020-02-28 22:11:55
                                      i++的通俗的解释即是先赋值再自增,其实这里赋值的值是从操作数栈取的值,也就是说先将i的值压入栈中, 而自增是局部变量表的值自增。 而++i则相反,是先自增后赋值,就是局部变量表的自增,然后把局部变量表的值...
                                    • 入手评测 i510400f和i711700f选哪个好

                                      万次阅读 2021-09-10 16:56:34
                                      组装电脑选i510400f还是i711700f怎么搭配更合适这些点很重要http://www.adiannao.cn/du i5-10400F的基础频率为2.9GHz,最大睿频可达4.3GHz,对比去年的i5-9400F睿频频率增加了0.2GHz,TDP只有65W,并且超线程的加入...
                                    • GitLab CI/CD Variables 中文文档

                                      万次阅读 2019-06-27 18:29:00
                                      GitLab CI/CD environment variables 当GitLab CI 中接受到一个job后,Runner就开始准备构建环境。开始设置预定义的变量(环境变量)和用户自定义的变量。 variables 的执行顺序 变量可以被重写,并且是按照下面的...
                                    • ++ii++的区别详解

                                      万次阅读 多人点赞 2020-08-25 10:15:50
                                      ++ii++有什么区别 a = i++; a = i; i = i + 1; a = ++i; i = i + 1; a = i; i++和++i的 最重要的区别就是 +1和返回值顺序从效率上来说++ii++来的更有效率,因为i++多声明了一个过渡的变量。如下所示: ...
                                    • i=i+1 与 i+=1 区别

                                      万次阅读 2019-03-19 09:34:41
                                      满脑子的黑人问号有没有,有的话点一波关注:) 起初我也是一脸蒙逼,随着知识的积累,有了新的思考,记录下来仅供参考。 从执行效率上来说,i+=1更优 我们先来看一段代码 public class PlusEqualTest {... i=i...
                                    • glibc-2.5-49.i686.rpm

                                      千次下载 热门讨论 2012-04-25 11:49:51
                                      glibc-2.5-49.i686.rpm
                                    • T420 T420i 详细拆机手册

                                      热门讨论 2011-09-06 15:26:35
                                      T420 T420i 详细拆机手册 精确到每颗螺丝
                                    • Linux驱动之I2C驱动架构

                                      万次阅读 2019-09-15 10:48:35
                                      一、Linux的I2C体系结构 主要由三部分组成: (1) I2C核心 提供I2C控制器和设备驱动的注册和注销方法,I2C通信方法,与适配器无关的代码以及探测设备等。 (2) I2C控制器驱动(适配器) (3) I2C设备驱动 二、重要的...
                                    • i3-10100基于祖传的14nm++制程工艺,拥有4核8线程,默认主频3.6Ghz,最大睿频4.3Ghz,三级缓存为6MB,不支持超频,内置UHD630核显,设计功耗65W。 选i3 10100还是i3 10100f 这些点很重要!看完你就知道了 ...
                                    • Myeclipse 2015 CI 破解工具

                                      千次下载 热门讨论 2014-11-21 17:03:23
                                      在网上下载了很多 myeclipse-2015 CI破解方法都无法破解,这个本人亲测试可以破解,在此分析给大家; 另贴上myeclipes2015 下载地址 http://pan.baidu.com/s/1mgFVmjQ
                                    • 破解文件myeclipse 2016 ci 7(windows版本)

                                      千次下载 热门讨论 2016-10-01 11:27:23
                                      破解文件myeclipse 2016 ci 7(windows版本)

                                    空空如也

                                    空空如也

                                    1 2 3 4 5 ... 20
                                    收藏数 16,445,584
                                    精华内容 6,578,233
                                    关键字:

                                    i

                                    友情链接: An Enemy Called Average.zip