程序员面试宝典(第二版)要点汇总

第五章

P34,作用域符::,代表的是程序中的全局变量或者标准程序库的函数

P35,判断条件与(&、i++),其中当判断条件中前面的条件为0后面的条件就不用计算了,或也是如此

P36,编程风格:常量在判断等式前面

P36,优先级:》的优先级在~之前

P38,float和double的存储方式:其中float是这样的,1位符号位,8位阶码位,23位尾数位,先将数据化为二进制,再将其画成科学数位的样式,存储阶码=其画成阶码所要移动的位数+127,23位尾数=原本科学计数法的数-1,如1.0f=3F800000,0=0x00000000

int 智能取值,int & 取地址相应的值

大端模式:高位存储低位(这样其实很方便查看),51单片机等等。小端模式:低位存储低位,高位存储高位。arm、dsp、x86都是这钟

P38,强行转换时,太多会直接取低位

P38-39,

unsigned int a=0xFFFFFFF7;

unsigned char i=(unsigned char )a

char *b=(char *)&a;

结果是,i 的值为0x000000f7,0xfffffff7.

P41,x&(x-1)的妙用,2的N次方对x&(x-1)计算结果为0,同时while(m){count ++,m=m&(m-1)}可以计算m中1的个数

P42,ab交换的方法 .方法一:a=a+b;b=a-b;a=a-b;方法二:a=a^b;b=a^b;a=a^b;

不判断找出最大的:max=((a+b)+abs(a-b))/2

    int c=a-b,然后讲c右移31位,再看是1或者0即可

 

第六章

P49,内存对齐,这个知识点有很多内容,只发散以下两点

结构体的大小,遇到长的必须以4或者8为单位对齐,如

{ char a;double b,int a}的大小为24,

{ char a[9];double b,int a}的大小为32,

{char a[9];int c;double b;}的大小为24

类的大小

空类为1

父类是空类的,对子类无影响

如果类中有定义变量,仿照结构体大小加上即可

静态变量是存放在全军区域的不计算在内

继承:子本身+父类大小就是=子类大小

虚继承:也要执行内存对齐的方式,继承所加的虚继承指针要加上4,如果父类或者子类中有double子类的大小就加8,并且出现后者子类中有char或者int的就对齐的方式

class I{double a;}//大小为8

class J:public virtual I{char c[4];};//大小为16,但是如果charc[4]改成char c[5]就变成了24了

虚函数:虚函数和虚继承一样,一般加4,本身有double+8.(虚函数的个数不管,多少都是照此办理,并且虚函数的位置也不管,都一样)(指针),继承还得算上父类的大小,都得算,有两个就都加,而且采用对齐

其实虚函数和虚继承都是多了个指针而已,但都讲求内存对齐,其中虚函数是放在那里都一样,但是虚继承放在子类和父类就不一样了,放在父类还能补缺

虚函数继承,上述综合体(实继承了有虚函数的类,可以当成没有

 

P49,问题2,关于预处理的程序

#include <iostream>

using namespace std;

#define  FIND(struc,e)(size_t)&(((struc *)0)->e)

struct student

{

 int a;

 char b[20];

 double ccc;

};

void main()

{

        intm=FIND(student,ccc);

        cout<<m;

}

 

           先分析下上述程序,其中奇怪的地方有两三处,从main()入手:第一,find中的元素两个都不是变量,其实这就是预处理的作用,替换,而不会管你是否有语法错误

    第二,defin中的(struc )* 0表示什么意思,其实这就是将常数0转化成struc的所指向的地址且为0,&(((struc *)0)->e)表示取结构体中e的地址,首地址为0,取得e地址就是其偏移地址,

    第三,size_t是一种数据类型,和机器的位数长一样,在32位系统中等于unsigned int 

 

P50

#defineyear_abc (unsigned long)(60*60*24*365)

收到的要点有两个:第一个书也有错误,根据自己心里来,第二,宏的用处不少

 

const与#define的异同:

都可以定义不可改变的数,但是const需要经过编译需要经过安全检查,有数据类型,而且占用内存空间,并且可以调试,但是#define这些都没有,所以他更简单

P52

sizeof:1指针和数组(本质是指针)不一样的地方就在于sizeof

      2\n在字符串中算一位

    3指针的大小是定值,不受指针类型的影响

#pragmapack(1)与#pragma pack()前后包夹可以防止内存对齐,

 

const

1)指针本身是常量不可变

     (char*) const pContent; 

     const (char*) pContent; 

     const (char*) pContent; 

(2)指针所指向的内容是常量不可变

     const (char) *pContent; 

     (char) const *pContent; 

     (char) const *pContent; 

a. const成员函数不被允许修改它所在对象的任何一个数据成员。

b. const成员函数能够访问对象的const成员,而其他成员函数不可以。

但是mutable 修饰除外(mutable int a;)

常函数的意义:标记函数为只读函数防修改,增强可读性

 

P57         float 大小为4(拜托我今天才知道)

         sizeof求的是内存大小(指针有指针的大小,数组有数组的大小(length*单个大小))

         strlen求的是字符串的大小(必须以\0结尾),char 的数组还能计算出来,int的一定会报错,

strlent(*ss)指的是第一个字符的大小

 

P58

sizeof(string)的大小为4

 

P62 其中例题8的答案很奇怪,我认为是AB

