C++面试题

1、new、delete、malloc、free关系

delete会调用对象的析构函数,和malloc对应free只会释放内存;

new调用构造函数。

malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。

它们都可用于申请动态内存和释放内存。

对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。

参考回答:首先,new/delete是C++的关键字,而malloc/free是C语言的库函数,后者使用必须指明申请内存空间的大小,
对于类类型的对象,后者不会调用构造函数和析构函数

2、delete与 delete []区别

  • delete只会调用一次析构函数,而delete[]会调用每一个成员的析构函数。
  • 在MoreEffective C++中有更为详细的解释:“当delete操作符用于数组时,它为每个数组元素调用析构函数,然后调用operatordelete来释放内存。”,delete与New配套,delete[]与new []配套

    如下例:

    =[10];=;int*=[10];int*=;delete[];//-1-delete[];//-2-delete[];//-3-delete[];//-4-在-4-处报错。
    这就说明:对于内建简单数据类型,delete和delete[]功能是相同的。对于自定义的复杂数据类型,delete和delete[]不能互用。delete[]删除一个数组,delete删除一个指针简单来说,用new分配的内存用delete删除用new[]分配的内存用delete[]删除delete[]会调用数组元素的析构函数。内部数据类型没有析构函数,所以问题不大。如果你在用delete时没用括号,delete就会认为指向的是单个对象,否则,它就会认为指向的是一个数组。

3、C和C++的共同点?不同之处?

  • 设计思想上:C++是面向对象的语言,而C是面向过程的结构化编程语言
  • 语法上:
  *   <font size="2">C++具有封装、继承和多态三种特性</font>

C++相比C,增加多许多类型安全的功能,比如强制类型转换、

C++支持范式编程,比如模板类、函数模板等

4、继承的优缺点

优点:类继承是在编译时刻静态定义的,且可直接使用,类继承可以较方便地改变父类的实现。

缺点:

  • 首先,因为继承在编译时刻就定义了,所以无法在运行时刻改变从父类继承的实现。
  • 其次,父类通常至少定义了子类的部分行为,父类的任何改变都可能影响子类的行为。如果继承下来的实现不适合解决新的问题,则父类必须重写或被其他更适合的类替换。这种依赖关系限制了灵活性并最终限制了复用性。

5、C++有哪些性质(面向对象特点)

封装,继承和多态。

在面向对象程序设计语言中,封装是利用可重用成分构造软件系统的特性,它不仅支持系统的可重用性,而且还有利于提高系统的可扩充性;消息传递可以实现发送一个通用的消息而调用不同的方法;封装是实现信息隐蔽的一种技术,其目的是使类的定义和实现分离。

6、子类析构时要调用父类的析构函数吗?

析构函数调用的次序是先派生类的析构后基类的析构,也就是说在基类的的析构调用的时候,派生类的信息已经全部销毁了定义一个对象时先调用基类的构造函数、然后调用派生类的构造函数;析构的时候恰好相反:先调用派生类的析构函数、然后调用基类的析构函数JAVA无析构函数深拷贝和浅拷贝

7、多态,虚函数,纯虚函数

  • 多态:是对于不同对象接收相同消息时产生不同的动作。C++的多态性具体体现在运行和编译两个方面:在程序运行时的多态性通过继承和虚函数来体现;
    在程序编译时多态性体现在函数和运算符的重载上
  • 虚函数:在基类中冠以关键字 virtual 的成员函数。 它提供了一种接口界面。允许在派生类中对基类的虚函数重新定义。
  • 纯虚函数的作用:在基类中为其派生类保留一个函数的名字,以便派生类根据需要对它进行定义。作为接口而存在 纯虚函数不具备函数的功能,一般不能直接被调用。
    从基类继承来的纯虚函数,在派生类中仍是虚函数。如果一个类中至少有一个纯虚函数,那么这个类被称为抽象类(abstractclass)。
    抽象类中不仅包括纯虚函数,也可包括虚函数。l抽象类必须用作派生其他类的基类,而不能用于直接创建对象实例。但仍可使用指向抽象类的指针支持运行时多态性。

8、什么是“引用”?申明和使用“引用”要注意哪些问题?

  • 引用就是某个目标变量的“别名”(alias),对应用的操作与对变量直接操作效果完全相同。
  • 注意问题:
*   <font size="2">申明一个引用的时候,切记要对其进行初始化。</font>

引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,不能再把该引用名作为其他变量名的别名。

声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。

不能建立数组的引用。

9、将“引用”作为函数参数有哪些特点?

  • 传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。
  • 使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。
  • 使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用"*指针变量名"的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。

10、在什么时候需要使用“常引用”?

如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。常引用声明方式:const类型标识符 &引用名=目标变量名;

例1

int;constint&=;=1;//错误=1;//正确例2

foo();voidbar(&);那么下面的表达式将是非法的:

bar(foo());bar(“hello world”);原因在于foo( )和"helloworld"串都会产生一个临时对象,而在C++中,这些临时对象都是const类型的。因此上面的表达式就是试图将一个const类型的对象转换为非const类型,这是非法的。引用型参数应该在能被定义为const的情况下,尽量定义为const 。

11、将“引用”作为函数返回值类型的格式的好处和需要遵守的规则?

