c++面试题

1.new、delete、malloc、free关系
相同点:它们都可用于申请动态内存和释放内存。
区别:1)malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。
2)New和delete会调用构造函数和析构函数,所以在创建对象时不能使用malloc和free。
2.delete与 delete []区别
delete只会调用一次析构函数,而delete[]会调用每一个成员的析构函数。在More Effective C++中有更为详细的解释:“当delete操作符用于数组时,它为每个数组元素调用析构函数,然后调用operator delete来释放内存。”delete与new配套,delete []与new []配套
MemTest *mTest1=new MemTest[10];
MemTest *mTest2=new MemTest;
Int *pInt1=new int [10];
Int *pInt2=new int;
delete[]pInt1; //-1-
delete[]pInt2; //-2-
delete[]mTest1;//-3-
delete[]mTest2;//-4-
在-4-处报错。
这就说明:对于内建简单数据类型,delete和delete[]功能是相同的。对于自定义的复杂数据类型,delete和delete[]不能互用。delete[]删除一个数组,delete删除一个指针。简单来说,用new分配的内存用delete删除;用new[]分配的内存用delete[]删除。delete[]会调用数组元素的析构函数。内部数据类型没有析构函数,所以问题不大。如果你在用delete时没用括号,delete就会认为指向的是单个对象,否则,它就会认为指向的是一个数组。
3.子类析构时要调用父类的析构函数吗?
定义一个对象时先调用基类的构造函数、然后调用派生类的构造函数;析构的时候恰好相反:先调用派生类的析构函数、然后调用基类的析构函数。
4.基类为什么需要虚析构函数?
防止内存泄漏。想去借助父类指针去销毁子类对象的时候,不能去销毁子类对象。假如没有虚析构函数,释放一个由基类指针指向的派生类对象时,不会触发动态绑定,则只会调用基类的析构函数,不会调用派生类的。派生类中申请的空间则得不到释放导致内存泄漏。

5.虚函数,纯虚函数
虚函数:用virtual关键字申明的成员函数。允许在派生类中对基类的虚函数重新定义。引入虚函数的目的是为了动态绑定;存在虚函数的类都有一个一维的虚函数表叫做虚表,类的对象有一个指向虚表开始的虚指针。虚表是和类对应的,虚表指针是和对象对应的;

纯虚函数:virtual void fun()=0引入纯虚函数是为了派生接口。
从基类继承来的纯虚函数,在派生类中仍是虚函数。如果一个类中至少有一个纯虚函数,那么这个类被称为抽象类(abstract class)。
抽象类中不仅包括纯虚函数,也可包括虚函数。抽象类必须作为派生其他类的基类,而不能用于直接创建对象实例。
6.C++的多态
多态:是对于不同对象接收相同消息时产生不同的动作。
C++的多态性用一句话概括:在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数。如果对象类型是派生类,就调用派生类的函数;如果对象类型是基类,就调用基类的函数。
C++的多态性具体体现在运行和编译两个方面:在程序运行时的多态性通过继承和虚函数来体现;在程序编译时多态性体现在函数和运算符的重载上;
实现:C++多态性主要是通过虚函数实现的,虚函数允许子类重写override(注意和overload的区别,overload是重载,是允许同名函数的表现,这些函数参数列表/类型不同)。
目的:接口重用。封装可以使得代码模块化,继承可以扩展已存在的代码,他们的目的都是为了代码重用。而多态的目的则是为了接口重用。
用法:声明基类的指针,利用该指针指向任意一个子类对象,调用相应的虚函数,可以根据指向的子类的不同而实现不同的方法。

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

