1 不能做switch()的参数类型是什么数据类型?

switch(表达式),表达式可以是整型、字符型以及枚举类型等表达式。switch()的参数不能是实型。


2 static VS. const

static:

(1)函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值。

(2)在模块内的static全局变量可以被模块内所用函数访问。

(3)在模块内的static函数只可以被这一模块的其他函数调用。

(4)类中的static成员变量属于整个类所有,对类的所有对象只有一份拷贝。

(5)在类中的static成员函数属于整个类所拥有,这个函数不接受this指针,因而只能访问类的static


const:

(1)阻止一个变量被改变,可以使用const关键字。

(2)对指针来说,可以指定指针本身为const;或指定指针所指的数据为const,或二者同时指定为const。

(3)在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值。

(4)对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量。

(5)对于类的成员函数,有时必须指定其返回值为const类型,以使其返回值不为“左值”。


3 预编译

预编译就是处理#开头的命令。预编译就是为编译做的预备工作的阶段,主要处理#开始的预编译指令。

预编译指令指示了在程序正式编译前就由编译器进行的操作,可以放在程序中的任何位置。


何时需要预编译,具体如下:

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

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


4 写一个宏定义,返回较小一个

#define Min(X,Y) ((X)>(Y) ? (Y):(X));


5 嵌入式系统中如何使用C语言编写死循环?

while(1) {}  或 for(;;)


6 int(*s[10])(int) 表示:表示一个函数指针数组,将每一个指针指向一个 int func(int param)的函数。


7 换两个变量的值而不使用第3个变量,即a=3,b=5,交换后a=5,b=3;

分析:两种解析方法。一种是算术,一种是异或。

a=a+b;

b=a-b;

a=a-b;

a=a^b;//只能对int ,char

b=a^b;

a=a^b;


8 C和C++中的struct区别

C和C++中,struct的主要区别是C中的struct不可以含有成员函数,而C++中的struct可以。

在C++中struct和class的主要区别在于默认的存取权限不同,struct默认为public,而class默认为private。


9 如何让程序跳转到绝对地址0x100000去执行?

若要对绝对地址0x100000赋值,可以用(unsigned int *)0x100000=1234.那么如何让程序跳转到绝对地址0x100000去执行?

分析:

*(  (void (*)  ( )  )  0x100000 )  (  );

首先将0x100000强制转换成函数指针,即(void (*)  ( )  )  0x100000;

然后再调用它,即  *(void (*)  ( )  )  0x100000  ();  


10 已知一个数组table,用一个宏定义求出数据的元素个数

#define NTBL;

#define NTBL (sizeof(table)/sizeof(table[0]));


11 关于线程和进程的典型问题

线程与进程有何区别和联系?线程是否具有相同的堆栈?DLL是否有独立的堆栈?

分析:进程是死的,只是一些资源的集合,真正的程序执行都是线程来完成的。在程序启动时,操作系统就是帮助用户创建一个主线程的。

每个线程都有自己的堆栈。

DDL数据库模式定义语言中有独立的堆栈。因为DDL的代码是被某些线程所执行,只有线程拥有堆栈,如果DLL中的代码是EXE中的线程所调用,那么这个时候是不是说这个DLL没有自己独立的堆栈?如果DLL中的代码是由自己创建的线程所执行,那么是不是说DLL有独立的堆栈?

以上介绍的是堆栈。因为对于堆来说,每个DLL都有自己的堆,所以如果是中DLL中动态分配的内存,最好是从DLL中删除。如果你再DLL中分配内存,然后再EXE中或另外一个DLL中删除,很有可能导致程序崩溃。


12 什么是引用?说明申明和使用引用要注意的问题

引用是某个目标变量的“别名”,对引用的操作与对变量直接操作效果完全相同。申明一个引用的时候,一定要对其进行初始化。引用申明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,不能再把该引用名作为其他变量名的别名。申明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。不能建立数组的引用。


13 将引用作为函数的参数有哪些特点:

(1)传递引用给函数与传递指针的效果是相同的。被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被掉函数中对形参变量的操作就是对其相应的目标对象的操作、

(2)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。

(3)使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用“*指针变量名”的形参进行运算,很容易产生错误且程序的阅读性较差。另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。


14  什么时候需要使用常引用

分析:既需要使用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应该使用常引用,常引用声明方式:

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类型,这是非法的。


