c++ 不会做题目,题目如下?

一、局部变量可否和全局变量重名?

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

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

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

  能够用引用头文件的方式,也能够用extern关键字,若是用引用头文件方式来引用某个在头文件中声明的全局变理,假定你将那个变写错了,那么在编译期间会报错,若是你用extern方式引用时,假定你犯了一样的错误,那么在编译期间不会报错,而在链接期间报错。

  三、全局变量可不能够定义在可被多个.C文件包含的头文件中?为何?

  答:能够,在不一样的C文件中以static形式来声明同名全局变量。

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

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

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

  答:前一个循环一遍再判断,后一个判断之后再循环

  六、请写出下列代码的输出内容

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

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

答案:不是。两个不一样类型的指针之间能够强制转换(用reinterpret cast)。C#是类型安全的。

25. main 函数执行之前,还会执行什么代码?答案:全局对象的构造函数会在main 函数以前执行。

26. 描述内存分配方式以及它们的区别?
1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量
2) 在栈上建立。在执行函数时,函数内局部变量的存储单元均可以在栈上建立,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集。
3) 从堆上分配亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员本身负责在什么时候用free 或delete 释放内存。动态内存的生存期由程序员决定,使用很是灵活,但问题也最多。

答案:struct 的成员默认是公有的,而类的成员默认是私有的。struct 和 class 在其余方面是功能至关的。

从感情上讲,大多数的开发者感到类和结构有很大的差异。感受上结构仅仅象一堆缺少封装和功能的开放的内存位,而类就象活的而且可靠的社会成员,它有智能服务,有牢固的封装屏障和一个良好定义的接口。既然大多数人都这么认为,那么只有在你的类有不多的方法而且有公有数据(这种事情在良好设计的系统中是存在的!)时,你也许应该使用 struct 关键字,不然,你应该使用 class

28.当一个类A 中没有生命任何成员变量与成员函数,这时sizeof(A)的值是多少,若是不是零,请解释一下编译器为何没有让它为零。(Autodesk)答案:确定不是零。举个反例,若是是零的话,声明一个class A[10]对象数组,而每个对象占用的空间是零,这时就没办法区分A[0],A[1]…了。

29. 在8086 汇编下,逻辑地址和物理地址是怎样转换的?(Intel)
答案:通用寄存器给出的地址,是段内偏移地址,相应段寄存器地址*10H+通用寄存器内地址,就获得了真正要访问的地址。

常量有数据类型,而宏常量没有数据类型。编译器能够对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,而且在字符替换可能会产生意料不到的错误。

34.类成员函数的重载、覆盖和隐藏区别?答案:
a.成员函数被重载的特征:
(1)相同的范围(在同一个类中);
(4)virtual 关键字无关紧要。
b.覆盖是指派生类函数覆盖基类函数,特征是:
(1)不一样的范围(分别位于派生类与基类);
(4)基类函数必须有virtual 关键字。
c.“
隐藏
”是指派生类的函数屏蔽了与其同名的基类函数,规则以下:
(1)若是派生类的函数与基类的函数同名,可是参数不一样。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。
(2)若是派生类的函数与基类的函数同名,而且参数也相同,可是基类函数没有virtual 关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)

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




(1)已知链表的头结点head,写一个函数把这个链表逆序 ( Intel)


43. 写一个在一个字符串(n)中寻找一个子串(m)第一个位置的函数。

KMP算法效率最好,时间复杂度是O(n+m)。

若是不考虑有虚函数、虚继承的话就至关简单;不然的话,至关复杂。
能够参考《深刻探索C++对象模型》,或者:

分享给大家的算是比较全面的C++面试题,也都是版本升级之后,重新整理归纳的最新面试题,会让C++面试者少走很多不必要的弯路,同时每个C++面试题都尽量做到了详尽的面试解析文档,以确保每个阶段的读者都能看得懂!

哔哩哔哩、阿里、百度、华为、美团、腾讯、头条C++面试题可以关注微信公众号“C和C加加”回复“面试题”即可获取相关C++面试题!

C中,内存分为5个区:堆(malloc)、栈(如局部变量、函数参数)、程序代码区(存放二进制代码)、全局/静态存储区(全局变量、static变量)和常量存储区(常量)。此外,C++中有自由存储区(new)一说。
全局变量、static变量会初始化为缺省值,而堆和栈上的变量是随机的,不确定的。

1).堆存放动态分配的对象——即那些在程序运行时动态分配的对象,比如 new 出来的对象,其生存期由程序控制;
2).栈用来保存定义在函数内的非static对象,如局部变量,仅在其定义的程序块运行时才存在;
3).静态内存用来保存static对象,类static数据成员以及定义在任何函数外部的变量,static对象在使用之前分配,程序结束时销毁;
4).栈和静态内存的对象由编译器自动创建和销毁。

