30道题/60minutes.
涉及:数据库知识、C++基础、Linux基础、http协议、基础数据结构、进程线程、死锁、代码读结果、编程题
- 删除mysql的数据库中表abc,代码是:
DROP TABLE abc
- 以下基类中的成员函数表示纯虚函数的是virtual void tt() = 0 和 void tt(int) = 0
【纯虚函数】
虚函数 的作用是允许在派生类中重定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数。这就是多态性,对于同一消息,不同对象有不同的响应方式。
纯虚函数 是一种特殊的虚函数,很多情况中,基类中不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给基类的派生类去做,相当于上层基类先声明好接口,下层子类根据特性对接口进行实现。定义语法:virtual 函数类型 函数名(参数列表) = 0; //纯虚函数
比如:
#include <iostream>
using namespace std;
class A//是一个基类,声明好接口
{
public:
virtual void print()=0;//声明纯虚函数
};
class B:public A//BC类根据需要对函数print进行实现
{
public:
virtual void print(){ cout<<"this is B!"<<endl;}
};
class C:public A
{
public:
virtual void print(){ cout<<"this is C!"<<endl;}
};
int main()
{
A *p1 = new B;
p1->print();
p1 = new C;
p1->print();
delete p1;
return 0;
}
- fscanf函数的正确调用形式是:fscanf(文件指针, 格式字符串, 输出表列)//返回输入的数据个数,操作不成功返回EOF
fprintf(文件指针, 格式字符串, 输出表列)//返回实际输出的字符数,操作不成功返回EOF
- linux系统中调度的基本单位是:线程
【进程与线程的区别】
一个进程中可以有多个线程,它们共享进程资源。
(1)拥有资源:进程是资源分配的基本单位;线程是独立调度的基本单位。所以线程不拥有资源,线程是可以访问隶属进程的资源。
(2)调度:线程的切换不会引起进程的切换
(3)系统开销:创建或撤销或切换进程时,开销都要大于线程。
(4)通信方面:线程之间可以通过直接读写统一进程中的数据进行通信;而进程通信需要借助IPC(进程之间的通信)。
操作系统的设计,可以归结为三点:
(1)多进程形式,允许多个任务同时运行;
(2)多线程形式,允许单个任务分成不同的部分运行;
(3)提供协调机制,一方面防止进程之间和线程之间产生冲,另一方面允许进程和线程之间共享资源。
- 对于C语言中变量的命名:变量命名只能是字母、数字或下划线,其中开头必须是字母或者下划线,不能以关键字作为变量的命名
- http协议属于哪一层协议:应用层
【http协议复习】
HTTP(超文本传输协议)是客户端浏览器或其他程序与web服务器之间的应用层通信协议,因此工作在最高层,即应用层。HTTP是一个基于TCP/IP的关于数据如何在万维网中通信的协议。
- stack和queue的共同点是:只允许在端点处插入和删除元素
【栈和队列性质复习】
线性表: n个数据元素组成的有限序列。栈和队列都是线性表。
栈: 栈是线性表中元素的插入和删除只能在线性表的同一端进行的一种特殊线性表。栈是一种先进后出,后进先出的表。应用:在函数执行的时候;计算表达式的时候都会用到栈的知识。
队列: 队列也是只允许在一端进行插入,另一端进行删除的线性表。队列是一种先进先出的特殊线性表。应用:CPU资源竞争问题,每次把CPU分配给队头用户,当相应程序运行结束时,令其出队,然后再将CPU分配给新的队头用户;主机与外部设备之间速度不匹配问题,用队列缓存要打印的数据,保证了打印数据的正确并提高了主机效率。
- 考虑函数
void test(int a, int b=7, char ch="*")
,下面的函数调用不合法的是:C
A test(5)
B test(5, 8)
C test(6, “#”)
D test(0,0,"*")
【考点:函数缺省参数】函数缺省参数,只能从右往左缺省,所以C选项中的话字符#时不能被赋值到int型数据变量中的。
传递参数时时从左往右传参。
9.略
char a; float b; double c;
执行c = a+b+c之后c变量的类型为:double
【解析】运算时“类型自动转换”转换的是“值”,而不是“变量本身的类型”。因此运行结束之后a、b、c的类型是不会变的。本题中 a+b+c执行完之后的结果是double,但c的类型本身还是不会变的还是double。
- 关于堆/栈空间的申请和释放描述,正确的是:堆的空间是由开发人员申请和释放的;栈的空间是由系统申请和释放的。
【堆和栈的区别】
(1)内存地址方面:栈内存的地址是连续的;堆内存地址不连续。
(2)空间申请和释放方面:栈是系统自动分配和释放空间;堆是由开发人员申请和释放空间。
(3)数据结构方面:堆是一种后进先出的数据结构;栈是一种经过排序的树形数据结构,有最大堆,最小堆。
- 网络协议规定接收到得第一个字节是高字节,存放到低地址,所以发送时会首先去低地址取数据的高字节。
不同平台进行通信时必须进行转换。
如果当前平台是大端,则直接返回不进行转换,如果当前平台是小端,会将接收到得网络字节序进行转换。
- 以下命令得在(每周三03:06分)自动执行:
06 03 ** 3 "echo test >> /tmp/log.txt"
- 写文件数据比较大的情况下fwrite(), fflush(), fsync(),哪个函数耗时可能会比较长(fwrite)
【这些命令都是啥】
fwrite函数只是将所写内容存入用户缓存,并不立刻写入文件.;
fflush函数将用户缓存中的内容写入内核缓冲区;
fsync函数则是将内核缓冲写入文件;
- 若进栈顺序是1,2,3,4,5,请指出不可能出现的出栈顺序()
【分析】选择12534.对于这个顺序的话1进栈1出栈,2进栈2出栈,然后要得到5只能是345都入栈,然后5出栈,这个时候顺序只能是543
- 下列关于线程状态正确的是(新建、就绪(可运行)、阻塞、终止)
【线程有6个状态】New, Runnable, Blocked, Waiting, Timed_Waiting, Terminated.‘
分别是:新建、就绪(可运行)、阻塞、等待、限时等待、终止
- 产生死锁的必要条件有:4个,互斥、请求与保持、不可剥夺、循环等待。
【死锁产生的原因】(1)竞争资源;(2)进程间的推进顺序非法。
- 只能在派生类构造函数初始化列表中去初始化类型的几种情况:
(1)const成员变量
(2)引用成员变量
(3)没有默认构造函数的类成员
- 关于多进程,多线程,多任务的区别与关系描述正确的有()
【线程池】线程池是一种线程的使用模式,就是首先创建一些线程,它们的集合称为线程池。 线程池(thread pool):一种线程的使用模式,线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。
(1)使用线程池可以很好地提高性能,有效降低频繁的创建和销毁线程对性能所带来的开销。因为线程过多会带来调度开销,进而影响局部性和整体性能,而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。
(2)线程池能有效地处理多个线程的并发问题,避免大量的线程因为互相强占系统资源导致阻塞现象。
使用线程池的原因:多线程运行时,系统不断的启动和关闭新线程,成本非常高,会过度消耗系统资源,以及过度切换线程的危险,从而导致系统资源的崩溃。这时,线程池就是最好的选择了。线程池不仅能够保证内核的充分调用,还能防止过分调度。
- 给定声明:
int a[9]; int const* const p = a;
以下操作错误的是:
【分析】这道题中const既修饰了指针a又修饰了常量a,因此常量和指针地址都是不可变的。
【有关const定义】
(1)const int a;
与int const a;
这两个作用是一样的,a都被定义成一个常整型数,一旦被定义之后,就不能再在其他地方重新赋值。
(2)const int *a;
这个const修饰的是整型数int,而不是指针。因此整型数a是不能被修改的;但指针(本身的地址)是可以修改的,可以重新指向另外一个内存地址。
(3)int *const a;
这个const修饰的是指针a,而不是整数a。因此指针指向的整型数*a是可以改变的;但指针(本身的地址)不可以被改变。
- 程序的局部变量存储在(栈)中;全局变量存储在(全局存储区即静态存储区);动态申请的数据存储在(堆)中。
【C程序的存储类别】
(1)栈:就是那些由编译器在需要的时候分配,在不需要的时候自动清除的变量的存储区。里边的变量通常是局部变量、函数参数等。
(2)堆:就是那些由new/malloc分配的内存块,他们的释放由开发人员操作。
(3)全局存储区(静态存储区):存全局变量和静态变量。程序结束后由系统释放。
- 程序中使用如下语句申请了一个对象数组:
place *p = new place[3];
当要释放p指向的动态数组对象时,使用的语句是(delete[] p;)
- 基类的析构函数不是虚函数,会带来什么问题?
答:基类的析构函数不是虚函数的话,删除指针时,只有基类的内存被释放,派生类的没有,导致内存泄露。 所以基类的析构一定要加virtual。(另一个解释:父类指针可以指向子类对象,如果父类析构函数不是虚函数,那么delete该指针时只调用父类的析构函数,可能导致内存泄漏)
【复习】
基类(父类):就是已经存在的类
派生类(子类):就是根据基类去新建的类。子类从父类那里获得一些特性称为继承。
析构函数:析构函数就是类名前加~符号来定义的。在对象释放前自动执行析构函数。先构造的后虚构,后构造的先虚构。
虚函数:virtual。虚函数作用是允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数。实现了多态性:就是同一类族中的不同类的对象,对同一函数调用做出不同的响应。
- 某CPU执行一条指令需要取指,分析,执行3步,分别耗时5,2,3单位时间,则以流水线方式执行100条指令的时间为:5+2+3+(100-1)*5=505
【CPU指令执行计算公式】
(1)按照顺序执行方式:执行指令完成时间 = (取指时间+分析时间+执行时间)*指令数
(2)按照流水线执行方式:(取指时间+分析时间+执行时间)+(指令数-1)*(最大时间)
- 自己实现一个strcpy函数,实现内存拷贝功能。
错误的代码:
#include<assert.h>
char* MyStrcpy(char* dst, const char*src)
{
assert(dst != NULL);
assert(src != NULL);
char *ret = dst;
while((*dst++ = *src++) != ‘\0’)//说明:要求两字符串内存区域 不可以 有重叠
;
return ret;
}
原因:因为这种复制函数有一个“坑”,这在函数说明中也提到了,那就是如果字符串内存有重叠复制就可能会失败(前往后复制会失败),而且可能不会出现任何异常!
【C语言中的assert】:assert宏原型定义在<assert.h>中,其作用是如果它的条件返回错误,则中止程序执行。
改进版:
char *MySTtrcpy(char *strDst, const char *strSrc)
{
//功能:实现字符串的拷贝
//说明:两字符串内存区域 可以 有重叠
// 负责添加'\0'
// 发生错误返回NULL
// 指针判空
if(strDst==NULL || strSrc==NULL){
return NULL;
}
//复制到临时字符串
int len = strlen(strSrc)+1;
char *strTmp = new char[len];
if(strTmp == NULL){
return NULL;
}
char *pTmp = strTmp;//保存首地址
while((*strTmp++ = *strSrc++) != '\0');
//复制到目标字符串
strTmp = pTmp;//恢复起点
while((*strDst++ = *strTmp++) != '\0');
delete[] pTmp;
return strDst;
}
- 设计程序判断自己的IP地址是否合法
来源
- 已知两个链表head1和head2各自有序(升序),请把他们合并成一个链表依然有序(保留所有节点,即便大小相同)
答:递归实现
//定义结构体
struct ListNode
{
int value;
ListNode* next;
}
//合并链表
ListNode* merge(ListNode* head1, ListNode* head2)
{
if(head1 == nullptr)
return head2;
else if(head2 == nullptr)
return head1;
ListNode* res = nullptr;
if(head1->value <head2->value)
{
res = head1;
res->next = merge(head1->next, head2);
}
else
{
res = head2;
res->next = merge(head2->next, head1);
}
return res;
}