精华内容
下载资源
问答
  • mysql 内存表和临时表学习

    千次阅读 2018-02-27 22:15:37
    临时表的数据和表结构都存储在内存之中,退出的时候所占的空间会被释放 创建临时表 create temporary table tmp_table( name varchar(10) not null, value int not null ); 关键字为temp...

    前景

    • mysql三种虚拟表
      • 临时表
      • 内存表
      • 视图

    临时表

    简介:

    临时表是建立在系统临时文件夹中的表。临时表的数据和表结构都存储在内存之中,退出的时候所占的空间会被释放

    创建临时表

    create temporary table tmp_table(
    name varchar(10) not null,
    value int not null
    );

    关键字为temporary

    查看表结构

    注意:

    show tables;
    show table status

    这两个命令无法查看临时表。 但是可以查内存表


    可以查看表建立sql语句

    show create table tmp_table;

    直接将查询结果导入临时表

    CREATE TEMPORARY TABLE tmp_table SELECT * FROM table_name

    设定临时表大小

    tmp_table_size临时表的容量

    临时表的应用场景

    当工作在十分大的表上运行时,运行相关查询,来获的一个大量数据的小的子集。较好的办法,不是对整个表运行这些查询,而是让MySQL每次找出所需的少数记录,将记录选择到一个临时表,然后对这些表运行查询

    • 一个sql语句关联两个表以上的时候,查询到的结果存放在临时表中。
    • 程序执行过程中可能需要存放一些临时的数据,这些数据在整个程序的会话过程中都需要用的等等
    • 临时表默认的是MyISAM,但是可以修改
    • 内部临时表,就是查询的时候,服务器会优化查询,使用内部临时表。比如 order by 的列不是from中的第一列。
    select *
    from instructor natural join teaches
    where dept_name='Accounting'
    order by semester;

    get到的新技能- - -查看执行计划

    explain extended
    select *
    from instructor natural join teaches
    where dept_name='Accounting'
    order by semester;
    show warnings;
    - show warnings 能查看优化查询的sql语句
    

    show warnings
    - explain extended能查看执行使用的各种东西
    explain

    临时表的注意事项

    • 临时表只在当前连接可见,当这个连接关闭的时候,会自动drop。比如打开mysql 就是一个连接会话。两个不同的连接可以使用相同名字的临时表,两个表之间不存在什么关系,如果临时表的名字和已经存在的磁盘表名字一样,那么临时表会暂时覆盖磁盘表。就是说,你select 查询,只会显示临时表里面的,不会显示磁盘表。
    • 临时表的存储引擎:memor,myisam,merge,innodb
    • 临时表不支持,mysql cluster
    • 同一个查询语句,只能用一次临时表,就是说不能将表和自己做连接等。
    • 重命名表,不能用rename 可以用alter table代替
    • 如果超出了临时表的容量,临时表会转换成磁盘表
    alter table old_name rename new_name

    内存表

    简介:
    内存表的表结构建立在磁盘里面,数据放在内存里面,当mysql重启之后,内存表的数据会丢失,表结构依旧存在会执行一次truncate操作

    内存表的建立

    CREATE TEMPORARY TABLE tmp_table (
    name VARCHAR(10) NOT NULL,       
    value INTEGER NOT NULL 
    )  TYPE = HEAP  注意: TYPE = HEAP必须要有

    和临时表不同的地方在于,多了个type=heap

    使用场景及注意事项

    内存表使用hash索引把数据保存在内存中,具有更快的速度,可以用来缓存。

    • 内存表对所有的用户连接都是可用的。这就意味着,多个会话连接的内存表名字不能重复,具有唯一性
    • 内存表如果复制数据进去的话,所有的原有格式都不会存在,需要重新设置
    • 重启造成数据丢失,可以drop表之后重新复制数据等。这是最傻瓜的方法了。一定有更好的方法(待补充)
    • 支持简单的操作符>=<这三个,我认为内存表用来缓存的话,应该不会涉及很复杂的操作。
    • 不好的地方的话,应该在于数据了,因为数据都在内存里,处理起来应该蛮麻烦
    • 内存表的默认引擎是memory

    总结

    对比一下内存表和临时表的一些主要区别吧

    • 存储
      • 内存表 表结构存储在磁盘中,数据存储在内存中
      • 临时表 表结构和数据都存储在内存中
    • 会话
      • 内存表 是可以多个会话共享的
      • 临时表 是单个会话独享的,是会话级别的
    • 引擎
      • 内存表默认,memory
      • 临时表默认,myisam
    • 断开连接
      • 临时表 啥都不剩
      • 内存表 只剩下表结构
    • 性能
      • 内存表由于所有的内容都是放在内存中,所以相对来说,速度较快但是同时数据的维护较为困难
    展开全文
  • 根据其存储的形态不同,可以分为磁盘临时表和内存临时表。在系统参数中,有MAX_HEAP_SIZE和TMP_TABLE_SIZE两个参数来控制临时表的大小。当临时数据超过这两个参数的规定时,系统就会将内存临时表转换为磁盘临时表。...

     【IT168 技术】临时表是系统采取某些作业时所需要用到的一些临时数据。根据其存储的形态不同,可以分为磁盘临时表和内存临时表。在系统参数中,有MAX_HEAP_SIZE和TMP_TABLE_SIZE两个参数来控制临时表的大小。当临时数据超过这两个参数的规定时,系统就会将内存临时表转换为磁盘临时表。这也就是说,磁盘临时表是内存临时表的一个替代品。

    临时表要舍磁盘临时表取内存临时表

      一、磁盘临时表与内存临时表的差异

      从磁盘临时表与内存临时表的差异中大家可以看到,磁盘临时表只是内存临时的一个替代品。这就好像操作系的虚拟内存一样。当内存不够用时,可以在硬盘上的一个空间作为其替代品,将内存中的部门数据转移到虚拟内存中。这个磁盘临时表也是相同的道理。

      但是这里需要注意的是,硬盘的效率与内存的效率是不同的。在执行相同的一个作业时,内存的性能要高于硬盘的性能,一般会高上百倍,甚至上千倍。从这里就可以看出,为了提高数据库系统的性能,我们最好选择内存临时表,而放弃使用磁盘临时表。

      二、BLOB和TEXT数据类型与临时表的关系

      在讨论如何来取磁盘临时表舍内存临时表这个话题时,我认为有必要先谈谈BLOB和TEXT这两个数据类型。这两个数据类型都用来存储大容量的数据。前者是采用二进制的形式来保存,而后者是采用字符形式来保存。

      这两个数据类型与其他数据类型有本质的不同。在MYSQL数据库中,是将这两个数据类型当做有实体的对象来处理。存储引擎也会采用特别的方式来保存他们。BLOB数据类型是采用二进制的方式来存储数据。而采用二进制来存储数据时,系统没有字符集的要求,也不会设置排序规则。相反,TEXT采用字符形式来存储数据,为此有字符集和排序规则的限制。

      这两种数据类型,跟今天要谈到的临时表有什么关系呢?其实很有关系。因为这两种数据类型的容量比较大,为此对对这些类型的字段进行操作时,临时表就会一下子变得很大。此时就很容易超过上面两个参数的限制。系统就会将内存临时表转换为磁盘临时表。为此这两种数据类型会增加产生磁盘临时表的几率。

      三、项目建议

      采用磁盘临时表会在很大程度上降低数据库的性能开销。为此在实际工作中要尽量的避免。那么该如何来限制这个磁盘临时表的数量呢?这是一个很复杂的话题。在这里,我只结合自己的工作经验,谈谈一些经验心得。大家在实际工作中,可以尝试着试一下,看看是否有效。

      一是不同的存储类型对于数据类型的支持力度是不同的。如果某种存储类型不支持某些数据类型,那么系统就会直接采用磁盘临时表,即使数据没有超过其规定的大小。简单的说,就是对于存储引擎,如果其不支持某些数据类型,那么对这些数据类型进行操作时,系统只能够使用磁盘临时表,而不能够使用磁盘临时表。如对于Memory存储引擎来说,其不支持BLOB和TEXT数据类型。在系统运行中,如果使用了BLOB和TEXT列,并且需要隐式临时表时,查询将不会使用内存临时表,而直接采用磁盘临时表。即使两个数据类型中的列存储的数据不多,也是如此。显然这会大大的降低数据库的性能。

      二是尽可能的避免使用BLOB和TEXT数据类型。因为这两种数据类型存储的数据比较大,而且某些存储引擎并不支持这两种数据类型,为此在实际工作中,最好尽量避免使用这两种数据类型。如果真的需要使用,那么也需要采取一些措施来避免其带来的负面影响。如只在特定的情况下(用户特别指定时),前台客户端才调用这两个数据对象。另外,从数据库角度出发,也可以采取一些措施。如数据库管理员可以采用ORDER BY SUBSTRING子句,将这些值转换为字符串,他某些存储引擎可以使用内存中的临时表。RDER BY SUBSTRING这个子句,顾名思义,是对这两个数据类型的记录进行排序。不过因为其内容太长,往往无法对某个字段的全部的值进行比较,然后进行排序。此时为了提高排序的效率,往往是采用SUBSTRING关键字来限制,只比较某几位的值。为了保证其数据量不超过内存临时表的限制值,此时要确保使用的子字符串足够的短,不能够让临时表变得很大。就是说,SUBSTRING关键字的参数要短一点。否则的话,容量足够大时,系统还是会将内存临时表转换为磁盘临时表。

      四、MAX_HEAP_SIZE和TMP_TABLE_SIZE参数

      在文章一开头,笔者就谈到MAX_HEAP_SIZE和TMP_TABLE_SIZE这两个参数。这是用来控制磁盘临时表和内存临时表的一把钥匙。当临时表的容量超过这两个参数的限制时,系统就会将内存临时表转换为磁盘临时表。那么有些用户就会考虑,直接将MAX_HEAP_SIZE和TMP_TABLE_SIZE这两个参数设置为最大就可以了。却是,调整这两个参数,能够达到取内存临时文件、舍磁盘临时文件的目的。但是这往往是有限制的。

      因为内存的容量是有限制的,而且这个内存不光光是MYSQL数据库在使用。内存是服务器上各个服务所共用的。像操作系统、杀毒软件以及其他应用服务,都需要用到。此时系统管理员就需要考虑,系统资源在各个服务之间的分配,包括内存。通常情况下,如果一台服务器上部署有多种服务,如数据库、ERP、文件服务器等等,系统管理员就会设置各个服务可以使用的最大内存数量,以防止不同应用之间相互的干扰。

      为此在调整MAX_HEAP_SIZE和TMP_TABLE_SIZE这两个参数时,数据库管理员需要考虑,调整这两个参数之后,对其他服务的影响。至少提高这两个参数的值之后,不能够对其他的应用程序产生不利的影响。

      在实际项目中,一般建议调整MAX_HEAP_SIZE和TMP_TABLE_SIZE这两个参数要慎重行事。在调整之前,需要先对服务器内存的使用做一段时间的追踪。然后评估各种服务(包括数据库和其他应用程序)所占用的内存。然后根据其最大峰值时剩余内存的情况,来调整这两个参数的值。

      如果这么操作觉得麻烦的话,那么笔者就建议,在调整参数之前,先升级内存。给服务器加一条新的内存,或者提升现有内存的大小,然后将增加的内存的一部分作为临时文件的存储区域。这也是可行的。不过一般在这么操作时,建议系统管理员先根据服务器上现有的服务,重新调整各个应用程序可以使用的内存。然后数据库管理员再相应的调整临时文件所能够占用内存的大小。通过在这种顺序来进行配置的话,比较保险、不容易出错。

      最后需要说明的是,磁盘临时文件我们只能够采取措施去尽量的避免,而不能够完全的舍弃。系统中也没有相关的参数说不能够使用磁盘文件。某些作业(如对某个大表进行排序)等等,其容量足够大时,还是要能够允许系统采用磁盘临时表。不然的话,前端应用程序就会出错。在遇到这种作业时,一般可以在前台进技巧性的设置。如在应用程序开发时,开发人员根据经验判断某个作业可能会用到磁盘临时表。此时用户的等待时间会比较长。那么在应用程序开发时,就可以在前台客户端加一个控制标签,可以让用户选择让这个作业在后台运行。结果出来后再反馈给用户。这也是避免磁盘临时表负面影响的一个常用措施。

    展开全文
  • C++面试题汇总 (一)

    万次阅读 多人点赞 2019-06-27 08:54:39
    C++面试题汇总 (一)一,基础题二,算法题 一,基础题 new、delete、malloc、free关系 ...它们都可用于申请动态内存和释放内存。对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要...

    C++面试题汇总 (一)

    一,基础题

    1. new、delete、malloc、free关系
      delete会调用对象的析构函数,和new对应free只会释放内存,new调用构造函数。malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。

    2. delete与 delete []区别
      delete只会调用一次析构函数,而delete[]会调用每一个成员的析构函数。在More Effective C++中有更为详细的解释:“当delete操作符用于数组时,它为每个数组元素调用析构函数,然后调用operator delete来释放内存。”delete与new配套,delete []与new []配套

    MemTest *mTest1=new MemTest[10];

    MemTest *mTest2=new MemTest;

    Int *pInt1=new int [10];

    Int *pInt2=new int;

    delete[]pInt1; //-1-

    delete[]pInt2; //-2-

    delete[]mTest1;//-3-

    delete[]mTest2;//-4-

    在-4-处报错。

    这就说明:对于内建简单数据类型,delete和delete[]功能是相同的。对于自定义的复杂数据类型,delete和delete[]不能互用。delete[]删除一个数组,delete删除一个指针。简单来说,用new分配的内存用delete删除;用new[]分配的内存用delete[]删除。delete[]会调用数组元素的析构函数。内部数据类型没有析构函数,所以问题不大。如果你在用delete时没用括号,delete就会认为指向的是单个对象,否则,它就会认为指向的是一个数组。

    1. C++有哪些性质(面向对象特点)
      封装,继承和多态。

    2. 子类析构时要调用父类的析构函数吗?
      析构函数调用的次序是先派生类的析构后基类的析构,也就是说在基类的的析构调用的时候,派生类的信息已经全部销毁了。定义一个对象时先调用基类的构造函数、然后调用派生类的构造函数;析构的时候恰好相反:先调用派生类的析构函数、然后调用基类的析构函数。

    3. 多态,虚函数,纯虚函数
      多态:是对于不同对象接收相同消息时产生不同的动作。C++的多态性具体体现在运行和编译两个方面:在程序运行时的多态性通过继承和虚函数来体现;

    在程序编译时多态性体现在函数和运算符的重载上;

    虚函数:在基类中冠以关键字 virtual 的成员函数。 它提供了一种接口界面。允许在派生类中对基类的虚函数重新定义。

    纯虚函数的作用:在基类中为其派生类保留一个函数的名字,以便派生类根据需要对它进行定义。作为接口而存在 纯虚函数不具备函数的功能,一般不能直接被调用。

    从基类继承来的纯虚函数,在派生类中仍是虚函数。如果一个类中至少有一个纯虚函数,那么这个类被称为抽象类(abstract class)。

    抽象类中不仅包括纯虚函数,也可包括虚函数。抽象类必须用作派生其他类的基类,而不能用于直接创建对象实例。但仍可使用指向抽象类的指针支持运行时多态性。

    1. 求下面函数的返回值(微软)
      int func(x)

    {

    int countx = 0;

    while(x)

    {

    countx ++;

    x = x&(x-1);

    }

    return countx;

    }

    假定x = 9999。 答案:8

    思路:将x转化为2进制,看含有的1的个数。

    1. 什么是“引用”?申明和使用“引用”要注意哪些问题?
      答:引用就是某个目标变量的“别名”(alias),对应用的操作与对变量直接操作效果完全相同。申明一个引用的时候,切记要对其进行初始化。引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,不能再把该引用名作为其他变量名的别名。声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。不能建立数组的引用。

    2. 将“引用”作为函数参数有哪些特点?
      (1)传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。

    (2)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。

    (3)使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用"*指针变量名"的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。

    1. 在什么时候需要使用“常引用”? 
      如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。常引用声明方式:const 类型标识符 &引用名=目标变量名;

    例1

    int a ;

    const int &ra=a;

    ra=1; //错误

    a=1; //正确

    例2

    string foo( );

    void bar(string & s);

    那么下面的表达式将是非法的:

    bar(foo( ));

    bar(“hello world”);

    原因在于foo( )和"hello world"串都会产生一个临时对象,而在C++中,这些临时对象都是const类型的。因此上面的表达式就是试图将一个const类型的对象转换为非const类型,这是非法的。引用型参数应该在能被定义为const的情况下,尽量定义为const 。

    1. 将“引用”作为函数返回值类型的格式、好处和需要遵守的规则?

    格式:类型标识符 &函数名(形参列表及类型说明){ //函数体 }

    好处:在内存中不产生被返回值的副本;(注意:正是因为这点原因,所以返回一个局部变量的引用是不可取的。因为随着该局部变量生存期的结束,相应的引用也会失效,产生runtime error!

    注意事项:

    (1)不能返回局部变量的引用。这条可以参照Effective C++[1]的Item 31。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了"无所指"的引用,程序会进入未知状态。

    (2)不能返回函数内部new分配的内存的引用。这条可以参照Effective C++[1]的Item 31。虽然不存在局部变量的被动销毁问题,可对于这种情况(返回函数内部new分配内存的引用),又面临其它尴尬局面。例如,被函数返回的引用只是作为一个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memory leak。

    (3)可以返回类成员的引用,但最好是const。这条原则可以参照Effective C++[1]的Item 30。主要原因是当对象的属性是与某种业务规则(business rule)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其它对象可以获得该属性的非常量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。

    (4)流操作符重载返回值申明为“引用”的作用:

    流操作符<<和>>,这两个操作符常常希望被连续使用,例如:cout << “hello” << endl; 因此这两个操作符的返回值应该是一个仍然支持这两个操作符的流引用。可选的其它方案包括:返回一个流对象和返回一个流对象指针。但是对于返回一个流对象,程序必须重新(拷贝)构造一个新的流对象,也就是说,连续的两个<<操作符实际上是针对不同对象的!这无法让人接受。对于返回一个流指针则不能连续使用<<操作符。因此,返回一个流对象引用是惟一选择。这个唯一选择很关键,它说明了引用的重要性以及无可替代性,也许这就是C++语言中引入引用这个概念的原因吧。

    赋值操作符=。这个操作符象流操作符一样,是可以连续使用的,例如:x = j = 10;或者(x=10)=100;赋值操作符的返回值必须是一个左值,以便可以被继续赋值。因此引用成了这个操作符的惟一返回值选择。

    #include<iostream.h>

    int &put(int n);

    int vals[10];

    int error=-1;

    void main()

    {

    put(0)=10; //以put(0)函数值作为左值,等价于vals[0]=10;

    put(9)=20; //以put(9)函数值作为左值,等价于vals[9]=20;

    cout<<vals[0];

    cout<<vals[9];

    }

    int &put(int n)

    {

    if (n>=0 && n<=9 ) return vals[n];

    else { cout<<“subscript error”; return error; }

    }

    (5)在另外的一些操作符中,却千万不能返回引用:±*/ 四则运算符。它们不能返回引用,Effective C++[1]的Item23详细的讨论了这个问题。主要原因是这四个操作符没有side effect,因此,它们必须构造一个对象作为返回值,可选的方案包括:返回一个对象、返回一个局部变量的引用,返回一个new分配的对象的引用、返回一个静态对象引用。根据前面提到的引用作为返回值的三个规则,2、3两个方案都被否决了。静态对象的引用又因为((a+b) == (c+d))会永远为true而导致错误。所以可选的只剩下返回一个对象了。

    1. 结构与联合有和区别?

    (1). 结构和联合都是由多个不同的数据类型成员组成, 但在任何同一时刻, 联合中只存放了一个被选中的成员(所有成员共用一块地址空间), 而结构的所有成员都存在(不同成员的存放地址不同)。

    (2). 对于联合的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于结构的不同成员赋值是互不影响的。

    1. 试写出程序结果:
      int a=4;

    int &f(int x)

    { a=a+x;

      return  a;
    

    }

    int main(void)

    { int t=5;

     cout<<f(t)<<endl;  a = 9
    
    f(t)=20;             a = 20
    
    cout<<f(t)<<endl;     t = 5,a = 20  a = 25
    
     t=f(t);                a = 30 t = 30
    
    cout<<f(t)<<endl;  }    t = 60
    

    }

    1. 重载(overload)和重写(overried,有的书也叫做“覆盖”)的区别?
      常考的题目。从定义上来说:

    重载:是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。

    重写:是指子类重新定义父类虚函数的方法。

    从实现原理上来说:

    重载:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。如,有两个同名函数:function func(p:integer):integer;和function func(p:string):integer;。那么编译器做过修饰后的函数名称可能是这样的:int_func、str_func。对于这两个函数的调用,在编译器间就已经确定了,是静态的。也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关!

    重写:和多态真正相关。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚绑定)。

    1. 有哪几种情况只能用intialization list 而不能用assignment?
      答案:当类中含有const、reference 成员变量;基类的构造函数都需要初始化表。

    2. C++是不是类型安全的?
      答案:不是。两个不同类型的指针之间可以强制转换(用reinterpret cast)。C#是类型安全的。

    3. main 函数执行以前,还会执行什么代码?
      答案:全局对象的构造函数会在main 函数之前执行。

    4. 描述内存分配方式以及它们的区别?
      1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。

    2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集。

    3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活,但问题也最多。

    1. 分别写出BOOL,int,float,指针类型的变量a 与“零”的比较语句。
      答案:

    BOOL : if ( !a ) or if(a)

    int : if ( a == 0)

    float : const EXPRESSION EXP = 0.000001

    if ( a < EXP && a >-EXP)

    pointer : if ( a != NULL) or if(a == NULL)

    1. 请说出const与#define 相比,有何优点?
      答案:

    const作用:定义常量、修饰函数参数、修饰函数返回值三个作用。被Const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。

    1) const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。

    2) 有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。

    1. 简述数组与指针的区别?
      数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。指针可以随时指向任意类型的内存块。

    (1)修改内容上的差别

    char a[] = “hello”;

    a[0] = ‘X’;

    char *p = “world”; // 注意p 指向常量字符串

    p[0] = ‘X’; // 编译器不能发现该错误,运行时错误

    (2) 用运算符sizeof 可以计算出数组的容量(字节数)。sizeof§,p 为指针得到的是一个指针变量的字节数,而不是p 所指的内存容量。C++/C 语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。

    char a[] = “hello world”;

    char *p = a;

    cout<< sizeof(a) << endl; // 12 字节

    cout<< sizeof§ << endl; // 4 字节

    计算数组和指针的内存容量

    void Func(char a[100])

    {

    cout<< sizeof(a) << endl; // 4 字节而不是100 字节

    }

    1. 题: int (*s[10])(int) 表示的是什么?
      int (*s[10])(int) 函数指针数组,每个指针指向一个int func(int param)的函数。

    2. 题:栈内存与文字常量区

         char str1[] = "abc";
      

    char str2[] = “abc”;

    const char str3[] = “abc”;
      const char str4[] = “abc”;

    const char *str5 = “abc”;
      const char *str6 = “abc”;

    char *str7 = “abc”;
      char *str8 = “abc”;

    cout << ( str1 == str2 ) << endl;//0 分别指向各自的栈内存
      cout << ( str3 == str4 ) << endl;//0 分别指向各自的栈内存
      cout << ( str5 == str6 ) << endl;//1指向文字常量区地址相同

    cout << ( str7 == str8 ) << endl;//1指向文字常量区地址相同

    结果是:0 0 1 1

    解答:str1,str2,str3,str4是数组变量,它们有各自的内存空间;而str5,str6,str7,str8是指针,它们指向相同的常量区域。

    1. 题:将程序跳转到指定内存地址
      要对绝对地址0x100000赋值,我们可以用(unsigned int*)0x100000 = 1234;那么要是想让程序跳转到绝对地址是0x100000去执行,应该怎么做?

    ((void ()( ))0x100000 ) ( );
      首先要将0x100000强制转换成函数指针,即:
      (void ()())0x100000
      然后再调用它:
      ((void ()())0x100000)();
      用typedef可以看得更直观些:
      typedef void(
    )() voidFuncPtr;
      *((voidFuncPtr)0x100000)();

    1. 题:int id[sizeof(unsigned long)];这个对吗?为什么?

    答案:正确 这个 sizeof是编译时运算符,编译时就确定了 ,可以看成和机器有关的常量。

    1. 题:引用与指针有什么区别?

    【参考答案】

    1. 引用必须被初始化,指针不必。

    2. 引用初始化以后不能被改变,指针可以改变所指的对象。

    3. 不存在指向空值的引用,但是存在指向空值的指针。

    1. 题:const 与 #define 的比较 ,const有什么优点?

    【参考答案】

    (1) const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误(边际效应) 。

    (2) 有些集成化的调试工具可以对 const 常量进行调试,但是不能对宏常量进行调试。

    1. 题:复杂声明

    void * ( * (*fp1)(int))[10];

    float (( fp2)(int,int,int))(int);

    int (* ( * fp3)())10;

    分别表示什么意思?
    【标准答案】

    1.void * ( * (fp1)(int))[10]; fp1是一个指针,指向一个函数,这个函数的参数为int型,函数的返回值是一个指针,这个指针指向一个数组,这个数组有10个元素,每个元素是一个void型指针。

    2.float (( fp2)(int,int,int))(int); fp2是一个指针,指向一个函数,这个函数的参数为3个int型,函数的返回值是一个指针,这个指针指向一个函数,这个函数的参数为int型,函数的返回值是float型。

    3.int (* ( * fp3)())10; fp3是一个指针,指向一个函数,这个函数的参数为空,函数的返回值是一个指针,这个指针指向一个数组,这个数组有10个元素,每个元素是一个指针,指向一个函数,这个函数的参数为空,函数的返回值是int型。

    1. 题:内存的分配方式有几种?
      【参考答案】

    一、从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量。

    二、在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

    三、从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。

    1. 题:基类的析构函数不是虚函数,会带来什么问题?
      【参考答案】派生类的析构函数用不上,会造成资源的泄漏。

    2. 题:全局变量和局部变量有什么区别?是怎么实现的?操作系统和编译器是怎么知道的?
      【参考答案】

    生命周期不同:

    全局变量随主程序创建和创建,随主程序销毁而销毁;局部变量在局部函数内部,甚至局部循环体等内部存在,退出就不存在;

    使用方式不同:通过声明后全局变量程序的各个部分都可以用到;局部变量只能在局部使用;分配在栈区。

    操作系统和编译器通过内存分配的位置来知道的,全局变量分配在全局数据段并且在程序开始运行的时候被加载。局部变量则分配在堆栈里面 。

    二,算法题

    1. 实现strcpy.
     char* MyStrCpy( char *pDest, const char *pSrc )    
    {    
        if( nullptr == pDest || nullptr == pSrc )    
        {    
           return nullptr;    
        }    
        if( pDest == pSrc )    
         {    
            return pDest;    
         }    
         char *pIter = pDest;    
        while( ( *pIter++=*pSrc++ ) !='\0' );    
         return pDest;    
     }    
    
    1. 实现strcat
     char* MyStrCat( char *pDest, const char *pSrc )    
    {    
         if( nullptr == pDest || nullptr == pSrc )    
         {    
             return nullptr;    
         }    
      
        char *pIter = pDest + strlen( pDest );    
         while( ( *pIter++=*pSrc++ ) != '\0' );    
        return pDest;    
     }    
    
    1. 实现CString字符串类缺省四个方法
    class MyCString    
     {    
     public:    
       
         MyCString( char *pData = nullptr )    
        {    
            if( nullptr == pData )    
            {    
                 mpData = new char[ 1 ];    
                 assert( nullptr != mpData );    
                 *mpData = '\0';    
             }    
            else    
            {    
                mpData = new char[ strlen( pData ) + 1 ];    
                 assert( nullptr != mpData );    
                 strcpy( mpData, pData );    
            }    
         }    
       
         MyCString( const MyCString &Other )    
         {    
             mpData = new char[ strlen( Other.mpData ) + 1 ];    
             assert( nullptr != mpData );    
             strcpy( mpData, Other.mpData );    
         }    
      
         ~MyCString()    
        {    
             if( nullptr != mpData )    
             {    
                delete [] mpData;    
                 mpData = nullptr;    
             }    
         }    
       
         const MyCString& operator =( const MyCString &Other )    
         {    
             if( this == &Other )    
             {    
                return *this;    
             }    
             delete [] mpData;    
             mpData = new char[ strlen( Other.mpData ) + 1 ];    
            assert( nullptr != mpData );    
            strcpy( mpData, Other.mpData );    
             return *this;    
         }    
       
     private:    
       
        char *mpData;    
     };    
    
    1. 不使用第三个变量交换两个数的值
    void SwapA( int &A, int &B )    
    {    
         if( A == B )    
         {    
            return;    
         }    
         A = A + B;    
         B = A - B;    
         A = A - B;    
     }    
     void SwapB( unsigned int &A, unsigned int &B )    
    {    
        if( A == B )    
         {    
             return;    
         }  
         A = A ^ B;    
        B = A ^ B;    
         A = A ^ B;    
     }    
    
    1. C语言中字符串转数字的方法是什么( atoi ),请实现它
     int Myatoi( const char *pString )    
    {    
        assert( nullptr != pString );    
        const int Len = strlen( pString );    
         int Value = 0;    
        int Times = 1;    
        for( int i = Len -1; i >= 0; --i, Times *= 10 )    
         {    
             Value += ( pString[ i ] - '0' ) * Times;    
         }    
         return Value;    
    }    
    
    1. 实现一个将字符串逆序的方法
    char* MyInverted( char *pDest )    
    {    
         assert( nullptr != pDest );    
         const int Len = strlen( pDest );    
         char T = 0;    
         for( int i = 0; i < Len / 2; ++i )    
         {    
             T = pDest[ i ];    
            pDest[ i ] = pDest[ Len - i - 1 ];    
             pDest[ Len - i -1 ] = T;    
         }    
         return pDest;    
     }    
    
    1. 实现一个将字符串中所有字母转换为大写的方法
    char* MyUpper( char *pDest )    
    {    
         assert( nullptr != pDest );    
         for( char *i = pDest; *i != '\0'; ++i )    
         {    
             if( *i < 'a' || *i > 'z' )    
             {    
                 continue;    
            }    
             *i -= 'a' - 'A';    
         }    
         return pDest;    
     }    
    
    1. 已知一个数组已经降序排序请用二分查字法找到其中的某个元素找到返回索引否则返回-1
    int BinarySearch( int *pArray, int Count, int Value )    
      {    
         assert( nullptr != pArray );    
         int Left = 0;    
         int Right = Count -1;    
          int Mid = 0;    
          while( Left <= Right )    
          {    
            Mid = ( Left + Right ) / 2;    
             if( Value < pArray[ Mid ] )    
            {    
                Right = Mid - 1;    
            }          
               else if( Value > pArray[ Mid ] )    
            {    
                Left = Mid + 1;    
             }    
             else    
             {    
                 return Mid;    
             }    
        }    
         return -1;    
     }    
       
     struct Node    
     {    
         Node *mpNext;    
         int mData;    
     };  
    
    1. 删除链表中值为Value的所有元素( [Head]->[node1]->[node2]->…[noden] )
    void DeleteFromList( Node *pHead, int Value )  
    {  
        Node *pPrev = pHead;  
         Node *pNext = pHead->mpNext;  
        while( nullptr != pNext )  
        {  
            if( pNext->mData != Value )  
            {  
                pPrev = pNext;  
               pNext = pNext->mpNext;  
             }  
            else  
            {  
               pPrev->mpNext = pNext->mpNext;  
                 delete pNext;  
                pNext = pPrev->mpNext;  
             }  
        }  
     }    
    
    1. 在链表Index位置插入新的值为Value的元素
     void InsertFromList( Node *pHead, int Index, int Value )    
    {  
         Node *pIter = pHead;  
         for( int i = 0; i < Index && nullptr != pIter; ++i, pIter = pIter->mpNext );  
        assert( nullptr != pIter );  
        Node *pNew = new Node;  
        pNew->mData = Value;  
        pNew->mpNext = pIter->mpNext;  
         pIter->mpNext = pNew;  
     }    
    
    1. 将链表逆序
     Node* InvertedFromList( Node *pHead )    
     {    
         //A->B->C  
         Node *pPrev = pHead;            //A  
         Node *pNext = pHead->mpNext;        //B  
         Node *pNextNext = nullptr;        //C  
        while( nullptr != pNext )    
         {  
             pNextNext = pNext->mpNext;    //C = B->C  
            pNext->mpNext = pPrev;        //B->A  
       
            pPrev = pNext;                //A = B  
             pNext = pNextNext;            //B = C  
         }  
         pHead->mpNext = nullptr;//C->B->A->null  
         return pPrev;            //return C( new head )  
     }    
    
    1. 判断X年X月X日是这年的第几天
    int GetDay( int Year, int Month, int Day )  
     {    
        int MonthDays[ 13 ] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };    
      
        if( ( Year % 4 == 0 && Year % 100 != 0 ) || ( Year % 400 == 0 ) )    
         {    
             ++MonthDays[ 2 ];    
         }    
      
        int Days = 0;    
        for( int i = 1; i < Month; ++i )    
         {    
            Days += MonthDays[ i ];    
         }    
         Days += Day;    
      
        return Days;    
    }  
    
    1. 求斐波拉契数列第N项
    int GetFibonacci1( int N )  
     {  
         if( 1 == N || 2 == N )  
         {  
             return 1;  
         }  
         if( 3 == N )  
         {  
             return 2;  
         }  
         int A = 2;  
        int B = 3;  
         int C = 5;  
         for( int i = 0; i < N - 4; ++i )  
         {  
            C = A + B;  
            A = B;  
             B = C;  
         }  
         return C;  
     }  
    
    1. 递归求斐波拉契数列数列第N项
    int GetFibonacci2( int N )  
     {  
        if( 1 == N || 2 == N )  
         {  
             return 1;  
         }  
         return GetFibonacci2( N - 1 ) + GetFibonacci2( N - 2 );  
     }  
    
    1. 实现一个产生[N-M]区间数字的随机方法
    int GetRandomRange( int N, int M )  
    {  
         if( N == M )  
         {  
             return N;  
         }  
         if( N > M )  
         {  
             N = N + M;  
             M = N - M;  
             N = N - M;  
         }  
         return N + ( rand() % ( M - N + 1 ) );  
     }  
    
    1. 实现一个产生[0~1]之间的随机浮点数
    double GetRandomRange()  
    {  
         return rand() / static_cast< double >( RAND_MAX );  
     }  
    
    1. 实现一个打印出1-1000之间的所有素数的方法
    void PrintfPrime()  
     {  
        //1不是素数  
         //2是最小非奇数素数  
        //直接从3开始  
        printf( "2\n" );  
        bool b = false;  
       for( int i = 3; i <= 1000; ++i )  
         {  
             b = true;  
            for( int j = 2; j <= i / 2; ++j )  
            {  
                if( i % j == 0 )  
                {  
                     b = false;  
                    break;  
                }  
            }  
            if( b )  
             {  
                 printf( "%d\n", i );  
             }  
         }  
     }  
    
    1. 已知Z = X + Y 其中 Z, X, Y 均为无符号int型 定义一个宏判断Z是否已经越界 2 3 #define IS_OVER_FLOW( Z, X, Y ) ( Z < ( ( X ) < ( Y ) ? ( Y ) : ( X ) ) )
    3 #define IS_OVER_FLOW( Z, X, Y ) ( Z < ( ( X ) < ( Y ) ? ( Y ) : ( X ) ) )  
    
    1. 请用栈实现队列
    int QueuePop( std::stack< int > &StackA )  
    {  
         std::stack< int > StackB;  
         while( false == StackA.empty() )  
         {  
            StackB.push( StackA.top() );  
             StackA.pop();  
        }  
      
         const int top = StackB.top();  
        StackB.pop();  
      
         while( false == StackB.empty() )  
        {  
            StackA.push( StackB.top() );  
             StackB.pop();  
         }  
        return top;  
     }  
    
    1. 已知X班X成绩0-100分编写一个方法实现0-59打印不合格,60-69打印合格,70-79打印良好,80-100打印优秀
      2 //不能使用if,:?,switch
    void PrintScore( int Score )  
    {  
         assert( Score >= 0 && Score <= 100 );  
         const char *pString[] =  
         {   
             "不合格",  
             "不合格",  
             "不合格",  
             "不合格",  
            "不合格",  
            "不合格",  
             "合格",  
            "良好",  
             "优秀",  
             "优秀",  
             "优秀",  
        };  
        printf( "%s\n", pString[ Score / 10 ] );  
    }  
    
    1. 实现strncpy
    char *Mystrncpy( char *pDest, const char *pSrc, int Count )  
    {  
         assert( NULL != pDest && NULL != pSrc );  
         if( pDest == pSrc )  
        {  
            return pDest;  
        }  
        if( Count <= 0 )  
         {  
             return pDest;  
         }  
        char *pStart = pDest;  
         while( ( Count-- ) > 0 && ( *pStart++=*pSrc++ ) );  
         *pStart = '\0';  
         return pDest;  
     }   
    
    1. C语言中数字转字符串的方法是什么?(itoa)请实现他
    char* Myitoa( char *pDest, int val, int radix )  
     {  
         assert( NULL != pDest );  
         assert( radix > 1 );  
         const bool IsMinu = val < 0;  
         char buffer[ 16 ] = {};  
        int count = 0;  
      
        do  
        {  
             buffer[ count++ ] = abs(val) % radix;  
            val /= radix;  
         }  
         while( val );  
       
       
        if( IsMinu )  
        {  
            pDest[ 0 ] = '-';  
            for( int i = 0; i < count; ++i )  
             {  
                 pDest[ i + 1 ] = '0' + buffer[ count - i - 1 ];  
             }  
            pDest[ count + 1 ] = '\0';  
         }  
         else  
         {  
            for( int i = 0; i < count; ++i )  
             {  
                pDest[ i ] = '0' + buffer[ count - i - 1 ];  
            }  
            pDest[ count ] = '\0';  
         }  
         return pDest;  
     }  
    
    1. 如何判断链表是否有环
    bool IsLoop( Node *pHead )  
    {  
        //[H->A->B->C->A]  
         assert( NULL != pHead );  
         Node *pNext = pHead->mpNext;  
         Node *pNextNext = pHead->mpNext;  
         while( NULL != pNext && NULL != pNextNext->mpNext )  
          {  
             pNext = pNext->mpNext;//[ B、C、A ]  
             pNextNext = pNextNext->mpNext->mpNext;//[C、B、A]  
             if( pNext == pNextNext )  
             {  
                 return true;  
             }  
         }  
         return false;  
    }  
    
    1. 统计出一个字符串每种字母出现的次数要求时间复杂度为O(n)
    void CountLetter( const char *pSrc )  
     {  
         int count[ 256 ] = {};  
        for( ; *pSrc !='\0'; ++pSrc )  
        {  
            const char &c = *pSrc;  
            if( ( c < 'A' || c > 'z') && ( c < 'a' || c > 'z' ) )  
           {  
                 continue;  
             }  
            ++count[ c ];  
        }  
     }   
    
    1. 选择排序的思想是什么?( 每次找到最大或最小的值放在数组的低位上 )请实现它
    void SelectSort( int *pArray, int count )  
    {  
        for( int i = 0; i < count; ++i )  
        {  
            //默认低位元素最小  
            int MinValue = pArray[ i ];  
          //默认保存低位元素的索引  
             int MinIndex = i;  
             //除开第一个元素找是否还有比它还小的元素( 升序 )  
            for( int j = i + 1; j < count; ++j )  
            {  
                 //发现找到比它还小的元素重新赋值和保存索引  
                if( pArray[ j ] < MinValue )  
               {  
                    MinValue = pArray[ j ];  
                    MinIndex = j;  
                }  
             }  
            //将找到最小元素放在数组低位上面  
            const int Temp = pArray[ i ];  
            pArray[ i ] = MinValue;  
            pArray[ MinIndex ] = Temp;  
        }  
     }  
    
    1. 冒泡排序的思想是什么?(升序排序中越小的数往低位走,越大的数往高位走,每次与相邻元素比较导致的特点)请实现它
    void BubbleSort( int *pArray, int count )  
     {  
         //eg.[6][8][8][0][9][1]  
        //i = 0,j < 5    [6][8][0][8][1][9]  
         //i = 1,j < 4    [6][0][8][1][8][9]  
         //i = 2,j < 3    [0][6][1][8][8][9]  
         //i = 3,j < 2    [0][1][6][8][8][9]  
       
         //到此为止已经排序OK了  
        //i = 4,j < 1    [0][1][6][8][8][9]  
         //i = 5,j < 0    [0][1][6][8][8][9]  
         for( int i = 0; i < count; ++i )  
         {  
             for( int j = 0; j < count - i - 1; ++j )  
             {  
                 if( pArray[ j ] > pArray[ j + 1 ] )  
                 {  
                     const int Temp = pArray[ j ];  
                     pArray[ j ] = pArray[ j + 1 ];  
                     pArray[ j + 1 ] = Temp;  
                 }  
             }  
         }  
     }  
    
    1. 已知两个数组有序实现一个方法将他们合并后任然有序
    void MergeSort( int *pMerge, int *p1, int p1len, int *p2, int p2len )  
     {  
        assert( nullptr != pMerge && nullptr != p1 && nullptr != p2 );               
    
         int i = 0;  
         int j = 0;  
         int k = 0;  
         while( i < p1len && j < p2len )  
         {  
             if( p1[ i ] < p2[ j ] )  
             {  
                 pMerge[ k ] = p1[ i ];  
                ++k;  
                ++i;  
             }  
             else  
             {  
                pMerge[ k ] = p2[ j ];  
                ++k;  
                ++j;  
            }  
         }  
         while( i < p1len )  
        {  
             pMerge[ k ] = p1[ i ];  
            ++k;  
             ++i;  
         }  
         while( j < p2len )  
         {  
            pMerge[ k ] = p2[ j ];  
            ++k;  
            ++j;  
         }  
     }  
    
    1. 实现一个算法找到数组中第二大的数
    int FindSec( int *p, int len )  
      {  
        assert( nullptr != p );  
         int maxv = p[ 0 ];  
         int secv = p[ 0 ];  
        for( int i = 1; i < len; ++i )  
         {  
            if( maxv < p[ i ] )  
            {  
                 secv = maxv;  
                 maxv = p[ i ];  
             }  
         }  
         return secv;  
     }  
    
    
    
    
    
    
    
    
    
    展开全文
  • java数组定义、使用、以及数组内存分析详解

    千次阅读 多人点赞 2019-10-04 22:17:30
    旨在全面了解java数组。 文章目录1、什么是容器2、什么是数组3、数组的三种定义定义方式一定义方式二定义方式三数组定义格式详解:4、数组的访问5、什么是内存5.1 Java虚拟机的内存划分5.2 数组在内存中的存储...

    前言

    本文主要是从容器概念到数组概念再到数组的三种定义以及各种情况的数组内存分析,以及一些数组常用的遍历以及反转、取最大值等操作。旨在全面性了解java数组。

    1、什么是容器

    在讲数组之前,很有必要来谈谈容器的概念 所谓容器就是将多个数据存储到一起,每个数据称为该容器的元素。

    生活中的容器:水杯,衣柜,教室

    2、什么是数组

    所谓数组就是存储数据长度固定容器,保证多个数据的数据类型一致

    3、数组的三种定义

    下面主要讲解下数组的三种定义方式。

    定义方式一

    数据类型[] 数组名字 = new 数据类型 [长度] ;

    举例: 定义可以存储3个整数的数组容器,代码如: int[] arr = new int[3]

    定义方式二

    数据类型[] 数组名 = new 数据类型[]{元素1,元素2,元素3...};

    举例: 定义存储1,2,3,4,5整数的数组容器。 int[] arr = new int[]{1,2,3,4,5};

    定义方式三

    数据类型[] 数组名 = {元素1,元素2,元素3...};

    举例: 定义存储1,2,3,4,5整数的数组容器 int[] arr = {1,2,3,4,5};

    数组定义格式详解:

    数组存储的数据类型: 创建的数组容器可以存储什么数据类型。
    [] : 表示数组。
    数组名字:为定义的数组起个变量名,满足标识符规范,可以使用名字操作数组
    new:关键字,创建数组使用的关键字。
    数组存储的数据类型: 创建的数组容器可以存储什么数据类型。
    [长度]:数组的长度,表示数组容器中可以存储多少个元素。
    注意:数组有定长特性,长度一旦指定,不可更改。 和水杯道理相同,买了一个2升的水杯,总容量就是2升,不能多也不能少。

    4、数组的访问

    索引:从0开始,索引 (index)可以访问到数组中的元素。

    格式数组名[索引]

    数组长度属性: 数组长度是固定的,由于索引从0开始,所以数组的最大索引值为数组名.length-1

    public static void main(String[] args) { 
    int[] arr = new int[]{1,2,3,4,5}; 
    //打印数组的属性,输出结果是5 
    System.out.println(arr.length); 
    }
    

    5、什么是内存

    在讲数组的原理内存前,必须了解的一个叫内存的概念。

    内存是计算机中的重要原件,临时存储区域,作用是运行程序。我们编写的程序是存放在硬盘中的,在硬盘中的程 序是不会运行的,必须放进内存中才能运行,运行完毕后会清空内存。 Java虚拟机要运行程序,必须要对内存进行空间的分配和管理。

    5.1 Java虚拟机的内存划分

    为了提高运算效率,就对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据方式和内存管理方式。
    在这里插入图片描述

    5.2 数组在内存中的存储

    5.2.1 一个数组内存图
    public static void main(String[] args) { 
    	int[] arr = new int[3]; 
    	System.out.println(arr);//[I@5f150435 
    	}
    

    以上方法执行,输出的结果是[I@5f150435,这个是什么呢?是数组在内存中的地址。new出来的内容,都是在堆内存中存储的,而方法中的变量arr保存的是数组的地址。

    输出arr[0],就会输出arr保存的内存地址中数组中0索引上的元素,如没有具体确定值,则是类型的默认值!比如:

     String[] arr=new String[3];
       System.out.println(arr);     //  [Ljava.lang.String;@1b6d3586
       System.out.println(arr[0]);  //  null
       
     int[] arrInt=new int[3];
       System.out.println(arrInt);    // [I@1b6d3586
       System.out.println(arrInt[0]); // 0
    

    在这里插入图片描述

    5.2.2两个数组内存图
     public static void main(String[] args) { 
    	 int[] arr = new int[3]; 
    	 int[] arr2 = new int[2]; 
    	 System.out.println(arr); 
    	 System.out.println(arr2); 
     }
    

    在这里插入图片描述

    5.2.3 两个变量指向一个数组
     public static void main(String[] args) { 
    	 // 定义数组,存储3个元素 
    	 int[] arr = new int[3]; 
    	 //数组索引进行赋值 
    	 arr[0] = 5; 
    	 arr[1] = 6; 
    	 arr[2] = 7; 
    	 //输出3个索引上的元素值 
    	 System.out.println(arr[0]);
    	 System.out.println(arr[1]); 
    	 System.out.println(arr[2]); 
    	 //定义数组变量arr2,将arr的地址赋值给arr2 
    	 int[] arr2 = arr; 
    	 arr2[1] = 9; 
    	 System.out.println(arr[1]); 
     }
    

    在这里插入图片描述

    6、数组常见的异常

    数组常见的异常主要有数组越界异常以及空指针异常,这是非常基础的,就不多做介绍了,这里主要分析下空指针异常在内存的情况
    在这里插入图片描述

    7、 数组遍历【重点】

    所谓数组遍历 就是将数组中的每个元素分别获取出来,就是遍历。遍历数组非常重要!!!

     public static void main(String[] args) { 
    	 int[] arr = { 1, 2, 3, 4, 5 }; 
    	 System.out.println(arr[0]); 
    	 System.out.println(arr[1]); 
    	 System.out.println(arr[2]); 
    	 System.out.println(arr[3]); 
    	 System.out.println(arr[4]); 
    	 } 
    

    以上代码是可以将数组中每个元素全部遍历出来,但是如果数组元素非常多,我把这种方式叫做傻瓜式遍历,这种傻瓜式写法肯定不行,因此我们需要改 造成循环的写法。数组的索引是 0lenght-1 ,可以作为循环的条件出现。如下

     public static void main(String[] args) { 
    	 int[] arr = { 1, 2, 3, 4, 5 }; 
    	 for (int i = 0; i < arr.length; i++) {
    	 System.out.println(arr[i])
    	    } 
    	  } 
    

    8、数组获取最大值元素

    实现思路:
    定义变量,保存数组0索引上的元素
    遍历数组,获取出数组中的每个元素
    将遍历到的元素和保存数组0索引上值的变量进行比较
    如果数组元素的值大于了变量的值,变量记录住新的值
    数组循环遍历结束,变量保存的就是数组中的最大值

    真香警告大家小心点,别被第一个和第二个婆娘给迷住了
    在这里插入图片描述
    代码如下:

    public static void main(String[] args) { 
    	int[] arr = { 5, 15, 2000, 10000, 100, 4000 }; 
    	//定义变量,保存数组中0索引的元素 
    	int max = arr[0]; 
    	//遍历数组,取出每个元素 
    	for (int i = 0; i < arr.length; i++) { 
    	//遍历到的元素和变量max比较 
    	//如果数组元素大于max 
    	if (arr[i] > max) { 
    	//max记录住大值 
    	max = arr[i]; 
        } 
      }
    System.out.println("数组最大值是: " + max);
    } 
    

    9、 数组反转

    所谓反转就是把数组元素位置颠倒过来

    实现思想:数组最远端的元素互换位置
    实现反转,就需要将数组最远端元素位置交换
    定义两个变量,保存数组的最小索引和最大索引
    两个索引上的元素交换位置 最小索引++,最大索引–,再次交换位置
    最小索引超过了最大索引,数组反转操作结束

    在这里插入图片描述

    具体代码如下

    public static void main(String[] args) { 
    	int[] arr = { 1, 2, 3, 4, 5 }; 
    	/*循环中定义变量min=0最小索引 
    	max=arr.length‐1最大索引 
    	min++,max‐‐ */ 
    	for (int min = 0, max = arr.length ‐ 1; min <= max; min++, max‐‐){
    	//利用第三方变量完成数组中的元素交换 
    	int temp = arr[min]; 
    	arr[min] = arr[max]; 
    	arr[max] = temp;
    	 }
    	// 反转后,遍历数组 
    	for (int i = 0; i < arr.length; i++) {
    	 System.out.println(arr[i])
    	 } 
     } 
    

    10、数组作为方法参数和返回值

    首先明确一点:数组作为方法的返回值,返回的是数组的内存地址

    public static void main(String[] args) {
    	 //调用方法,接收数组的返回值 
    	 //接收到的是数组的内存地址 
    	 int[] arr = getArray(); 
    	 for (int i = 0; i < arr.length; i++) { 
    	 System.out.println(arr[i])
    	    } 
    	 }
    	 /* 创建方法,返回值是数组类型创建方法,返回值是数组类型 
    	 return返回数组的地址 */
    	 public static int[] getArray() { 
    	 int[] arr = { 1, 3, 5, 7, 9 };
    	  //返回数组的地址,返回到调用者
         return arr; 
       }
    

    在这里插入图片描述

    11、方法的参数类型区别

    方法的参数为基本类型时,传递的是数据值. 方法的参数为引用类型时,传递的是地址值.

    分析下列程序代码,计算输出结果。

     public static void main(String[] args) { 
    	 int a = 1; int b = 2; 
    	 System.out.println(a); 
    	 System.out.println(b); 
    	 change(a, b); 
    	 System.out.println(a); 
    	 System.out.println(b); 
    	 }
    	 public static void change(int a, int b) { 
    	 a = a + b; b = b + a; 
     } 
    
    1. 分析下列程序代码,计算输出结果。
    public static void main(String[] args) { 
    	int[] arr = {1,3,5}; 
    	System.out.println(arr[0]); 
    	change(arr); 
    	System.out.println(arr[0]); 
    }
    	
    	public static void change(int[] arr) {
    	arr[0] = 200; 
       }
    

    总结: 方法的参数为基本类型时,传递的是数据值. 方法的参数为引用类型时,传递的是地址值.

    相信各位同学已经将上面的代码copy到eclipse或者idea中运行了,是不是结果很意外?而且对下面的总结不是很理解?这位童靴,我估计你可能是忽略了这一点:数组是引用类型,数组本身元素类型没有影响的,就像数组元素全是int,数组依旧是引用类型,哈哈,小白同学真可爱 中枪了咩,中枪的话点个赞呗支持支持下,关注楼主,楼主带你学java,关注楼主不迷路hhhhh

    展开全文
  • 时间复杂度和空间复杂度的概念...时间和空间(即寄存器)都是计算机资源的重要体现,而算法的复杂就是体现在运行该算法时的计算机所需的资源多少。 各种算法的复杂度如下: 时间复杂度: 1:算法的时间复杂度反映...
  • 前端面试题集锦——前言

    万次阅读 多人点赞 2018-12-02 20:37:54
    数据类型、运算、对象、Function、继承、闭包、作用域、原型链、事件、RegExp、JSON、Ajax、DOM、BOM、内存泄漏、跨域、异步装载、模板引擎、前端MVC、路由、模块化、Canvas、ECMAScript 6、Nodejs 其他: ...
  • 【数据库学习】数据库总结

    万次阅读 多人点赞 2018-07-26 13:26:41
    1,数据库 1)概念 ...数据库是长期存储在计算机内、有组织的、可共享的大量数据的集合。...常见数据库管理系统有:Access、mysql、sql server ...数据的共享,冗余度,易扩充 ...逻辑数据独立(logical data...
  • 内存管理:程序是如何被优雅的装载到内存中的

    千次阅读 多人点赞 2021-11-04 09:26:35
    内存作为计算机中一项比较重要的资源,它的主要作用就是解决CPU和磁盘之间速度的鸿沟,但是由于内存条是需要插入到主板上的,因此对于一台计算机来说,由于物理限制,它的内存不可能无限大的。我们知道我们写的代码...
  • 在序列化期间,对象将其当前状态写入到临时或持久存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。 序列化使其他代码可以查看或修改,那些不序列化便无法访问的对象实例数据。确切地...
  • SQLite剖析之临时文件、内存数据库

    千次阅读 2017-03-10 17:49:42
    原文地址:http://www.cnblogs.com/5211314jackrose/p/5816013.html一、7种临时文件 SQLite中,一个数据库由单个磁盘...但是,当在单个文件中存储一个数据库时,SQLite会在处理数据库的过程中使用许多临时文件。 S
  • 全面理解Java内存模型(JMM)及volatile关键字

    万次阅读 多人点赞 2017-06-12 11:25:05
    操作完成后再将变量x写回主内,而对于B线程的也是类似的,这样就有可能造成主内存与工作内存间数据存在一致问题,假如A线程修改完后正在将数据写回主内存,而B线程此时正在读取主内存,即将x=1拷贝到自己的工作...
  • 日期 内核版本 架构 作者 GitHub CSDN 2016-08-31 Linux-4.7 X86 & arm ... Linux内存管理 http://blog.csdn.net/vanbreaker/article/details/75799411 前景回顾前面我们讲到服务器体系(SMP, NUMA, M
  • 文章目录1、类加载和实例化2、Java是值传递还是引用传递3、类加载的主要过程4、什么是GC5、简述垃圾回收过程6、内存泄漏7、导致内存泄漏的场景8、Java中堆和栈的区别9、ArrayList、LinkedList、Vector的区别10、...
  • 内存测试入门

    千次阅读 2018-06-27 19:48:27
    由于内存组成的复杂,实际上并没有简单通用的方法就能够发现所有的内存问题。下面的章节里,我们会围绕一组案例展开,通过对案例的分析讲解各种内存测试的工具和方法。这些例子都是从真实的测试案例中提取的,经过...
  • Java内存泄漏与内存溢出详解

    千次阅读 2017-02-04 21:08:08
    内存泄漏指你用malloc或new申请了一块内存,但是没有通过free或delete将内存释放,导致这块内存一直处于占用状态。 内存溢出指你申请了10个字节的空间,但是你在这个空间写入11或以上字节的数据,就是溢出。
  • 计算机内存模型概念

    万次阅读 多人点赞 2017-10-27 14:38:39
    由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,这时就存在一个问题,由于CPU执行速度很快,而从内存读取数据和向内存写入数据的过程跟CPU执行指令的速度比起来要慢的多,因此如果任何时候对数据的...
  • 由于事实上发现内存“不再被需要”是不可判定的,因此垃圾收集的通常解决方案都存在局限。本节将介绍理解主要垃圾收集算法及其局限的必要概念。 内存引用 垃圾回收算法依靠的主要概念就是 引用 。 ...
  •  我们提出的弹性分布式数据集(RDDs),是一个让程序员在大型集群上以容错的方式执行基于内存计算的分布式内存抽象。RDDs受启发于两类使用当前计算框架处理不高效的应用:迭代算法和交互式数据挖掘工具。这二者在...
  • 日期 内核版本 架构 作者 GitHub CSDN 2016-08-31 ... Linux内存管理 1 前景回顾前面我们讲到服务器体系(SMP, NUMA, MPP)与共享存储器架构(UMA和NUMA)1.1 UMA和NUMA两种模型共享存储型多处理机有两种模型
  • App内存占用优化

    千次阅读 2017-01-12 18:47:04
    RAM(Random-access memory)在任何软件开发中都是非常宝贵的资源,移动操作系统由于其物理内存的局限更是如此。尽管ART(Android Runtime)与Dalvik虚拟机会执行常规的垃圾回收,但这并不意味着可以忽略App中的...
  • 常用内存数据库介绍

    万次阅读 2018-07-09 23:18:55
    1. 内存数据库简介 1.1 概念 一、什么是内存数据库 传统的数据库管理系统把所有数据都放在磁盘上进行管理,所以称做磁盘数据库(DRDB:Disk-Resident Database)。磁盘数据库需要频繁地访问磁盘来进行数据的操作,...
  • 记一次堆外内存溢出排查过程

    万次阅读 2018-09-30 12:58:14
    服务器发布/重启后,进程占用内存 21%(3g),观察进程占用内存,以一天4%左右的速度增长,一定时间过后,java 进程内存增长到接近 90%,服务器报警。此时 old 区占用 50%,未达到 CMS GC 阈值,因此不会触发 CMS GC,...
  • 内存屏障简介

    千次阅读 多人点赞 2020-06-22 15:34:57
    为了限制性能下降,CPU被设计成在从内存 中获取数据的同时,可以执行其他指令和内存引用。这明显会导致指令 和内存引用乱序执行,并导致严重的混乱 仅仅在两个CPU之间或者CPU与设备之间存在需要交互的可能 时,才...
  • 一、内存映射 内存映射文件允许开发人员预定一块地址空间区域并给区域调拨物理存储器。内存映射文件的物理存储器来自磁盘已有的文件,而不是来自系统的页交换文件。一旦把文件映射到地址空间,就可以对它进行访问,...
  • Java多线程之内存可见

    万次阅读 热门讨论 2015-11-03 02:47:55
    1、什么是JAVA 内存模型 Java Memory Model (JAVA 内存模型)描述线程之间如何通过内存(memory)来进行交互。 具体说来, JVM中存在一个主存区(Main Memory或Java Heap Memory),对于所有线程进行共享,而每个线程...
  • 摘要:高端内存页框的内核映射分为三种情况:永久内核映射、临时内核映射和非连续内存映射。那么这三者有什么区别和联系呢?临时内核映射如何保证不会被阻塞呢?本文主要为你解答这些疑问,并详细探讨高端内存映射的...
  • 一文搞懂JVM内存结构

    万次阅读 多人点赞 2019-04-11 20:30:23
    可能是觉得学习成本较高又或者是感觉没什么实用,所以干脆懒得“搭理”它了。其实这种想法是错误的。举个最简单的例子,JVM 基本上是每家招聘公司都会问到的问题,它们会这么无聊问这些不切实际的问题吗?很显然...
  • 真香!Linux 原来是这么管理内存

    千次阅读 多人点赞 2020-07-28 13:49:48
    Linux 内存管理模型非常直接明了,因为 Linux 的这种机制使其具有可移植并且能够在内存管理单元相差不大的机器下实现 Linux,下面我们就来认识一下 Linux 内存管理是如何实现的。 基本概念 每个 Linux 进程都会有...
  • JVM成神之路-Java内存模型(JMM)

    千次阅读 2018-07-03 15:32:05
    Java 内存模型基础 什么是 Java 内存模型(JMM-共享内存模型) 内存模型描述了程序中各个变量... Java内存模型定义了多线程之间共享变量的可见以及如何在需要的时候对共享变量进行同步。原始的Java内存模型效率...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 298,839
精华内容 119,535
关键字:

内存是临时性的吗