总的来说,堆是C语言和操作系统的术语,是操作系统维护的一块动态分配内存;自由存储是C++中通过new与delete动态分配和释放对象的抽象概念。他们并不是完全一样。
从技术上来说,堆(heap)是C语言和操作系统的术语。堆是操作系统所维护的一块特殊内存,它提供了动态分配的功能,当运行程序调用malloc()时就会从中分配,稍后调用free可把内存交还。而自由存储是C++中通过new和delete动态分配和释放对象的抽象概念,通过new来申请的内存区域可称为自由存储区。基本上,所有的C++编译器默认使用堆来实现自由存储,也即是缺省的全局运算符new和delete也许会按照malloc和free的方式来被实现,这时藉由new运算符分配的对象,说它在堆上也对,说它在自由存储区上也正确。

程序编译的过程中就是将用户的文本形式的源代码(c/c++)转化成计算机可以直接执行的机器代码的过程。主要经过四个过程:预处理、编译、汇编和链接。具体示例如下。
一个hello.c的c语言程序如下

负数比较容易,就是通过一个标志位和补码来表示。
对于浮点类型的数据采用单精度类型(float)和双精度类型(double)来存储,float数据占用32bit,double数据占用64bit,我们在声明一个变量float f= 2.25f的时候,是如何分配内存的呢?如果胡乱分配,那世界岂不是乱套了么,其实不论是float还是double在存储方式上都是遵从IEEE的规范的,float遵从的是IEEE R32.24 ,而double 遵从的是R64.53。更多可以参考浮点数表示。
无论是单精度还是双精度在存储中都分为三个部分:

2). 指数位(Exponent):用于存储科学计数法中的指数数据,并且采用移位存储
其中float的存储方式如下图所示:

而双精度的存储方式如下图:

调用fun()的过程大致如下:

不是很严谨的来说,左值指的是既能够出现在等号左边也能出现在等号右边的变量(或表达式),右值指的则是只能出现在等号右边的变量(或表达式)。举例来说我们定义的变量 a 就是一个左值,而malloc返回的就是一个右值。或者左值就是在程序中能够寻值的东西,右值就是一个具体的真实的值或者对象,没法取到它的地址的东西(不完全准确),因此没法对右值进行赋值,但是右值并非是不可修改的,比如自己定义的class, 可以通过它的成员函数来修改右值。

可以取地址的,有名字的,非临时的就是左值
不能取地址的,没有名字的,临时的,通常生命周期就在某个表达式之内的就是右值

用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元即为内存泄露。

1). 使用的时候要记得指针的长度.
3). 对指针赋值的时候应该注意被赋值指针需要不需要释放.
4). 动态分配内存的指针最好不要再次赋值.
5). 在C++中应该优先考虑使用智能指针.

2). C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现过程(事务)控制),而对于C++,首要考虑的是如何构造一个对象模型,让这个模型能够契合与之对应的问题域,这样就可以通过获取对象的状态信息得到输出或实现过程(事务)控制。

这里考察的是c 中的默认类型机制。

在c中,int fun() 会解读为返回值为int(即使前面没有int,也是如此,但是在c++中如果没有返回类型将报错),输入类型和个数没有限制, 而int fun(void)则限制输入类型为一个void。
在c++下,这两种情况都会解读为返回int类型,输入void类型。

1).定义只读变量,或者常量(只读变量和常量的区别参考下面一条);
2).修饰函数的参数和函数的返回值;
3).修饰函数的定义体,这里的函数为类的成员函数,被const修饰的成员函数代表不能修改成员变量的值,因此const成员函数只能调用const成员函数;
4).只读对象。只读对象只能调用const成员函数。

不能。c中的const仅仅是从编译层来限定,不允许对const 变量进行赋值操作,在运行期是无效的,所以并非是真正的常量(比如通过指针对const变量是可以修改值的),但是c++中是有区别的,c++在编译时会把const常量加入符号表,以后(仍然在编译期)遇到这个变量会从符号表中查找,所以在C++中是不可能修改到const变量的。

1)c中的局部const常量存储在栈空间,全局const常量存在只读存储区,所以全局const常量也是无法修改的,它是一个只读变量。
2)这里需要说明的是,常量并非仅仅是不可修改,而是相对于变量,它的值在编译期已经决定,而不是在运行时决定。
3)c++中的const 和宏定义是有区别的,宏是在预编译期直接进行文本替换,而const发生在编译期,是可以进行类型检查和作用域检查的。
4)c语言中只有enum可以实现真正的常量。
5)c++中只有用字面量初始化的const常量会被加入符号表,而变量初始化的const常量依然只是只读变量。
6)c++中const成员为只读变量,可以通过指针修改const成员的值,另外const成员变量只能在初始化列表中进行初始化。

下面我们通过代码来看看区别。
同样一段代码,在c编译器下,打印结果为*pa = 4, 4

另外值得一说的是,由于c++中const常量的值在编译期就已经决定,下面的做法是OK的,但是c中是编译通不过的。

1)首先宏是C中引入的一种预处理功能;
2)内联(inline)函数是C++中引用的一个新的关键字;C++中推荐使用内联函数来替代宏代码片段;
3)内联函数将函数体直接扩展到调用内联函数的地方,这样减少了参数压栈,跳转,返回等过程;
4)  由于内联发生在编译阶段,所以内联相较宏,是有参数检查和返回值检查的,因此使用起来更为安全;
5)  需要注意的是, inline会向编译期提出内联请求,但是是否内联由编译期决定(当然可以通过设置编译器,强制使用内联);
6)  由于内联是一种优化方式,在某些情况下,即使没有显示的声明内联,比如定义在class内部的方法,编译器也可能将其作为内联函数。
7)  内联函数不能过于复杂,最初C++限定不能有任何形式的循环,不能有过多的条件判断,不能对函数进行取地址操作等,但是现在的编译器几乎没有什么限制,基本都可以实现内联。

1). malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。
2). 对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。
由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。
最后补充一点体外话,new 在申请内存的时候就可以初始化(如下代码), 而malloc是不允许的。另外,由于malloc是库函数,需要相应的库支持,因此某些简易的平台可能不支持,但是new就没有这个问题了,因为new是C++语言所自带的运算符。

特别的,在C++中,如下的代码,用new创建一个对象(new 会触发构造函数, delete会触发析构函数),但是malloc仅仅申请了一个空间,所以在C++中引入new和delete来支持面向对象。

C中是直接在变量或者表达式前面加上(小括号括起来的)目标类型来进行转换,一招走天下,操作简单,但是由于太过直接,缺少检查,因此容易发生编译检查不到错误,而人工检查又及其难以发现的情况;而C++中引入了下面四种转换:

  • 不能用于基本类型指针间的转换

  • 用于有继承关系类对象间的转换和类指针间的转换

  • 用于有继承关系的类指针间的转换

  • 用于有交叉关系的类指针间的转换

  • 用于整数和指针间的类型转换

  • 用于去掉变量的const属性

  • 转换的目标类型必须是指针或者引用

在C++中,普通类型可以通过类型转换构造函数转换为类类型,那么类可以转换为普通类型吗?答案是肯定的。但是在工程应用中一般不用类型转换函数,因为无法抑制隐式的调用类型转换函数(类型转换构造函数可以通过explicit来抑制其被隐式的调用),而隐式调用经常是bug的来源。实际工程中替代的方式是定义一个普通函数,通过显式的调用来达到类型转换的目的。

  • 静态(局部/全局)变量

  • 静态成员变量需要在类内声明(加static),在类外初始化(不能加static),如下例所示;

  • 静态成员变量在类外单独分配存储空间,位于全局数据区,因此静态成员变量的生命周期不依赖于类的某个对象,而是所有类的对象共享静态成员变量;

  • 可以通过对象名直接访问公有静态成员变量;

  • 可以通过类名直接调用公有静态成员变量,即不需要通过对象,这一点是普通成员变量所不具备的。

  •  静态成员函数是类所共享的;

  • 静态成员函数可以访问静态成员变量,但是不能直接访问普通成员变量(需要通过对象来访问);需要注意的是普通成员函数既可以访问普通成员变量,也可以访问静态成员变量;

  • 可以通过对象名直接访问公有静态成员函数;

  • 可以通过类名直接调用公有静态成员函数,即不需要通过对象,这一点是普通成员函数所不具备的。

C++语言支持函数重载,C语言不支持函数重载,函数被C++编译器编译后在库中的名字与C语言的不同,假设某个函数原型为:

该函数被C编译器编译后在库中的名字为 _foo, 而C++编译器则会产生像: _foo_int_int 之类的名字。为了解决此类名字匹配的问题,C++提供了C链接交换指定符号 extern “C”。

它们的作用是防止头文件被重复包含。

  • ifndef 由语言本身提供支持,但是 program once 一般由编译器提供支持,也就是说,有可能出现编译器不支持的情况(主要是比较老的编译器)。

  • 通常运行速度上 ifndef 一般慢于 program once,特别是在大型项目上, 区别会比较明显,所以越来越多的编译器开始支持 program once。

  • 如果用 ifndef 包含某一段宏定义,当这个宏名字出现“撞车”时,可能会出现这个宏在程序中提示宏未定义的情况(在编写大型程序时特性需要注意,因为有很多程序员在同时写代码)。相反由于program once 针对整个文件, 因此它不存在宏名字“撞车”的情况, 但是如果某个头文件被多次拷贝,program once 无法保证不被多次包含,因为program once 是从物理上判断是不是同一个头文件,而不是从内容上。

答:理论上++i更快,实际与编译器优化有关,通常几乎无差别。

i++和++i的考点比较多,简单来说,就是i++返回的是i的值,而++i返回的是i+1的值。也就是++i是一个确定的值,是一个可修改的左值,如下使用:

可以不停的嵌套++i。
这里有很多的经典笔试题,一起来观摩下:

首先是函数的参数入栈顺序从右向左入栈的,计算顺序也是从右往左计算的,不过都是计算完以后再进行的压栈操作:
对于第1个printf,首先执行++i,返回值是i,这时i的值是2,再次执行++i,返回值是i,得到i=3,将i压入栈中,此时i为3,也就是压入3,3;
对于第2个printf,首先执行i++,返回值是原来的i,也就是3,再执行++i,返回值是i,依次将3,5压入栈中得到输出结果
对于第3个printf,首先执行i++,返回值是5,再执行i++返回值是6,依次将5,6压入栈中得到输出结果
对于第4个printf,首先执行++i,返回i,此时i为8,再执行i++,返回值是8,此时i为9,依次将i,8也就是9,8压入栈中,得到输出结果。