8.什么是“引用”?申明和使用“引用”要注意哪些问题?
答:引用就是某个目标变量的“别名”(alias),对应用的操作与对变量直接操作效果完全相同。注意的问题:申明一个引用的时候,切记要对其进行初始化。引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,不能再把该引用名作为其他变量名的别名。声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。不能建立数组的引用。
9.将“引用”作为函数参数有哪些特点?
(1)传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。
(2)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。
(3)使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用"*指针变量名"的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。
10.将“引用”作为函数返回值类型的格式、好处和需要遵守的规则?
格式:类型标识符 &函数名(形参列表及类型说明){ //函数体 }
好处:在内存中不产生返回值的副本
注意事项:
(1)不能返回局部变量的引用。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了"无所指"的引用,程序会进入未知状态。
(2)不能返回在函数内部new分配的内存的引用。虽然不存在局部变量的被动销毁问题,可对于这种情况(返回函数内部new分配内存的引用),又面临其它尴尬局面。例如,被函数返回的引用只是作为一个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memory leak。
(3)可以返回类成员的引用,但最好是const。主要原因是当对象的属性是与某种业务规则(business rule)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其它对象可以获得该属性的非常量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。
(4)流操作符重载返回值申明为“引用”的作用:
流操作符<<和>>,这两个操作符常常希望被连续使用,例如:cout << “hello” << endl; 因此这两个操作符的返回值应该是一个仍然支持这两个操作符的流引用。可选的其它方案包括:返回一个流对象和返回一个流对象指针。但是对于返回一个流对象,程序必须重新(拷贝)构造一个新的流对象,也就是说,连续的两个<<操作符实际上是针对不同对象的!这无法让人接受。对于返回一个流指针则不能连续使用<<操作符。因此,返回一个流对象引用是惟一选择。这个唯一选择很关键,它说明了引用的重要性以及无可替代性,也许这就是C++语言中引入引用这个概念的原因吧。
赋值操作符=。这个操作符象流操作符一样,是可以连续使用的,例如:x = j = 10;或者(x=10)=100;赋值操作符的返回值必须是一个左值,以便可以被继续赋值。因此引用成了这个操作符的惟一返回值选择。

#include
int &put(int n);
int vals[10];
int error=-1;
void main()
{
put(0)=10; //以put(0)函数值作为左值,等价于vals[0]=10;
put(9)=20; //以put(9)函数值作为左值,等价于vals[9]=20;
cout< cout< }
int &put(int n)
{
if (n>=0 && n<=9 ) return vals[n];
else { cout<<“subscript error”; return error; }
}
(5)在±*/ 四则运算符,却千万不能返回引用。它们不能返回引用,主要原因是这四个操作符没有side effect,因此,它们必须构造一个对象作为返回值。

11.在什么时候需要使用“常引用”? 
如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。常引用声明方式:const 类型标识符 &引用名=目标变量名;
例1
int a ;
const int &ra=a;
ra=1; //错误
a=1; //正确
例2
string foo( );
void bar(string & s);
那么下面的表达式将是非法的:
bar(foo( ));
bar(“hello world”);
原因在于foo( )和"hello world"串都会产生一个临时对象,而在C++中,这些临时对象都是const类型的。因此上面的表达式就是试图将一个const类型的对象转换为非const类型,这是非法的。引用形参应该在能被定义为const的情况下,尽量定义为const 。
12.指针和引用的区别

  1. 引用必须被初始化,指针不必。
  2. 引用初始化以后不能被改变,指针可以改变所指的对象。
  3. 不存在指向空值的引用,但是存在指向空值的指针。

本质上的区别是,指针是一个新的变量,只是这个变量存储的是另一个变量的地址,我们通过访问这个地址来修改变量。
而引用只是一个别名,还是变量本身。对引用进行的任何操作就是对变量本身进行操作,因此以达到修改变量的目的。
如:
int a=1;int *p=&a;
int a=1;int &b=a;
上面定义了一个整形变量和一个指针变量p,该指针变量指向a的存储单元,即p的值是a存储单元的地址。
而下面2句定义了一个整形变量a和这个整形a的引用b,事实上a和b是同一个东西,在内存占有同一个存储单元。
(2)可以有const指针,但是没有const引用
注:引用可以指向常量,也可以指向变量。因为引用的指向本来就是不可变的,无需加const声明。即指针存在常量指针int const *p和指针常量int const p,而引用只存在常量引用int const &a,不存在引用常量int& const a。
(3)指针可以有多级,但是引用只能是一级(int **p;合法 而 int &&a是不合法的)
(4)指针的值可以为空,但是引用的值不能为NULL,并且引用在定义的时候必须初始化;
(5)指针的值在初始化后可以改变,即指向其它的存储单元,而引用在进行初始化后就不会再改变了。
(6)"sizeof引用"得到的是所指向的变量(对象)的大小,而"sizeof指针"得到的是指针本身的大小;
(7)指针和引用的自增(++)运算意义不一样;
(8)指针使用时需要解引用(
),引用则不需要;

13.简述数组与指针的区别?
数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。指针可以随时指向任意类型的内存块。
(1)修改内容上的差别
char a[] = “hello”;
a[0] = ‘X’;
char *p = “world”; // 注意p 指向常量字符串
p[0] = ‘X’; // 编译器不能发现该错误,运行时错误
(2) 用运算符sizeof 可以计算出数组的容量(字节数)。sizeof§,p 为指针得到的是一个指针变量的字节数,而不是p 所指的内存容量。C++/C 语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。
char a[] = “hello world”;
char *p = a;
cout<< sizeof(a) << endl; // 12 字节
cout<< sizeof§ << endl; // 4 字节
计算数组和指针的内存容量
void Func(char a[100])
{
cout<< sizeof(a) << endl; // 4 字节而不是100 字节
}

14.指针常量与常量指针
常量指针(被指向的对象是常量)又叫常指针,可以理解为常量的指针,指向的是个常量
关键点:
常量指针指向的对象不能通过这个指针来修改,可是仍然可以通过原来的声明修改;
常量指针可以被赋值为变量的地址,之所以叫常量指针,是限制了通过这个指针修改变量的值;
指针还可以指向别处,因为指针本身只是个变量,可以指向任意地址;
const int *p或int const *p
#include // 常量指针(被指向的对象是常量)int main() {
int i = 10;
int i2 = 11;
const int *p = &i;
printf("%d\n", *p);//10
i = 9; //OK,仍然可以通过原来的声明修改值,
//Error,*p是const int的,不可修改,即常量指针不可修改其指向地址
//*p = 11; //error: assignment of read-only location ‘*p’
p = &i2;//OK,指针还可以指向别处,因为指针只是个变量,可以随意指向;
printf("%d\n", p);//11
return 0;
}
指针常量(指针本身是常量)本质是一个常量,而用指针修饰它。指针常量的值是指针,这个值因为是常量,所以不能被赋值。
关键点:
它是个常量!
指针所保存的地址可以改变,然而指针所指向的值却不可以改变;
指针本身是常量,指向的地址不可以变化,但是指向的地址所对应的内容可以变化;
int
const p;
//指针常量(指针本身是常量)
#include
int main() {
int i = 10;
int *const p = &i;
printf("%d\n", *p);//10
//Error,因为p是const 指针,因此不能改变p指向的内容
//p++;//error: increment of read-only variable ‘p’
(*p)++; //OK,指针是常量,指向的地址不可以变化,但是指向的地址所对应的内容可以变化
printf("%d\n", *p);//11
i = 9;//OK,仍然可以通过原来的声明修改值,
return 0;
}