P63 内联函数(inline或者是类的函数(把代码直接写到函数出现的地方,不需要经过压弹操作,能够提高效率,和宏的单独代替不同,编译是否进过检测的问题(2),)

 

第七章 指针和引用

P65

引用不可为空,但是指针可以,所以指针要时刻检测其是否为空(合法性测试)

指针是变量,课重新复制,但是引用指向的地址无法改变,但内容可变

引用及长指针使用的特别注意事项

Int&rev=iv   (如果不在初始化时刻赋值,则错误,这句话表示rev为iv的别名

常指针初始化时候必须赋值,比如const double maxWage=10.0;

给某个地址赋值为0(

long*p;

p=(long*)0x123456789;

*p=0)

或者

*((long *)0x123456789)=0

上述这种做法相当危险,不管是这个地址的内存是否被使用,都可能会导致运行问题,程序会崩溃

P68

用指针作为参数申请内存必须用申请的内存地址作为函数返回值

注意如果是用数组应付上述情况就错了,因为数组内存会随时收回堆栈里面的数值,用静态变量或者是指针都可以

子类与父类的变量同名时候,各自函数都只调用各自的变量

注意,在声明函数指针(在主函数内部)为int max(int,int)

              Int (*p)(int,int)=&max

P76 指针数组和数组指针

指针数组就是数组元素为指针的数组(int *p[5],五个指针)

int *p[3];

int a[3][4];

for(i=0;i<3;i++)

p[i]=a[i];

数组指针就是指向数组的指针,也称行指针(int (*p)[5])

         int(*p)[4];

         inta[3][4]={{1,2,4,3},{1,2,4,3},{1,2,4,3}};

         p=&a[0];

访问的第一组的第一个元素应该用(*p)[0]

注意,优先级:()>[]>*

这里面涉及一个二维数组概念,要厘清

函数指针示例int (*a)(int)

函数指针数组示例 int (*a[10])(int)

注意数组名和其他指针不一样的地方 (数组名本来就是指针,加上&就是双指针,这里的指针指的就是数组,所以加1就是数组整体加一行)

         int  a[]={1,2,3,4,5}

         int*ptr =(int *)(&a+1)

         printf(“%d,%d”,*(a+1))

答案是2和5

指针的指针都是加一行,例如

chara[]={“hello”,”the”,”world”};

char**pa=a;

pa++;

cout<<*pa<<endl;

运行结果是the

P81

空指针,即是没有分配内存空间的的指针,其为NULL,地址是0x00000000,迷途指针,也就是野指针,即是分配了内存空间,但是内存空间的被删除,但是指针没指向0的指针。

使用空指针,最多是程序崩溃,使用野指针,崩溃不可预料

P83

由智能指针的问题,想起来类本身的问题,构造类和析构类就是需要很多的时间,不如数组轻便,这个教训应该心理有数

New和delete这是一对,如果有异常导致delete没有执行,那么系统会自动收回

第八章 循环递归与概率

P86

 第一次知道字符串匹配除了朴素匹配和KMP或者改进的KMP以外还有递归方法,而且这种可以将所有匹配住的情况全部写下来

 

适合递归的非递归版本:遍历树是while一直到叶子节点

P90

         数组查找匹配中比较难的就是降低比较次数:各自排序后再比较是最好的

这里必须要复习下数据结构了

冒泡排序

直接插入排序,第一个为标准,然后大的插后面,小的插前面(记了一次没记住,把代码贴在下面)

voidInsertSort(SqList *L)

{

  int i,j;

  for(i=2;i<=L->length;i++)

  {

   if (L->r[i]<L->r[i-1])   /* 需将L->r[i]插入有序子表 */

   {

    L->r[0]=L->r[i];    /* 设置哨兵这里的作用实际是当L->r[i]被覆盖了之后将会讲L->r[i]能够插入到正确的位置(最后一个需要挪动的)*/

    for(j=i-1;L->r[j]>L->r[0];j--)

     L->r[j+1]=L->r[j];  /* 记录后移 */

    L->r[j+1]=L->r[0];   /* 插入到正确位置 */

   }

  }

}

选择排序,找最小的放第一个,再找次小的放第二个

Ps:快速排序、希尔排序、堆排序、选择排序不是稳定的排序算法,而冒泡排序、插入排序、归并排序和基数排序是稳定的排序算法

选择排序,按照找最小值,与无序区首部交换的思想则不稳定:
排序前:                     排序后:

 2,4,4*3                    2,3,4*4

希尔排序,以步长划分组进行直接插入排序,变化步长直到步长为1

 increment=increment/3+1;            /* 增量序列 */

堆排序,根据序号冒泡呗

归并排序

快速排序,第一个作为基准,然后开始比较,各自找大小,跟key替换,来来回回直到i=j,返回这时候的key的位置值,然后以这个变量为中心分成两组,分别进行快排,直到low=high

/*对顺序表L中的子序列L->r[low..high]作快速排序 */

 

voidQSort(SqList *L,int low,int high)

{

intpivot;

 if(low<high)

 {

  pivot=Partition(L,low,high);  /* 将L->r[low..high]一分为二,算出枢轴值pivot */

  QSort(L,low,pivot-1);   /*  对低子表递归排序 */

  QSort(L,pivot+1,high);   /*  对高子表递归排序 */

 }

}

/*交换顺序表L中子表的记录,使枢轴记录到位,并返回其所在位置 */

 

/*此时在它之前(后)的记录均不大(小)于它。 */

intPartition(SqList *L,int low,int high)

{

 int pivotkey;

 pivotkey=L->r[low];  /* 用子表的第一个记录作枢轴记录 */

 while(low<high)      /* 从表的两端交替地向中间扫描 */

 {

  while(low<high&&L->r[high]>=pivotkey)

   high--;

   swap(L,low,high); /* 将比枢轴记录小的记录交换到低端 */

  while(low<high&&L->r[low]<=pivotkey)

   low++;

   swap(L,low,high); /* 将比枢轴记录大的记录交换到高端 */

 }

 return low;    /* 返回枢轴所在位置 */

}

除了上诉的其中算法,在百度的时候还发现以下两种算法

基数排序法:先用个数排序,再用十位数排序(这就是高位优先策略)

P92螺旋队列:

这个问题在循环和数组比较典型,之字形和回字形都输属于这个问题

解决办法:先用坐标求出在第几层,每层的第一个数可以根据规律和层数计算出来,然后看清楚增长方向,分为几种类型,然后加上尾数即可

P95 概率问题

         概率问题出现是由于rand引起,两种计算,一是由面积之比算概率,一直是由rand扩大缩小求新的rand

第九章 stl

最难的是API太多

其中向量算法等等的都不说了,泛型编程自己也编程过,最新接触的就是模板,用函数指针把几个输入输出的函数统一到一个函数里面调用,test(int(*p)(int,int),inta,int b),调用时就可以把符合函数指针的函数名填入进去即可

第十章 面对对象

         Struct和class是一样的,不过是为了兼容c才有这个关键字的,但是其变量默认是public,struct可以用{}的方式赋值(Aa={'p',7,3.1415926}),但是如果struct中加入了函数,那么久不能用{}赋值了,第三个不同点是“class”这个关键字还用于定义模板参数,就像“typename”。但关键字“struct”不用于定义模板参数。很

         很牛叉的是struct居然也可以继承和多态,并且继承是默认是公有还是私有,完全取决于其是继承的是struct还是class

         类的静态成员变量作用是统计对象的个数,需要初始化,并且需要再类外初始化,比如类中定义是static int howmanycat  那么在类外就是 int cat:: howmanycat=0;

         类的初始化列表变量顺序是根据变量的声明顺序来执行的

Static常量允许在初始化声明处赋初值(记得常量不允许在初始化声明处赋初值,静态变量也不允许)

析构函数是virtual修饰的话,作为基类将不会被调用,当析构函数为virtual类型时候,派生类的

在这一个章节出现了一些困扰已久的问题

1 virtual的作用:

                class Base

{

public:Base(){}

public:

    //virtual

    void print(){cout<<"Base";}

};

classDerived:public Base

{

public:Derived(){}

public:

    voidprint(){cout<<"Derived";}

};

intmain()

{

    Base*point=new Derived();

    point->print();

    return 0;

}

其中如果base的virtual不加,结果就是base,如果加了就实现了覆盖为Derived。然而覆盖与隐藏虽然从代码上我能识别出来,覆盖必须父类与子类函数名与参数完全相同且父类函数有virtual关键字修饰,但一直让我从功能上无法区分

覆盖就是看不见,隐藏就是通过类名::函数名可以访问到。如果基类被重写的函数是虚函数的话就是覆盖,否则就是隐藏。

加了virtual遇上程序中的情况,子函数实现了覆盖就能够收回在先分配内存,不会造成内存泄露,所以在使用类的时候,最好加virtual

                只要有函数是虚函数,一般代表其析构函数默认是virtual析构函数

这里分清楚隐藏和覆盖(),只是申请自己初始化自己的时候各用各自的

析构函数可以是内联函数

单个参数的构造函数,如果不添加 explicit关键字会定义一个隐含类型转换(从参数转换成自己),添加了exlicit会消除这种隐含转换

是否需要拷贝构造函数(凡是需要统一分配系统资源的用户定义类型都需要一个拷贝构造函数),拷贝构造函数的格式为:类名(const 类名& 对象名);//拷贝构造函数的原型,参数是常量对象的引用。由于拷贝构造函数的目的是成员复制,不应修改原对象,所以建议使用const关键字。

String构造函数分配内存空间时应该多分配一位,这样可以加入\0

空字符指针判断必须要根深蒂固

重载是同一个类,不同的函数参数,多态是不同类,同样的参数

友元函数必须要在类里面申明,在外面实现函数体 friend +一般函数声明

第十一章 继承和接口

如果有爷父子三代继承,调用函数由上往下调用,虚函数是被覆盖,只有本类调用时候是调用本类的那个虚函数内部程序,其他情况都是由子决定,子没有看父

如果调用函数中出现两个父类的函数同名,调用方法c.A::foo()

当类的函数声明了virtual并且没有函数体(直接函数声明=0),这样整个类是不能实例化的(抽象类)

虚指针:虚函数的的实现细节,每个带有虚函数的类都有一个指向该类虚函数表的的指针

构造函数设置为私有的时候,类的对象不能实例化,这时可以通过调用内部的静态函数来初始化,这样就控制了入口

Com口的知识好像不重要

 

第十二章

printf("%f",5);由int转f或者d就越界了(注意如果用内心强制类型转换就不会有这个问题)

printf(“%d”,5.1)大数

定义结构体位制:不能超过32,不然就越界了(位段成员的类型仅能够为unsigned或者int

),如

Struct{int x:1;

int  y:2;

int z:33}

P148

四种基本的转换:数制转换、动态转换,const转换,reinterpret转换

P149

联合体是共享内存,相互覆盖

P150

注意指针题目 int *pa=BULL;int pb=pa+15,求pb的值,答案是60,因为pa是int*类型的指针所以加上15也强制转换成这个类型的,所以就是15*4=60

嵌入式中,中断里面不允许传参和返回值,并且少用printf和浮点运算

P151

Volatile是用来避免被编译器优化的标志

Const能够让代码写出更少的垃圾,让代码更紧凑,代表只读

中断指针是volatile的

P154

合法指针指向的内容也可能为NULL,验证方法为strlen(指针)==0或者是sizeof(指针)==4来验证

其中需要说明的是malloc(0)是有分配空间的(编译器对malloc(N)都有预留),不过如果使用free就会报错,而且malloc(0)得出的指针是合法指针

P156

         用共用体能够检测机器到底是大端模式还是小端模式

         静态变量只能被初始化一次

         比如

fun(inta)

{  int b=0;

static int c=3;

   b++;

   c++;

   return(a+b+c);}

main()

{int i,a=5;

  for(i=0;i<3;i++)

  printf("   %d   %d ",i,fun(a));

   printf("\n"); }

中的static int c=3;只会在开始的时候执行一次

第十三章 数据结构基础

单链表,创建插入 删除(注意是否是头结点) 改动,排序(冒泡)

            其中以创建为最难,其次就是改动(特别是逆置),创建中先声明头结点head,然后每个点s先申请空间,给s内存空间赋值,然后将传递值q的下一个指针指向申请内存s,然后让p=s

其中遵循的原则是,先接后断(先让插入的元素和已有的元素扯上关系,之后再拆分原有元素)。

双链表的逆置:(每次只改一个指向,然后让其不断的往后走,最后改head)

P1=head,p2=p1->next

While(p2)

{

P3=p2->next;

P2->next=p1;

P1=p2;

P2=p3;

}

Head->next=null;

Head=p1;

Returnhead;

 单链表求中间结点:用两个指针,p,q,p=p->next->next,q=q->next,当p达到终点时,q就是中间结点啦,不过这个得先检测q的next结点是否是null      

P167

双链表

创建(跟单链表同,先申请地方,给数据,然后再结两个关系,最后再挪动地盘)

删除(先判断是否是头结点再判断是否是尾结点,头结点先把头的位置让出来,并让其pre指空,再free(p1),尾结点的话先把次尾结点指空,然后对尾结点free)

插入(按照大小插入)

除去插在头上的特殊情况,其他都一样

循环链表

            建立(先写头结点,之后申请空间,前驱结点与当前结点相互指向)

            删除(改前驱指向,然后free,最后过渡到下一个删除)

队列(入队和出队,除了单链表的数据结构外,加头尾两个指针的数据结构,单链表的指针由头指向尾)

            入队,申请空间,判断是否为空,改原来队尾的指针,然后改队尾HQ->rear=s

出队,判断是否为空,取数,判断是否为1,改指针,释放空间(本来每次释放空间之后都要令指针为0的)

栈(入栈和出栈,除了单链表结构,还有栈顶和栈底两个指针,和队列不同的地方是出去位置都是同一个,并且单链表的指向是由顶指向底,我的想法和书上不一样,书上太死板了,删除时候费功夫)(记住,系统函数的嵌套是用堆栈完成,但是函数递归层数过多,x86的堆栈大小为1MB栈会造成堆栈溢出,相当于平均每个函数用100byte,只能用来保存10000个函数信息)

入栈,申请空间,判断是否为空(为空就两个指针都指向s),改原来队尾的指针,然后改栈顶HQ->rear=s

出栈,判断是否为空(判断栈顶或者栈顶为NULL),取数,判断是否为1(判断栈顶和栈底是否相等),改指针,释放空间(本来每次释放空间之后都要令指针为0的)

联想知识:查过多遍,都不没记住,x86是大端模式,查看数据很顺眼的这种,高位存低位的数据,低位存高位的数据

堆栈的基本常识:调用函数时首先进行参数压栈,一般压栈的顺序为由由右到左(从近到远,反正跟阅读顺序的正反有关,这个关键点在编译器,c调用的话就是如此,s调用就不是了),除了压栈顺序之外,还要考虑生长方向,事实上,window中,堆栈方向都是从上往下生长的(即栈顶地址总是小于等于栈的基地址,记住那个图)

网上摘抄了一段堆和栈的

堆和栈的区别

一、预备知识—程序的内存分配

一个由c/C++编译的程序占用的内存分为以下几个部分

1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。(1MB)

2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。(虚拟内存大小)

3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放 

4、文字常量区—常量字符串就是放在这里的。 程序结束后由系统释放

5、程序代码区—存放函数体的二进制代码。

二、例子程序 

这是一个前辈写的,非常详细 

//main.cpp 

int a = 0; 全局初始化区 

char *p1; 全局未初始化区 

main() 

int b; 栈 

char s[] = "abc"; 栈 (s变量在栈上,指向应该在数据区

char *p2; 栈 

char *p3 = "123456"; 123456\0在常量区,p3在栈上。 

static int c =0; 全局(静态)初始化区 

p1 = (char *)malloc(10); 

p2 = (char *)malloc(20); 

分配得来得10和20字节的区域就在堆区。 

strcpy(p1, "123456"); 123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。 

堆(由上面程序总结出来,malloc申请出来的就是在堆中)

            里面扯出来东西很多,设计函数调用方式对堆栈的压入顺序的影响,已经放到前面

                   突然忘记了各种遍历的具体是怎么回事:前中后都是根据根节点的位置 来定的(相互推算的时候必须不能少中序遍历)

            创建,不难,只不过根据大小看是创建左子树还是友子树

            插入,判断是否其要插入的左子树右子树是否为空

            删除,删除叶子节点不难,难的是不是叶子节点,必须要遍历删除其左子树和右子树才行

查找,二分查找看来是最简单的(排序后)

这个其实和字符串很有关系

排序(在上面已经讲得很详细)

几个典型的数据结构问题

大数据

限制时间复杂度和空间复杂度情况

留着后面一起解决?

求素数问题(用筛选法)

第十四章 字符串

整数转化成字符串问题:(1)itoa,(number,string,10),其中string是申请了空间的char*或者是数组名,(2)可以通过+’0’,然后再逆序就可以(我的想法是先复制一个数统计位数,然后直接转)

字符串转化为整数:(1) int atoi(constchar *nptr)(2)取出每个字母减去‘0’之后,然后再乘以10,直到末尾

Strcpy不用库函数

         char * strcpy(char * strDest,constchar * strSrc)
        {                
             if ((strDest==NULL)||(strSrc==NULL))//[1]  
             throw "Invalid argument(s)";//[2]
             char *strDestCopy=strDest;   //[3]
             while ((*strDest++=*strSrc++)!='\0');//[4]

         *strDest=\0;(字符数组最后一位必须加上‘\0’)

             returnstrDestCopy;      
         }

malloc注意:当内存不足时,函数未能成功分配存储空间,返回为NULL,这也就是各个函数要检查地址是否为NULL的原因

free省了

还剩下几个函数

memset,        void *memset(void *s, char ch, size_t n)

memcpy        void *memcpy(void *dest, const void *src, size_t n)

发现一个问题,在main()函数中,数组申明在前面的数组地址反而在后面,地址是倒过去用的

 

数组初始化与数组越界,过大的字符串写到没申请空间地址上,会出现数组越界的问题

Int n=7,a[n];是不可以的,因为数位里面必须有常量

字符串问题

字符串匹配

朴素匹配

改进KMP

 

 

 

 

 

出现次数最多最长子串统计:

所有字符串放到向量里面去,从长到短,然后取出来根据长度一个个比较compare函数,后面比前面次数多才

正序查找 num1=find(str)

逆序查找num2=find(str)

 

第十五章设计模式

 

第十六章 操作系统

操作系统常见面试题总结

1、什么是进程(Process)和线程(Thread)?有何区别?

进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。

  进程与应用程序的区别在于应用程序作为一个静态文件存储在计算机系统的硬盘等存储空间中,而进程则是处于动态条件下由操作系统维护的系统资源管理实体。

2Windows下的内存是如何管理的?

Windows提供了3种方法来进行内存管理:虚拟内存,最适合用来管理大型对象或者结构数组;内存映射文件,最适合用来管理大型数据流(通常来自文件)以及在单个计算机上运行多个进程之间共享数据;内存堆栈,最适合用来管理大量的小对象。

Windows操纵内存可以分两个层面:物理内存和虚拟内存。

  其中物理内存由系统管理,不允许应用程序直接访问,应用程序可见的只有一个2G地址空间,而内存分配是通过堆进行的。对于每个进程都有自己的默认堆,当一个堆创建后,就通过虚拟内存操作保留了相应大小的地址块(不占有实际的内存,系统消耗很小)。当在堆上分配一块内存时,系统在堆的地址表里找到一个空闲块(如果找不到,且堆创建属性是可扩充的,则扩充堆大小),为这个空闲块所包含的所有内存页提交物理对象(在物理内存上或硬盘的交换文件上),这时就可以访问这部分地址。提交时,系统将对所有进程的内存统一调配,如果物理内存不够,系统试图把一部分进程暂时不访问的页放入交换文件,以腾出部分物理内存。释放内存时,只在堆中将所在的页解除提交(相应的物理对象被解除),继续保留地址空间。

  如果要知道某个地址是否被占用/可不可以访问,只要查询此地址的虚拟内存状态即可。如果是提交,则可以访问。如果仅仅保留,或没保留,则产生一个软件异常。此外,有些内存页可以设置各种属性。如果是只读,向内存写也会产生软件异常。

3Windows消息调度机制是?

A)指令队列;B)指令堆栈;C)消息队列D)消息堆栈

  答案:C

  处理消息队列的顺序。首先Windows绝对不是按队列先进先出的次序来处理的,而是有一定优先级的。优先级通过消息队列的状态标志来实现的。首先,最高优先级的是别的线程发过来的消息(通过sendmessage);其次,处理登记消息队列消息;再次处理QS_QUIT标志,处理虚拟输入队列,处理wm_paint;最后是wm_timer