格式:类型标识符 &函数名(形参列表及类型说明){//函数体 }

好处:在内存中不产生被返回值的副本;(注意:正是因为这点原因,所以返回一个局部变量的引用是不可取的。因为随着该局部变量生存期的结束,相应的引用也会失效,产生runtimeerror!

注意事项:

*   <font size="2">不能返回局部变量的引用。这条可以参照EffectiveC++[1]的Item 31。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了"无所指"的引用,程序会进入未知状态。</font>
  • 不能返回函数内部new分配的内存的引用。这条可以参照Effective C++[1]的Item 31。虽然不存在局部变量的被动销毁问题,可对于这种情况(返回函数内部new分配内存的引用),又面临其它尴尬局面。例如,被函数返回的引用只是作为一个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memory leak。
  • 可以返回类成员的引用,但最好是const。这条原则可以参照Effective C++[1]的Item 30。主要原因是当对象的属性是与某种业务规则(business rule)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其它对象可以获得该属性的非常量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。
  • 流操作符重载返回值申明为“引用”的作用:
    流操作符<<和>>,这两个操作符常常希望被连续使用,

    例如: <<“hello”<<;
   *   1</pre> <font size="2">因此这两个操作符的返回值应该是一个仍然支持这两个操作符的流引用。可选的其它方案包括:返回一个流对象和返回一个流对象指针。但是对于返回一个流对象,程序必须重新(拷贝)构造一个新的流对象,也就是说,连续的两个&lt;&lt;操作符实际上是针对不同对象的!这无法让人接受。对于返回一个流指针则不能连续使用&lt;&lt;操作符。因此,返回一个流对象引用是惟一选择。这个唯一选择很关键,它说明了引用的重要性以及无可替代性,也许这就是C++语言中引入引用这个概念的原因吧。 赋值操作符=。这个操作符象流操作符一样,是可以连续使用的,
   例如:x = j= 10;或者(x=10)=100;赋值操作符的返回值必须是一个左值,以便可以被继续赋值。因此引用成了这个操作符的惟一返回值选择。

例3

#include <iostream.h>int&put(int);int[10];int=-1;voidmain(){put(0)=10;//以put(0)函数值作为左值,等价于vals[0]=10;put(9)=20;//以put(9)函数值作为左值,等价于vals[9]=20;<<[0];<<[9];}int&put(int){if(>=0&&<=9)return[];else{<<“subscript error”;return;}}

在另外的一些操作符中,却千万不能返回引用:±*/四则运算符。它们不能返回引用,Effective C++[1]的Item23详细的讨论了这个问题。主要原因是这四个操作符没有side effect,因此,它们必须构造一个对象作为返回值,可选的方案包括:返回一个对象、返回一个局部变量的引用,返回一个new分配的对象的引用、返回一个静态对象引用。根据前面提到的引用作为返回值的三个规则,第2、3两个方案都被否决了。静态对象的引用又因为((a+b) == (c+d))会永远为true而导致错误。所以可选的只剩下返回一个对象了。

12、“引用”与多态的关系?

引用是除指针外另一个可以产生多态效果的手段。这意味着,一个基类的引用可以指向它的派生类实例。

例4:

A;B:A{…};;&=;

13、“引用”与指针的区别是什么?

  • 指针有自己的一块空间,而引用只是一个别名;
  • 使用sizeof看一个指针的大小是4,而引用则是被引用对象的大小; + 指针可以被初始化为NULL,而引用必须被初始化且必须是一个已有对象 的引用;
  • 作为参数传递时,指针需要被解引用才可以对对象进行操作,而直接对引 用的修改都会改变引用所指向的对象;
  • 可以有const指针,但是没有const引用;
  • 指针在使用中可以指向其它对象,但是引用只能是一个对象的引用,不能 被改变;
  • 指针可以有多级指针(**p),而引用至于一级;
  • 指针和引用使用++运算符的意义不一样;
  • 如果返回动态内存分配的对象或者内存,必须使用指针,引用可能引起内存泄露。
  • 指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。此外,就是上面提到的对函数传ref和pointer的区别。

14、什么时候需要“引用”?

流操作符<<和>>、赋值操作符=的返回值、拷贝构造函数的参数、赋值操作符=的参数、其它情况都推荐使用引用。

15、结构与联合有和区别?

  • 结构和联合都是由多个不同的数据类型成员组成, 但在任何同一时刻, 联合中只存放了一个被选中的成员(所有成员共用一块地址空间), 而结构的所有成员都存在(不同成员的存放地址不同)。
  • 对于联合的不同成员赋值,将会对其它成员重写, 原来成员的值就不存在了, 而对于结构的不同成员赋值是互不影响的。

16、关联、聚合(Aggregation)以及组合(Composition)的区别?

涉及到UML中的一些概念:关联是表示两个类的一般性联系,比如“学生”和“老师”就是一种关联关系;聚合表示has-a的关系,是一种相对松散的关系,聚合类不需要对被聚合类负责,如下图所示,用空的菱形表示聚合关系:从实现的角度讲,聚合可以表示为:

classA{…}classB{*;…}而组合表示contains-a的关系,关联性强于聚合:组合类与被组合类有相同的生命周期,组合类要对被组合类负责,采用实心的菱形表示组合关系:实现的形式是:

classA{…}classB{;…}

17、面向对象的三个基本特征,并简单叙述之?

  • 封装:将客观事物抽象成类,每个类对自身的数据和方法实行protection(private,protected,public)
  • 继承:广义的继承有三种实现形式:实现继承(指使用基类的属性和方法而无需额外编码的能力)、可视继承(子窗体使用父窗体的外观和实现代码)、接口继承(仅使用属性和方法,实现滞后到子类实现)。前两种(类继承)和后一种(对象组合=>接口继承以及纯虚函数)构成了功能复用的两种方式。
  • 多态:是将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。

18、重载(overload)和重写(overried,有的书也叫做“覆盖”)的区别?

常考的题目。从定义上来说:

*   <font size="2">重载:是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。</font>

重写:是指子类重新定义父类虚函数的方法。* 从实现原理上来说:

  • 重载:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。如,有两个同名函数:functionfunc(p:integer):integer;和functionfunc(p:string):integer;。那么编译器做过修饰后的函数名称可能是这样的:int_func、str_func。对于这两个函数的调用,在编译器间就已经确定了,是静态的。也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关!

重写:和多态真正相关。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚绑定)。

19、多态的作用?

主要是两个:

  • 隐藏实现细节,使得代码能够模块化;扩展代码模块,实现代码重用;
  • 接口重用:为了类在继承和派生的时候,保证使用家族中任一类的实例的某一属性时的正确调用。

20、Ado与Ado.net的相同与不同?

除了“能够让应用程序处理存储于DBMS中的数据“这一基本相似点外,两者没有太多共同之处。但是Ado使用OLE DB 接口并基于微软的COM技术,而ADO.NET 拥有自己的ADO.NET 接口并且基于微软的.NET体系架构。众所周知.NET 体系不同于COM 体系,ADO.NET接口也就完全不同于ADO和OLE DB 接口,这也就是说ADO.NET和ADO是两种数据访问方式。ADO.net 提供对XML的支持。

21、New delete 与malloc free 的联系与区别?

都是在堆(heap)上进行动态的内存操作。用malloc函数需要指定内存分配的字节数并且不能初始化对象,new 会自动调用对象的构造函数。delete 会调用对象的destructor,而free 不会调用对象的destructor.

22、#define DOUBLE(x) x+x ,i = 5*DOUBLE(5); i 是多少?

i 为30。

23、有哪几种情况只能用intialization list 而不能用assignment?

当类中含有const、reference成员变量;基类的构造函数都需要初始化表。

24、C++是不是类型安全的?

不是。两个不同类型的指针之间可以强制转换(用reinterpret cast)。C#是类型安全的。

25、main 函数执行以前,还会执行什么代码?

全局对象的构造函数会在main 函数之前执行。

26、描述内存分配方式以及它们的区别?

  • 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
  • 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集。
  • 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活,但问题也最多。

27、struct 和 class 的区别

在C++中,可以用struct和class定义类,都可以继承。区别在于:struct的默认继承权限和默认访问权限是public,而class的默认继承权限和默认访问权限是private。

另外,class还可以定义模板类形参,比如template <class T, int i>。

28、当一个类A 中没有任何成员变量与成员函数,这时sizeof(A)的值是多少?如果不是零,请解释一下编译器为什么没有让它为零。

sizeof(A) = 1。编译器不允许一个类的大小为0。那是被编译器插进去的一个char ,使得这个class的不同实体(object)在内存中配置独一无二的地址。 也就是说这个char是用来标识类的不同对象的。肯定不是零。举个反例,如果是零的话,声明一个class A[10]对象数组,而每一个对象占用的空间是零,这时就没办法区分A[0],A[1]…了。

29、在8086 汇编下,逻辑地址和物理地址是怎样转换的?(Intel)

通用寄存器给出的地址,是段内偏移地址,相应段寄存器地址*10H+通用寄存器内地址,就得到了真正要访问的地址。

30、比较C++中的4种类型转换方式?

C++中四种类型转换是:static_cast, dynamic_cast, const_cast, reinterpret_cast

  • const_cast
    用于将const变量转为非const
  • static_cast
    用于各种隐式转换,比如非const转const,void*转指针等, static_cast能用于多态向上转化,如果向下转能成功但是不安全,结果未知;
  • dynamic_cast
    用于动态类型转换。只能用于含有虚函数的类,用于类层次间的向上和向下转化。只能转指针或引用。向下转化时,如果是非法的对于指针返回NULL,对于引用抛异常。要深入了解内部转换的原理。

    向上转换:指的是子类向基类的转换

    向下转换:指的是基类向子类的转换

    它通过判断在执行到该语句的时候变量的运行时类型和要转换的类型是否相同来判断是否能够进行向下转换。
  • reinterpret_cast
    几乎什么都可以转,比如将int转指针,可能会出问题,尽量少用;

31、分别写出BOOL,int,float,指针类型的变量a 与“零”的比较语句。

:if(!)orif()int:if(==0)float:const=0.000001if(<&&>-):if(!=NULL)orif(==NULL)

32、请说出const与#define 相比,有何优点?

Const作用:定义常量、修饰函数参数、修饰函数返回值三个作用。被Const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。

  • const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。
  • 有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。

33、简述数组与指针的区别?

数组指针
保存数据保存数据的地址
直接访问数据间接访问数据,首先获得指针的内容,然后将其作为地址,从该地址中提取数据
隐式的分配和删除通常用于动态的数据结构
通常用于固定数目且数据类型相同的元素通过Malloc分配内存,free释放内存
自身即为数据名通常指向匿名数据,操作匿名函数

数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。

指针可以随时指向任意类型的内存块。

(1)修改内容上的差别

char[]=;[0]=;char*=;// 注意p 指向常量字符串[0]=;// 编译器不能发现该错误,运行时错误(2) 用运算符sizeof 可以计算出数组的容量(字节数)。sizeof§,p 为指针得到的是一个指针变量的字节数,而不是p 所指的内存容量。C++/C 语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。

char[]=“hello world”;char*=;<<sizeof()<<;// 12 字节<<sizeof()<<;// 4 字节计算数组和指针的内存容量

voidFunc(char[100]){<<sizeof()<<;// 4 字节而不是100字节}

34、类成员函数的重载、覆盖和隐藏区别?

成员函数被重载的特征:

(1)相同的范围(在同一个类中);

(2)函数名字相同;

(3)参数不同;

(4)virtual 关键字可有可无。

覆盖是指派生类函数覆盖基类函数,特征是:

(1)不同的范围(分别位于派生类与基类);

(2)函数名字相同;

(3)参数相同;

(4)基类函数必须有virtual 关键字。

“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:

(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。

(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual 关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)

35、求出两个数中的较大者

There are two int variables:a and b, don’t use “if”, “? :”, “switch”or other judgement statements, find outthe biggest one of the two numbers.

答案:( ( a + b ) + abs( a - b ) ) / 2

36、如何打印出当前源文件的文件名以及源文件的当前行号?

<<FILE;<<LINE;FILE和LINE是系统预定义宏,这种宏并不是在某个文件中定义的,而是由编译器定义的。

37、main主函数执行完毕后,是否可能会再执行一段代码,给出说明?

可以,可以用_onexit 注册一个函数,它会在main之后执行int fn1(void), fn2(void),fn3(void), fn4 (void);
voidmain(void){str(“zhanglin”);_onexit();_onexit();_onexit();_onexit();printf(“This is executed first.n”);}intfn1(){printf(“next.n”);return0;}intfn2(){printf("executed ");return0;}intfn3(){printf("is ");return0;}intfn4(){printf("This ");return0;}The onexit function is passed the address of a function (func) to be calledwhen the program terminates normally. Successive calls to onexit create aregister of functions that are executed in LIFO (last-in-first-out) order. Thefunctions passed to _onexit cannot take parameters.

程序正常终止时,将向onexit函数传递要调用的函数(func)的地址。 连续调用onexit会创建以LIFO(后进先出)顺序执行的功能的寄存器。 传递给_onexit的函数不能使用参数。

38、如何判断一段程序是由C 编译程序还是由C++编译程序编译的?

#ifdef __cplusplus<<“c++”;#else<<“c”;#endif

39、文件中有一组整数,要求排序后输出到另一个文件中

#include<iostream>
#include<fstream>
 
using namespace std;
 
void Order(vector<int>& data) //bubble sort
{
int count = data.size() ;
for ( int i = 0 ; i < count ; i++)
{
for ( int j = 0 ; j < count - i - 1 ; j++)
{
if ( data[j] > data[j+1])
{
 
int temp = data[j] ;
data[j] = data[j+1] ;
data[j+1] = temp ;
}
}
}
 
 
void main( void )
{
vector<int>data;
ifstream in("c:\data.txt");
if ( !in)
{
cout<<"file error!";
exit(1);
}
int temp;
while (!in.eof())
{
in>>temp;
data.push_back(temp);
}
in.close(); //关闭输入文件流
Order(data);
ofstream out("c:\result.txt");
if ( !out)
{
cout<<"file error!";
exit(1);
}
for ( i = 0 ; i < data.size() ; i++)
out<<data[i]<<" ";
out.close(); //关闭输出文件流
}

40、请你详细地解释一下IP协议的定义,在哪个层上面?主要有什么作用?TCP与UDP呢 ?

IP处在互连网络层。负责提供基本的数据封包传送功能,让每一块数据包都能够到达目的主机(但不检查是否被正确接收)。

TCP与UDP在传输层。它提供了节点间的数据传送,应用程序之间的通信服务,主要功能是数据格式化、数据确认和丢失重传等。如传输控制协议(TCP)、用户数据报包议(UDP)等,TCP和UDP给数据包加入传输数据并把它传输到下一层中,这一层负责传送数据,并且确定数据已被送达并接收。

41、请问交换机和路由器各自的实现原理是什么?分别在哪个层次上面实现的?

交换机属于OSI第二层即数据链路层设备。它根据MAC地址寻址,通过站表选择路由,站表的建立和维护由交换机自动进行。路由器属于OAI第三层即网络层设备,它根据IP地址进行寻址,通过路由表路由协议产生。交换机最大的好处是快速,路由器最大的好处是控制能力强。

42、全局变量和局部变量有什么区别?是怎么实现的?操作系统和编译器是怎么知道的 ?

  • 作用域不同:全局变量的作用域为整个程序,而局部变量的作用域为当前函数或循环等
  • 内存存储方式不同:全局变量存储在全局数据区中,局部变量存储在栈区
  • 生命期不同:全局变量的生命期和主程序一样,随程序的销毁而销毁,局部变量在函数内部或循环内部,随函数的退出或循环退出就不存在了
  • 使用方式不同:全局变量在声明后程序的各个部分都可以用到,但是局部变量只能在局部使用。函数内部会优先使用局部变量再使用全局变量。
    全局变量在数据段,而局部变量在栈,局部 变量在函数结束时内存空间就被系统收回,所以要返回的数组或字符串不要用局部变量定义.extren和在main()函数外定义的变量都称为全局变量,操 作系统和编译器从定义变量为变量分配内存时,从变量的定义和存储区域来分别局部变量和全局变量

43、8086是多少位的系统?在数据总线上是怎么实现的?

8086微处理器共有4个16位的段寄存器,在寻址内存单元时,用它们直接或间接地存放段地址。

  代码段寄存器CS:存放当前执行的程序的段地址。

  数据段寄存器DS:存放当前执行的程序所用操作数的段地址。

  堆栈段寄存器SS:存放当前执行的程序所用堆栈的段地址。

  附加段寄存器ES:存放当前执行程序中一个辅助数据段的段地址。

由cs:ip构成指令地址,ss:sp构成堆栈的栈顶地址指针。DS和ES用作数据段和附加段的段地址(段起始地址或段值)

8086/8088微处理器的存储器管理

  • 地址线(码)与寻址范围:N条地址线 寻址范围=2N
  • 8086有20地址线 寻址范围为1MB 由00000H~FFFFFH
  • 8086微处理器是一个16位结构,用户可用的寄存器均为16位:寻址64KB
  • 8086/8088采用分段的方法对存储器进行管理。具体做法是:把1MB的存储器空间分成若干段,每段容量为64KB,每段存储器的起始地址必须是一个能被16整除的地址码,即在20位的二进制地址码中最低4位必须是“0”。每个段首地址的高16位二进制代码就是该段的段号(称段基地址)或简称段地址,段号保存在段寄存器中。我们可对段寄存器设置不同的值来使微处理器的存储器访问指向不同的段。
  • 段内的某个存储单元相对于该段段首地址的差值,称为段内偏移地址(也叫偏移量)用16位二进制代码表示。
  • 物理地址是由8086/8088芯片地址引线送出的20位地址码,它用来参加存储器的地址译码,最终读/写所访问的一个特定的存储单元。
  • 逻辑地址由某段的段地址和段内偏移地址(也叫偏移量)两部分所组成。写成:
    段地址:偏移地址(例如,1234H:0088H)。
  • 在硬件上起作用的是物理地址,物理地址=段基地址×10H十偏移地址

44、解释局部变量、全局变量和静态变量的含义。

局部变量

在一个函数内部定义的变量是内部变量,它只在本函数范围内有效,也就是说只有在本函数内才能使用它们,在此函数以外时不能使用这些变量的,它们称为局部变量;

说明:

*   <font size="2">主函数main中定义的变量也只在主函数中有效,而不因为在主函数中定义而在整个文件或程序中有效</font>

不同函数中可以使用名字相同的变量,它们代表不同的对象,互不干扰

形式参数也使局部变量

在一个函数内部,可以在复合语句中定义变量,这些变量只在本符合语句中有效*

全局变量

在函数外定义的变量是外部变量,外部变量是全局变量,全局变量可以为本文件中其它函数所共用,它的有效范围从定义变量的位置开始到本源文件结束;

说明:

设全局变量的作用:增加了函数间数据联系的渠道

建议不再必要的时候不要使用全局变量,因为:

   *   <font size="2">a.全局变量在程序的全部执行过程中都占用存储单元;</font>

b.它使函数的通用性降低了c.使用全局变量过多,会降低程序的清晰性*

如果外部变量在文件开头定义,则在整个文件范围内都可以使用该外部变量,如果不再文件开头定义,按上面规定作用范围只限于定义点到文件终了。如果在定义点之前的函数想引用该外部变量,则应该在该函数中用关键字extern作外部变量说明

4.如果在同一个源文件中,外部变量与局部变量同名,则在局部变量的作用范围内,外部变量不起作用;*

静态变量

在程序运行期间分配固定的存储空间的变量,叫做静态变量

45、论述含参数的宏与函数的优缺点。

  • 函数调用时,先求出实参表达式的值,然后带入形参。而使用带参的宏只是进行简单的字符替换。
  • 函数调用是在程序运行时处理的,分配临时的内存单元;而宏展开则是在编译时进行的,在展开时并不分配内存单元,不进行值的传递处理,也没有“返回值”的概念。
  • 对函数中的实参和形参都要定义类型,二者的类型要求一致,如不一致,应进行类型转换;而宏不存在类型问题,宏名无类型,它的参数也无类型,只是一个符号代表,展开时带入指定的字符即可。宏定义时,字符串可以是任何类型的数据。
  • 调用函数只可得到一个返回值,而用宏可以设法得到几个结果。
  • 使用宏次数多时,宏展开后源程序长,因为每展开一次都使程序增长,而函数调用不使源程序变长。
  • 宏替换不占运行时间,只占编译时间;而函数调用则占运行时间(分配单元、保留现场、值传递、返回)。
    一般来说,用宏来代表简短的表达式比较合适。

    内联函数和宏很类似,而区别在于,宏是由预处理器对宏进行替代,而内联函数是通过编译器控制来实现的。而且内联函数是真正的函数,只是在需要用到的时候,内联函数像宏一样的展开,所以取消了函数的参数压栈,减少了调用的开销。你可以象调用函数一样来调用内联函数,而不必担心会产生于处理宏的一些问题。

    当然,内联函数也有一定的局限性。就是函数中的执行代码不能太多了,如果,内联函数的函数体过大,一般的编译器会放弃内联方式,而采用普通的方式调用函数。这样,内联函数就和普通函数执行效率一样了。

    内联函数是不能为虚函数的,但样子上写成了内联的,即隐含的内联方式。在某种情况下,虽然有些函数我们声明为了所谓“内联”方式,但有时系统也会把它当作普通的函数来处理,这里的虚函数也一样,虽然同样被声明为了所谓“内联”方式,但系统会把它当然非内联的方式来处理。

46、C++里面是不是所有的动作都是main()引起的?如果不是,请举例。

不是

对于C++程序而言:


第一:静态变量、全局变量、全局对象的分配早在main()函数之前已经完成了;

第二:系统会为某个启动的程序分配地址空间,创建进程和主线程,并为main()指供参数(如果有的话),然后才转到main()执行;

第三:是C/C++的运行时启动函数:wWinMainCRTStartup 引起的,main函数也接受这个函数的调用。所以并不是所有的动作都是由main()引起的,只是编译器是由main()开始执行的,main()只不过是一个约定的函数入口,在main()函数中的显示代码执行之前,会调用一个由编译器是生成的main()函数,而main()函数会进行所有全局对象的构造及初始化工作。如:

classA();;
intmain(){
//main func
}

程序在执行时,因为会首先初始化全局变量,当这个变量是一个对象时,则会首先调用该对象的构造函数,所以上例中,a的构造函数先执行,然后再执行main()函数,C++中并非所有的动作都是main()函数引起的
引申:怎样在main()函数退出以后再执行一段代码?

全局变量。当程序退出时,全局变量必须销毁,自然会调用全局对象的析构函数,所以剩下的就同构造函数一样了

47、如何定义和实现一个类的成员函数为回调函数?

回调函数定义

就是被调用者回头调用的函数,它是通过函数指针调用的函数。如果把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所所指向的函数时,此时,就可以称它为回调函数。

进一步解释

回调函数不是由该函数的实现方直接调用的,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

使用回调函数实际上就是在调用某个函数(通常是API函数)时,将自己的一个函数(这个函数为回调函数)的地址作为参数传递给那个被调用的函数。而被调用函数在需要的时候,利用传递的地址调用回调函数。

回调函数由程序员自己调用,当需要调用另一个函数时,这个函数的其中一个参数就是这个回调函数名。系统在必要的时候会调用程序员写的回调函数,这样就可以在回调函数里完成要做的事了。

回调函数实现

(1)声明

(2)定义

(3)设置触发条件,就是在函数中把回调函数名作为一个参数,以便系统调用。

举例

typedefvoid(*)(void);//定义回调函数classA{public://回调函数,必须是声明为staticstaticvoidcallBackFun(void){…}};//设置触发条件voidFuntype(){p();}voidmain(void){Functype(:😃;}回调函数和API对比

相同点:

回调函数与应用程序接口(API)很相似,都是跨层调用的函数

不同点:

*   <font size="2">API是低层提供给高层的调用,一般这个函数对高层都是已知的。</font>

回调函数是高层提供给低层的调用,对于低层它是未知的,必须由高层进行安装,这个安装函数就是一个低层提供的API,安装后低层不是道这个回调的名字,但它通过一个函数指针来保存这个回调函数,在需要调用时,只需引用这个函数指针和相关的参数指针。

48、解释堆和栈的区别。

1、栈由编译器自动分配释放空间;堆 一般由程序员分配释放。

2、栈使用的是一级缓存, 它们通常都是被调用时处于存储空间中,调用完毕立即释放;堆则是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定。”

49、C++里面如何声明const void f(void)函数为C程序中的库函数?

extern “C” const void f(void);

50、内联函数在编译时是否做参数类型检查?

A、不做检查,和宏一样

B、做类型检查

C、和编译器相关

先说宏和函数的区别:

1.宏做的是简单的字符串替换(注意是字符串的替换,不是其他类型参数的替换),而函数的参数的传递,参数是有数据类型的,可以是各种各样的类型.

2.宏的参数替换是不经计算而直接处理的,而函数调用是将实参的值传递给形参,既然说是值,自然是计算得来的.

3.宏在编译之前进行,即先用宏体替换宏名,然后再编译的,而函数显然是编译之后,在执行时,才调用的.因此,宏占用的是编译的时间,而函数占用的是执行时的时间.

4.宏的参数是不占内存空间的,因为只是做字符串的替换,而函数调用时的参数传递则是具体变量之间的信息传递,形参作为函数的局部变量,显然是占用内存的.

5.函数的调用是需要付出一定的时空开销的,因为系统在调用函数时,要保留现场,然后转入被调用函数去执行,调用完,再返回主调函数,此时再恢复现场,这些操作,显然在宏中是没有的.

内联函数与宏的区别:

1.内联函数在运行时可调试,而宏定义不可以;

2.编译器会对内联函数的参数类型做安全检查或自动类型转换(同普通函数),而宏定义则不会;

3.内联函数可以访问类的成员变量,宏定义则不能;

4.在类中声明同时定义的成员函数,自动转化为内联函数。 

答案:B

51、static有什么用途?(请至少说明两种)

答案一

在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。

在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。

在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用

答案二:

在C语言中,static主要定义全局静态变量,定义局部静态变量,定义静态函数

一、 定义全局静态变量 :在全局变量前面加上关键字static,该全局变量变成了全局静态变量。全局静态变量有以下特点:

(1) 在全局数据区内分配内存

(2) 如果没有初始化,其默认值为0

(3) 该变量在本文件内从定义开始到文件结束可见

二、 定义局部静态变量:在局部静态变量前面加上关键字static,该局部变量便成了静态局部变量。静态局部变量有以下特点:

(1) 该变量在全局数据区分配内存

(2) 如果不显示初始化,那么将被隐式初始化为0

(3) 它始终驻留在全局数据区,直到程序运行结束

(4) 其作用域为局部作用域,当定义它的函数或语句块结束时,其作用域随之结束。

三、 定义静态函数:在函数的返回类型加上static关键字,函数即被定义成静态函数。静态函数有以下特点:

(1) 静态函数只能在本源文件中使用

(2) 在文件作用域中声明的inline函数默认为static

说明:静态函数只是一个普通的全局函数,只不过受static限制,他只能在文件坐在的编译单位内使用,不能呢个在其他编译单位内使用。

在C++语言中新增了两种作用:定义静态数据成员或静态函数成员

(1) 定义静态数据成员。静态数据成员有如下特点:

(1) 内存分配:在程序的全局数据区分配

(2) 初始化和定义:静态数据成员定义时要分配空间,所以不能在类声明中定义

(3) 静态成员函数。静态成员函数与类相联系,不与类的对象相联系。静态成员函数不能访问非静态数据成员。原因很简单,非静态数据成员属于特定的类实例,主要用于对静态数据成员的操作。

(4) 静态成员函数和静态数据成员都没有this指针。

答案三:

1、全局静态变量

在全局变量前加上关键字static,全局变量就定义成一个全局静态变量。

静态存储区,在整个程序运行期间一直存在。

初始化:未经初始化的全局静态变量会被自动初始化为0(自动对象的值是任意的,除非他被显式初始化)。

作用域:全局静态变量在声明他的文件之外是不可见的,准确地说是从定义之处开始,到文件结尾。

2、局部静态变量

在局部变量之前加上关键字static,局部变量就成为一个局部静态变量。

内存中的位置:静态存储区。

初始化:未经初始化的全局静态变量会被自动初始化为0(自动对象的值是任意的,除非他被显式初始化)。

作用域:作用域仍为局部作用域,当定义它的函数或者语句块结束的时候,作用域结束。但是当局部静态变量离开作用域后,并没有销毁,而是仍然驻留在内存当中,只不过我们不能再对它进行访问,直到该函数再次被调用,并且值不变。

3.、静态函数

在函数返回类型前加static,函数就定义为静态函数。函数的定义和声明在默认情况下都是extern的,但静态函数只是在声明他的文件当中可见,不能被其他文件所用。

函数的实现使用static修饰,那么这个函数只可在本cpp内使用,不会同其他cpp中的同名函数引起冲突。

warning:不要再头文件中声明static的全局函数,不要在cpp内声明非static的全局函数,如果你要在多个cpp中复用该函数,就把它的声明提到头文件里去,否则cpp内部声明需加上static修饰。

4.、类的静态成员

在类中,静态成员可以实现多个对象之间的数据共享,并且使用静态数据成员还不会破坏隐藏的原则,即保证了安全性。因此,静态成员是类的所有对象中共享的成员,而不是某个对象的成员。对多个对象来说,静态数据成员只存储一处,供所有对象共用。

5.、类的静态函数

静态成员函数和静态数据成员一样,它们都属于类的静态成员,它们都不是对象成员。因此,对静态成员的引用不需要用对象名。

在静态成员函数的实现中不能直接引用类中说明的非静态成员,可以引用类中说明的静态成员(这点非常重要)。如果静态成员函数中要引用非静态成员时,可通过对象来引用。从中可看出,调用静态成员函数使用如下格式:<类名>::<静态成员函数名>(<参数表>);

52、引用与指针有什么区别?

指针有自己的一块空间,而引用只是一个别名;

使用sizeof看一个指针的大小是4,而引用则是被引用对象的大小;

指针可以被初始化为NULL,而引用必须被初始化且必须是一个已有对象 的引用;

作为参数传递时,指针需要被解引用才可以对对象进行操作,而直接对引 用的修改都会改变引用所指向的对象;

可以有const指针,但是没有const引用;

指针在使用中可以指向其它对象,但是引用只能是一个对象的引用,不能 被改变;

指针可以有多级指针(**p),而引用至于一级;

指针和引用使用++运算符的意义不一样;

如果返回动态内存分配的对象或者内存,必须使用指针,引用可能引起内存泄露。

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

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

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

应该说,实时操作系统是在早期的操作系统基础上发展起来的,早期的操作系统的各种资源都是事先已经分配好的,工作期间这些资源不能再重新进行分配。因此其处理事务的能力较差、速度较慢,现在则称之为“非实时操作系统”。但“非实时操作系统”诞生时,其功能、性能等在当时也是非常强的,人们在未认识到更好的操作系统之前并不将其这样称呼。将来如果新的、功能更强的、实时性能更高的操作系统出现,也许现在称之为“实时”的操作系统则可能将让位于新的“实时操作系统”了。从这方面讲“实时操作系统”是一个相对的概念的。

54、全局变量和局部变量在内存中是否有区别?如果有,是什么区别?

  • 生存周期不同
    全局变量:全局区(静态区)(static):全局变量和静态变量是存储在一起的,初始化过的全局变量和静态变量在同一块区域,未初始化的全局变量和静态变量存放在一块相邻的区域内。此区域由系统在程序结束后释放 局部变量: 放在堆栈中。由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈
  • 作用范围不同
    全局变量具有全局作用域。全局变量只需在一个源文件中定义,就可以作用于所有的源文件。当然,其他不包含全局变量定义的源文件需要用extern 关键字再次声明这个全局变量。 局部变量也只有局部作用域,它是自动对象(auto),它在程序运行期间不是一直存在,而是只在函数执行期间存在,函数的一次调用执行结束后,变量被撤销,其所占用的内存也被收回。

    静态变量分为 全局静态变量(常称为全局变量)和局部静态变量(static修饰的变量)

55、什么是平衡二叉树?

左右子树都是平衡二叉树 且左右子树的深度差值的绝对值不大于1。

56、堆栈溢出一般是由什么原因导致的?

1.没有回收垃圾资源

2.层次太深的递归调用

57、什么函数不能声明为虚函数?

什么样的函数不能声明为虚函数?
1)不能被继承的函数。
2)不能被重写的函数。
1.普通函数(不能被覆盖)
2.友元函数(C++不支持友元函数继承)
3.内联函数(编译期间展开,虚函数是在运行期间绑定)
4.构造函数(没有对象不能使用构造函数,先有构造函数后有虚函数,虚函数是对对象的动作)
5.静态成员函数(只有一份大家共享)
1)普通函数
普通函数不属于成员函数,是不能被继承的。普通函数只能被重载,不能被重写,因此声明为虚函数没有意义。因为编译器会在编译时绑定函数。
而多态体现在运行时绑定。通常通过基类指针指向子类对象实现多态。
2)友元函数
友元函数不属于类的成员函数,不能被继承。对于没有继承特性的函数没有虚函数的说法。
3)构造函数
首先说下什么是构造函数,构造函数是用来初始化对象的。假如子类可以继承基类构造函数,那么子类对象的构造将使用基类的构造函数,而基类构造函数并不知道子类的有什么成员,显然是不符合语义的。从另外一个角度来讲,多态是通过基类指针指向子类对象来实现多态的,在对象构造之前并没有对象产生,因此无法使用多态特性,这是矛盾的。因此构造函数不允许继承。
4)内联成员函数
我们需要知道内联函数就是为了在代码中直接展开,减少函数调用花费的代价。也就是说内联函数是在编译时展开的。而虚函数是为了实现多态,是在运行时绑定的。因此显然内联函数和多态的特性相违背。
5)静态成员函数
首先静态成员函数理论是可继承的。但是静态成员函数是编译时确定的,无法动态绑定,不支持多态,因此不能被重写,也就不能被声明为虚函数。