15、结构与联合有和区别?
(1). 结构和联合都是由多个不同的数据类型成员组成, 但在任何同一时刻, 联合中只存放了一个被选中的成员(所有成员共用一块地址空间), 而结构的所有成员都存在(不同成员的存放地址不同)。
(2). 对于联合的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于结构的不同成员赋值是互不影响的。
16.重载(overload)和重写(overried,有的书也叫做“覆盖”)的区别?
常考的题目。从定义上来说:
重载:是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。
重写:是指子类重新定义父类虚函数的方法。
从实现原理上来说:
重载:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。如,有两个同名函数:function func(p:integer):integer;和function func(p:string):integer;。那么编译器做过修饰后的函数名称可能是这样的:int_func、str_func。对于这两个函数的调用,在编译器间就已经确定了,是静态的。也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关!
重写:和多态真正相关。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚绑定)。

17.有哪几种情况只能用intialization list 而不能用assignment?
答案:当类中含有const、reference 成员变量;基类的构造函数都需要初始化表。
18. C++是不是类型安全的?
答案:不是。两个不同类型的指针之间可以强制转换(用reinterpret cast)。C#是类型安全的。
19. main 函数执行以前,还会执行什么代码?
答案:全局对象的构造函数会在main 函数之前执行。

20、C和C++的特点与区别?
答:(1)C语言特点:
1.作为一种面向过程的结构化语言,易于调试和维护;
2.表现能力和处理能力极强,可以直接访问内存的物理地址;
3.C语言实现了对硬件的编程操作,也适合于应用软件的开发;
4.C语言还具有效率高,可移植性强等特点。
(2)C++语言特点:
1.在C语言的基础上进行扩充和完善,使C++兼容了C语言的面向过程特点,又成为了一种面向对象的程序设计语言;
2.可以使用抽象数据类型进行基于对象的编程;
3.可以使用多继承、多态进行面向对象的编程;
4.可以担负起以模版为特征的泛型化编程。
C++与C语言的本质差别:在于C++是面向对象的,而C语言是面向过程的。或者说C++是在C语言的基础上增加了面向对象程序设计的新内容,是对C语言的一次更重要的改革,使得C++成为软件开发的重要工具。

21.深拷贝与浅拷贝的区别?
1.什么时候用到拷贝函数?
a.一个对象以值传递的方式传入函数体;
b.一个对象以值传递的方式从函数返回;
c.一个对象需要通过另外一个对象进行初始化。
如果在类中没有显式地声明一个拷贝构造函数,那么,编译器将会自动生成一个默认的拷贝构造函数,该构造函数完成对象之间的位拷贝。位拷贝又称浅拷贝;
2.是否应该自定义拷贝函数?
自定义拷贝构造函数是一种良好的编程风格,它可以阻止编译器形成默认的拷贝构造函数,提高源码效率。
3.什么叫深拷贝?什么是浅拷贝?两者异同?
如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝,反之,没有重新分配资源,就是浅拷贝。
4.深拷贝好还是浅拷贝好?
如果实行位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。

int a[10]占用空间
如果是TC2.0,int占2字节,那int a[10]就是102=20字节;
如果其他编译器,int占4字节,那么int a[10]就是10
4=40字节;

为了安全你需要用到这个的话可以用10*sizeof(int).这样比较安全。

sizeof(str)和strlen(str)区别:
A.sizeof
     sizeof(…)是运算符,在头文件中typedef为unsigned int,其值在编译时即计算好了,参数可以是数组、指针、类型、对象、函数等。
    它的功能是:获得保证能容纳实现所建立的最大对象的字节大小。
    由于在编译时计算,因此sizeof不能用来返回动态分配的内存空间的大小。实际上,用sizeof来返回类型以及静态分配的对象、结构或数组所占的空间,返回值跟对象、结构、数组所存储的内容没有关系。
    具体而言,当参数分别如下时,sizeof返回的值表示的含义如下:
    数组——编译时分配的数组空间大小;
    指针——存储该指针所用的空间大小(存储该指针的地址的长度,是长整型,应该为4);
    类型——该类型所占的空间大小;
    对象——对象的实际占用空间大小;
    函数——函数的返回类型所占的空间大小。函数的返回类型不能是void。
B.strlen
 strlen(…)是函数,要在运行时才能计算。参数必须是字符型指针(char*)。当数组名作为参数传入时,实际上数组就退化成指针了。
    它的功能是:返回字符串的长度。该字符串可能是自己定义的,也可能是内存中随机的,该函数实际完成的功能是从代表该字符串的第一个地址开始遍历,直到遇到结束符NULL。返回的长度大小不包括NULL。
2.Linux查找命令(在指定的文件下查找文件名里包含xxx的文件),显示文件内容的命令。
查找:示例:查找/root下的与test相关的目录(文件)  find /root -name ‘test*’
显示:命令:cat/more/less/tail 文件
示例:使用cat查看/etc/sudo.conf文件,只能显示最后一屏内容

6.有101个硬币,只有一个硬币的质量与其他硬币质量不同,现有一个天平,请问称量多少次可以判断这个假币重还是轻。
题目:101枚硬币中有一枚假币,有一个无砝码的天平,在最坏情况下最少称 多少 次,可以判断假币比真币重还是轻。 
解答:
先拿出1枚硬币
将100枚对半称重
1)如果平衡,那你RP爆发,拿出来的那枚就是假的,再称一次就可以
2)如果不平衡,那就将轻的50枚对半分
   假如两边平衡,说明轻的都是真的,假币重
   假如不平衡,说明假币就在这里,假币轻
这样就可以2次判断出来

线程与进程的区别
1.定义
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
2.关系
一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.
相对进程而言,线程是一个更加接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。
3.区别
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
1)?简而言之,一个程序至少有一个进程,一个进程至少有一个线程.
2)?线程的划分尺度小于进程,使得多线程程序的并发性高。
3)?另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
4)?线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
5)?从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。
4.优缺点
线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。

