自我介绍加一堆知识点,其实很基础,还需要好好记!
1.程序编译时内存分为几个存储区?
答:五个区,分别是 堆(heap)、栈(stack)、全局区/静态区(static)、文字常量区、程序代码区。
堆:就是那些由new分配的内存块,他们的释放编译器不去管,而是由应用程序去控制,一般一个new对应一个 delete。如果程序员没有释放,则在程序运行结束后由操作系统(OS)自动回收,分配方式类似于链表。
栈:由编译器在需要的时候分配,不需要的时候自动清除。一般存的是局部变量、函数参数,操作方式类似于数 据结构中的栈。注意:堆栈指的是栈
全局区/静态区:编译器编译时分配内存,初始化的全局变量和静态变量区在一块区域,未初始化的全局变量和静 态变量在相邻的另一块区域。程序结束后由OS释放。
文字常量区:常量字符串存放在此。结束后由OS释放。
程序代码区:存放函数体的二进制代码。
(a)函数体中定义的变量通常是在栈上,
(b)用malloc, calloc, realloc等分配内存的函数分配得到的就是在堆上。
(c)在所有函数体外定义的是全局量,
(d)加了static修饰符后不管在哪里都存放在全局区(静态区),
(e)在所有函数体外定义的static变量表示在该文件中有效,不能extern到别的文件中用
(f)在函数体内定义的static表示只在该函数体内有效。
(7)另外,函数中的"adgfdf"这样的字符串存放在常量区。
2.堆和栈的区别
答:管理方式不同、空间大小不同、能否产生碎片不同、生长方向不同、分配方式和效率不同
栈效率高,使用的内存有限;堆生存期由程序员决定,使用灵活,问题多
管理方式不同:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生内存泄漏memory leak。
空间大小不同:栈在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在 WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存,由此可见,堆获得的空间比较灵活,也比较大,而且我们可以修改它。
能否产生碎片不同:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题, 因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出,在他弹出之前,在他上面的后进的栈内容已经被弹出。
生长方向不同:对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。
分配方式不同:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配(变长数组,STL源码)和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。
分配效率不同:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。
堆和栈相比,由于大量new/delete的使用,容易造成大量的内存碎片;由于没有专门的系统支持,效率很低;由于可能引发用户态 和核心态的切换,内存的申请,代价变得更加昂贵。所以栈在程序中是应用最广泛的,就算是函数的调用也利用栈去完成,函数调用过程中的参数,返回地址, EBP和局部变量都采用栈的方式存放。所以,我们推荐大家尽量用栈,而不是用堆。虽然栈有如此众多的好处,但是由于和堆相比不是那么灵活,有时候分配大量的内存空间,还是用堆好一些。
3.堆产生内存碎片该怎么办,你了解过内存池吗,内存池怎么记录首地址?
防止内存碎片:在初始化时new所有变量,在运行中不动态生成任何变量,在结束时delete所有变量。这样做的话内存在运行中不会变化,自然也就不会产生内存碎片了。
如何避免内存碎片的产生:
1、少用动态内存分配的函数(尽量使用栈空间)
2、分配内存和释放的内存尽量在同一个函数中
3、尽量一次性申请较大的内存2的指数次幂大小的内存空间,而不要反复申请小内存(少进行内存的分割)
4、使用内存池来减少使用堆内存引起的内存碎片
5、尽可能少地申请空间。
6、尽量少使用堆上的内存空间~
7、做内存池:new和delete内存的开销相对大,直接从已经分配好的内存数组中找一块空闲的内存块相对小。所以预先分配一块大内存,分割为小内存块,不用时标记空闲,使用时再获取一下更快速,这就是内存池的雏形,使内存分配效率得到提高。(未完待续)
http://blog.chinaunix.net/uid-479984-id-2114952.html //关于内存池原理的介绍
4.什么情况下会溢出,溢出该怎么办?
数据类型超过了计算机字长的界限就会出现数据溢出的情况。导致内存溢出问题的原因有很多,比如:
(1) 使用非类型安全(non-type-safe)的语言如 C/C++ 等。
(2) 以不可靠的方式存取或者复制内存缓冲区。
(3)编译器设置的内存缓冲区太靠近关键数据结构。
1. 内存溢出问题是 C 语言或者 C++ 语言所固有的缺陷,它们既不检查数组边界,又不检查类型可靠性(type-safety)。众所周知,用 C/C++ 语言开发的程序由于目标代码非常接近机器内核,因而能够直接访问内存和寄存器,这种特性大大提升了 C/C++ 语言代码的性能。只要合理编码,C/C++ 应用程序在执行效率上必然优于其它高级语言。然而,C/C++ 语言导致内存溢出问题的可能性也要大许多。其他语言也存在内容溢出问题,但它往往不是程序员的失误,而是应用程序的运行时环境出错所致。
2. 当应用程序读取用户(也可能是恶意攻击者)数据,试图复制到应用程序开辟的内存缓冲区中,却无法保证缓冲区的空间足够时(换言之,假设代码申请了 N 字节大小的内存缓冲区,随后又向其中复制超过 N 字节的数据)。内存缓冲区就可能会溢出。想一想,如果你向 12 盎司的玻璃杯中倒入 16 盎司水,那么多出来的 4 盎司水怎么办?当然会满到玻璃杯外面了!
3. 最重要的是,C/C++ 编译器开辟的内存缓冲区常常邻近重要的数据结构。现在假设某个函数的堆栈紧接在在内存缓冲区后面时,其中保存的函数返回地址就会与内存缓冲区相邻。此时,恶意攻击者就可以向内存缓冲区复制大量数据,从而使得内存缓冲区溢出并覆盖原先保存于堆栈中的函数返回地址。这样,函数的返回地址就被攻击者换成了他指定的数值;一旦函数调用完毕,就会继续执行“函数返回地址”处的代码。非但如此,C++ 的某些其它数据结构,比如 v-table 、例外事件处理程序、函数指针等,也可能受到类似的攻击。
容易导致内存溢出的函数:strcpy() strcat() gets() sprintf() scanf() strdup()
5.C++有哪些常用的关键字,new和malloc的区别?
new和malloc的区别:new delete 是运算符,malloc,free是函数
1、malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。
2、对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数,这个时候就要用new和delete。
3、new可以认为是malloc加构造函数的执行。new出来的指针是直接带类型信息的。而malloc返回的都是void指针。
十大最常用关键字:if, return, void, int, const, this, else, for, virtual, class
一个很齐全的解释网址:http://3y.uu456.com/bp_1kbyc7y18n6msol1o41b_1.html
1. inline:定义内联函数,该关键字是基于定义,如果只在函数声明时给出inline,则函数不会被认为是内联函数,所以必须在函数定义的地方也加上inline,同时inline只是向编译器建议函数以内联函数处理,不是强制的。
2. const:定义常成员,包括const数据成员和const成员函数,const数据成员必须,也只能通过构造函数的初始化列表进行初始化,const成员函数只能访问类的成员,不能进行修改,如果需要修改,则引入下面的mutable关键字。
3.mutable:这个关键字的引入是解决const成员函数要修改成员变量,通常而言,const成员函数只能访问成员变量,不能修改,但是如果成员变量被mutable修饰了,则在const成员函数中可以修改该变量。mutable和const不能同时用于修饰成员变量。
4. static:声明静态成员,包括静态数据成员和静态成员函数,它们被类的所有对象共享,静态数据成员在使用前必须初始化,而静态成员函数只能访问静态数据成员,不能访问非静态数据成员,因为该函数不含有this指针。
5. virtual:声明虚函数,用于实现多态,该关键字是基于声明的。
6. friend:声明友元函数和友元类,该关键字也是基于声明的。
7. volatile:被该关键字修饰的变量是指其值可能在编译器认识的范围外被修改,因此编译器不要对该变量进行的操作进行优化。可以与const同时修饰一个变量,有些变量是用volatile关键字声明的。当两个线程都要用到某一个变量且该变量的值会被改变时,应该用volatile声明,该关键字的作用是防止优化编译器把变量从内存装入CPU寄存器中。如果变量被装入寄存器,那么两个线程有可能一个使用内存中的变量,一个使用寄存器中的变量,这会造成程序的错误执行。volatile的意思是让编译器每次操作该变量时一定要从内存中真正取出,而不是使用已经存在寄存器中的值
8.define:
9.entern:置于变量或者函数前,以表示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义,此外extern也可用来进行链接指定。extern关键字的作用是声明变量和函数为外部链接,即该变量或函数名在其它文件中可见。用其声明的变量或函数应该在别的文件或同一文件的其它地方定义。
例如语句:extern int a;
仅仅是一个变量的声明,其并不是在定义变量a,并未为a分配内存空间。变量a在所有模块中作为一种全局变量只能被定义一次,否则会出现连接错误。通常,在模块的头文件中对本模块提供给其它模块引用的函数和全局变量以关键字extern声明。例如,如果模块B欲引用该模块A中定义的全局变量和函数时只需包含模块A的头文件即可。这样,模块B中调用模块A中的函数时,在编译阶段,模块B虽然找不到该函数,但是并不会报错;它会在连接阶段中从模块A编译生成的目标代码中找到此函数
6.inline有什么特点,为什么适合频繁使用的短小函数?
普通函数的调用要经过“保存现场、转到被调函数执行、执行完毕返回调用处、恢复现场”这一过程,产生时空开销。内联函数是通过代码膨胀来执行的,在内联函数调用处复制函数代码,这样省去了普通函数调用的时空开销,提高了程序执行效率,但是由于代码复制增加了内存开销,所以内联函数应当是小函数、执行耗时短的函数。这也就说明了为什么不能把所有的函数都作为内联函数。内联函数是C++为提高程序运行速度所做的一项改进。常规函数和内联函数之间的主要区别不再于编写方式,而在于C++编译器如何将它们组合到程序中。内联函数的编译代码与其他程序代码”内联”起来,即编译器将使用相应的函数代码替换函数调用。对于内联代码,程序无需跳到另一个位置处执行代码,然后再跳回来。因此,内联函数的运行速度比常规函数稍快。但代价是需要占用更多内存。
要使用这项特性,必须采取下述措施:
1、在函数声明前加上关键字inline。
2、在函数定义前加上关键字inline。
最好将内联函数定义放在头文件中。内联函数和常规函数一样,也是按值来传递参数的。并且内联函数本身不能是直接递归函数(即,自己内部还调用自己的函数)。
7.static的生存周期,它有什么特点和作用?
在C或C++语言中,static主要有以下几个作用:
(1) 函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此值在下次调用时仍维持上次的值。
(2) 在模块内的static全局变量可以被模块内的所以函数访问,但不能被模块外的其他函数访问。
(3) 在模块内的static函数只可被这一模块内的其他函数调用,但这个函数的使用范围被限制在声明它的模块内。
(4) 在类中的static成员变量属于整个类所拥有,对类的所以对象只是有一份拷贝。
(5) 在类中的static成员变量属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量。
8.你有没有使用过throw?在throw的函数体,大括号内怎么处理?
throw是自己抛出了个异常,一般异常是系统抛出异常,用try catch来截获异常
throw关键字通常用于方法体中,并且抛出一个异常对象。程序在执行到throw语句时立即终止,它后面的语句都不执行。通过throw抛出异常后,如果想在上一级代码中来捕获并处理异常,则需要在抛出异常的方法中使用throws关键字在方法的声明中指明要抛出的异常;如果要捕捉throw抛出的异常,则必须使用try-catch语句。
9.网络层和传输层有什么区别和作用?
传输层:管理两个节点之间的数据传输,确保数据被可靠的传送到目标地址。只在通信双方节点上进行处理,无需在路由器上处理。传输层进行建立连接或断开连接的处理,在两个主机之间创建逻辑上的通信连接。此外,传输层为了确保所传输的数据到达目标地址,会在通信两端的计算机之间进行确认,如果数据没有到达,会进行重传。包括TCP(面向连接)、UDP(面向无连接)、SCDP、DCCP
网络层:将数据传输到目标地址,主要负责寻址和路由选择,包括IP协议(不重传,非可靠的协议)、ARP协议、ICMP协议(数据包无法到达,发送异常)。
10.TCP/IP协议分布在哪几层中?
TCP/IP协议并不完全符合OSI的七层参考模型。传统的OSI参考模型,是一种通信协议的7层抽象的参考模型,其中每一层执行某一特定任务。该模型的目的是使各种硬件在相同的层次上相互通信。这7层是:物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。而TCP/IP通讯协议采用了4层的层级结构,每一层都呼叫它的下一层所提供的网络来完成自己的需求。这4层分别为:
应用层:应用程序间沟通的层,如简单电子邮件传输(SMTP)、文件传输协议(FTP)、网络远程访问协议(Telnet)等。
传输层:在此层中,它提供了节点间的数据传送服务,如传输控制协议(TCP)、用户数据报协议(UDP)等,TCP和UDP给数据包加入传输数据并把它传输到下一层中,这一层负责传送数据,并且确定数据已被送达并接收。
互连网络层:负责提供基本的数据封包传送功能,让每一块数据包都能够到达目的主机(但不检查是否被正确接收),如网际协议(IP)。
网络接口层:对实际的网络媒体的管理,定义如何使用实际网络(如Ethernet、Serial Line等)来传送数据
OSI参考模型注重“通信协议的必要功能是什么” TCP/IP则更强调“在计算机上实现协议应该开发哪种程序”
11.IP报文头部包含哪些信息,首部多长?
IP数据报首部中有一个首部长度字段,4位长,可表示的最大十进制数字是15。因此首部长度的最大值是15个4字节长的字,即60字节。典型的IP数据报不使用首部中的选项,因此典型的IP数据报首部长度是20字节。
12.TCP和UDP有什么区别?
TCP提供面向连接的、可靠的数据流传输,而UDP提供的是非面向连接的、不可靠的数据流传输。 TCP传输单位称为TCP报文段,UDP传输单位称为用户数据报。 TCP注重数据安全性,UDP数据传输快,因为不需要连接等待,少了许多操作,但是其安全性却一般。TCP对应的协议和UDP对应的协议
TCP对应的协议:
(1) FTP:定义了文件传输协议,使用21端口。
(2) Telnet:一种用于远程登陆的端口,使用23端口,用户可以以自己的身份远程连接到计算机上,可提供基于DOS模式下的通信服务。
(3) SMTP:邮件传送协议,用于发送邮件。服务器开放的是25号端口。
(4) POP3:它是和SMTP对应,POP3用于接收邮件。POP3协议所用的是110端口。
(5)HTTP:是从Web服务器传输超文本到本地浏览器的传送协议。
UDP对应的协议:
(1) DNS:用于域名解析服务,将域名地址转换为IP地址。DNS用的是53号端口。
(2) SNMP:简单网络管理协议,使用161号端口,是用来管理网络设备的。由于网络设备很多,无连接的服务就体现出其优势。
(3) TFTP(Trival File Tran敏感词er Protocal),简单文件传输协议,该协议在熟知端口69上使用UDP服务。
1.基于连接与无连接;
2.对系统资源的要求(TCP较多,UDP少);
3.UDP程序结构较简单;
4.流模式与数据报模式 ;
5.TCP保证数据正确性,UDP可能丢包,TCP保证数据顺序,UDP不保证。
13.TCP有哪些拥塞控制机制?窗口滑动机制的窗口是两边都有吗(是)?
滑动窗口协议,是TCP使用的一种流量控制方法。该协议允许发送方在停止并等待确认前可以连续发送多个分组。由于发送方不必每发一个分组就停下来等待确认,因此该协议可以加速数据的传输。
当发送窗口和接收窗口的大小都等于 1时,就是停止等待协议。
当发送窗口大于1,接收窗口等于1时,就是回退N步协议。
当发送窗口和接收窗口的大小均大于1时,就是选择重发协议
14.哈希表解决冲突有哪几种方法?
开放定址法(再散列法)、再哈希法、链地址法、建立公共溢出区
15.局部变量可以返回吗?
返回局部变量是没有问题的,返回指向局部变量的指针才有问题, 函数退栈之后,局部变量消失, 指针将指向未知区域,所以出现问题。返回局部变量的引用也是绝对不可以的。 引用只是变量的一个别名,变量本体都不存在了,引用当然也没有任何意义。
16.TCP的三次握手和四次挥手过程
TCP三次握手过程
1 主机A通过向主机B 发送一个含有同步序列号的标志位的数据段给主机B ,向主机B 请求建立连接,通过这个数据段,
主机A告诉主机B 两件事:我想要和你通信;你可以用哪个序列号作为起始数据段来回应我.
2 主机B 收到主机A的请求后,用一个带有确认应答(ACK)和同步序列号(SYN)标志位的数据段响应主机A,也告诉主机A两件事:
我已经收到你的请求了,你可以传输数据了;你要用哪佧序列号作为起始数据段来回应我
3 主机A收到这个数据段后,再发送一个确认应答,确认已收到主机B 的数据段:"我已收到回复,我现在要开始传输实际数据了
这样3次握手就完成了,主机A和主机B 就可以传输数据了.
3次握手的特点:没有应用层的数据SYN这个标志位,只有在TCP建产连接时才会被置1,握手完成后SYN标志位被置0
四次挥手:
1 当主机A完成数据传输后,将控制位FIN置1,提出停止TCP连接的请求
2 主机B收到FIN后对其作出响应,确认这一方向上的TCP连接将关闭,将ACK置1
3 由B 端再提出反方向的关闭请求,将FIN置1
4 主机A对主机B的请求进行确认,将ACK置1,双方向的关闭结束.
由TCP的三次握手和四次断开可以看出,TCP使用面向连接的通信方式,大大提高了数据通信的可靠性,使发送数据端
和接收端在数据正式传输前就有了交互,为数据正式传输打下了可靠的基础
名词解释:
ACK TCP报头的控制位之一,对数据进行确认.确认由目的端发出,用它来告诉发送端这个序列号之前的数据段
都收到了.比如,确认号为X,则表示前X-1个数据段都收到了,只有当ACK=1时,确认号才有效,当ACK=0时,确认号无效,这时会要求重传数据,保证数据的完整性.
SYN 同步序列号,TCP建立连接时将这个位置1
FIN 发送端完成发送任务位,当TCP完成数据传输需要断开时,提出断开连接的一方将这位置1