58、冒泡排序算法的时间复杂度是什么?

O(n^2)

59、Internet采用哪种网络协议?该协议的主要层次结构?

tcp/ip

应用层/传输层/网络层/数据链路层/物理层

60、Internet物理地址和IP地址转换采用什么协议?

ARP (Address Resolution Protocol)(地址解析协议)

61、IP地址的编码分为哪俩部分?

IP地址由两部分组成,网络号和主机号。不过是要和“子网掩码”按位与之后才能区分哪些是网络位哪些是主机位。

62、不能做switch()的参数类型是?

switch的参数不能为实型。

63、局部变量能否和全局变量重名?

能,局部会屏蔽全局。要用全局变量,需要使用"::"

局部变量可以与全局变量同名,在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量。对于有些编译器而言,在同一个函数内可以定义多个同名的局部变量,比如在两个循环体内都定义一个同名的局部变量,而那个局部变量的作用域就在那个循环体内

64、如何引用一个已经定义过的全局变量?

可以用引用头文件的方式,也可以用extern关键字,如果用引用头文件方式来引用某个在头文件中声明的全局变理,假定你将那个变写错了,那么在编译期间会报错,如果你用extern方式引用时,假定你犯了同样的错误,那么在编译期间不会报错,而在连接期间报错

65、全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么?