数组指针,是指向数组的指针,而指针数组则是指该数组的元素均为指针。

数组指针,是指向数组的指针,其本质为指针,形式如下。如 int (*p)[10],p即为指向数组的指针,()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。数组指针是指向数组首元素的地址的指针,其本质为指针,可以看成是二级指针。

类型名 (*数组标识符)[数组长度]

指针数组,在C语言和C++中,数组元素全为指针的数组称为指针数组,其中一维指针数组的定义形式如下。指针数组中每一个元素均为指针,其本质为数组。如 int *p[n], []优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素。这里执行p+1时,则p指向下一个数组元素,这样赋值是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]…p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 *p=a; 这里*p表示指针数组第一个元素的值,a的首地址的值。

类型名 *数组标识符[数组长度]

左值引用就是我们通常所说的引用,如下所示。左值引用通常可以看作是变量的别名。

右值引用是 C++11 新增的特性,其形式如下所示。右值引用用来绑定到右值,绑定到右值以后本来会被销毁的右值的生存期会延长至与绑定到它的右值引用的生存期。

在汇编层面右值引用做的事情和常引用是相同的,即产生临时量来存储常量。但是,唯一 一点的区别是,右值引用可以进行读写操作,而常引用只能进行读操作。

  • 右值引用支持移动语义的实现,可以减少拷贝,提升程序的执行效率。

    下面的代码时没有采用右值引用时的实现。

执行代码的过程中调用拷贝构造,将内存中的内容逐个拷贝,在 C++ 11 中可以借助右值引用实现移动拷贝构造和移动赋值来解决这个问题。

执行结果如下。可以看到,在有拷贝构造和移动拷贝构造函数的时候,优先调用了移动拷贝构造和移动赋值。在移动拷贝构造和移动赋值中直接把资源所有权进行了转移,而非拷贝,这就大大提高了执行效率。

右值引用在可以使重载函数变得更加简洁。右值引用可以适用 const T& 和 T& 形式的参数。

  • 没有任何构造函数时,编译器会自动生成默认构造函数,也就是无参构造函数;当类没有拷贝构造函数时,会生成默认拷贝构造函数

  • 深拷贝是指拷贝后对象的逻辑状态相同,而浅拷贝是指拷贝后对象的物理状态相同;默认拷贝构造函数属于浅拷贝。

  • 当系统中有成员指代了系统中的资源时,需要深拷贝。比如指向了动态内存空间,打开了外存中的文件或者使用了系统中的网络接口等。如果不进行深拷贝,比如动态内存空间,可能会出现多次被释放的问题。是否需要定义拷贝构造函数的原则是,是类是否有成员调用了系统资源,如果定义拷贝构造函数,一定是定义深拷贝,否则没有意义。
    更多可以参考下面的代码,比较容易混淆的是赋值操作符,其实区分很简单,在出现等号的时候,如果有构造新的对象时调用的就是构造,不然就是赋值操作符。

基类采用虚析构函数可以防止内存泄漏。比如下面的代码中,如果基类 A 中不是虚析构函数,则 B 的析构函数不会被调用,因此会造成内存泄漏。

但并不是要把所有类的析构函数都写成虚函数。因为当类里面有虚函数的时候,编译器会给类添加一个虚函数表,里面来存放虚函数指针,这样就会增加类的存储空间。所以,只有当一个类被用来作为基类的时候,才把析构函数写成虚函数。

封装是实现面向对象程序设计的第一步,封装就是将数据或函数等集合在一个个的单元中(我们称之为类)

封装的意义在于保护或者防止代码(数据)被我们无意中破坏。

不管那种属性,内类都是可以访问的

public 是一种暴露的手段,比如暴露接口,类的对象可以访问

private 是一种隐藏的手段,类的对象不能访问

和 public 一样可以被子类继承

和 private 一样不能在类外被直接调用

特例:在衍生类中可以通过衍生类对象访问,如下代码所示

继承主要实现重用代码,节省开发时间。

子类可以继承父类的一些东西。

a.**公有继承(public)**公有继承的特点是基类的公有成员和保护成员作为派生类的成员时,它们都保持原有的状态(基类的私有成员仍然是私有的,不能被这个派生类的子类所访问)。

b.**私有继承(private)**私有继承的特点是基类的公有成员和保护成员都作为派生类的私有成员(并且不能被这个派生类的子类所访问)。

c.**保护继承(protected)**保护继承的特点是基类的所有公有成员和保护成员都成为派生类的保护成员(并且只能被它的派生类成员函数或友元访问,基类的私有成员仍然是私有的)。

这里特别提一下虚继承。虚继承是解决C++多重继承问题(其一,浪费存储空间;第二,存在二义性问题)的一种手段。比如菱形继承,典型的应用就是 iostream, 其继承于 istream 和 ostream,而 istream 和 ostream 又继承于 ios。

多态是指通过基类的指针或者引用,在运行时动态调用实际绑定对象函数的行为。与之相对应的编译时绑定函数称为静态绑定。多态是设计模式的基础,多态是框架的基础。

解答:三目运算符为左结合,w

