21、请说出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显然不符合编程者的初衷,也没有任何意义。
22、编写类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加NULL 判断
{
m_data = new char[1];
*m_data = ‘\0′;// 得分点:对空字符串自动申请存放结束标志’\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; //得分点:返回本对象的引用
}
23、多态的作用
主要是两个:1. 隐藏实现细节,使得代码能够模块化;扩展代码模块,实现代码重用;2. 接口重用:为了类在继承和派生的时候,保证使用家族中任一类的实例的某一属性时的正确调用。
24、static全局变量与普通的全局变量有什么区别?static局部变量和普通局部 变量有什么区别?static函数与普通函数有什么区别?
static全局变量与普通的全局变量有什么区别:static全局变量只初使化一次,防止在其他文件单元中被引用;
static局部变量和普通局部变量有什么区别:static局部变量只被初始化一次,下一次依据上一次结果值;
static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝
25、生命周期,作用域的定义;说明全局变量、静态变量、局部变量、const变量的生命周期、作用域
生命周期,作用域的定义;说明全局变量、静态变量、局部变量、const变量的生命周期、作用域:
生命周期:是一个变量存在的周期。
作用域:是一个变量可以被引用的范围。最常见的如:{}、static修饰符等等。
1)全局变量:
作用域:全局作用域(只需要在一个源文件中定义,就可以作用于所有的源文件);
生命周期:程序运行期一直存在;
引用方法:其他文件如果要使用,必须用extern 关键字声明要引用的全局变量;
内存分布:全局(静态存储区)。
注意:如果再两个文件中都定义了相同名字的全局变量,则连接错误:变量重定义。
2)全局静态变量:
生命周期:程序运行期一直存在;
作用域:文件作用域(只在被定义的文件中可见:static的一个作用就是隐藏)
内存分布:全局(静态存储区)。
定义方法:static关键字,const关键字(注意C/C++意义不同)
注意:只要文件不相互包含,两个不同的文件中是可以定义完全相同的两个全局静态变量的。
3)静态局部变量:
生命周期:程序运行期一直存在;(超过其作用域便无法被引用)
作用域:局部作用域(只在局部作用于可见)
内存分布:全局(静态存储区)。
定义方法:局部作用域中用static定义。
注意:只被初始化一次,多线程中需要加锁保护。
4)局部变量:
生命周期:程序运行处局部作用域 即被销毁。
作用域:局部作用域(只在局部作用于可见)
内存分布:栈区
定义方法:在局部作用域中用auto指示符定义,可省略auto
static int c = 1;//全局初始化去(静态全局变量)
int a = 0; //全局初始化区(全局变量)
char *p1; //全局未初始化区(全局变量)
int main(int argc, char const *argv[])
{
int b;//存放于栈区(局部变量)
char s[] = "abc";//s存放于栈区(局部变量)
char *p2 ;//栈(局部变量)
char *p3 = "123456";//p3存放于栈区(局部变量);“123456”存放区文字常量区
static int c = 0;// c存放于全局初始化区(静态局部变量)
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);//分配得来的10和20字节的区域在堆区;
//变量p1、p2指向堆区分配的内存
strcpy(p1, "123456");//“123456”存放区文字常量区
return 0;
}
26、内存分配有哪几种形式?分别为何?区别是什么?对编译速度影响是何?
内存分配有三种方式:(1)从静态存储区域分配, 内存在编译时就已分配好, 这块内存在程序整个运行期间都存在, 如:全局变量,STATIC变量.
(2)在栈上分配, 如函数的局部变量可以在栈上分配,函数结束时自己被释放,栈内存分配的运算内置于处理器的指令集中,效率高,内存分配是连续的,,栈是向低地址扩展的数据结构,但容量有限;
(3)从堆上分配,亦即动态内存分配, 程序运行时用New/Malloc分配, 程序员自己负责何时用Delete/Free释放, 若程序员不释放,程序结束时可能由操作系统回收.类似于链表,在内存中的分布不是连续的,它们是不同区域的内存块通过指针链接起来的.一旦某一节点从链中断开,我们要人为的把所断开的节点从内存中释放.动态内存的生存期由我们决定,使用灵活(堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存),但问题也多
申请效率的比较:
栈由系统自动分配,速度较快。但程序员是无法控制的。
堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.
27、struct 和class有什么区别?c语言中的struct 和c++中的struct一样么?有什么区别?
从语法上来讲,class和struct做类型定义时只有两点区别:
1、默认继承权限,如果不指定,来自class的继承按照private继承处理,来自struct的继承按照public继承处理;
2、成员的默认访问权限。class的成员默认是private权限,struct默认是public权限。以上两点也是struct和class最基本的差别,也是最本质的差别;
但是在C++中,struct进行了扩展,现在它已经不仅仅是一个包含不同数据类型的数据结构了,它包括了更多的功能。Struct能包含成员函数,Struct有自己的构造函数,Struct可以有析构函数,Struct支持继承,Struct支持多态,Struct支持Private、Protected和Public关键字。
详细介绍:http://www.jb51.net/article/55877.htm
28、说说什么是野指针?野指针什么情况下出现?(没有初始化,delete后没有赋值为NULL)
29、你熟悉预编译指令么?条件编译是用来做什么的?你会写么?
预编译又称为预处理,是做些代码文本的替换工作
预编译又称为预处理,是做些代码文本的替换工作
处理#开头的指令,比如拷贝#include包含的文件代码,#define宏定义的替换,条件编译等
就是为编译做的预备工作的阶段
主要处理#开始的预编译指令
预编译指令指示了在程序正式编译前就由编译器进行的操作,可以放在程序中的任何位置。常见的预编译指令有:
(1)、#include 指令
该指令指示编译器将xxx.xxx文件的全部内容插入此处。若用<>括起文件则在系统的INCLUDE目录中寻找文件,若用" "括起文件则在当前目录中寻找文件。一般来说,该文件是后缀名为"h"或"cpp"的头文件。
注意:<>不会在当前目录下搜索头文件,如果我们不用<>而用""把头文件名扩起,其意义为在先在当前目录下搜索头文件,再在系统默认目录下搜索。
(2)、#define指令
该指令有三种用法:
第一种是定义标识,标识有效范围为整个程序,形如#define XXX,常与#if配合使用;
第二种是定义常数,如#define max 100,则max代表100(这种情况下使用const定义常数更好,原因见注1);
第三种是定义"函数",如#define get_max(a, b) ((a)>(b)?(a):(b)) 则以后使用get_max(x,y)就可以得到x和y中较大的数(这种方法存在一些弊病,见注2)。
第四种是定义"宏函数",如#define GEN_FUN(type) type max_##type(type a,type b){return a>b?a:b;} ,使用时,用GEN_FUN(int),则此处预编译后就变成了 max_int(int a,int b){return a>b?a:b;},以后就可以使用max_int(x,y)就可以得到x和y中较大的数.比第三种,增加了类型的说明。
(3)、#if、#else和#endif指令
这些指令一般这样配合使用:
#if defined(标识) //如果定义了标识
要执行的指令
#else
要执行的指令
#endif
在头文件中为了避免重复调用(比如说两个头文件互相包含对方),常采用这样的结构:
#if !(defined XXX) //XXX为一个在你的程序中唯一的标识符,
//每个头文件的标识符都不应相同。
//起标识符的常见方法是若头文件名为"abc.h"
//则标识为"abc_h"
#define XXX
真正的内容,如函数声明之类
#endif
一般情况下,源程序中所有的行都参加编译。但有时希望对其中一部分内容只在满足一定条件下才进行编译,即对一部分内容指定编译条件,这就是“条件编译”(conditional compile)。
#if、#else、#elif和#endif指令
30、strcpy函数的实现
已知strcpy函数的原型是:
char *strcpy(char *dst, const char *src);
实现strcpy函数
解释为什么要返回char *
假如考虑dst和src内存重叠的情况,strcpy该怎么实现
char * strcpy(char *dst,const char *src) //[1]
{
assert(dst != NULL && src != NULL); //[2]
char *ret = dst; //[3]
while ((*dst++=*src++)!='\0'); //[4]
return ret;
}
31、知道断言ASSERT()怎么样么?一定要常用。它是函数还是宏?为什么不能是函数?
assert不仅仅是个报错函数,事实上,它居然是个宏,并且作用并非“报错”。
在经过对其进行一定了解之后,对其作用及用法有了一定的了解,assert()的用法像是一种“契约式编程”,在我的理解中,其表达的意思就是,程序在我的假设条件下,能够正常良好的运作,其实就相当于一个if语句:
if(假设成立)
{
程序正常运行;
}
else
{
报错&&终止程序!(避免由程序运行引起更大的错误)
}
32、懂什么是链表么?会链表的一些基本操作么?
#include<stdio.h>
#include<stdlib.h>
typedef struct node
{
int data;
node* pNext;
}Node;
//链表的操作,以有头节点为例,无头节点类似
Node* head = NULL;
//创建链表,头结点data=0,pNext=NULL;
bool createNodeList()
{
head = (Node*) malloc(sizeof(Node));
if(NULL == head)
{
return false;
}
else
{
head->data = 0;
head->pNext = NULL;
return true;
}
}
//增加节点
bool addNode(Node* node)
{
if(NULL == head)
{
return false;
}
Node* p = head->pNext;
Node* q = head;
while(NULL != p)
{
q = p;
p = p->pNext;
}
q->pNext = node;
node->pNext = NULL;
return true;
}
//删除节点
bool deleteNode(int index)
{
if(NULL == head)
{
return false;
}
Node* p = head->pNext;
int length = 0;
while(NULL != p)
{
length ++;
p = p->pNext;
}
if(length < index)
{
return false;
}
else
{
Node* q = head;
p = head;
for(int i=0;i<index;i++)
{
q = p;
p = p->pNext;
}
Node* t = p->pNext;
q->pNext = t;
free(p);
return true;
}
}
//逆序
void reverseNodeList()
{
if(NULL == head)
{
return;
}
//如果链表长度为1
if(head->pNext == NULL)
{
return;
}
Node* p = head->pNext;
Node* q = p->pNext;
Node* t = NULL;
while(NULL != q)
{
t = q->pNext;
q->pNext = p;
p = q;
q = t;
}
head->pNext->pNext = NULL;
head->pNext = p;
}
//排序(降序)
void sort()
{
//冒泡排序
Node* pHead = head;
if(head == NULL)
{
return;
}
if(pHead->pNext == NULL)
{
return;
}
Node* pi = pHead->pNext;
Node* pj = pi->pNext;
for(;pi != NULL;pi=pi->pNext)
{
for(pj = pi->pNext;pj != NULL;pj=pj->pNext)
{
if(pj->data>pi->data)
{
int tmp = pj->data;
pj->data = pi->data;
pi->data = tmp;
}
}
}
}
//销毁
void destroyNodeList()
{
if(NULL == head)
{
return;
}
if(NULL == head->pNext)
{
free(head);
head = NULL;
return;
}
Node* p = head->pNext;
while(NULL != p)
{
Node* tmp = p;
p = p->pNext;
free(tmp);
}
free(head);
head = NULL;
}
void main()
{
createNodeList();
Node* node1 = (Node*)malloc(sizeof(Node));
node1->data = 1;
node1->pNext = NULL;
Node* node2 = (Node*)malloc(sizeof(Node));
node2->data = 2;
node2->pNext = NULL;
addNode(node1);
addNode(node2);
reverseNodeList();
Node* node3 = (Node*)malloc(sizeof(Node));
node3->data = 3;
node3->pNext = NULL;
addNode(node3);
sort();
deleteNode(2);
destroyNodeList();
}
33、明白队列,双链表,循环链表,栈是怎么回事?会写这些类。
34、一定要知道二叉树的遍历有几种,一定要会写用递归的方式来遍历它们。
35、在一个字符串中,你能计算里边“数字字符”的个数么?
36、知道sizeof(Type*)=4吗?注:win32下
37、
int array[5]= {}; sizeof(array)=?
int Fuction(int a[])
{
return sizeof(a);
}
Fuction(array)=?
38、你知道函数不能返回栈指针么?
39、知道局部变量为什么比全局变量快么?
40、知道为什么要人为写拷贝构造函数么?