c语言笔试常见问题文档格式.docx
《c语言笔试常见问题文档格式.docx》由会员分享,可在线阅读,更多相关《c语言笔试常见问题文档格式.docx(22页珍藏版)》请在冰豆网上搜索。
staticconst应该就是上面两者的合集.
下面分别说明:
全局:
const,只读的全局变量,其值不可修改.
static,规定此全局变量只在当前模块(文件)中可见.
staticconst,既是只读的,又是只在当前模块中可见的.
文件:
文件指针可当作一个变量来看,与上面所说类似.
函数:
const,返回只读变量的函数.
static,规定此函数只在当前模块可见.
类:
const,一般不修饰类,(在VC6.0中试了一下,修饰类没啥作用)
static,C++中似乎没有静态类这个说法,一般还是拿类当特殊的变量来看.C#中有静态类的详细说明,且用法与普通类大不相同.
3newdelete和mallocfree的区别
malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。
它们都可用于申请动态内存和释放内存。
对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。
对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。
由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。
因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。
注意new/delete不是库函数。
4main(){intx=10,y=10,i;
for(i=0;
x>
8;
y=++i)printf("
%d%d"
x--,y);
}
在循环语句for(表达式1;
表达式2;
表达式3)中,先执行表达式一,再执行表达式二,如果表达式二成立,就进入循环,第一次循环执行完后(本程序共两次循环),才执行表达式三(这是表达式三第一次被执行),然后再执行表达式二,看其是否成立,如果成立,就进行第二次循环。
如此循环,表达式一只在第一次循环时执行,以后不再执行,表达式三在第一次循环不执行,以后的每次循环都执行。
如果你要这是为什么,我只能说这是规定,别的就不知道了。
5什么是静态成员变量
在C++类的成员变量被声明为static(称为静态成员变量),意味着它为该类的所有实例所共享,也就是说当某个类的实例修改了该静态成员变量,其修改值为该类的其它所有实例所见。
比如在某个类A中声明一个staticintnumber;
初始化为0。
这个number就能被所有A的实例共用。
在A的构造函数里加上number++,在A的析构函数里加上number--。
那么每生成一个A的实例,number就加一,每销毁一个A的实例,number就减一,这样,number就可以记录程序中共生成了多少个A的实例。
这只是静态成员的一种用法而已。
6静态数据成员和静态成员函数在程序中是如何声明和定义的
classFoo
{
public:
staticinta;
stataicvoidfunc();
}
静态数据成员和函数都是在声明前加static
静态成员必须要在类外初始化,无法在构造函数内初始化。
新标准的C++也允许在生命静态数据成员的是后直接加等于号进行初始化,但是大部分编译器不支持。
所以最保险的办法就是在类定义的外面再写:
intFoo:
:
a=0;
注意,这时候不需要再static了。
函数则很普通成员函数的声明以及实现没区别,唯一要注意的是,静态函数是没有this指针的,因此不能访问任何非静态的其他成员函数或成员变量,如果要访问需要传递this指针进去,比如
inta;
staticvoidfunc(Foo*ptrFoo)
{
a=0;
//错误!
!
a不是静态变量,无法访问!
ptrFoo->
a=0;
//正确。
voidtest()
//非静态成员函数调用静态成员函数可以传递this指针,让静态成员函数通过他来访问
//其他成员函数和成员变量。
Foo:
func(this);
7虚函数和纯虚函数区别
虚函数和纯虚函数
在面向对象的C++语言中,虚函数(virtual
function)是一个非常重要的概念。
因为它充分体现了面向对象思想中的继承和多态性这两大特性,在C++语言里应用极广。
比如在微软的MFC类库中,你会发现很多函数都有virtual关键字,也就是说,它们都是虚函数。
难怪有人甚至称虚函数是C++语言的精髓。
那么,什么是虚函数呢,我们先来看看微软的解释:
虚函数是指一个类中你希望重载的成员函数,当你用一个基类指针或引用指向一个继承类对象的时候,你调用一个虚函数,实际调用的是继承类的版本。
——摘自MSDN
这个定义说得不是很明白。
MSDN中还给出了一个例子,但是它的例子也并不能很好的说明问题。
我们自己编写这样一个例子:
#i
nclude
"
stdio.h"
conio.h"
class
Parent
{
char
data[20];
void
Function1();
virtual
void
Function2();
//
这里声明Function2是虚函数
}parent;
Parent:
Function1()
printf("
This
is
parent,function1\n"
}
Function2()
parent,function2\n"
Child:
public
child;
child,function1\n"
printf("
child,function2\n"
int
main(int
argc,
char*
argv[])
Parent
*p;
定义一个基类指针
if(_getch()=='
c'
)
如果输入一个小写字母c
p=&
指向继承类对象
else
parent;
否则指向基类对象
p->
这里在编译时会直接给出Parent:
Function1()的
入口地址。
注意这里,执行的是哪一个Function2?
return
0;
用任意版本的Visual
C++或Borland
C++编译并运行,输入一个小写字母c,得到下面的结果:
parent,function1
child,function2
为什么会有第一行的结果呢?
因为我们是用一个Parent类的指针调用函数Fuction1(),虽然实际上这个指针指向的是Child类的对象,但编译器无法知道这一事实(直到运行的时候,程序才可以根据用户的输入判断出指针指向的对象),它只能按照调用Parent类的函数来理解并编译,所以我们看到了第一行的结果。
那么第二行的结果又是怎么回事呢?
我们注意到,Function2()函数在基类中被virtual关键字修饰,也就是说,它是一个虚函数。
虚函数最关键的特点是“动态联编”,它可以在运行时判断指针指向的对象,并自动调用相应的函数。
如果我们在运行上面的程序时任意输入一个非c的字符,结果如下:
parent,function2
请注意看第二行,它的结果出现了变化。
程序中仅仅调用了一个Function2()函数,却可以根据用户的输入自动决定到底调用基类中的Function2还是继承类中的Function2,这就是虚函数的作用。
我们知道,在MFC中,很多类都是需要你继承的,它们的成员函数很多都要重载,比如编写MFC应用程序最常用的CView:
OnDraw(CDC*)函数,就必须重载使用。
把它定义为虚函数(实际上,在MFC中OnDraw不仅是虚函数,还是纯虚函数),可以保证时刻调用的是用户自己编写的OnDraw。
虚函数的重要用途在这里可见一斑。
再看下面的
-----------------------------------------------------------
上一篇:
为何析构函数中的cout不起作用
下一篇:
菱形虚拟继承后,派生类的大小问题C++中虚函数和纯虚函数的概念,差别和分别存在的原因
摘自:
C++中虚函数和纯虚函数的概念,差别和分别存在的原因
首先:
强调一个概念
定义一个函数为虚函数,不代表函数为不被实现的函数
定义他为虚函数是为了允许用基类的指针来调用子类的这个函数
定义一个函数为纯虚函数,才代表函数没有被实现
定义他是为了实现一个接口,起到一个规范的作用,规范继承这个类的程序员必须实现这个函数。
8请教C++深拷贝与浅拷贝区别并详细说一下如何应用
浅拷贝就是对象的数据成员之间的简单赋值,如你设计了一个没有类而没有提供它的复制构造函数,当用该类的一个对象去给令一个对象赋值时所执行的过程就是浅拷贝,如:
classA
A(int_data):
data(_data){}
A(){}
private:
intdata;
};
intmain()
Aa(5),b=a;
//仅仅是数据成员之间的赋值
这一句b=a;
就是浅拷贝,执行完这句后b.data=5;
如果对象中没有其他的资源(如:
堆,文件,系统资源等),则深拷贝和浅拷贝没有什么区别,但当对象中有这些资源时,例子:
A(int_size):
size(_size){data=newint[size];
}//假如其中有一段动态分配的内存
A(){};
~A(){delete[]data;
}//析构时释放资源
int*data;
intsize;
//注意这一句
这里的b=a会造成未定义行为,因为类A中的复制构造函数是编译器生成的,所以b=a执行的是一个浅拷贝过程。
我说过浅拷贝是对象数据之间的简单赋值,比如:
b.size=a.size;
b.data=a.data;
//Oops!
这里b的指针data和a的指针指向了堆上的同一块内存,a和b析构时,b先把其data指向的动态分配的内存释放了一次,而后a析构时又将这块已经被释放过的内存再释放一次。
对同一块动态内存执行2次以上释放的结果是未定义的,所以这将导致内存泄露或程序崩溃。
所以这里就需要深拷贝来解决这个问题,深拷贝指的就是当拷贝对象中有对其他资源(如堆、文件、系统等)的引用时(引用可以是指针或引用)时,对象的另开辟一块新的资源,而不再对拷贝对象中有对其他资源的引用的指针或引用进行单纯的赋值。
如:
A(constA&
_A):
size(_A.size){data=newint[size];
}//深拷贝
//这次就没问题了
总结:
深拷贝和浅拷贝的区别是在对象状态中包含其它对象的引用的时候,当拷贝一个对象时,如果需要拷贝这个对象引用的对象,则是深拷贝,否则是浅拷贝
嵌入式篇
1结构体位制,内存对齐
05#include<
stdio.h>
06
07typedefstruct_A
08{
09
unsigneda:
4;
//位段成员的类型仅能够为unsigned或者int
10
unsignedb:
11
unsignedc:
2;
12
unsignedd:
6;
13
unsignedE:
1;
14
unsignedD:
15
unsignedT:
3;
16
unsignedA:
9;
17
unsignedh:
//前面已经为31,故4+31>
32已超过一个存储单元,所以4在一个新的存储单元存放
18
unsignedy:
29;
//由于前面的4在一个新的存储单元的开头存放,且29+4>
32,故在另一个新的存储单元存放
19}A;
//所以最后求出的A的大小是4+4+4=12
20
21/*对上面的具体解释:
一个位段必须存储在同一个存储单元中,不能跨两个单元.如果某存储单元空间中不能容纳下一个位段,则改空间不用,而从下一个存储单元起存放该位段.结构体A中的h和y就是这种情况.在gcc环境下,测试后,一个存储单元为4个字节.
24*/
25
26typedefstruct_S
27{
28
29
30
22;
31
unsignedq:
32
33
//unsignedi:
33;
//错误:
‘i’的宽度超过它自身的类型
34
当多出此行时,该结构体大小由4变为8,因为此行之前正好为32位
35}S;
36
37typedefstruct_T
38{
//当没有占满一个存储单元时,结构体的大小对齐为一个存储单元的大小
39
40
41
unsignedj:
42
unsigned:
1;
//可以定义无名位段,此例中该无名位段占用1位的空间,该空间将不被使用
43}T;
44
45typedefstruct_V
46{
47
48
49
//定义长度为0的位段时不能指定名字,否则编译不过
50
//定义了0字段后,紧接着的下一个成员从下一个存储单元开始存放;
51}V;
2语言auto定义变量
#include"
voidmian()
inti,num;
num=2;
for(i=0;
i<
i++)
Thenumequal%d\n"
num);
num++;
autointnum=1;
Theinternalblocknumequal%d\n"
执行结构为:
Thenumequal2
Theinternalblocknumequal1
Thenumequal3
Thenumequal4
请问结果为什么是那样啊
在函数内部定义的变量成为局部变量。
在某些C语言教材中,局部变量称为自动变量,这就与使用可选关键字auto定义局部变量这一作法保持一致。
局部变量仅由其被定义的模块内部的语句所访问。
换言之,局部变量在自己的代码模块之外是不可知的。
切记:
模块以左花
括号开始,以右花括号结束。
对于局部变量,要了解的最重要的东西是:
它们仅存在于被定义的当前执行代码块中,即局部变量在进入模块时生成,在退出模块时消亡。
定义局部变量的最常见的代码块是函数。
\\num=1
\\num=1输出Theinternalblocknumequal1
\\num=2
这个区域又是一个模块,里面num的对外部num的不影响
二叉树的操作
#include<
iostream.h>
typedefstructBiTNode
chardata;
intbit;
structBiTNode*lchild,*rchild,*parent;
}BiTNode;
voidInitBT(BiTNode*&
t)//1、初始化,不带头结点
t=NULL;
/*voidInitBT(BiTNode*t)//初始化,带头结点
t=newBiTNode;
t->
lchild=t->
rchild=t->
parent=NULL;
}*/
intEmptyBT(BiTNode*t)//判断队空
if(t==0)
return1;
else
return0;
BiTNode*creatBT(BiTNode*t,intb)//2、创建二叉树
BiTNode*p;
charch;
cin>
>
ch;
if(ch=='
#'
)return0;
p=newBiTNode;
p->
data=ch;
parent=t;
bit=b;
t=p;
lchild=creatBT(t,0);
rchild=creatBT(t,1);
returnt;
voidpreorder(BiTNode*t)//3、先序遍历
if(!
EmptyBT(t))
cout<
<
data;
preorder(t->
lchild);
rchild);
voidinorder(BiTNode*t)//中序遍历
inorder(t->
voidpostorder(BiTNode*t)//后序遍历
postorder(t->
voidcoutBT(BiTNode*t,int&
m,int&
n,int&
i)
//4、计算二叉树中叶子结点、度为2的结点和度为1的结点的个数
if((t->
lchild==0)&
&
(t->
rchild==0))
m++;
//叶子结点
elseif((t->
lchild!
=0)&
rchild!
=0))
i++;
//度为2的结点
n++;
//度为1的结点
coutBT(t->
lchild,m,n,i);
rchild,m,n,i);
voidcoutNode(BiTNode*t,int&
k)//5、求二叉树中结点个数
k++;
coutNode(t->
lchild,k);
rchild,k);
intBTdepth(BiTNode*t)//6、求二叉树的深度
inti,j;
if(EmptyBT(t))
i=BTdepth(t->
j=BTdepth(t->
return(i>
j?
i:
j)+1;
intXdepth(BiT