运算符中,只有单目运算符,三目运算符,和赋值运算符为左结合,其余都是右结合。

7、下列哪种方法不能用于文本加密()

解答:RSA:由 RSA 公司发明,是一个支持变长密钥的公共密钥算法,需要加密的文件块的长度也是可变的,非对称算法; 

RC2和RC4:对称算法,用变长密钥对大量数据进行加密,比 DES 快;

MD5:严格来说不算加密算法,只能说是摘要算法。

用表达式:return x & -2; 替代,以下说法中不正确的是( )。

计算机的补码表示使得两段代码等价

用第二段代码执行起来会更快一些

这段代码只适用于x为正数的情况

解答:代码的本意是返回偶数。计算机内部表示的整数的方式为补码,正数为自身,负数为符号位不变其余各位取反加1。理解本体分两部分:

  • -2的补码为1111,1110,正数与-2相与很明显就是使整数变为2的整数倍(相当于减一);
  • 负数时,当负数为偶数时,补码的最后一位比为0,与-2相与仍为自身;

当为奇数时,补码的最后一位比为1,与-2相与时其它位不变最后一位为1,此时根据负数到源码的转换规则,各位取反加1,此时相当于加上-1。

9、定义函数模板add,以下说法正确的有:()

B:pfun1的参数和返回值一致,可以自动推导,正确赋值;

C:pfun3 = add赋值失败,失败原因和A一样;

判断下列说法哪个是正确的(注意是地址):______。

解答:str1和str2毫无干系,他们都是字符常量数组,恰巧存储的值相同而已。

后面的两个“abc”存储在常量区,因为他们是常量,但是p1 p2存储在栈中。所以p1和p2的值不同,但是他们指向同一块静态存储区域,也就是"abc"的地址。

解答:(详情参看 《C++ Primer》(第5版) P310 ”下标操作和安全的随机访问“):

下标运算符接受一个下标参数,返回容器中该位置的元素的引用。给定下标必须保证”在范围内“(即,大于等于0,且小于容器的大小)。保证下标有效是程序员的责任,下标运算符并不检查下标是否在合法范围内。使用越界的下标是一种严重的程序设计错误,而且编译器并不检查这种错误。

也就是说:c[n]:返回c中下标为n的元素的引用,n是一个无符号整数。若n>=c.size(),则函数行为未定义。

如果希望确保下标是合法的,可以使用at成员函数。at成员函数类似下标运算符,但如果下标越界,at会抛出一个out_of_range异常。

也就是说:c.at(n):返回下标为n的元素的引用。如果下标越界,则抛出一个out_of_range异常。

12、以下对枚举类型名的定义中正确的是()。

解答:枚举变量定义的方式和结构体、共用体类似:

枚举值[=整型常量], 枚举值[=整型常量], 枚举值[=整型常量],

13、通过一个对象调用虚函数时,C++系统对该调用采用(  )。

解答:静态联编:通过对象名调用虚函数,在编译阶段就能确定调用的是哪一个类的虚函数;

动态联编:通过基类指针调用,在编译阶段无法通过语句本身来确定调用哪一个类的虚函数,只有在运行时指向一个对象后,才能确定调用时哪个类的虚函数。

解答:一个二维数组相当于一个数组指针。而fun函数是一个指针数组,归根结底就是一个数组,数组元素为指针。所以合法参数就需要是数组首元素的地址就行了,而首元素是一个指针,所以一个二级指针就行了。

也可以通过+1来确定。int a[4][4]和int (*a)[4]表示的数组指针,+1的话表示越过一行,间隔int[4];int **a表示的是指向指针的指针,+1的话表示越过一个指针,间隔int*;而题目的int *p[4]表示指针数组,+1的话表示越过一个指针,间隔int*。很明显,应该选择B。

解答:在存储信息时,不必用一个或多个字节,而只需要占几个二进制位就可以了。例如在存放一个开关量时,只有开和关两个状态,用一个二进制位即可。为了节省存储空间,并使处理简便,C语言允许在一个结构体中以位为单位来指定某成员所占内存长度,这种以位为单位的成员,称为“位段”或“位域”。

位段定义的一般形式为:

类型说明符 位段名1:位段长度1; 类型说明符 位段名2:位段长度2; 类型说明符 位段名3:位段长度3; 类型说明符 位段名4:位段长度4;

位段中数据引用的一般形式为:

  • 位段成员的类型只能是unsigned 或int类型;
  • 一个位段必须存储在同一个存储单元中,不能跨两个单元;
  • 位段的长度不能大于存储单元的长度,也不能定义位段数组;
  • 位段可以用整型格式符输出,也可以用%u、%o、%x等格式符输出;
  • 位段可以在数值表达式中引用,它会被系统自动转换成为整型数。

16、下列关于makefile描述正确的有?

makefile文件保存了编译器和连接器的参数选项

主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释

默认的情况下,make命令会在当前目录下按顺序找寻文件名为“GNUmakefile”、“makefile”、“Makefile”的文件, 找到了解释这个文件

解答:makefile文件保存了编译器和连接器的参数选项,还表述了所有源文件之间的关系(源代码文件需要的特定的包含文件,可执行文件要求包含的目标文件模块及库等)。

