?请填写bool、intfloat、、指针与“零值”进行相等判断的if语句
--------------------------------------------------------
bool a;
int b;
float c;
void* d;
//上述变量经过赋值之后与“零值”进行相等判断的if语句如下
if (a)
{
//具体执行语句
}
if (0 == b)
{
//具体执行语句
}
if (c >= -0.0001 && c <= +0.0001)
{
//具体执行语句
}
if (NULL == d)
{
//具体执行语句
}
bool类型本来的结果就是真或假,所以直接填写在()中;
int类型就直接和0进行相等判断即可,注意最好常量0写在左侧,int变量写在右侧,这样的话一旦我们不小心把==写为=那么编译器将直接报错,因为赋值运算符的左侧必须是变量;
float的使用中,我们一般不能接和0.0f进行相等判断,浮点数的具体数值往往和业务需要的精度有关系,所以我们往往是确定一个接近于0的范围,如果待判定浮点数在这个范围内我们就认为它等于了0;
指针应该和标准库预定义好的宏NULL来比较,NULL的含义代表“空”,虽然一般情况下NULL的结果就是0,但是依据指针存储地址的概念来讲还是用NULL而不是0。
?sizeof的隐式转换算数计算的2个准则
所有小于整数类型的有序类型的算数表达式在计算之前类型会被转换为整型
小的类型会转换为大的类型
?面向对象和面向过程的
简单的说面向对象,面向对象的的基本特征就是 封装,继承,多态(类),面向过程 就是很多函数但是没有类
?假设程序运行环境为32位操作系统,请填写以下sizeof的结果
sizeof('A' + false)的结果为___
sizeof(1 + 1.0)的结果为___
sizeof(sizeof(double))的结果为___
short x[] = {1, 2, 3, 4, 5};
sizeof(x[0])的结果为___
sizeof(x)的结果为___
short y[8] = {1, 2, 3, 4, 5};
short* z = y + 2;
sizeof(y)的结果为___
sizeof(z)的结果为___
char s[] = "Hello";
char* p = s;
char* q = (char*)malloc(sizeof(double));
sizeof("Hello")的结果为___
sizeof("Hello"[2])的结果为___
sizeof(s)的结果为___
sizeof(*s)的结果为___
sizeof(p)的结果为___
sizeof(q)的结果为___
sizeof(*q)的结果为___
sizeof(&q)的结果为___
struct ABC
{
char x;
int y;
short z;
};
sizeof(ABC)的结果为___
#pragma pack(push, 1)
struct DEF
{
char d;
int e;
short f;
};
#pragma pack(pop)
sizeof(DEF)的结果为___
--------------------------------------------------------
sizeof('A' + false)的结果为_4_;char和bool相交会被隐式转换为int再相加,所以结果为int占4字节
sizeof(1 + 1.0)的结果为_8_;int和double相交会被隐式转换为double再相加,所以结果为double占8字节
sizeof(sizeof(double))的结果为_4_;sizeof运算符的结果为unsigned int,所以左侧相当于是sizeof(unsigned int),结果就是4字节
short x[] = {1, 2, 3, 4, 5};
sizeof(x[0])的结果为_2_;x[0]是short变量,占2字节
sizeof(x)的结果为_10_;x是数组名,sizeof(x)得到整个数组占据的字节数为10字节
short y[8] = {1, 2, 3, 4, 5};
short* z = y + 2;
sizeof(y)的结果为_16_;y是数组名,sizeof(y)得到整个数组占据的字节数为16字节
sizeof(z)的结果为_4_;z是指针存储了y数组首地址,sizeof(指针)的结果都是4字节
char s[] = "Hello";
char* p = s;
char* q = (char*)malloc(sizeof(double));
sizeof("Hello")的结果为_6_;sizeof()填写字符串字面常量的话,将得到存储这个字符串字面常量所需要的字节空间并包含结束符0,结果为Hello长度加1一共6字节
sizeof("Hello"[2])的结果为_1_;"Hello"作为表达式的结果是一个char类型地址,所以"Hello"[2]的结果就是一个char类型变量,所以sizeof结果为1字节
sizeof(s)的结果为_6_;s是数组名,sizeof(s)得到整个数组占据的字节数为6字节
sizeof(*s)的结果为_1_;*s是char类型变量,sizeof的结果就是1字节
sizeof(p)的结果为_4_;p是指针存储了s数组首地址,sizeof(指针)的结果都是4字节
sizeof(q)的结果为_4_;q是指针存储了堆内存的地址,sizeof(指针)的结果都是4字节
sizeof(*q)的结果为_1_;*q是char类型变量,sizeof的结果就是1字节
sizeof(&q)的结果为_4_;q是指针本身也存储在内存中,&q就是q所在内存的首字节地址,sizeof(地址)的结果都是4字节
struct ABC
{
char x; 1
int y; 4
short z; 2
};
sizeof(ABC)的结果为_12_;结构体有内存对齐的设置,ABC结构体中y的起始下标因为必须为sizeof(int)的整数倍所以会被设置为4,即x后y前会填充3个无用字节,此时情况为x占1字节、补充了3字节、y占4字节、z占2字节,总共10字节,又由于结构体的总大小必须为最大成员变量大小的整数倍,所以最终会在z之后再补充2个无用字节,使得整体sizeof(ABC)的结果为12字节
#pragma pack(push, 1)
struct DEF
{
char d;
int e;
short f;
};
#pragma pack(pop)
sizeof(DEF)的结果为_7_;#pragma pack(push, 1)的作用是限制结构体的内存对齐功能,填写1表示强制让结构体中每个成员变量都按1字节的整数倍对齐,当然这种情况相当于就不进行任何内存对齐,所以sizeof(DEF)的结果就是sizeof(char)+sizeof(int)+sizeof(short)一共7字节,最后的#pragma pack(pop)表示取消强制对齐功能
?全局变量和局部变量在内存存储上有什么区别
--------------------------------------------------------
全局变量存储在静态内存区,在程序进入入口点之前分配内存,在程序退出入口点之后释放内存;局部变量存储在栈内存区,在程序执行其定义语句的时候分配内存,在程序退出其所在作用域之后释放内存
?请简述C++语言中的标识符原则
--------------------------------------------------------
标识符只能由字母、数字、下划线组成;标识符只能由字母、下划线开头;标识符不能是关键字
?请简述return关键字的作用
--------------------------------------------------------
有两个作用,作用一是结束函数的执行;作用二是在非void返回值类型的函数中返回一个类型与返回值类型相同的表达式
?请简述void关键字在C++语言中的作用
--------------------------------------------------------
有两个作用,作用一是修饰函数的返回值类型表明该函数不返回任何数据;作用二是用于定义void类型的指针,这种指针存储的地址只有地址编号信息,没有地址类型信息,所以使用的时候一般需要强制转换为确定类型的地址再使用
?局部变量能够和全局变量重名吗
--------------------------------------------------------
可以重名,如果局部变量和全局变量重名,则默认在函数中访问的相同标识符是代表局部变量,如果要访问全局变量,则可以在标识符前面加上::即可,::在此处表示要访问全局域中的标识符
?指针常量和常量指针
常量指针:它表示一个指向常量的指针,这个指针指向的变量不能通过指针来修改,但是可以修改指针的指向
写法const int* a = c;
指针常量:指针本身就是一个常量 (必须初始化)可以通过指针修改变量值,不能修改指针指向;
写法int* const a = c;
?请简述const关键字在C++语言中的作用
--------------------------------------------------------
可以定义符号常量、常量指针、指针常量,还可以可以修饰函数的参数、返回值,被const修饰的东西都受到强制保护只能进行读操作不能进行写操作,可以预防意外的修改数据从而提高程序的健壮性
?请简述extern关键字在C++语言中的作用
--------------------------------------------------------
有两个作用,作用一是用于声明全局变量;作用二是告知C++编译器,某些函数是被C语言编译器编译出来的,这样C++就能通过正确的名称来链接C语言编译器编译出来的函数了
?请简述头文件包含中使用<>和""的不同
--------------------------------------------------------
<>是从编译系统设置的头文件搜索目录中搜索头文件,找不到就报错;""是先在工程所在目录中搜索头文件,找不到就再去编译系统设置的头文件搜索目录中搜索头文件,再找不到就报错
?请简述头文件中使用#ifndef、#define、#endif的目的
--------------------------------------------------------
防止头文件被同一份源文件重复包含
?请简述static关键字在C++语言中的作用
--------------------------------------------------------
static修饰局部变量,则被修饰的局部变量被放在静态内存区,初次执行定义语句的时候分配内存,程序退出入口点函数之后释放内存;static修饰全局变量或函数,则被修饰的全局变量或函数只能被其所在的源文件中的其它函数访问;static修饰类中成员变量,则被修饰的类中成员变量被放在静态内存区,在进入入口点函数之前分配内存,程序退出入口点函数之后释放内存,所有的类对象都共享一份静态成员变量;static修饰类中成员函数,则被修饰的成员函数没有this指针,且只能访问类中静态成员
?请简述C、C++语言中涉及到的所有内存类型及其各自的特点(加入英文)
--------------------------------------------------------
栈内存(stack),非静态局部变量和形式参数都在栈内存中,在执行非静态局部变量的定义语句的时候分配非静态局部变量的内存,在调用函数的时候分配形式参数的内存,在退出它们的作用域的时候释放内存。静态内存(static),静态局部变量和全局变量都在静态内存中,在初次执行静态局部变量的定义语句的时候分配静态局部变量的内存,在进入入口点函数之前分配全局变量的内存,在退出入口点函数之后释放它们的内存。堆内存(heap),C语言中使用malloc(calloc、realloc)、free函数分配和释放堆内存,C++语言中使用new(new [])、delete(delete [])关键字分配和释放堆内存,并且相比C语言的malloc(calloc、realloc)、free函数,它们还要执行类对象的构造函数和析构函数
?请简述malloc、free和new、delete的区别
--------------------------------------------------------
malloc是C语言中分配堆内存的函数,free是C语言中释放堆内存的函数;new是C++语言中分配堆内存的关键字,在分配了堆内存之后还会执行类对象的构造函数,delete是C++语言中释放堆内存的关键字,在释放堆内存之前还会执行类对象的析构函数
?请简述引用与指针的区别
--------------------------------------------------------
?请简述重载与重写的区别
--------------------------------------------------------
重载是指允许存在多个同名函数,而这些函数的形式参数表不同,可以是参数个数不同,可以参数类型不同,也可以两者都不同;重写是指在类的继承体系下子类重新定义父类的虚成员函数
?什么函数不能声明为虚函数
--------------------------------------------------------
类中的构造函数和静态成员函数不能声明为虚函数
?请简述C++语言中对虚函数调用的处理细节
--------------------------------------------------------
对于每个包含一个或多个虚函数的类,C++编译器都自动为其分配一个虚函数指针,这个虚函数指针被设置为自动指向一个虚函数表,这个虚函数表的本质就是一个虚函数地址数组,数组中存储了继承体系中的虚函数地址,一旦子类重写了父类的虚函数,那么虚函数表中对应位置的父类虚函数地址将被更新为子类虚函数地址。在代码运行过程中,如果我们使用类指针来调用虚函数,则编译器将会将其处理为通过虚函数指针的间接调用,是用类指针调用虚函数指针,再通过虚函数指针调用指向的函数,这样就可以使得当指针指向不同的类对象时候,就调用不同的类的虚成员函数了,这也是多态在C++中的最常见实现形式
?请简述面向对象的三个基本特征
--------------------------------------------------------
封装,将客观事物抽象成类,每个类对自身的数据和方法实行访问权限管理;继承,即子类自动拥有父类的所有成员变量和成员函数,保护、公有成员可以被子类成员函数访问,私有成员不能被子类成员函数访问;多态,简单地说就是通过相同的代码根据不同情况调用不同函数的过程,具体实现一般是父类指针指向子类对象调用子类对象的虚成员函数
?请简述多态的作用
--------------------------------------------------------
有两个作用,作用一是隐藏实现细节,使得代码能够模块化,扩展代码模块,实现代码重用;作用二是接口重用,为了类在继承和派生的时候,保证使用继承体系下任一类的实例的某一属性时的正确调用
?请简述C++语言中struct和class的区别
--------------------------------------------------------
struct 的成员默认是公有的,而类的成员默认是私有的。struct和class在其他方面是功能相同的。一般约定俗成的规矩,如果一个复合数据类型仅仅包含一些公有成员变量而没有成员函数或仅仅是很少的成员函数,那么这个复合数据类型可以设置为struct,否则的话应该设置为class
?当一个类A中没有任何成员变量与成员函数,这时sizeof(A)的值是多少,如果不是0,请解释一下编译器为什么没有让它为0
--------------------------------------------------------
肯定不是零,举个反例,如果是零的话,声明一个class A[10]对象数组,而每一个对象占用的空间是零,这时就没办法区分A[0]、A[1]…A[9]了,编译器会默认给类A加一个char类型
?请说出const与#define相比有什么优势
--------------------------------------------------------
const常量有数据类型,编译器可以对其进行类型安全检查,而宏仅仅是文本替换,没有“类型”;另外有些集成化的调试工具可以对const常量进行调试,但是不能对宏进行调试
?将引用作为函数形式参数有哪些特点
--------------------------------------------------------
有三个特点,特点一是传递引用给函数与传递指针的效果是一样的,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其在主调函数中相应目标对象的操作;特点二是使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作,而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数,因此当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好;特点三是使用指针作为函数形参虽然也能达到与使用引用的效果,但是在被调函数中同样要给形参分配存储单元,且需要重复使用"*指针变量名"的形式进行运算,这很容易产生错误且程序的阅读性较差,另一方面,在主调函数的调用点处,必须用变量的地址作为实参,而引用更易使用
?请说出下面程序的执行情况以及原因
void f(char* p)
{
p = (char*)malloc(10);
}
void main()
{
char* q = 0;
f(q);
strcpy(q, "abc");
std::cout<<q<<std::endl;
}
--------------------------------------------------------
程序在执行strcpy(q, "abc")这句代码的时候会崩溃,因为函数f的形式参数p被main中局部变量q初始化为0之后,二者再无任何联系,f函数中分配了堆内存并将地址赋值给了p,这对q没有任何作用,q指针依然指向0,所以退出f函数之后,希望把字符串"abc"拷贝给q指针指向的内存单元肯定是不行的,会引发程序崩溃
?请说出下面程序的执行情况以及原因
char* f()
{
char p[] = "abc";
return p;
}
void main()
{
char* q = f();
std::cout<<q<<std::endl;
}
--------------------------------------------------------
打印结果未知,因为p是栈内存中的数组,其作用域是在f函数内,一旦退出f函数,那么p所占据的内存区将被释放,f函数将p数组的地址返回给主调函数,在主调函数中打印p数组存储的字符串的时候,其内容已经不得而知,所以打印结果不明,一般来说这样返回局部变量地址都会引发编译器的警告
?请说出下面程序的执行情况以及原因
void main()
{
char* p = (char*)malloc(10);
strcpy(p, "abc");
std::cout<<p<<std::endl;
free(p);
if (p)
std::cout<<"没有释放p"<<std::endl;
else
std::cout<<"释放了p"<<std::endl;
}
--------------------------------------------------------
打印字符串"abc"和"没有释放p",因为虽然free(p),但这仅仅是把p所指向的地址上的堆内存释放,并没有改变p的指向,p依然指向刚才分配出来的堆内存首地址,只不过目前已经不能再访问了,所以p的值非0就为真,if语句中就会打印"没有释放p"
?请说出下面程序的执行情况以及原因
class A
{
public:
void f1()
{
std::cout<<"A::f1"<<std::endl;
}
virtual void f2()
{
std::cout<<"A::f2"<<std::endl;
}
};
class B : public A
{
int x;
public:
void f1()
{
std::cout<<"B::f1"<<std::endl;
}
void f2()
{
std::cout<<"B::f2"<<std::endl;
}
};
void main()
{
A a;
B b;
a = b;
a.f1();
a.f2();
A* p = &b;
p->f1();
p->f2();
A& q = b;
q.f1();
q.f2();
std::cout<<sizeof(A)<<std::endl;
std::cout<<sizeof(B)<<std::endl;
}
--------------------------------------------------------
a.f1()打印结果为A::f1,因为f1函数是普通函数,其调用规则是函数左侧是什么类型的对象就调用什么类型对象的普通函数,因为a是A类型对象所以打印A::f1
a.f2()打印结果为A::f2,因为f2虽然是虚函数,但是调用的方法是对象调用,而对象调用虚函数是不经过虚函数指针指向的虚函数表来调用的,依然是看函数左侧是什么类型的对象就调用什么类型对象的虚函数,所以此处因为a是A类型对象所以打印A::f2
p->f1()打印结果为A::f1,因为f1是普通函数,被指针调用的话也是看指针是什么类型就调用什么类型的普通函数,而p是A类型指针,所以调用结果为A::f1
p->f2()打印结果为B::f2,因为f2是虚函数,用指针调用虚函数的时候是看指针指向什么类型就调用什么类型的虚函数,此时因为p指针指向了B类型对象b,所以打印B::f2
q.f1()打印结果为A::f1,因为f1是普通函数,被引用调用的话也是看引用是什么类型就调用什么类型的普通函数,而q是A类型引用,所以调用结果为A::f1
q.f2()打印结果为B::f2,因为f2是虚函数,用引用调用虚函数的时候是看引用被什么类型初始化就调用什么类型的虚函数,此时因为q引用是被B类型对象b初始化,所以打印B::f2
std::cout<<sizeof(A)<<std::endl打印结果为4字节,因为A类是一个包含了虚函数的类,会被编译器分配一个4字节的虚函数指针,所以sizeof的结果为4
std::cout<<sizeof(B)<<std::endl打印结果为8字节,因为B类继承自A,所以也包含了虚函数,也有虚函数指针,除此之外还有一个int类型成员变量x,所以sizeof的结果为8
?请简述以下两个for循环的优缺点
循环1
for (int i = 0; i < N; i++)
{
if (condition)
DoSomething();
else
DoOtherthing();
}
循环2
if (condition)
{
for (int i = 0; i < N; i++)
DoSomething();
}
else
{
for (int i = 0; i < N; i++)
DoOtherthing();
}
--------------------------------------------------------
循环1的优点是程序简洁,缺点是多执行了N-1次逻辑判断,并且打断了循环流水线作业,使得编译器不能对循环进行优化处理,降低了效率。循环2的优点是执行效率高,缺点是代码不简洁
?请完不使用库函数成字符串处理相关函数,声明如下
得到字符串长度
int StrLength(const char* s);
字符串拷贝
char* StrCopy(char* s1, const char* s2);
字符串连接
char* StrConnect(char* s1, const char* s2);
字符串比较
int StrCompare(const char* s1, const char* s2);
在父串中查找子串首次出现的地址
char* StrStr(char* s1, char* s2);
--------------------------------------------------------
int StrLength(const char* s)
{
int i = 0;
for (; s[i]; ++i)
;
return i;
}
char* StrCopy(char* s1, const char* s2)
{
char* p = s1;
while (*(s1++) = *(s2++));
return p;
}
char* StrConnect(char* s1, const char* s2)
{
char* p = s1;
while (*s1)
s1++;
while (*(s1++) = *(s2++));
return p;
}
int StrCompare(const char* s1, const char* s2)
{
int i = 0, b = 0;
while (s1[i] || s2[i])
{
if (s1[i] > s2[i])
{
b = 1;
break;
}
else if (s2[i] > s1[i])
{
b = -1;
break;
}
i += 1;
}
return b;
}
char* StrStr(char* s1, char* s2)
{
int i1 = 0, i2 = 0;
while (s1[i1])
i1 += 1;
while (s2[i2])
i2 += 1;
int i = i1 - i2 + 1;
for (int j = 0; j < i; ++j)
{
bool b = true;
for (int k = 0; k < i2; ++k)
{
if (s1[j + k] != s2[k])
{
b = false;
break;
}
}
if (b)
return s1 + j;
}
return 0;
}
?请完成字符串类的成员函数,类体声明如下
class CMyString
{
private:
int m_Len;
char* m_Str;
public:
CMyString(); //无参构造
CMyString(const char* Str); //带参构造1
CMyString(const CMyString& that); //拷贝构造
~CMyString(); //析构
CMyString& operator = (const char* Str); //赋值1
CMyString& operator = (const CMyString& that); //同类赋值
CMyString operator + (const char* Str); //加法1
CMyString operator + (const CMyString& that); //同类加法
char& operator [] (const int Index); //下标
int GetLen(); //得到长度
const char* GetStr(); //得到字符串
operator const char*(); //自动转换为const chat*
};
--------------------------------------------------------
CMyString::CMyString()
{
m_Len = 0;
m_Str = new char[m_Len + 1];
m_Str[0] = 0;
}
CMyString::CMyString(const char* Str)
{
m_Len = (int)strlen(Str);
m_Str = new char[m_Len + 1];
strcpy(m_Str, Str);
}
CMyString::CMyString(const CMyString& that)
{
m_Len = that.m_Len;
m_Str = new char[m_Len + 1];
strcpy(m_Str, that.m_Str);
}
CMyString::~CMyString()
{
delete [] m_Str;
}
CMyString& CMyString::operator = (const char* Str)
{
if (m_Len != (int)strlen(Str))
{
delete [] m_Str;
m_Len = (int)strlen(Str);
m_Str = new char[m_Len + 1];
strcpy(m_Str, Str);
}
return *this;
}
CMyString& CMyString::operator = (const CMyString& that)
{
if (this != &that)
{
if (m_Len != that.m_Len)
{
delete [] m_Str;
m_Len = that.m_Len;
m_Str = new char[m_Len + 1];
strcpy(m_Str, that.m_Str);
}
}
return *this;
}
CMyString CMyString::operator + (const char* Str)
{
CMyString s;
delete [] s.m_Str;
s.m_Len = m_Len + (int)strlen(Str);
s.m_Str = new char[s.m_Len + 1];
strcpy(s.m_Str, m_Str);
strcat(s.m_Str, Str);
return s;
}
CMyString CMyString::operator + (const CMyString& that)
{
CMyString s;
delete [] s.m_Str;
s.m_Len = m_Len + that.m_Len;
s.m_Str = new char[s.m_Len + 1];
strcpy(s.m_Str, m_Str);
strcat(s.m_Str, that.m_Str);
return s;
}
char& CMyString::operator [] (const int Index)
{
assert(Index >= 0 && Index < m_Len);
return m_Str[Index];
}
int CMyString::GetLen()
{
return m_Len;
}
const char* CMyString::GetStr()
{
return m_Str;
}
CMyString::operator const char*()
{
return m_Str;
}