在浏览器中输入网址后都发生了什么
1.浏览器发起DNS查询请求
浏览器会根据本地客户端DNS服务器配置(下图为DNS服务器配置),向DNS服务器获取域名对应的IP地址。
2.域名服务器向客户端返回查询结果域名,从而完成域名到IP地址的转换。
3.客户端向web服务器发送HTTP请求
在得到了域名对应的IP地址后,客户端便可以向真正的web服务器发生HTTP请求了。
4.发送响应数据给客户端
Web服务器通常通过监听80端口,来获取客户端的HTTP请求。与客户端建立好TCP连接后,web服务器开始接受客户端发来的数据,并通过HTTP解码,从接受到的网络数据中解析出请求的url信息以前其他诸如Accept-Encoding、Accept-Language等信息。

cocos2dx的几种常见设计模式
一 单例模式
Cocos2dx中的单例有:CCDirector,CCTextureCache,CCSpriteFrameCache,CCScriptEngineManager等等,那为什么存在这些单例呢?
如导演类负责控制场景对象等等,由此存在一个场景控制对象即可;类似那种操作缓存数据文件之类的,或者配置文件的类等,在程序运行过程也是只需一个便可。
二 观察者模式
指被观察者含有一个数组,里边存放了所有观察者的引用,在被观察者的状态发生改变的时候,通过调用观察者的函数来通知观察者,实现了信息的传递。
三 简单工厂模式
CCSpriteFrameCache类通过定制的plist文件来实例化一系列相关的CCSpriteFrame实例,然后只需要提供精灵帧的名字就可以得到相应的CCSpriteFrame实例了。从这个意义上来说,CCSpriteFrameCache类也可以说是一个工厂类,专门负责生产CCSpriteFrame实例。同时,如果精灵帧名字相同的话,那么获取的精灵帧实例也是相同的。
四 管理者模式
管理者(Manager)专门负责管理其它类的实例的类,如CCTextureCache。
管理者类(cache类)可以简化一些可以重用的资源(比如字体、纹理、精灵帧等)的创建和管理工作,可以提高游戏性能。
但凡那些对象,在运行时创建的时间开销特别大时,而又要经常重复使用时,都可以采取此模式来提高运行时性能。
MVC — 按照我的理解就拿组队系统界面为例,CPortTeamUI就是Model,定义了业务逻辑相关的行为方法,如initUI,UpdateView等;load加载的面板Panel便是View,由一组UI元素组成; ClsTeamData是Controller,负责逻辑数据管理中心。 打开界面时View要显示最新数据,则请求Controller问服务端要,当数据返回时Controller会寻找对应的Model,调用其定义的方法updateView从而执行更新行为,实际上Model会通知相关的View去实时更新。 Controller作为Model和View两者之间的联系桥梁。

问题描述
螺旋矩阵是一个nxn的方阵,其中元素为自然数,但像螺旋方向一样递增。举例如下:
?若n = 3,螺旋矩阵为:
1 2 3
8 9 4
7 6 5
若n = 4,螺旋矩阵为:
1 2 3 4
12 13 14 5
11 16 15 6
10 9 8 7
若n = 5,螺旋矩阵是:
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
那么如何打印这样的矩阵呢?
解析
当然它的规律很简单,直接的方法就是先申请一个矩阵,然后按螺旋方向填入相应的元素,填充完毕后再打印出来。它的时间按复杂为O(n2),已经是最优的(为什么?)。空间复杂度也为O(n2)。似乎已经很好了。 但是还不够好。
按照矩阵规律填充元素时,我们是随机访问矩阵元素的(如果可以按顺序访问,根本不用先存起来再打印)。随机访问内存,效率当然不高。所以即使时间复杂度已为最优,但那只是理论上的最优,在实践中表现并不一定就好。

假如能根据行列号直接计算出对应的矩阵元素就好了。当n给定后,这个矩阵就已经唯一确定了,那么每一个元素也是确定的。也就是说,每一个位置放什么元素仅仅取决于n。因此我们可以找到一个函数element(i, j),将行号i和列号j映射成对应这个行列号的元素。当然这个函数肯定不是一个简单的函数,不是一眼就可以看出来的,但也并不是不可能。
现在我们就来考查一下这个矩阵有什么特点。注意观察一下螺旋矩阵的最外层,它的左上角的元素是最小的,然后沿顺时针方向递增,就如同一个环一样(比如n为4时,1, 2, …, 12就是最外面一层环)。再注意一下里面一层,也是一样,顺时针方向递增的一个环(比如n为4时,13, 14, 15, 16就是里面一层环)。以此类推,环里面还有一层环(n为4时有2层环,n为5时有3层环,最里面一层只有一个元素25),实际上是一个圆环套圆环结构。每一圆环最关键的元素就是左上角的那一个元素。只要知道了这个元素,再加上这个正方形环的边长就可以计算出剩下的元素。设左上角元素为a,边长为l(ell),也就是边上有几个元素,并假设左上角的行号和列号均为0,其它元素的行号和列号都以它作参考,计算方法如下所示:

  1. 若i == 0,element(i, j) = a + j;

  2. 否则若j == 0,element(i, j) = a + 4(l-4) - (i-1) - 1;

  3. 否则若i == l-1,element(i, j) = a + 4(l-4) - (l-2) - 1 - j;

  4. 否则element(i, j) = a + l - 1 + i;

螺旋矩阵代码:

//螺旋矩阵

#include
using namespace std;

int a[10][10];