15 将引用作为函数返回值类型的格式及其优点和需遵守的规则

好处:在内存中不产生被返回值的副本。

 注意:正是因为这点原因,所以返回一个局部变量的引用是不可取的。因为随着该局部变量生存期的结束,相应的引用也会失效,产生runtime error。


需要遵守的规则:

(1)不能返回局部变量的引用。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了“无所指”的引用,程序进入未知状态。

(2)不能返回函数内部new分配的内存和引用。

(3)可以返回类成员的引用,但最好是const。


16 引用于多态的关系

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


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

C++编译时定义了_cplusplus

C编译时都以了_STDC_


18 结构与联合有何区别?

区别:结构和联合都是由多个不同的数据类型成员组成的。但是任何时刻,联合中只存放一个被选中的成员(所有成员共用一块地址空间),而结构的所有成员都存在(不同成员的存放地址不同)。

(2)对于联合的不同成员赋值,将会对其他成员重写,原来成员的值就不存在了。而对于结构不同成员赋值是互不影响的。


19 分析下列程序:

#include <iostream>
union {
	int i;
	char x[2];
}a;

using namespace std;
int main() {
	a.x[0]=10;
	a.x[1]=1;
	printf("%d",a.i);	
return 0;	
}

输出结果266. 低位地地址,高位高地址,内存占用情况是0x010A。



#include <iostream>
using namespace std;
int main() {
	union {
		int i;
		struct {
			char first;
			char second;
		}
		half;
	}number;
	number.i=0x4241;
	printf("%c%c\n",number.half.first,number.half.second);	
	number.half.first='a';
	number.half.second='b';
	printf("%x\n",number.i);
return 0;	
}

输出结果:AB(0x41对应'A'是低位;0x42对应‘B’是高位),6261两者共用一个内存空间。


20 一个32位机器的指针是多少位

分析:指针是多少位只要看地址总线的位数即可,80836以后的机子都是32位的数据总线,所以指针的位数就是4个字节。


21 类成员函数的重载、覆盖、隐藏

分析: 

成员函数重载的特征:相同范围(同一个类中)、函数名字相同、参数不同、virtual关键字可有可无。

覆盖是指派生类函数覆盖基类函数,特征: 不同范围、函数名字相同、参数相同、基类函数必需有virtual。

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

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

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


22 const与#define相比有何优点

(1)const常量有数据类型,而宏常量没有数据类型。使用的时候,编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生一些意料不到的错误。

(2)有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。


23 h头文件的ifdef/define/endif有何作用?

防止该头文件被重复引用。


24 #include<file.h>与#include"file.h"有何区别?

前者是从Standard Library的路径寻找和引用file.h,而后者是从当前工作路径搜寻并引用file.h。


25 C++中调用被C编译器编译后的函数为何要加extern “C”?

(1)作为extern是C/C++语言中表明函数和全局变量作用范围的关键字,该关键字告诉编译器,其申明的函数和变量可以在本模块或其他模块中使用。

(2)在模块的头文件中对本模块提供给其他模块引用的函数和全局变量以关键字extern声明。

(3)extern “C”是连接申明,被extern “C”修饰的变量和函数是按照C语言方式编译和连接的。


一句话概括extern C这个声明的真实目的: 实现C++与C及其他语言的混合编程。

任何语言中的任何语法特性的诞生都不是随意而为ide,来源于真实世界的需求驱动。我们在思考问题时,不能只停留在这个语言是怎么做到的,还要问一问它为什么这样做,动机是什么,这样我们可以更深入地理解许多问题。


26 关联、聚合及组合有何区别

关联是表示两个类的一般性联系。比如老师和学生就是一种关联关系。

聚合表示has-a的关系,是一种相对松散的关系,聚合类不需要对被聚合类负责。如下所示,使用空的菱形表示聚合关系。

从实现角度讲,聚合可以表示为:

class A
{
.
.
.
}
class B
{
A *a;
.
.
.
}


组合表示contains-a的关系,关联性强于聚合:组合类与被组合类有相同的生命周期,组合类要对被组合类负责,采用实心的菱形表示组合关系。实现形式如下:

class A
{
.
.
.
}
class B
{
A a;
.
.
.
}




27 多重继承如何消除向上继承的二义性

因为使用了多重继承,就可能会存在向上继承的二义性,如何解决呢?使用虚拟继承即可。


《C/C++》程序员面试宝典》  P252-P266