1.用#define声明一个常数,表明1年中有多少秒(忽略闰年问题)?
答案:#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL
要点:
A.没有以分号结束;
B.懂得预处理器将为你计算常数表达式的值。即60 * 60 * 24 * 365而不是31536000.
C.考虑到了16位机将会溢出,巧妙运用了UL。
2.写一个“标准”宏MIN,这个宏输入两个参数并返回较小的一个?
答案:#define MIN(A,B) ((A) <= (B) ? (A) : (B))
要点:
A.参数用括号括起来;
B.考察能否合理运用条件运算符;
3.Heap与Stack的差别?
答案:Heap是堆,Stack是栈。
Stack的空间由系统自动分配/释放,Heap上的空间手动分配/释放。
Stack空间有限,Heap是很大的自由存储区,malloc函数分配的内存空间即在堆上。
网上经典例子:
int a = 0; //全局初始化区
char *p1;//全局未初始化区
void main(void)
{
int b; //栈
char s[] = "abc"; //栈
char *p2; //栈
char *p3 = "123456"; //123456\0在常量区,p3在栈上
static int c =0; //全局(静态)初始化区
p1 = (char *)malloc(10); //堆
p2 = (char *)malloc(20); //堆
}
4.用变量a给出下面的定义
A.一个整型数?
答案:int a;
B.一个指向整型数的指针?
答案:int *a;
C.一个指向指针的的指针,它指向的指针是指向一个整型数?
答案:int **a;
D.一个有10个整型数的数组?
答案:int a[10];
E.一个有10个指针的数组,该指针是指向一个整型数的?
答案:int *a[10];
F.一个指向有10个整型数数组的指针?
答案:int (*a)[10];
G.一个指向函数的指针,该函数有一个整型参数并返回一个整型数?
答案:int (*a)(int);
H.一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数?
答案:int (*a[10])(int);
5.关键字static的作用是什么?
A.在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变;
B.在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的局变量;
C.在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用;
6.关键字const有什么含意?
答案:const意味着"只读"。
如果回答"const意味着常数",面试官会觉得你只是一个业余的人。
7.下面的声明都是什么意思?
const int a;
int const a;
const int *a;
int * const a;
int const * a const;
答案:
A.前两个的作用是一样,a是一个常整型数;
B.第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以);
C.第四个意思a是一个指向整型 数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的);
D.最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数 是不可修改的,同时指针也是不可修改的)。
8.关键字volatile有什么含意?
答案:一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。
9.const, volatile区别
(1)const含义是“请做为常量使用”,而并非“放心吧,那肯定是个常量”,是不可修改的只读变量。
volatile的含义是“请不要做自以为是的优化,这个值可能变掉的”,而并非“你可以修改这个值”。
(2)const只在编译期有用,在运行期无用
const在编译期保证在C的“源代码”里面,没有对其修饰的变量进行修改的地方(如有则报错,编译不通过),而运行期该变量的值是否被改变则不受const的限制。
volatile在编译期和运行期都有用
在编译期告诉编译器:请不要做自以为是的优化,这个变量的值可能会变掉;
在运行期:每次用到该变量的值,都直接从内存中取该变量的值。
(3)const, volatile同时修饰一个变量
合法性
“volatile”的含义并非是“non-const”,volatile 和 const 不构成反义词,所以可以放一起修饰一个变量。
同时修饰一个变量的含义
表示一个变量在程序编译期不能被修改且不能被优化;在程序运行期,变量值可修改,但每次用到该变量的值都要从内存中读取,以防止意外错误。
10.sizeof和strlen的区别?
答案:sizeof是运算符,在编译时即计算好了; 而strlen是函数,要在运行时才能计算。
11.在某工程中,要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务?
答案:
int *p;
p = (int*)0x67a9;
*p = 0xaa66;
12.给定一个整型变量a,写两段代码,第一个设置a的bit 3,第二个清除a 的bit 3。在以上两个操作中,要保持其它位不变?
答案:
#define BIT3 (0x1 << 3)
static int a;
void set_bit3(void)
{
a |= BIT3;
}
void clear_bit3(void)
{
a &= ~BIT3;
}
13.下面函数错误吗?
int square(volatile int *ptr)
{
return *ptr * *ptr;
}
答案:错误;
正确写法:
long square(volatile int *ptr)
{
int a;
a = *ptr;
return a * a;
}
14 空指针和悬垂指针的区别
答:空指针是指被赋值为NULL的指针;delete指向动态分配对象的指针将会产生悬垂指针。
(1)空指针可以被多次delete,而悬垂指针再次删除时程序会变得非常不稳定。
(2)使用空指针和悬垂指针都是非法的,而且有可能造成程序崩溃,如果指针是空指针,尽管同样是崩溃,但和悬垂指针相比是一种可预料的崩溃。
15 虚函数如何实现多态
答:基类的指针指向子类的对象,子类对象重写了基类中的虚函数,那么在用基类指针操作该函数时就会根据其指向对象的类型(基类还是子类)分别调用不同的函数。
这是通过虚函数表实现的。只要类定义中含有虚函数声明,那么编译器就会为该类建立对应的虚函数表,按照虚函数声明顺序将函数入口地址保存在虚函数表中,这个表不占用对象内存,但是编译器会为对象分配一个指向虚函数表地址的指针vptr,在对象中占有一个指针大小的内存。子类重写的虚函数的地址直接替换了父类虚函数在虚函数表中的位置,而子类中独有的虚函数在其虚函数表中会依次排在从父类继承的虚函数后面。
在实际调用函数时,编译器先检查该函数是否为虚函数,如果是,则在对象的虚函数表中找到函数入口地址进行调用。对于在子类总重写的虚函数,这样就实现了多态。
16 C++虚基类的作用、用法和意义
如果一个派生类有多个直接基类,而这些直接基类又有一个共同的基类,则在最终的派生类中会保留该间接共同基类数据成员的多份同名成员。这种现象是人们不希望出现的。C++提供虚基类(virtual base class )的方法,使得在继承间接共同基类时只保留一份成员。虚基类并不是在声明基类时声明的,而是在声明派生类时,指定继承方式时声明的。因为一个基类可以在生成一个派生类时作为虚基类,而在生成另一个派生类时不作为虚基类。
17 分别简单介绍一下c++中的 虚函数 纯虚函数 虚基类
虚函数 ,纯虚函数, 虚基类,它们都和virtual有关,这三个带有虚字的定义容易使人混淆,下面先从作用上来解释这三个定义的区别:
1.虚函数是用于多态中virtual修饰父类函数,确保父类指针调用子类对象时,运行子类函数的。
2.纯虚函数是用来定义接口的,也就是基类中定义一个纯虚函数,基类不用实现,让子类来实现。
3.虚基类是用来在多继承中,如果父类继承自同一个父类,就只实例化一个父类(说的有点绕,就是只实例化一个爷爷的意思。
18 析构函数什么情况下要定义为虚函数
答:析构函数的作用与构造函数正好相反,是在对象的生命期结束时,释放系统为对象所分配的空间,即要撤消一个对象。
用对象指针来调用一个函数,有以下两种情况:
1.如果是虚函数,会调用派生类中的版本。(在有派生类的情况下)
2.如果是非虚函数,会调用指针所指类型的实现版本。
析构函数也会遵循以上两种情况,因为析构函数也是函数嘛,不要把它看得太特殊。 当对象出了作用域或是我们删除对象指针,析构函数就会被调用。
1.当派生类对象出了作用域,派生类的析构函数会先调用,然后再调用它父类的析构函数, 这样能保证分配给对象的内存得到正确释放。
2.但是,如果我们删除一个指向派生类对象的基类指针,而基类析构函数又是非虚的话, 那么就会先调用基类的析构函数(上面第2种情况),派生类的析构函数得不到调用,造成内存泄露。