void Fun(int n)
{
int m=1;
int i,j;
for(i =0;i for(j=0;j if(a[i][j] ==0)
a[i][j] = m++;
}
for(j=i+1;j if(a[j][n-1-i] ==0)
a[j][n-1-i] = m++;
}
for(j=n-i-1;j>i;j–){
if(a[n-i-1][j] ==0)
a[n-i-1][j] = m++;
}
for(j=n-i-1;j>i;j–){
if(a[j][i] 0)
a[j][i] = m++;
}
}
if(n%2
1)
a[n/2][n/2]=m;
}

int main(void)
{
int n,i;
cout<<"请输入螺旋矩阵维数: "<< endl;
cin>>n;
cout<<"显示螺旋矩阵数值: "<< endl;
for(int i=0;i for(int j=0;j a[i][j]=0;
}
}
Fun(n);
for(i=0;i for( int j=0;j cout< }
cout< }
}
螺旋队列

问题描述: 设1的坐标是(0,0),x方向向右为正,y方向向下为正,例如,7的坐标为(-1,-1),2的坐标为(1,0)。编程实现输入任意一点坐标(x,y),输出所对应的数字.

螺旋队列代码:

// 螺旋队列.cpp : Defines the entry point for the console application.
//

#include “stdafx.h”
#include
#define max(a,b) ((a)<(b)?(b):(a))
#define abs(a) ((a)>0?(a):-(a))

using namespace std;

int foo(int x,int y)
{
int t = max(abs(x),abs(y));
int u = t+t;
int v = u-1;
v= vv+u;
if(x == -t)
v+=u+t-y;
else if(y==-t)
v+=3
u+x-t;
else if(y ==t)
v+= t-x;
else
v+=y-t;
return v;
}

int _tmain(int argc, _TCHAR* argv[])
{
int x ,y;
int N;
cout<<“请输入螺旋队列数字: “< cin>>N;
cout<<“显示螺旋队列数值: “< for(y=-N;y<=N;y++)
{
for(x=-N;x<=N;x++)
cout<<”\t”< cout< }
while(scanf(”%d%d”,&x,&y)==2)
//printf("%d\n",foo(x,y));
cout<<"\t"< return 0;
}
: 在扑克牌游戏中, 需要实现一个查找顺子的算法:(连续的数字即为顺子), 随机发N张牌, 从中挑出最长的顺子, 并返回其长度, 如果没有顺子返回0. 现在手上的牌为[12,3,4,10,6,5,6,8,11,9,11,11,9,12,1].

1.去重,排序

m= [12,3,4,10,6,5,6,8,11,9,11,11,9,12,1]
m.sort()
s = set(m)
m1 = list(s)

2. 不定数循环去找出每个元素连续间隔1的长度, n不断叠加, 找到最大长度

def foo1(m1):
def foo(m1):
# 字典作为容器
m = dict()
for i in range(len(m1)):
# 初始长度为1
n=1
# 如果里面条件成立,开始循环
while 1:
# 如果下标加上顺子长度大于等于列表长度, 则退出循环
if i+n >= len(m1):
break
# 如果列表下标 i+n的元素 减去 i的元素, 差值是n, 则长度加1, 字典更新出key和value, 寻找长度加1(因为以及排序了, 所以如果是顺子, 肯定索引差值就等于数值差值)
elif m1[i+n] - m1[i] == n:
max_length = n+1
m[’%s’ % m1[i]] = max_length
n += 1
# 如果数值差值不等于索引差值, 则长度保留为上一次的差值, 退出循环.
else:
break
return m
x = foo(m1)
cards = []
# 遍历字典, 找出其中最大长度对应的起始数字, 即可得到该数字延伸长度的顺子组合.
for key,value in x.items():
if max(x.values()) == 1:
return 0
elif value == max(x.values()):
cards.append([int(key)+i for i in range(max(x.values()))])
return ‘最长顺子为: %s, 长度为:%s’ % (cards,max(x.values()))
print(foo1(m1))
结果如下:
最长顺子为: [[8, 9, 10, 11, 12]], 长度为:5

  1. 描述内存分配方式以及它们的区别?
    1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。
    2) 在栈上创建。在执行函数时,函数内局部变量的存储单元、函数参数都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集。
    3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。如果没有,程序结束后,操作系统自动回收。因为堆的分配需要使用频繁的new/delete,造成内存空间的不连续,会有大量的碎片。堆的生长空间向上,地址越大,栈的生长空间向下,地址越小。
    4、常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改)。
    5、代码区 (.text段),存放代码(如函数),不允许修改(类似常量存储区),但可以执行(不同于常量存储区)。

操作系统和编译器通过内存分配的位置来分辨,全局变量分配在全局数据段并且在程序开始运行的时候被加载。局部变量则分配在堆栈里面。

23题:栈内存与文字常量区

       char str1[] = "abc";

char str2[] = “abc”;
  const char str3[] = “abc”;
  const char str4[] = “abc”;
  const char *str5 = “abc”;
  const char *str6 = “abc”;
  char *str7 = “abc”;
  char *str8 = “abc”;
  cout << ( str1 == str2 ) << endl;//0 分别指向各自的栈内存
  cout << ( str3 == str4 ) << endl;//0 分别指向各自的栈内存
  cout << ( str5 == str6 ) << endl;//1指向文字常量区地址相同
  cout << ( str7 == str8 ) << endl;//1指向文字常量区地址相同
  结果是:0 0 1 1
  解答:str1,str2,str3,str4是数组变量,它们有各自的内存空间;而str5,str6,str7,str8是指针,它们指向相同的常量区域。

24.分别写出BOOL,int,float,指针类型的变量a 与“零”的比较语句。
BOOL : if ( !a ) or if(a)
int : if ( a == 0)
float : const EXPRESSION EXP = 0.000001
if ( a < EXP && a >-EXP)
pointer : if ( a != NULL) or if(a == NULL)