可以,在不同的C文件中以static形式来声明同名全局变量。

可以在不同的C文件中声明同名的全局变量,前提是其中只能有一个C文件中对此变量赋初值,此时连接不会出错

66、语句for( ;1 ;)有什么问题?它是什么意思?

死循环或者说无限循环。和while(1)相同。

67、do……while和while……do有什么区别?

前一个循环一遍再判断,后一个判断以后再循环

68、static 全局变量、局部变量、函数与普通全局变量、局部变量、函数区别?

  • static全局变量与普通的全局变量有什么区别?static局部变量和普通局部变量有什么区别?static函数与普通函数有什么区别?
    答 、全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用, 因此可以避免在其它源文件中引起错误。

    从以上分析可以看出, 把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域, 限制了它的使用范围。
  • static函数与普通函数作用域不同。仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件
  • static全局变量与普通的全局变量有什么区别:static全局变量只初使化一次,防止在其他文件单元中被引用;
  • static局部变量和普通局部变量有什么区别:static局部变量只被初始化一次,下一次依据上一次结果值;
  • static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝程序的局部变量存在于(堆栈)中,全局变量存在于(静态区 )中,动态申请数据存在于( 堆)中。

69、-1,2,7,28,126请问28和126中间那个数是什么?为什么?

应该是4^3-1=63