创建程序(make程序),首先读取makefile文件,然后再激活编译器,汇编器,资源编译器和连接器以便产生最后的输出,最后输出并生成的通常是可执行文件。创建程序利用内置的推理规则来激活编译器,以便通过对特定CPP文件的编译来产生特定的OBJ文件。

Makefile里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。 

  • 显式规则:显式规则说明了,如何生成一个或多的的目标文件。这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令;
  • 隐晦规则:由于make的自动推导功能,所以隐晦的规则可以让我们比较粗糙地简略地书写Makefile,这是由make所支持的;
  • 变量定义:在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点你C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上;
  • 文件指示:其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中的include一样;另一个是指根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一样;还有就是定义一个多行的命令
  • 注释:Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用“#”字符,这个就像C/C++中的“//”一样。如果你要在你的Makefile中使用“#”字符,可以用反斜框进行转义,如:“\#”。 

默认的情况下,make命令会在当前目录下按顺序找寻文件名为“GNUmakefile”、“makefile”、“Makefile”的文件,找到了解释这个文件。在这三个文件名中,最好使用“Makefile”这个文件名,因为,这个文件名第一个字符为大写,这样有一种显目的感觉。最好不要用 “GNUmakefile”,这个文件是GNU的make识别的。有另外一些make只对全小写的“makefile”文件名敏感,但是基本上来说,大多数的make都支持“makefile”和“Makefile”这两种默认文件名。

在Makefile使用include关键字可以把别的Makefile包含进来,这很像C语言的#include,被包含的文件会原模原样的放在当前文件的包含位置。

  • 推导隐晦规则,并分析所有规则;
  • 为所有的目标文件创建依赖关系链;
  • 根据依赖关系,决定哪些目标要重新生成;

参考文章:Makefile 使用总结、教你写Makefile(很全,含有工作经验的)。

解答:x86是小端存储,即高位存储在高地址,低位存储在低地址。int a = 0xabcd1234;,

18、如下代码,result变量的输出结果是多少?

i初始值为1,所以经过初始化列表初始化以后m_nFir=1,m_nSec=2,m_nThd=3,m_nFor为m_nThd的一个引用。并且此时i的值为4。对象成员的初始化结束后,开始执行本类的构造函数。构造函数中执行语句m_nThd=i后,m_nThd=4,m_nFor是它的一个引用,自然值也为4。

19、词法分析器用于识别()

解答:编译的过程为:扫描(词法分析)、语法分析、语义分析、源代码优化、代码生成和目标代码优化。

20、设顺序表的长度为n。下列算法中,最坏情况下比较次数小于n的是()。

解答:顺序表不是、不是、不是元素排列有序的线性表,而是指元素以顺序存储结构存储在相邻的物理存储单元上,元素之间没有直接大小关联。

2个数比较大小 比较1次即可找出最大,3个数比较两次即可找出最大。同理n个数最多比较n-1次即可找出最大。

21、有以下说明语句:

则下列错误的引用是( )。

解答:.的优先级高于*。

22、能够把指定长度的字节序列插入到输出流中的函数是()。

解答:write函数所在的头文件为 。write有两种用法:

其中:handle 是文件描述符;buf是指定的缓冲区,即指针,指向一段内存单元;nbyte是要写入文件指定的字节数;返回值:写入文档的字节数(成功);-1(出错)。str是字符指针或字符数组,用来存放一个字符串;n是int型数,它用来表示输出显示字符串中字符的个数。

23、下列关于赋值运算符“=”重载的叙述中,正确的是

赋值运算符只能作为类的成员函数重载

默认的赋值运算符实现了“深层复制”功能

重载的赋值运算符函数有两个本类对象作为形参

如果己经定义了复制拷贝构造函数,就不能重载赋值运算符

解答:默认的赋值运算符、拷贝构造函数都是实现了“浅层复制”功能。

  • 浅层复制:将新建立的对象的指针指向被复制的对象指向的内存单元,即两个对象指向同一块内存单元;
  • 深层复制:两个对象的成员变量都各自拥有自己的内存区域。
x = new int; //开辟新的空间,复制构造对象的同时将成员指针变量保存在新空间中 *x = *(a.x); //读取旧对象的成员指针变量x地址处的数据并赋值给新对象的成员指针变量x所指向的内存区域

参考文章:C++基础---浅层及深层拷贝构造函数。

24、类 CBase 的定义如下: 在构造函数 CDerive 的下列定义中,正确的是()

解答:三个变量分别用于基类构造函数初始化(使用基类)、对象成员构造函数初始化(使用对象成员名称)、派生类普通成员初始化(使用对象成员名称)。

25、下列程序执行后的输出结果是()。

解答:因为x是字符型,所以将0xFFFF赋值给x,x的值为0xFF,因为数据在内存中是其补码表示的,OxFF的真值是-1,所以显示的结果是-1,即正确答案为C。

26、假设A为抽象类,下列声明( )是正确的

解答:抽象类不能实例化, 不能参数类型和函数返回类型。但是,抽象类可以用作指针类型,因而B选项正确。

27、某二叉树的前序序列为ABCDEFG,中序序列为DCBAEFG,则该二叉树的后序序列为()。

