兴业数金8月5日笔试总结
笔试基本信息:
时间:120分钟
分数:单选题40题(共40分),多选题20题(共40分),编程题1题(共20分)
知识点:主要包括C++基本语法(sizeof,关键字,do...while,extern,volatile,i++与++i),基本数据类型以及数组内存,MySQL知识(很多题目)等,编程题为动态规划平方和(力扣279)
防作弊措施:共享屏幕,锁手机微信
关键知识总结:
sizeof问题与内存对齐
- 指针类型的sizeof
指针用来存放地址,那么它就等于计算机内部地址总线的宽度。所以在32位计算机中,指针的返回值是4(以字节为单位),在64位系统中指针变量的返回值为8
- 数组的sizeof
数组的sizeof值等于数组所占用的内存字节数。
char a1[] = "abc"; int a2[3]; sizeof( a1 ); // 结果为4,字符串末尾还存在一个NULL终止符 sizeof( a2 ); // 结果为3*4=12(依赖于int)
- 结构体的sizeof
struct S1{char c;int i;};
问sizeof(S1)等于多少聪明的你开始思考了,char占1个字节,int占4个字节,那么加起来就应该是5。是这样吗?你在你机器上试过了吗?也许你是对的,但很可能你是错的!VC6中按默认设置得到的结果为8。
这就引出了内存对齐问题:
why?这样做有助于加快计算机的取数速度,否则就得多花指令周期了。为此,编译器对数据进行处理,让宽度为2的基本数据类型(short等)都位于能被2整除的地址上,让宽度为4的基本数据类型都位于能被4整除的地址上。这样两个数中间就可能需要加入填充字节,所以整个结构体的sizeof值就正常了。
那么交换char与int的位置之后,内存为多少位呢?
struct S1{int i ; char c;};
发现还是8位,成员c后面还是有三个填充字节。因为字节对齐需要满足三个准则:
- 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
- 结构体每个成员相对结构体首地址的偏移量都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节、
- 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节
- 联合体的sizeof
结构体在内存组织上是顺序式的,联合体则是重叠式,各成员共享一段内存,所以整个联合体的sizeof也就是每个成员sizeof的最大值。结构体的成员也可以是复合类型,这里,复合类型成员是被作为整体考虑的。
- 类的sizeof
1.空类的sizeof是1
2.若类中包含成员,则类对象的大小只包括其中非静态成员经过对齐所占的空间,对齐方式和结构体相同
3.若类中包含虚函数,sizeof类都等于sizeof(数据成员)+sizeof(虚表,为4)
4.对于子类,它的sizeof是它父类成员(无论成员是public或private),再加上它自己的成员,对齐后的sizeof
5.对于子类和父类中都有虚函数的情况,子类的sizeof是它父类成员(无论成员是public或private),再加上它自己的成员,对齐后的sizeof,再加4(虚表指针)
6.对于虚继承的子类,其sizeof的值是其父类成员,加上它自己的成员,以及它自己一个指向父类的指针(大小为4),对齐后的sizeof。
7.对于既有虚继承又有虚函数的子类,其sizeof的值是其父类成员(计算虚表指针大小+4),加上它自己的成员(计算虚表指针大小+4),以及它自己一个指向父类的指针(大小为4),对齐后的sizeof。
extern关键字的作用
extern有两个作用:
- 当它与“C”一起连用时,表示编译器编译时按照C的规则去翻译相应的函数名而不是C++的(因为C++有函数重载)
- 当它不与“C”在一起修饰变量或函数时,它的作用就是声明函数或全局变量的作用范围的关键字,其声明的函数或变量可以在本模块或者其他模块中使用
i++与++i
#include<stdio.h> int main() { int i = 2; int j = 2; j += i++;//先赋值后加 printf("i= %d, j= %d\n",i, j); //i= 3, j= 4 i = 2; j = 2; j += ++i;//先加后赋值 printf("i= %d, j= %d",i, j); //i= 3, j= 5 }
- 赋值顺序不同:++ i 是先加后赋值;i ++ 是先赋值后加;++i和i++都是分两步完成的。
- 效率不同:后置++执行速度比前置的慢
- i++不能作为左值,而++i可以
- 二者都不是原子操作
volatile关键字的作用
编译器在存取变量时,为提高存取速度,编译器优化有时会先把变量读取到一个寄存器中;以后再取变量值时就直接从寄存器中取值。但在很多情况下会读取到脏数据,严重影响程序的运行效果。
原理作用:
volatile意思是“易变的”,应该解释为“直接存取原始内存地址”比较合适
“易变”是因为外在因素引起的,像多线程和中断等
volatile关键词影响编译器编译的结果,用volatile声明的变量表示该变量随时可能发生变化,与该变量有关的运算,不要进行编译优化,而是直接从变量内存地址中读取数据,以免出错
一般用处:
1.中断服务程序中修改的供其他程序检测的变量,需要加volatile;
当变量在触发某中断程序中修改,而编译器判断主函数里面没有修改该变量,因此可能只执行一次从内存到某寄存器的读操作,而后每次只会从该寄存器中读取变量副本,使得中断程序的操作被短路。
2.多任务环境下各任务间共享的标志,应该加volatile;
在本次线程内, 当读取一个变量时,编译器优化时有时会先把变量读取到一个寄存器中;以后,再取变量值时,就直接从寄存器中取值;当内存变量或寄存器变量在因别的线程等而改变了值,该寄存器的值不会相应改变,从而造成应用程序读取的值和实际的变量值不一致 。
3 存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;
假设要对一个设备进行初始化,此设备的某一个寄存器为0xff800000。for(i=0;i< 10;i++) *output = i;前面循环半天都是废话,对最后的结果毫无影响,因为最终只是将output这个指针赋值为9,省略了对该硬件IO端口反复读的操作。