4、描述实时系统的基本特性

  在特定时间内完成特定的任务,实时性与可靠性。

  所谓实时操作系统,实际上是指操作系统工作时,其各种资源可以根据需要随时进行动态分配。由于各种资源可以进行动态分配,因此,其处理事务的能力较强、速度较快。

5、中断和轮询的特点

  对I/O设备的程序轮询的方式,是早期的计算机系统对I/O设备的一种管理方式。它定时对各种设备轮流询问一遍有无处理要求。轮流询问之后,有要求的,则加以处理。在处理I/O设备的要求之后,处理机返回继续工作。尽管轮询需要时间,但轮询要比I/O设备的速度要快得多,所以一般不会发生不能及时处理的问题。当然,再快的处理机,能处理的输入输出设备的数量也是有一定限度的。而且,程序轮询毕竟占据了CPU相当一部分处理时间,因此,程序轮询是一种效率较低的方式,在现代计算机系统中已很少应用。

  程序中断通常简称中断,是指CPU在正常运行程序的过程中,由于预先安排或发生了各种随机的内部或外部事件,使CPU中断正在运行的程序,而转到为响应的服务程序去处理。

  轮询——效率低,等待时间很长,CPU利用率不高。

  中断——容易遗漏一些问题,CPU利用率高。

6、什么是临界区?如何解决冲突?

  每个进程中访问临界资源的那段程序称为临界区,每次只准许一个进程进入临界区,进入后不允许其他进程进入。

  (1)如果有若干进程要求进入空闲的临界区,一次仅允许一个进程进入;

  (2)任何时候,处于临界区内的进程不可多于一个。如已有进程进入自己的临界区,则其它所有试图进入临界区的进程必须等待;

  (3)进入临界区的进程要在有限时间内退出,以便其它进程能及时进入自己的临界区;

  (4)如果进程不能进入自己的临界区,则应让出CPU,避免进程出现忙等现象。