规律是n^3-1(当n为偶数0,2,4)

n^3+1(当n为奇数1,3,5)

70、用两个栈实现一个队列的功能?要求给出算法和思路!

设2个栈为A,B,一开始均为空.

入队:

将新元素push入栈A;

出队:

(1)判断栈B是否为空;

(2)如果不为空,则将栈A中所有元素依次pop出并push到栈B;

(3)将栈B的栈顶元素pop出;

这样实现的队列入队和出队的平摊复杂度都还是O(1), 比上面的几种方法要好。

71、在c语言库函数中将一个字符转换成整型的函数是atool()吗,这个函数的原型是什么?

函数名: atol

功 能: 把字符串转换成长整型数

用 法: long atol(const char *nptr);

72、对于一个频繁使用的短小函数,在C语言中应用什么实现,在C++中应用什么实现?

c用宏定义,c++用inline

73、用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)

#defineSECONDS_PER_YEAR (60 60 24 * 365)UL

我在这想看到几件事情:

1). #define 语法的基本知识(例如:不能以分号结束,括号的使用,等等)

2). 懂得预处理器将为你计算常数表达式的值,因此,直接写出你是如何计算一年中有多少秒而不是计算出实际的值,是更清晰而没有代价的。

3). 意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号L,告诉编译器这个常数是的长整型数。