25:请说出static和const关键字尽可能多的作用
  解答: const强调值不能被修改,而static强调唯一的拷贝,对所有类的对象。
  static关键字作用:
  (1)函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;
  (2)在模块内的static全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;
  (3)在模块内的static函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;
  (4)在类中的static成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;
  (5)在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量。
  const关键字作用:
  (1)欲阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了;
  (2)对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const;
  (3)在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;
  (4)对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量;
  (5)对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为"左值"。
const函数只能调用const函数,非const函数可以调用const函数

26.请说出const与#define 相比,有何优点?
答案:
const作用:定义常量、修饰函数参数、修饰函数返回值三个作用。被Const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。
1) const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。
2) 有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。
27题: int (*s[10])(int) 表示的是什么?
int (*s[10])(int) 函数指针数组,每个指针指向一个int func(int param)的函数。

28题:int id[sizeof(unsigned long)];这个对吗?为什么?
答案:正确 这个 sizeof是编译时运算符,编译时就确定了 ,可以看成和机器有关的常量。
29题:复杂声明
void * ( * (fp1)(int))[10];
float (
(* fp2)(int,int,int))(int);
int (* ( * fp3)())10;
分别表示什么意思?
【标准答案】
1.void * ( * (fp1)(int))[10]; fp1是一个指针,指向一个函数,这个函数的参数为int型,函数的返回值是一个指针,这个指针指向一个数组,这个数组有10个元素,每个元素是一个void型指针。
2.float (( fp2)(int,int,int))(int); fp2是一个指针,指向一个函数,这个函数的参数为3个int型,函数的返回值是一个指针,这个指针指向一个函数,这个函数的参数为int型,函数的返回值是float型。

3.int (* ( * fp3)())10; fp3是一个指针,指向一个函数,这个函数的参数为空,函数的返回值是一个指针,这个指针指向一个数组,这个数组有10个元素,每个元素是一个指针,指向一个函数,这个函数的参数为空,函数的返回值是int型。

找错题
  试题1:
void test1()
{
 char string[10];
 char* str1 = “0123456789”;
 strcpy( string, str1 );
}
  试题2:
void test2()
{
 char string[10], str1[10];
 int i;
 for(i=0; i<10; i++)
 {
  str1[i] = ‘a’;
 }
 strcpy( string, str1 );
}
  试题3:
void test3(char* str1)
{
 char string[10];
 if( strlen( str1 ) <= 10 )
 {
  strcpy( string, str1 );
 }
}
  解答:
  试题1字符串str1需要11个字节才能存放下(包括末尾的’\0’),而string只有10个字节的空间,strcpy会导致数组越界;
  对试题2,如果面试者指出字符数组str1不能在数组内结束可以给3分;如果面试者指出strcpy(string, str1)调用使得从str1 内存起复制到string内存起所复制的字节数具有不确定性可以给7分,在此基础上指出库函数strcpy工作方式的给10 分;
  对试题3,if(strlen(str1) <= 10)应改为if(strlen(str1) < 10),因为strlen的结果未统计’\0’所占用的1个字节。
  剖析:
  考查对基本功的掌握:
  (1)字符串以’\0’结尾;
  (2)对数组越界把握的敏感度;
  (3)库函数strcpy的工作方式,如果编写一个标准strcpy函数的总分值为10,下面给出几个不同得分的答案:
  2分
void strcpy( char *strDest, char *strSrc )
{
  while( (*strDest++ = * strSrc++) != ‘\0’ );
}
  4分
void strcpy( char *strDest, const char *strSrc )
//将源字符串加const,表明其为输入参数,加2分
{
  while( (*strDest++ = * strSrc++) != ‘\0’ );
}
  7分
void strcpy(char *strDest, const char *strSrc)
{
 //对源地址和目的地址加非0断言,加3分
 assert( (strDest != NULL) && (strSrc != NULL) );
 while( (*strDest++ = * strSrc++) != ‘\0’ );
}
  10分
//为了实现链式操作,将目的地址返回,加3分!

char * strcpy( char *strDest, const char *strSrc )
{
 assert( (strDest != NULL) && (strSrc != NULL) );
 char *address = strDest;
 while( (*strDest++ = * strSrc++) != ‘\0’ );
  return address;
}
  从2分到10分的几个答案我们可以清楚的看到,小小的strcpy竟然暗藏着这么多玄机,真不是盖的!需要多么扎实的基本功才能写一个完美的strcpy啊!
  (4)对strlen的掌握,它没有包括字符串末尾的’\0’。
  读者看了不同分值的strcpy版本,应该也可以写出一个10分的strlen函数了,完美的版本为:
int strlen( const char *str ) //输入参数const

{
 assert( strt != NULL ); //断言字符串地址非0
 int len;
 while( (*str++) != ‘\0’ )
 {
  len++;
 }
 return len;
}
  试题4:
void GetMemory( char *p )
{
 p = (char *) malloc( 100 );
}

void Test( void )
{
 char *str = NULL;
 GetMemory( str );
 strcpy( str, “hello world” );
 printf( str );
}
  试题5:
char *GetMemory( void )
{
 char p[] = “hello world”;
 return p;
}

void Test( void )
{
 char *str = NULL;
 str = GetMemory();
 printf( str );
}
  试题6:
void GetMemory( char **p, int num )
{
 *p = (char *) malloc( num );
}

void Test( void )
{
 char *str = NULL;
 GetMemory( &str, 100 );
 strcpy( str, “hello” );
 printf( str );
}
  试题7:
void Test( void )
{
 char *str = (char *) malloc( 100 );
 strcpy( str, “hello” );
 free( str );
 … //省略的其它语句
}
  解答:
  试题4传入中GetMemory( char *p )函数的形参为字符串指针,在函数内部修改形参并不能真正的改变传入形参的值,执行完