解答:前序序列:根左右;中序序列:左根右;后序序列:左右根。也就是说,左右的顺序是不会变的,根的顺序依次挪位。

前序序列为ABCDEFG 先访问结点,再到左孩子,右孩子 中序序列为DCBAEFG 先左,再结,后右 A是结点 BCD是A的左孩子 EFG是A的右孩子 A下是B结点,接着C是B的左孩子,D是C的左孩子 EFG以此类推,画图辅助更快。

前序ABCDEFG,所以A一定为根结点,在看一下中序DCBAEFG,看A的位置,可得出,DCB三个一定是在A的左边。分析前序BCD,所以B一定为A左边的根结点,看一下中序DCB中B的位置,所以DC一定是在B的左边……依次分析。

解答:在C++中,析构函数的作用是:当一个对象被销毁时,调用析构函数对类对象和对象成员进行释放内存资源。

当我们定义一个指向派生类类型对象指针时,构造函数按照从基类到派生类的顺序被调用,但是当删除指向派生类的基类指针时,派生类的析构函数没有被调用,只是调用了基类的析构函数,此时派生类将会导致内存泄漏。

我们需要将基类的析构函数声明为虚函数,此时在调用析构函数的时候是根据ptr指向的具体类型来调用析构函数,此时会调用派生类的析构函数。

解答:题中浮点数比较使用的是while( x != 1.0 ),正确方法应该是:判断浮点数a,b是否相等:

30、变量 val 的内存地址位于:

解答:初始化的全局变量和静态变量存放在 data段(全局初始化区);未初始化的全局变量和未初始化的静态变量存放在bss段(全局未初始化区)。

其中BSS段的特点是:在程序执行之前BSS段会自动清0,所以未初始化的全局变量和静态变量在程序执行之前就已经变成0,但是并不是存放在初始化段,是因为存放在未初始化段才初始化为0的。 所以选B。

  • bss段:bss段通常是指用来存放程序中未初始化的全局变量的一块内存区域;
  • data段:数据段通常是指用来存放程序中已初始化的全局变量的一块内存区域;
  • text段:代码段通常是指用来存放程序执行代码的一块内存区域;
  • 堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减;
  • 栈(stack):又称堆栈,是用户存放程序临时创建的局部变量。

text和data段都在可执行文件中(在嵌入式系统里一般是固化在镜像文件中),由系统从可执行文件中加载;而bss段不在可执行文件中,由系统初始化。

一个程序本质上都是由 bss段、data段、text段三个组成的。

比如,在C语言之类的程序编译完成之后,已初始化的全局变量保存在.data 段中,未初始化的全局变量保存在.bss 段中。

text和data段都在可执行文件中(在嵌入式系统里一般是固化在镜像文件中),由系统从可执行文件中加载;而bss段不在可执行文件中,由操作系统初始化清零。

同时,bss段并不给该段的数据分配空间,只是记录数据所需空间的大小;data段则为数据分配空间,数据保存在目标文件中。

31、没用参数的两个函数是不能重载的.说法是否正确?

解答:C++不允许仅根据函数的返回类型重载函数名称;可以编写两个名称相同,参数也相同的函数,其中一个是const,另一个不是。例如:

32、设有定义:int x = 0,﹡p; 紧接着的赋值语句正确的是?

解答:指针P没有指向某个地址,如果P初始化了的话,D就是对的。也就是说,申明*p之后p是一个空指针,只能p = &x,不能给空指针赋值。

33、下面程序应该输出多少?

解答:c是一个指针数组,每个数组单元都是一个指针,指向一个字符串。即c[0]="ENTER"......c[3]="FIRST"

由于[]与*运算本质几乎是一致的,以下用[]替换*更容易理解。也就是:c[i]=*(c+i)

cp是char**[]类型,它可以退化成char***类型,正好与cpp类型一致。

首先看 C,CP,CPP 之间的存储关系:

计算各个表达式结果的过程如下图:

34、在vs编译环境下,以下代码的运行情况:

解答:当用函数做实参时,编译器一般会根据参数传递顺序,先计算出函数的返回值,然后将返回值传递给原来的函数。

而函数的参数是通过栈传递的。因此参数从右往左入栈顺序是:printf("c"),printf("b"),printf("a")。依次计算出结果:cba。

35、下面程序的执行结果:

解答:这道题应该注意:指针类型加减时步长的问题。A大小为4;B大小为8。那么:当调用seta()方法的时候,由于传入的实参为B类型,大小为8,而形参为A类型,大小为4。

这道题如果改为void seta(B* data, int idx),那么形参中data指针加1步长为8,结果就是。但是由于步长为4,所以结果就是 。

解答:本题考查把数组名作为函数参数,执行fun函数后,s的值并没有发生变化,仍然是指向a,所以输出结果为1.10,选项D正确。

解答:迭代器当指针看,++指针自增,*取内容,==判断指针相等,

在下列选项中,正确的调用是(  )。

解答:对于函数指针来说,用它调用函数的方法可以有:

也就是说,fun6之前的指针符号可以使用,也可以不使用。

39、有如下C++代码:

解答:一般不建议在构造函数或者析构函数中调用虚函数,因为在构造函数和析构函数中调用虚函数不会呈现多态性,也就是说在构造基类构造函数的时候,派生类的部分还没有构造,怎么谈去用虚函数实现动态绑定派生生类对象呢,所以构造B基类部分的时候,调用的基类的函数bar。

也就是说:在构造函数中调用虚函数时,只调用自己类中定义的函数(若自己类中没有定义,则调用基类中定义的函数),而不是调用派生类中重新定义的虚函数。

40、下面代码的输出结果是()。

父子进程中输出的num相同,num地址不相同

父子进程中输出的num不同,num地址相同

父子进程中输出的num相同,num地址也相同

父子进程中输出的num不同,num地址不相同

解答:虚拟地址空间。num地址的值相同,但是其真实的物理地址却不一样。linux下实现了一下,发现地址值真的一样。fork之后子进程复制了父进程的数据、堆栈。但是由于地址重定位器之类的魔法存在,所以,看似一样的地址空间(虚拟地址空间),其实却是不同的物理地址空间。同时可以验证c程序中输出的地址空间其实都是虚拟地址空间。

也就是说,本题问题就出在地址相不相同,如果按照两个进程各处在独自的虚拟进程地址空间分析的话,这个题很容易会选择第四个答案,但是Linux中的资源分配都是虚拟机制,也就是说,他们还是会共用一个虚拟的地址,但是映射到物理内存就可能会不一样。

41、下面代码的执行结果是()。

解答:本题的逻辑可以画出下面的图:

printf(“%s”,**++ppp);即,ppp当前所指向的位置,再往下移一个位置,即pp的位置2,而pp的位置2指向的是p的位置2,p的位置2指向的是CAMPUS,所以先输出CAMPUS;

printf(“%s”,*++(*++ppp));所以我们首先看,++ppp,第一个printf语句中ppp已经指向了pp的位置2,所以再往下移一个,指向了pp的位置3,而(*++ppp)则代表pp位置3所指向的内容,即p的位置1(pp的位置3指向的是p的位置1),在此基础上前面再加上一个++,则代表指针p在位置1的基础上再往下移动,即指针p的位置2,而p的位置2所指向的内容是CAMPUS,所以第二行输出的也是CAMPUS。

也就是说,p、pp、ppp都是指针,都是指向上一级的指针。加上*就是取上一级的值,不加就是上一级的地址。ppp的值为pp的地址,pp的值为p的地址,p的值就是字符串。

42、以下叙述中正确的是()

在C语言中,逻辑真值和假值分别对应1和0

关系运算符两边的运算对象可以是C语言中任意合法的表达式

对于浮点变量x和y,表达式:x==y 是非法的,会出编译错误

分支结构是根据算术表达式的结果来判断流程走向的

解答:A选项中,在C语言中,逻辑真值对应非0; C选项中,表达式:x==y 是合法的;D选项中,分支结构的流程走向是根据表达式的值,并不仅仅是算数表达式的值。因此B选项正确。

43、在16位C编译系统上,以下程序的输出结果是()。

%-81d表示输出占8个空格的位置,左对齐,右边多余的位置补空格,故第一个y的输出为:│-43456□□│;

%-081d等价于%-81d,因为不可能在右边空位上补0,所以第二个y的输出也为:│-43456□□│;

%081d表示输出占8个空格的位置,右对齐,左边多余的位置补0,所以第三个y的输出为:│-0043456│;

%+81d表示输出占8个空格的位置,右对齐,左边多余的位置补空格,必须输出正负号,所以第四个y的输出为│□□-43456│。正确答案应为C。

44、一张分辨率的图片,假定每个像素用16位色彩表示,用位图文件(bitmap)格式存储,则这张图片文件需要占用多大的存储空间____。

解答:it1的前两个字符为ab没有异议,当it2的指针指向c时执行 it2++,运行后it2指向d,然后下一个字母不为c,所以it1的指针内复制为d,即此时it1为abd,之后遇到3个c,执行 it2++,直到it2指向d时才将d赋值给it1,也就是此时it1=abdd,但是接下来it2已经为空了,也就是“\0”,所以就不执行循环了,但是it1内本身后面还有cccd,前面的四个字母只是it2覆盖成abdd,所以最终的答案是abddcccd也就是D。

46、运行下面这段代码,会出现的情况是()。

47、在一个被调用函数中,关于return语句使用的描述,( )是错误的。

被调用函数中可以不用return语句

被调用函数中可以使用多个return语句

被调用函数中,如果有返回值,就一定要有return语句

被调用函数中,一个return语句可以返回多个值给调用函数

解答:被调用函数中,如果有返回值,就一定要有return语句。编译的时候并不出错,运行的时候会提示出错。

解答:当它与 “C” 一起连用时,如:extern “C” void fun(int a , int b);,会告诉C++编译器在编译fun这个函数名是按着C的规则去翻译相应的函数名而不是C++的,C++的规则在翻译这个函数名时会把fun这个名字变得面目全非。

49、下面定义语句是不正确的:

50、以下程序的输出结果是()。

解答:printf自右向左压栈,但这种写法是未定义行为!!!

我要回帖

更多关于 编程题不会做怎么办 的文章

 

随机推荐