4). 如果你在你的表达式中用到UL(表示无符号长整型),那么你有了一个好的起点。记住,第一印象很重要。

74、写一个“标准”宏MIN,这个宏输入两个参数并返回较小的一个。

#define MIN(A,B) ((A)<= (B) (A) : (B))

这个测试是为下面的目的而设的:

  • 标识#define在宏中应用的基本知识。这是很重要的,因为直到嵌入(inline)操作符变为标准C的一部分,宏是方便产生嵌入代码的唯一方法,
    对于嵌入式系统来说,为了能达到要求的性能,嵌入代码经常是必须的方法。
  • 三重条件操作符的知识。这个操作符存在C语言中的原因是它使得编译器能产生比if-then-else更优化的代码,了解这个用法是很重要的。
  • 懂得在宏中小心地把参数用括号括起来
  • 我也用这个问题开始讨论宏的副作用,例如:当你写下面的代码时会发生什么事?
    least = MIN(*p++, b);

75、预处理器标识#error的目的是什么?

编译程序时,只要遇到 #error 就会跳出一个编译错误,既然是编译错误,要它干嘛呢?其目的就是保证程序是按照你所设想的那样进行编译的。

76、嵌入式系统中经常要用到无限循环,你怎么样用C编写死循环呢?

这个问题用几个解决方案。我首选的方案是:

