1. 局部变量作用域会覆盖同名的全局变量。(在同一个源文件中,外部变量与局部变量同名,则在局部变量的作用范围内,外部变量不起作用。)
2.动态绑定实现了方法的定义与具体的对象无关,而对方法的调用则可以关联于具体的对象。
面向对象有三种特性:封装,继承,多态
其中多态性有两种,一种是依靠函数重载实现的静态绑定,一种是依靠虚函数实现的动态绑定。动态绑定跟对象的引用类型无关,它会根据具体的对象调用对应的方法。
静态绑定是在编译阶段所执行的函数就已经被确定了的,而动态绑定是在程序执行时才决定使用哪个函数。
使用virtual关键字可以使系统使用迟绑定技术。如果没有这个关键字,编译器就会直接将父类的函数进行绑定,而使用virtual关键字的函数在编译的时候就不会进行绑定,而在程序运行的时候,根据具体的调用对象来调用不同的方法,这就是c++的多态性,而在java中没有指针,就不会有这样的问题
3.(1)输出2个字节
#include<iostream>
#include<string.h>
using namespace std;
int i=1;
struct s1{
union{
short a,b;
};
};
int main() {
cout<<sizeof(s1);
return 0;
}
(2)输出4个字节。
#include<iostream>
#include<string.h>
using namespace std;
int i=1;
struct s1{
union{
unsigned int c:32,d:1;
};
};
int main() {
cout<<sizeof(s1);
return 0;
}
(3)输出4字节
#include<iostream>
#include<string.h>
using namespace std;
int i=1;
struct s1{
union{
short a,b;
unsigned int c:32,d:1;
};
};
int main() {
cout<<sizeof(s1);
return 0;
}
综上分析:实际为只有short a,b;其大小为2个字节,占用的是short类型的大小;当引入unsigned int,此时联合体的大小为4个字节,不管定义了几个这样类型的变量,只要它占用的位数为1-32,会使得它的贡献的字节数为4个字节,即最大变量类型的大小。
为什么要字节对齐?
为了提高效率,计算机从内存中取数据时按照一个固定长度的。它每次取32个位,也就是4个字节。字节对齐的好处?--------以int型数据为例,如果它在内存中存放的位置按4字节对齐,也就是说1个int的数据全部落在计算机一次取数的区间内,那么只需要取一次就可以了。
如果不对齐,很不巧,这个int数据刚好跨越了取数的边界,这样就需要取两次才能把这个int的数据全部取到,这样效率也就降低了。
内存对齐是会浪费一些空间,但是这种空间上的浪费却可以减少取数的时间,这是典型的一种以空间换时间的做法。
结构体的对齐
1)、结构体内成员按自身长队对齐。自身长度,如char=1, shor=2,int =4 ,double=8。所谓自身对齐,指的是该成员的起始位置的内存地址必须是它自身长度的整数倍。如int只能以0,4,8这类的地址开始。
2)、结构体的总大小为结构体的有效对齐值的整数倍。
结构体的有效对齐值的确定:
1) 未明确指定,以结构体中最长的成员的长度为其有效值。
2) 当#pragma pack(n)指定时,以n和结构体中最长的成员的长度较小者为其值。
3) 当用_attribute__((_packed_))指定长度时,强制按照此值为结构体的有效对齐值。
4. 内联函数不是在调用时发生控制转移,而是在编译时将函数体嵌入在每一个调用处。编译时,类似宏替换,使用函数体替换调用处的函数名。一般在代码中用inline修饰,但是能否形成内联函数,需要看编译器对该函数定义的具体处理。内联扩展是用来消除函数调用时的时间开销。它通常用于频繁执行的函数。一个小内存空间的函数非常受益。
内联函数只适合于只有1~5行的小函数。对一个含有许多语句的大函数,函数调用和返回的开销相对来说微不足道,所以也没有必要用内联函数实现。
在内联函数内不允许用循环语句和开关语句。否则编译将该函数视同普通函数。
5.(1)
#include<iostream>
#include<string.h>
using namespace std;
char *getMem(void) {
char p[]="hello world";
p[5]=0x0;
return p;
}
void test(void) {
char *s=0x0;
s=getMem();
printf(s);
}
int main() {
test();
return 0;
}
分析[Waring]:address of localvariable ‘p’returned.
p是个数组,在{}里面定义是个局部变量,说明这个函数执行完毕之后局部变量销毁p,这个数组中的值都没有了。
p虽然是个数组,但是单独用p这个变量,值是p这块数组的首地址,因为返回的是值传递,所以这个首地址被传到了下面的s中,s指向这个内存,而这个内存在getMem函数调用结束后就销毁了,里面存放的不知道是什么了。所以打印的话不一定出现什么。
(2)
#include<iostream>
#include<string.h>
using namespace std;
char *getMem(void) {
char *p="hello world";
return p;
}
void test(void) {
char *s=0x0;
s=getMem();
printf(s);
}
int main() {
test();
return 0;
}
分析:成功 helloworld字符串存放在常量区,这个地方存放的内容并不会在函数结束后销毁。p最后依然指向这里。
6.采用多路复用I/O监听3个套接字的数据时,如果套接字描述符分别是:5,17,19,则
select(int maxfd,struct fd_set* rdset,NULL,NULL)中的maxfd应取为:20、
分析:想要知道是是否可以从标准输入和一些套接字(sockfd)中读取数据,可以把文件描述符和sockfd加入readfds中。numfds的数值设成readfds中文件描述符中最大的那个加上一,也就是sockfd+1(因为标准输入的文件描述符的值为0,所以其他任何的文件描述符都会比标准输入的文件描述符大)。
Maxfd要监视的文件描述符的范围,一般取监视的描述符数的最大值+1。
7.(1)vector就是动态数组.它也是在堆中分配内存,元素连续存放,有保留内存,如果减少大小后,内存也不会释放.如果新值>当前大小时才会再分配内存.
它拥有一段连续的内存空间,并且起始地址不变,因此它能非常好的支持随即存取,即[]操作符,但由于它的内存空间是连续的,所以在中间进行插入和删除会造成内存块的拷贝,另外,当该数组后的内存空间不够时,需要重新申请一块足够大的内存并进行内存的拷贝。这些都大大影响了vector的效率。
(2)Set是一种关联容器,它用于存储数据,并且能从一个数据集合中取出数据。它的每个元素的值必须唯一,而且系统会根据该值来自动将数据排序。每个元素的值不能直接被改变。
【重点】内部结构采用红黑树的平衡二叉树。multiset跟set类似,唯一的区别是允许键值重复!!!
红黑树的操作时间跟二叉查找树的时间复杂度是一样的,执行查找、插入、删除等操作的时间复杂度为O(logn)。红黑树是特殊的AVL树,遵循红定理和黑定理。红定理:不能有两个相连的红节点。黑定理:根节点必须是黑节点,而且所有节点通向NULL的路径上,所经过的黑节点的个数必须相等。
(3)hashmap中查找元素,分为四步:
1).先根据key值计算出hash值以及h值(h值是java实现中处理得到的更优的index索引值);
2).查找table数组中的h位置,得到相应的键值对链表;
3).根据key值,遍历键值对链表,找到相应的键值对 ;
4).从键值对中取出value值。 只有以上四步都能在O(1)时间内完成,hashmap才能拥有O(1)的时间复杂 度。
(4)deque是双端队列,从队尾或队首插入删除元素都是O(1).