char *str = NULL;
GetMemory( str );
  后的str仍然为NULL;
  试题5中
char p[] = “hello world”;
return p;
  的p[]数组为函数内的局部自动变量,在函数返回后,内存已经被释放。这是许多程序员常犯的错误,其根源在于不理解变量的生存期。
  试题6的GetMemory避免了试题4的问题,传入GetMemory的参数为字符串指针的指针,但是在GetMemory中执行申请内存及赋值语句
*p = (char *) malloc( num );
  后未判断内存是否申请成功,应加上:
if ( *p == NULL )
{
 …//进行申请内存失败处理
}
  试题7存在与试题6同样的问题,在执行
char str = (char ) malloc(100);
  后未进行内存是否申请成功的判断;另外,在free(str)后未置str为空,导致可能变成一个"野"指针,应加上:
str = NULL;
  试题6的Test函数中也未对malloc的内存进行释放。
  剖析:
  试题4~7考查面试者对内存操作的理解程度,基本功扎实的面试者一般都能正确的回答其中50~60的错误。但是要完全解答正确,却也绝非易事。
  对内存操作的考查主要集中在:
  (1)指针的理解;
  (2)变量的生存期及作用范围;
  (3)良好的动态内存申请和释放习惯。
  再看看下面的一段程序有什么错误:
swap( int
p1,int
p2 )
{
 int *p;
 *p = *p1;
 *p1 = *p2;
 p2 = p;
}
  在swap函数中,p是一个"野"指针,有可能指向系统区,导致程序运行的崩溃。在VC++中DEBUG运行时提示错误"Access Violation"。该程序应该改为:
swap( int
p1,int
p2 )
{
 int p;
 p = *p1;
 *p1 = *p2;
 *p2 = p;
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
试题2:以下为Windows NT下的32位C++程序,请计算sizeof的值
void Func ( char str[100] )
{
 sizeof( str ) = ?
}

void *p = malloc( 100 );
sizeof ( p ) = ?
  解答:
sizeof( str ) = 4
sizeof ( p ) = 4
  剖析:
  Func ( char str[100] )函数中数组名作为函数形参时,在函数体内,数组名失去了本身的内涵,仅仅只是一个指针;在失去其内涵的同时,它还失去了其常量特性,可以作自增、自减等操作,可以被修改。
  数组名的本质如下:
  (1)数组名指代一种数据结构,这种数据结构就是数组;
  例如:
char str[10];
cout << sizeof(str) << endl;
  输出结果为10,str指代数据结构char[10]。
  (2)数组名可以转换为指向其指代实体的指针,而且是一个指针常量,不能作自增、自减等操作,不能被修改;
char str[10];
str++; //编译出错,提示str不是左值 
  (3)数组名作为函数形参时,沦为普通指针。
  Windows NT 32位平台下,指针的长度(占用内存的大小)为4字节,故sizeof( str ) 、sizeof ( p ) 都为4。
  试题3:写一个"标准"宏MIN,这个宏输入两个参数并返回较小的一个。另外,当你写下面的代码时会发生什么事?
least = MIN(*p++, b);
  解答:
#define MIN(A,B) ((A) <= (B) ? (A) : (B))
  MIN(*p++, b)会产生宏的副作用
  剖析:
  这个面试题主要考查面试者对宏定义的使用,宏定义可以实现类似于函数的功能,但是它终归不是函数,而宏定义中括弧中的"参数"也不是真的参数,在宏展开的时候对"参数"进行的是一对一的替换。
  程序员对宏定义的使用要非常小心,特别要注意两个问题:
  (1)谨慎地将宏定义中的"参数"和整个宏用用括弧括起来。所以,严格地讲,下述解答:
#define MIN(A,B) (A) <= (B) ? (A) : (B)
#define MIN(A,B) (A <= B ? A : B )
  都应判0分;
  (2)防止宏的副作用。
  宏定义#define MIN(A,B) ((A) <= (B) ? (A) : (B))对MIN(*p++, b)的作用结果是:
((*p++) <= (b) ? (*p++) : (*p++))
  这个表达式会产生副作用,指针p会作三次++自增操作。
  除此之外,另一个应该判0分的解答是:
#define MIN(A,B) ((A) <= (B) ? (A) : (B));
  这个解答在宏定义的后面加";",显示编写者对宏的概念模糊不清,只能被无情地判0分并被面试官淘汰。
  试题4:为什么标准头文件都有类似以下的结构?
#ifndef __INCvxWorksh
#define __INCvxWorksh
#ifdef __cplusplus

extern “C” {
#endif
//
#ifdef __cplusplus
}

#endif
#endif /* __INCvxWorksh */
  解答:
  头文件中的编译宏
#ifndef __INCvxWorksh
#define __INCvxWorksh
#endif
的作用是防止被重复引用。
  作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。函数被C++编译后在symbol库中的名字与C语言的不同。例如,假设某个函数的原型为:
void foo(int x, int y);
  该函数被C编译器编译后在symbol库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。_foo_int_int这样的名字包含了函数名和函数参数数量及类型信息,C++就是考这种机制来实现函数重载的。
  为了实现C和C++的混合编程,C++提供了C连接交换指定符号extern "C"来解决名字匹配问题,函数声明前加上extern “C"后,则编译器就会按照C语言的方式将该函数编译为_foo,这样C语言中就可以调用C++的函数了。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 试题5:编写一个函数,作用是把一个char组成的字符串循环右移n个。比如原来是"abcdefghi"如果n=2,移位后应该是"hiabcdefgh”
  函数头是这样的:
//pStr是指向以’\0’结尾的字符串的指针
//steps是要求移动的n

void LoopMove ( char * pStr, int steps )
{
 //请填充…
}
  解答:
  正确解答1:
void LoopMove ( char *pStr, int steps )
{
 int n = strlen( pStr ) - steps;
 char tmp[MAX_LEN];
 strcpy ( tmp, pStr + n );
 strcpy ( tmp + steps, pStr);
 *( tmp + strlen ( pStr ) ) = ‘\0’;
 strcpy( pStr, tmp );
}
  正确解答2:
void LoopMove ( char *pStr, int steps )
{
 int n = strlen( pStr ) - steps;
 char tmp[MAX_LEN];
 memcpy( tmp, pStr + n, steps );
 memcpy(pStr + steps, pStr, n );
 memcpy(pStr, tmp, steps );
}
  剖析:
  这个试题主要考查面试者对标准库函数的熟练程度,在需要的时候引用库函数可以很大程度上简化程序编写的工作量。
  最频繁被使用的库函数包括:
  (1) strcpy
  (2) memcpy
  (3) memset
  试题7:编写类String的构造函数、析构函数和赋值函数,已知类String的原型为:
class String
{
 public:
  String(const char *str = NULL); // 普通构造函数
  String(const String &other); // 拷贝构造函数
  ~ String(void); // 析构函数
  String & operate =(const String &other); // 赋值函数
 private:
  char *m_data; // 用于保存字符串
};
  解答:
//普通构造函数

String::String(const char *str)
{
 if(str==NULL)
 {
  m_data = new char[1]; // 得分点:对空字符串自动申请存放结束标志’\0’的空
  //加分点:对m_data加NULL 判断
  *m_data = ‘\0’;
 }
 else
 {
  int length = strlen(str);
  m_data = new char[length+1]; // 若能加 NULL 判断则更好
  strcpy(m_data, str);
 }
}

// String的析构函数

String::~String(void)
{
 delete [] m_data; // 或delete m_data;
}

//拷贝构造函数

String::String(const String &other)    // 得分点:输入参数为const型
{
 int length = strlen(other.m_data);
 m_data = new char[length+1];     //加分点:对m_data加NULL 判断
 strcpy(m_data, other.m_data);
}

//赋值函数

String & String::operate =(const String &other) // 得分点:输入参数为const型
{
 if(this == &other)   //得分点:检查自赋值
  return *this;
 delete [] m_data;     //得分点:释放原有的内存资源
 int length = strlen( other.m_data );
 m_data = new char[length+1];  //加分点:对m_data加NULL 判断
 strcpy( m_data, other.m_data );
 return *this;         //得分点:返回本对象的引用
}
  试题2:写一个函数返回1+2+3+…+n的值(假定结果不会超过长整型变量的范围)
  解答:
int Sum( int n )
{
 return ( (long)1 + n) * n / 2;  //或return (1l + n) * n / 2;
}
  剖析:
 
  对于这个题,只能说,也许最简单的答案就是最好的答案。下面的解答,或者基于下面的解答思路去优化,不管怎么"折腾",其效率也不可能与直接return ( 1 l + n ) * n / 2相比!
int Sum( int n )
{
 long sum = 0;
 for( int i=1; i<=n; i++ )
 {
  sum += i;
 }
 return sum;
}
  所以程序员们需要敏感地将数学等知识用在程序设计中。

在不用第三方参数的情况下,交换两个参数的值
#include

void main()
{
int i=60;
int j=50;
i=i+j;
j=i-j;
i=i-j;
printf(“i=%d\n”,i);
printf(“j=%d\n”,j);
}

方法二:
i^=j;
j^=i;
i^=j;

方法三:
// 用加减实现,而且不会溢出
a = a+b-(b=a)


18.有关位域的面试题(为什么输出的是一个奇怪的字符)
a.t = ‘b’;效果相当于 a.t= ‘b’ & 0xf;
‘b’ --> 01100010
‘b’ & 0xf -->>00000010
所以输出Ascii码为2的特殊字符
char t:4;就是4bit的字符变量,同样
unsigned short i:8;就是8bit的无符号短整形变量
用C++写个程序,如何判断一个操作系统是16位还是32位的?不能用sizeof()函数

A1:
16位的系统下,
int i = 65536;
cout << i; // 输出0;
int i = 65535;
cout << i; // 输出-1;

32位的系统下,
int i = 65536;
cout << i; // 输出65536;
int i = 65535;
cout << i; // 输出65535;

A2:

int a = ~0;
if( a>65536 )
{
cout<<“32 bit”< }
else
{
cout<<“16 bit”< }
6.下面是C语言中两种if语句判断方式。请问哪种写法更好?为什么?
int n;
if (n == 10) // 第一种判断方式
if (10 == n) // 第二种判断方式

如果少了个=号,编译时就会报错,减少了出错的可能行,可以检测出是否少了=

7.下面的代码有什么问题?
void DoSomeThing(…)
{
char* p;

p = malloc(1024); // 分配1K的空间
if (NULL == p)
return;

p = realloc(p, 2048); // 空间不够,重新分配到2K
if (NULL == p)
return;

}

A:
p = malloc(1024); 应该写成: p = (char *) malloc(1024);
没有释放p的空间,造成内存泄漏。

8.下面的代码有什么问题?并请给出正确的写法。
void DoSomeThing(char* p)
{
char str[16];
int n;
assert(NULL != p);
sscanf(p, “%s%d”, str, n);
if (0 == strcmp(str, “something”))
{

}
}

A:
sscanf(p, “%s%d”, str, n); 这句该写成: sscanf(p, “%s%d”, str, &n);

你可能感兴趣的:(c++面试题,c++,开发语言,后端)