while(1){}一些程序员更喜欢如下方案:

for(;😉{}这个实现方式让我为难,因为这个语法没有确切表达到底怎么回事。如果一个应试者给出这个作为方案,我将用这个作为一个机会去探究他们这样做的

基本原理。如果他们的基本答案是:“我被教着这样做,但从没有想到过为什么。”这会给我留下一个坏印象。

第三个方案是用 goto

:…goto;应试者如给出上面的方案,这说明或者他是一个汇编语言程序员(这也许是好事)或者他是一个想进入新领域的BASIC/FORTRAN程序员。

77、关键字static的作用是什么?

这个简单的问题很少有人能回答完全。在C语言中,关键字static有三个明显的作用:

在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。

在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。

在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。

78、关键字const是什么含意?

1)使用Const关键字的地方是为了说明这个参数为常量,是不应该被修改的。

2)合理使用const 可以是编译器自然的保护那些不希望被修改的参数,防止被无意的代码修改。

3)通过给优化器一些有用的信息,使用关键字const也许是代码更加紧凑。

下面的声明都是什么意思?

1)const int a;

2)int const a;

3)const int *a;

4)int * const a;

5)int const * a const;

1) a是一个常整数;
2) a是一个常整数;
3) a是一个指向常整形的指针
4) a是一个指向整型的常指针;
5) a是一个指向常整型的常指针;