7、说说分段和分页

  页是信息的物理单位,分页是为实现离散分配方式,以消减内存的外零头,提高内存的利用率;或者说,分页仅仅是由于系统管理的需要,而不是用户的需要

段是信息的逻辑单位,它含有一组其意义相对完整的信息。分段的目的是为了能更好的满足用户的需要。

  页的大小固定且由系统确定,把逻辑地址划分为页号和页内地址两部分,是由机器硬件实现的,因而一个系统只能有一种大小的页面。段的长度却不固定,决定于用户所编写的程序,通常由编辑程序在对源程序进行编辑时,根据信息的性质来划分。

  分页的作业地址空间是一维的,即单一的线性空间,程序员只须利用一个记忆符,即可表示一地址。分段的作业地址空间是二维的,程序员在标识一个地址时,既需给出段名,又需给出段内地址。

8、说出你所知道的保持进程同步的方法?

  进程间同步的主要方法有原子操作、信号量机制、自旋锁、管程、会合、分布式系统等。

9Linux中常用到的命令

  显示文件目录命令ls       ls

  改变当前目录命令cd       cd /home

  建立子目录mkdir          mkdir xiong

  删除子目录命令rmdir      rmdir /mnt/cdrom

  删除文件命令rm           rm /ucdos.bat

  文件复制命令cp           cp /ucdos /fox

  获取帮助信息命令man     man ls

  显示文件的内容less       less mwm.lx

  重定向与管道type         type readme>>direct,将文件readme的内容追加到文direct

