C 语言面试准备
这是我为面试 C 语言作的准备。
(1) 局部变量 全局变量 静态变量 const 常量 寄存器变量 宏定义的常量 static 变量 (注:包
括它们的内存分配,作用域,初始化等)
局部变量: 指在函数或者是在块语句中内部定义的变量,作用域为定义该变量的函数,内存分
配: 在栈中按地址从高到低分配。
全局变量: 通常在文件开头定义(理想位置),当这些函数以及同一个程序中的其他源程序文
件中的某些函数需要使用该全局变量时,在函数内部对该变量使用 extern 加以说明他是外部
的; 作用域: 在程序的执行过程中一直有效; 初始化: 定义时不做初始化则系统将自动为起
赋值数值型为 0 内存分配: 从静态储存区域分配。
静态变量: 用 static 语句可将变量声明为静态变量; 作用域是当前的子程序模块内, 而它的
生存期是到整个总程序的结束; 内存分配: 全局/静态存储区。
寄存器类型:使用关键字 register 声明寄存器类型的目的是将所声明的变量放入寄存器内,从
而加快程序的运行速度。
试题 8:请说出 static 和 const 关键字尽可能多的作用
解答:
static 关键字至少有下列 n 个作用:
(1)函数体内 static 变量的作用范围为该函数体,不同于 auto 变量,该变量的内存只被分
配一次,因此其值在下次调用时仍维持上次的值;
(2)在模块内的 static 全局变量可以被模块内所用函数访问,但不能被模块外其它函数访
问;
(3)在模块内的 static 函数只可被这一模块内的其它函数调用,这个函数的使用范围被限
制在声明它的模块内;
(4)在类中的 static 成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;
(5)在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,因而只能访
问类的 static 成员变量。
const 关键字至少有下列 n 个作用:
(1)欲阻止一个变量被改变,可以使用 const 关键字。在定义该 const 变量时,通常需要对
它进行初始化,因为以后就没有机会再去改变它了;
(2)对指针来说,可以指定指针本身为 const,也可以指定指针所指的数据为 const,或二
者同时指定为 const;
(3)在一个函数声明中,const 可以修饰形参,表明它是一个输入参数,在函数内部不能改
变其值;
4)对于类的成员函数,若指定其为 const 类型,则表明其是一个常函数,不能修改类的成
员变量;
(5)对于类的成员函数,有时候必须指定其返回值为 const 类型,以使得其返回值不为“左
值”。例如: const classA operator*(const classA& a1,const classA& a2);
operator*的返回结果必须是一个 const 对象。如果不是,这样的变态代码也不会编译出
错: classA a, b, c;
(a * b) = c; // 对 a*b 的结果赋值
操作(a * b) = c 显然不符合编程者的初衷,也没有任何意义。
(2) malloc/new, free/delete 之间的区别
malloc,free 是 C 语言的标准函数库,new/delete 是 C++的运算符,他们都可以动态申请内存和
释放内存;
对于非内部数据类型的对象而言, 光用 malloc/free 无法满足动态对象的需求. 对象要创建的
同时要自动执行构造函数,对象在消亡之前要执行析构函数,由于 malloc/free 不是运算符, 不
在编译器控制权限之内,不能把构造函数和析构函数的任务强加于 malloc/free 上. 因此,C++
语言需要一个动态内存分配和初始化的运算符 new 和一个能完成清理与释放内存的运算符
delete
我们不要企图用 malloc/free 来完成动态对象的内存管理,应该用 new/delete。由于内部数据
类型的“对象”没有构造与析构的过程,对它们而言 malloc/free 和 new/delete 是等价的。
既然 new/delete 的功能完全覆盖了 malloc/free,为什么 C++不把 malloc/free 淘汰出局呢?这
是因为 C++程序经常要调用 C 函数,而 C 程序只能用 malloc/free 管理动态内存。
malloc/free 功能还有一好处,就是可以和 realloc 组合使用,在需要扩大内存块时不一定会导
致内存移动;而用 new/delete 实现时只能用 new[]-copy-delete[]操作序列完成,每次都会导
致内存移动。
(3) 内联函数宏定义区别, 各有什么优点.
C++语言支持内联函数,目的就是为了提高函数的执行效率.
内联函数直接将代码插入调用外,从而减少了普通函数调用时的资源消耗, 要对参数类型进
行检查;而宏不是函数,只是在编译前(预编译处理之前)将程序中有关字符串替换成宏体,省去
了参数压栈、生成汇编语言的 CALL 调用、返回参数、执行 return 等过程, 不对参数类型进
行检查;
两者都能提高运行效率,但使用宏代码最大的缺点是容易出错,对于 C++ 而言,使用宏代码还
有另一种缺点:无法操作类的私有数据成员; C++ 语言的函数内联机制既具备宏代码的效
率,又增加了安全性,而且可以自由操作类的数据成员。所以在 C++ 程序中,应该用内联
函数取代所有宏代码,“断言 assert”恐怕是唯一的例外。
(4)内存分配有哪几种形式?分别为何?区别是什么?对编译速度影响是何?
内存分配有三种方式:
从静态存储区域分配, 内存在编译时就已分配好, 这块内存在程序整个运行期间都存在, 如:
全局变量,STATIC 变量.
在栈上分配, 如函数的局部变量可以在栈上分配,函数结束时自己被释放,栈内存分配的运算
内置于处理器的指令集中,效率高,内存分配是连续的,,栈是向低地址扩展的数据结构,但容量
有限;
从堆上分配,亦即动态内存分配, 程序运行时用 New/Malloc 分配, 程序员自己负责何时用
Delete/Free 释放, 若程序员不释放,程序结束时可能由操作系统回收.类似于链表,在内存
中的分布不是连续的,它们是不同区域的内存块通过指针链接起来的.一旦某一节点从链中
断开,我们要人为的把所断开的节点从内存中释放.动态内存的生存期由我们决定,使用灵
活(堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的
空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限
于计算机系统中有效的虚拟内存),但问题也多
申请效率的比较:
栈由系统自动分配,速度较快。但程序员是无法控制的。
堆是由 new 分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.
(5)struct 和 class 有什么区别?c 语言中的 struct 和 c++中的 struct 一样么?有什么区别?
(一)默认继承权限。如果不明确指定,来自 class 的继承按照 private 继承处理,来自 struct
的继承按照 publ ic 继承处理;
(二)成员的默认访问权限。class 的成员默认是 private 权限,struct 默认是 public 权限。
如果没有多态和虚拟继承,在 C++中,struct 和 class 的存取效率完全相同!简单的说就是,
存取 class 的 data member 和非 virtual function 效率和 struct 完全相同!不管该 data member
是定义在基类还是派生类的。
(6)说说什么是野指针?野指针什么情况下出现?(没有初始化,delete 后没有赋值为
NULL)
野指针不是 NULL 指针,是指向垃圾内存的指针,也就是指向不可用内存的指针,通常对这种
指针进行操作会 产生不可知道的错误,人们一般不会用错 NULL 指针,但是野指针很危险,IF
对它不起作用;于出现的情况有两种:没有初始化和 delete/free 没有赋值为 NULL.
(7)你熟悉预编译指令么?条件编译是用来做什么的?你会写么?
预处理过程扫描源代码,对其进行初步的转换,产生新的源代码提供给编译器。可见预处理
过程先于编译器对源代码进行处理.
下面是部分预处理指令:
指令 用途
# 空指令,无任何效果
#include 包含一个源代码文件
#define 定义宏
#undef 取消已定义的宏
#if 如果给定条件为真,则编译下面代码
#ifdef 如果宏已经定义,则编译下面代码
#ifndef 如果宏没有定义,则编译下面代码
#elif 如果前面的#if 给定条件不为真,当前条件为真,则编译下面代码
#endif 结束一个#if……#else 条件编译块
#error 停止编译并显示错误信息
条件编译指令将决定那些代码被编译,而哪些是不被编译的。可以根据表达式的值或者某个
特定的宏是否被定义来确定编译条件。
如:
为了避免那些只能包含一次的头文件被多次包含,可以在头文件中用编译时条件来进行控
制。例如:
/*my.h*/
#ifndef MY_H
#define MY_H
……
#endif
1.#if 指令
#if 指令检测跟在制造另关键字后的常量表达式。如果表达式为真,则编译后面的代码,知
道出现#else、#elif 或#endif 为止;否则就不编译。
2.#endif 指令
#endif 用于终止#if 预处理指令。
#define DEBUG 0
main()
{
#if DEBUG
printf(”Debugging\n”);
#endif
printf(”Running\n”);
}
由于程序定义 DEBUG 宏代表 0,所以#if 条件为假,不编译后面的代码直到#endif,所以程
序直接输出 Running。
如果去掉#define 语句,效果是一样的。
(8)sizeof()这个函数你理解么?你能说出各种变量类型在 win 32 下的值么?
sizeof 是运算符,可用于任何变量名、类型名或常量值,当用于变量名(不是数组名)或常
量时,它不需要用圆括号。它在编译时起作用,而不是运行时。
各个数据的值:int(4), char(1), double(8), short(2)。
(9)知道 sizeof(Type*)=4 吗?注:win32 下
指针变量用 4 个字节去存储,无论是什么类型,都是 4 个字节。
(10)题目求解
int array[5]= {}; sizeof(array)=?
int Fuction(int a[])
{
return sizeof(a);
}
Fuction(array)=?
(11)函数不能返回栈指针
因为栈在函数退出的时候就会消除内存,所以不能返回栈指针。