79、关键字volatile有什么含意 并给出三个不同的例子。

一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:

1). 并行设备的硬件寄存器(如:状态寄存器)

2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)

3). 多线程应用中被几个任务共享的变量

80、ASDL使用的是什么协议?并进行简单描述?

全称Point to Point Protocol over Ethernet,意思是基于以太网的点对点协议。实质是以太网和拨号网络之间的一个中继协议,所以在网络中,它的物理结构与原来的LAN接入方式没有任何变化,只是用户需要在保持原接入方式的基础上,安装一个PPPoE客户端(这个是通用的)。之所以采用该方式给小区计时/计流量用户,是方便计算时长和流量。此类用户在使用上比包月用户增加了PPPoE虚拟拨号的过程。电信的ADSL接入也是需要安装使用PPPoE。

81、什么是预编译,何时需要预编译?

预编译又称为预处理,是做些代码文本的替换工作。处理#开头的指令,比如拷贝#include包含的文件代码,#define宏定义的替换,条件编译等,就是为编译做的预备工作的阶段,主要处理#开始的预编译指令,预编译指令指示了在程序正式编译前就由编译器进行的操作,可以放在程序中的任何位置。

c编译系统在对程序进行通常的编译之前,先进行预处理。c提供的预处理功能主要有以下三种:1)宏定义 2)文件包含 3)条件编译

1、总是使用不经常改动的大型代码体。

2、程序由多个模块组成,所有模块都使用一组标准的包含文件和相同的编译选项。在这种情况下,可以将所有包含文件预编译为一个预编译头。

82、Itearator各指针的区别

游标和指针

我说过游标是指针,但不仅仅是指针。游标和指针很像,功能很像指针,但是实际上,游标是通过重载一元的”*”和”->”来从容器中间接地返回一个值。将这些值存储在容器中并不是一个好主意,因为每当一个新值添加到容器中或者有一个值从容器中删除,这些值就会失效。在某种程度上,游标可以看作是句柄(handle)。通常情况下游标(iterator)的类型可以有所变化,这样容器也会有几种不同方式的转变:

iterator——对于除了vector以外的其他任何容器,你可以通过这种游标在一次操作中在容器中朝向前的方向走一步。这意味着对于这种游标你只能使用“”操作符。而不能使用“–”或“+=”操作符。而对于vector这一种容器,你可以使用“+=”、“—”、“”、“-=”中的任何一种操作符和“<”、“<=”、“>”、“>=”、“==”、“!=”等比较运算符。

83、C++中的class和struct的区别

从语法上,在C中(只讨论C中)。class和struct做类型定义时只有两点区别:

(一)默认继承权限。如果不明确指定,来自class的继承按照private继承处理,来自struct的继承按照public继承处理;

(二)成员的默认访问权限。class的成员默认是private权限,struct默认是public权限。

除了这两点,class和struct基本就是一个东西。语法上没有任何其它区别。

不能因为学过C就总觉得连C中struct和class都区别很大,下面列举的说明可能比较无聊,因为struct和class本来就是基本一样的东西,无需多说。但这些说明可能有助于澄清一些常见的关于struct和class的错误认识:
(1)都可以有成员函数;包括各类构造函数,析构函数,重载的运算符,友元类,友元结构,友元函数,虚函数,纯虚函数,静态函数;
(2)都可以有一大堆public/private/protected修饰符在里边;
(3)虽然这种风格不再被提倡,但语法上二者都可以使用大括号的方式初始化:
A a = {1, 2, 3};不管A是个struct还是个class,前提是这个类/结构足够简单,比如所有的成员都是public的,所有的成员都是简单类型,没有显式声明的构造函数。
(4)都可以进行复杂的继承甚至多重继承,一个struct可以继承自一个class,反之亦可;一个struct可以同时继承5个class和5个struct,虽然这样做不太好。
(5)如果说class的设计需要注意OO的原则和风格,那么没任何理由说设计struct就不需要注意。
(6)再次说明,以上所有说法都是指在C语言中,至于在C里的情况,C里是根本没有“class”,而C的struct从根本上也只是个包装数据的语法机制。

最后,作为语言的两个关键字,除去定义类型时有上述区别之外,另外还有一点点:“class”这个关键字还用于定义模板参数,就像“typename”。但关键字“struct”不用于定义模板参数。

  • 关于使用大括号初始化
    class和struct如果定义了构造函数的话,都不能用大括号进行初始化

    如果没有定义构造函数,struct可以用大括号初始化。

    如果没有定义构造函数,且所有成员变量全是public的话,可以用大括号初始化。
  • 关于默认访问权限
    class中默认的成员访问权限是private的,而struct中则是public的。
  • 关于继承方式
    class继承默认是private继承,而struct继承默认是public继承。
  • 关于模版
    在模版中,类型参数前面可以使用class或typename,如果使用struct,则含义不同,struct后面跟的是“non-type template parameter”,而class或typename后面跟的是类型参数。
  • class中有个默认的this指针,struct没有
    不同点:构造函数,析构函数 this 指针

84、关系模型的基本概念

关系数据库以关系模型为基础,它有以下三部分组成:

●数据结构——模型所操作的对象、类型的集合

●完整性规则——保证数据有效、正确的约束条件

●数据操作——对模型对象所允许执行的操作方式

关系(Relation)是一个由行和列组成的二维表格,表中的每一行是一条记录(Record),每一列是记录的一个字段(Field)。表中的每一条记录必须是互斥的,字段的值必须具有原子性。

85、C语言中结构化程序设计的三种基本控制结构

顺序结构

选择结构

循环结构

86、三种基本的数据模型

层次模型、网状模型和关系模型。

87、设计模式:工厂模式 和 单例模式 介绍一下?

工程模式即将对象创建过程封装即为工厂模式。

单例模式即整个类只有一个对象,并且不允许显示创建。

88、const的作用有哪些,谈一谈你对const的理解?

89、描述char*、const char*、char* const、const char* const的区别?

90、指针常量和常量指针有什么区别?

91、static的作用是什么,什么情况下用到static?

92、全局变量与局部变量的区别?

93、宏定义的作用是什么?

94、内存对齐的概念?为什么会有内存对齐?

95、inline 内联函数的特点有哪些?它的优缺点是什么?

96、如何避免野指针?

97、如何计算结构体长度?

98、sizeof和strlen有什么区别?

99、知道条件变量吗?条件变量为什么要和锁配合使用?

100、如何用C 实现 C++ 的面向对象特性(封装、继承、多态)

101、memcpy怎么实现让它效率更高?

102、typedef和define有什么区别?

103、extern有什么作用,extern C有什么作用?

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值