10Linux文件属性有哪些?(共十位)

-rw-r--r--那个是权限符号,总共是- --- --- ---这几个位。

第一个短横处是文件类型识别符:-表示普通文件c表示字符设备(character);b表示块设备(block);d表示目录(directory);l表示链接文件(link);后面第一个三个连续的短横是用户权限位(User),第二个三个连续短横是组权限位(Group),第三个三个连续短横是其他权限位(Other。每个权限位有三个权限,r(读权限),w(写权限),x(执行权限)。如果每个权限位都有权限存在,那么满权限的情况就是:-rwxrwxrwx;权限为空的情况就是- --- --- ---

  权限的设定可以用chmod命令,其格式位:chmod ugoa+/-/=rwxfilename/directory。例如:

  一个文件aaa具有完全空的权限- --- --- ---

chmod u+rw aaa(给用户权限位设置读写权限,其权限表示为:- rw---- ---

chmod g+r aaa(给组设置权限为可读,其权限表示为:- ---r-- ---

chmod ugo+rw aaa(给用户,组,其它用户或组设置权限为读写,权限表示为:- rw-rw- rw-

  如果aaa具有满权限- rwx rwx rwx

chmod u-x aaa(去掉用户可执行权限,权限表示为:- rw-rwx rwx

  如果要给aaa赋予制定权限- rwx r-x r-x,命令为:

chmod u=rwxgo=rx aaa

11makefile文件的作用是什么?

  一个工程中的源文件不计其数,其按类型、功能、模块分别放在若干个目录中。makefile定义了一系列的规则来指定哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作。因为makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。makefile带来的好处就是——“自动化编译。一旦写好,只需要一个make命令,整个工程完全自动编译,极大地提高了软件开发的效率。make是一个命令工具,是一个解释makefile中指令的命令工具。一般来说,大多数的IDE都有这个命令,比如:DelphimakeVisual C++nmakeLinuxGNUmake。可见,makefile都成为了一种在工程方面的编译方法。

12、简术OSI的物理层Layer1,链路层Layer2,网络层Layer3的任务。

  网络层:通过路由选择算法,为报文或分组通过通信子网选择最适当的路径。

  链路层:通过各种控制协议,将有差错的物理信道变为无差错的、能可靠传输数据帧的数据链路。

  物理层:利用传输介质为数据链路层提供物理连接,实现比特流的透明传输。

13、什么是中断?中断时CPU做什么工作?

  中断是指在计算机执行期间,系统内发生任何非寻常的或非预期的急需处理事件,使得CPU暂时中断当前正在执行的程序而转去执行相应的事件处理程序。待处理完毕后又返回原来被中断处继续执行或调度新的进程执行的过程。

14、你知道操作系统的内容分为几块吗?什么叫做虚拟内存?他和主存的关系如何?内存管理属于操作系统的内容吗?

  操作系统的主要组成部分:进程和线程的管理,存储管理,设备管理,文件管理。虚拟内存是一些系统页文件,存放在磁盘上,每个系统页文件大小为4K,物理内存也被分页,每个页大小也为4K,这样虚拟页文件和物理内存页就可以对应,实际上虚拟内存就是用于物理内存的临时存放的磁盘空间。页文件就是内存页,物理内存中每页叫物理页,磁盘上的页文件叫虚拟页,物理页+虚拟页就是系统所有使用的页文件的总和。

15、线程是否具有相同的堆栈?dll是否有独立的堆栈?

每个线程有自己的堆栈。

dll是否有独立的堆栈?这个问题不好回答,或者说这个问题本身是否有问题。因为dll中的代码是被某些线程所执行,只有线程拥有堆栈。如果dll中的代码是exe中的线程所调用,那么这个时候是不是说这个dll没有独立的堆栈?如果dll中的代码是由dll自己创建的线程所执行,那么是不是说dll有独立的堆栈?

  以上讲的是堆栈,如果对于堆来说,每个dll有自己的堆,所以如果是从dll中动态分配的内存,最好是从dll中删除;如果你从dll中分配内存,然后在exe中,或者另外一个dll中删除,很有可能导致程序崩溃。

16、什么是缓冲区溢出?有什么危害?其原因是什么?

  缓冲区溢出是指当计算机向缓冲区内填充数据时超过了缓冲区本身的容量,溢出的数据覆盖在合法数据上。

  危害:在当前网络与分布式系统安全中,被广泛利用的50%以上都是缓冲区溢出,其中最著名的例子是1988年利用fingerd漏洞的蠕虫。而缓冲区溢出中,最为危险的是堆栈溢出,因为入侵者可以利用堆栈溢出,在函数返回时改变返回程序的地址,让其跳转到任意地址,带来的危害一种是程序崩溃导致拒绝服务,另外一种就是跳转并且执行一段恶意代码,比如得到shell,然后为所欲为。通过往程序的缓冲区写超出其长度的内容,造成缓冲区的溢出,从而破坏程序的堆栈,使程序转而执行其它指令,以达到攻击的目的。

  造成缓冲区溢出的主原因是程序中没有仔细检查用户输入的参数。

17、什么是死锁?其条件是什么?怎样避免死锁?

  死锁的概念:在两个或多个并发进程中,如果每个进程持有某种资源而又都等待别的进程释放它或它们现在保持着的资源,在未改变这种状态之前都不能向前推进,称这一组进程产生了死锁。通俗地讲,就是两个或多个进程被无限期地阻塞、相互等待的一种状态。

  死锁产生的原因主要是:?系统资源不足;?进程推进顺序非法。

  产生死锁的必要条件:

  (1)互斥(mutualexclusion),一个资源每次只能被一个进程使用;

  (2)不可抢占(nopreemption),进程已获得的资源,在未使用完之前,不能强行剥夺;

  (3)占有并等待(hold andwait),一个进程因请求资源而阻塞时,对已获得的资源保持不放;

  (4)环形等待(circularwait),若干进程之间形成一种首尾相接的循环等待资源关系。

  这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。

  死锁的解除与预防:理解了死锁的原因,尤其是产生死锁的四个必要条件,就可以最大可能地避免、预防和解除死锁。所以,在系统设计、进程调度等方面注意如何不让这四个必要条件成立,如何确定资源的合理分配算法,避免进程永久占据系统资源。此外,也要防止进程在处于等待状态的情况下占用资源。因此,对资源的分配要给予合理的规划。

  死锁的处理策略:鸵鸟策略、预防策略、避免策略、检测与恢复策略。

第十七章    数据库与SQL语言

先参见打印内容(相关word)

SQL常用语句一览

sp_password   null,'新密码','sa'   修改数据库密码
(1)
数据记录筛选:

sql="select * from 数据表where 字段名=字段值orderby 字段名 [desc] "
sql="select * from
数据表where 字段名 like '%字段值%'orderby 字段名 [desc]"
sql="select   top10 * from
数据表where 字段名 orderby 字段名[desc]"
sql="select * from
数据表 where 字段名in('1','2','3')"
sql="select * from
数据表 where 字段名between 1 and 2"
(2)
更新数据记录:
sql="update
数据表 set 字段名=字段值where 条件表达式"
sql="update
数据表 set 字段1=1,字段2=2……字段n=nwhere 条件表达式"
(3)
删除数据记录:
sql="delete from
数据表 where 条件表达式"
sql="delete from
数据表 "(将数据表所有记录删除)
(4)
添加数据记录:
sql="insert into
数据表(字段1,字段2,字段3…)values(1,2,3…)"
sql="insert into
目标数据表 select * from 源数据表"(把源数据表的记录添加到目标数据表)
(5)
数据记录统计函数:
AVG(
字段名)得出一个表格栏平均值
COUNT(*|
字段名)对数据行数的统计或对某一栏有值的数据行数统计
MAX(
字段名)取得一个表格栏最大的值
MIN(
字段名)取得一个表格栏最小的值
SUM(
字段名)把数据栏的值相加
引用以上函数的方法:
sql="selectsum(字段名)as别名from数据表where条件表达式"
setrs=conn.excute(sql)
rs("别名")获取统的计值,其它函数运用同上
(5)
数据表的建立和删除:
CREATETABLE
数据表名称(字段1类型1(长度),字段2类型2(长度)……)
例:CREATETABLEtab01 (namevarchar(50), datetimedefaultnow ())
DROPTABLE
数据表名称(永久性删除一个数据表)

视图

视图是虚表,是从一个或几个基本表(或视图)中导出的表,在系统的数据字典中仅存放了视图的定义,不存放视图对应的数据。

视图是原始数据库数据的一种变换,是查看表中数据的另外一种方式。可以将视图看成是一个移动的窗口,通过它可以看到感兴趣的数据。 视图是从一个或多个实际表中获得的,这些表的数据存放在数据库中。那些用于产生视图的表叫做该视图的基表

 

四个范式

第一,   不可分解

第二,   依赖性

第三,   非主属性都不传递依赖

第四,   多值依赖

         BC范式,第一 and 第三

存储过程(一些列SQL语句的集合)和函数(已定义的方法)

事务

         ACID

         ACID,指数据库事务正确执行的四个基本要素的缩写。包含:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。一个支持事务(Transaction)的数据库系统,必需要具有这四种特性,否则在事务过程(Transaction processing)当中无法保证数据的正确性,交易过程极可能达不到交易方的要求。

原子性

整个事务中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。

一致性

在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。

隔离性

隔离状态执行事务,使它们好像是系统在给定时间内执行的唯一操作。如果有两个事务,运行在相同的时间内,执行相同的功能,事务的隔离性将确保每一事务在系统中认为只有该事务在使用系统。这种属性有时称为串行化,为了防止事务操作间的混淆,必须串行化或序列化请求,使得在同一时间仅有一个请求用于同一数据。

持久性

在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。

由于一项操作通常会包含许多子操作,而这些子操作可能会因为硬件的损坏或其他因素产生问题,要正确实现ACID并不容易。ACID建议数据库将所有需要更新 以及修改的资料一次操作完毕,但实际上并不可行。

目前主要有两种方式实现ACID:第一种是Write ahead logging,也就是日志式的方式。第二种是Shadow paging。

游标的作用 用于定位结果集的行

触发,事前触发,事后触发,语句级触发,行级触发(所影响的每一行触发一次)

SQL注入式攻击

         相当大一部分程序员在编写代码的时候,没有对用户输入数据的合法性进行判断,使应用程序存在安全隐患。用户可以提交一段数据库查询代码,根据程序返回的结果,获得某些他想得知的数据,这就是所谓的SQL Injection,即SQL注入。

SQL语言

????

 

W3C 网站有SQL的教程,很全面

http://www.w3school.com.cn/sql/index.asp

官方网站,自己一节一节学吧,把关键字和函数的意思弄懂了,就好搞了

P258P259有详细的笔记

P266 SQL程序 

第十八章:计算机网络和分布式系统

Osi七层和TCPIP五层模型对比

TCP与UDP区别

TCP三次握手(启动链接)(一个syn,一个回复syn+ack,再回复)及4次挥手(关闭链接)(特别两个fin信号,两个回复)

 

套接字细节

网络攻击

       入侵检测与防火墙

       蠕虫病毒(icmp目的地无法抵达的包)

       阻塞会出现丢包的情况

端口说明

       21 GTP

23telnet

       25 SMTP

53 dns服务器,用于域名解析

  80  http

       109110pop2pop3

135  远程过程调用

       443 网页浏览

554  实时浏览

网络子网问题(子网掩码、可用地址)

       IP后面带/30 /29 /27等是什么意思?

子网掩码的意思
子网掩码是48位的2进制数字组成,
30表示前30位数字为1,11111111.11111111.11111111.11111100,换成十进制就是255.255.255.252

主机号在掩码内全是0代表的是多播地址,全是是1代表的是广播地址


29表示11111111.11111111.11111111.11111000,十进制就是255.255.255.248
27表示11111111.11111111.11111111.11100000,十进制就是255.255.255.224

各种协议命令Ping

ICMP

智力题

 

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     

未完待续。。。

 


本书针对当前各大it企业面试笔试中常见的问题以及注意事项,进行了深层次的分析。本书除了对传统的计算机相关知识(c/c++、数据结构与算法、操作系统、计算机网络与通信、软件工程、数据库、智力题、英语面试等)进行介绍外,还根据当前计算机技术的发展潮流,对面试笔试中常见的海量数据处理进行了详细的分析。同时,为了更具说服力,本书特邀多位it名企面试官现身说法,对面试过程中求职者存在的问题进行了深度剖析,同时本书引入了一批来自于名牌高校、就职于明星企业的职场达人的真实求职案例,通过他们的求职经验与教训,抛砖引玉,将整个求职过程生动形象地展示在读者面前,进而对求职者起到一定的指引作用。本书也对各种类型的it企业的招聘环节进行了庖丁解牛式的分析,帮助求职者能够更加有针对性地 进行求职准备。 本书是一本计算机相关专业毕业生面试笔试的求职用书,同时也适合期望在计算机软硬件行业大显身手的计算机爱好者阅读。 程序员面试笔试宝典 目录 前言 上篇 面试笔试经验技巧篇 第1章 面试官箴言 2 第2章 面试心得交流 9 第3章 企业面试笔试攻略 20 第4章 面试笔试技巧 42 第5章 英文面试攻略 82 第6章 智力题攻略 102 下篇 面试笔试技术攻克篇 第7章 程序设计基础 122 第8章 数据库 240 第9章 网络与通信 254 第10章 操作系统 270 第11章 软件工程 278 第12章 发散思维 289 第13章 数据结构与算法 295 第14章 海量数据处理 390
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值