一、请填写BOOL, float, 指针变量 与“零值”比较的if语句
请写出 BOOLflag 与“零值”比较的if语句:
if (flag)
{ }
if (!flag)
{ }
请写出 float x 与“零值”比较的 if 语句:
const float EPSINON = 0.00001;
if (x >= -EPSINON && x <=EPSINON)
{ }
if (!(x >= -EPSINON && x <=EPSINON))
{ }
请写出 char *p 与“零值”比较的if语句:
if (p == NULL)
{ }
if (p != NULL)
{ }
二、以下为Windows NT下的32位C++程序,请计算sizeof 的值
说明:数组当参数时,该数组会退化成指针,即char strp[] 变成了 char* str。
三、简答题
1、头文件中的ifndef/define/endif干什么用?
答:防止头文件被重复引用
2、#include <filename.h> 和 #include “filename.h” 有什么区别?
答:告诉编译器取哪里查找头文件
对于
#include <filename.h>,编译器从标准库路径搜索
filename.h
对于
#include “filename.h”,编译器从用户的工作路径
搜索
filename.h
3、const 有什么用途?(请至少说明两种)
答:
被const修饰的东西会受到强制的保护,可以防止意外的改动,提高程序的健壮性。
(1)可以定义const常量
(2)const可以修饰函数的参数、返回值、函数体。
4、在C++ 程序中调用被 C 编译器编译后的函数,为什么要加 extern “C”声明?
答:解决函数名字的匹配问题
在C++语言支持函数重载,C语言不支持函数重载。C++编译器对函数进行编译后,生成的名字和C编译器对函数编译后,生成的名字不同。
假设函数原型:void foo(int x,int y);
C++编译器编译后,该函数在库中的名字为 _foo_int_int。
C编译器编译后,该函数在库中的名字为 _foo。
为了解决这些被
C 编译器编译后的函数的名字问题,C++就提供了C连接交换指定符号 ”extern C“ 。
5、请简述以下两个for 循环的优缺点
四、有关内存的思考题
程序一
void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}
问题:
请问运行Test 函数会有什么样的结果?
答:程序直接崩溃
(1)内存泄露:p指针申请的空间没有被释放
(2)程序崩溃:函数GetMemory的参数时值传递,没不能把p申请的空间传到Test函数中,使得str一直为NULL。在执行函数strcpy时,相当于把内容写到一个空指针中,导致系统崩溃。
程序二
char *GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
printf(str);
}
问题:
请问运行Test 函数会有什么样的结果?
答:输出为乱码
原因:p指向的空间是一个栈内存,当函数GetMemory结束时,p指向的空间被释放了,因此str在接受函数GetMemory的值后,str的值仍是p指向的这块地址,但是这块地址已经被系统收回,其值可能发生改变,所以输出其值时,会输出乱码。
程序三
char *GetMemory(void)
{
char* p = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
printf(str);
}
问题:
请问运行Test 函数会有什么样的结果?
答:输出为 hello world
原因:p指向的空间是在全局内存区申请的内存,只有当整个程序结束时,才被os回收。当函数GetMemory结束时,p指向的空间没有被释放,因此str在接受函数GetMemory的值后,str的值仍是p指向的这块地址,输出str的值时,就直接输出hello world。
程序四
void GetMemory(char **p, int num)
{
*p = (char *)malloc(num);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}
问题:
请问运行Test 函数会有什么样的结果?
答:
(1)输出为 hello world
(2)内存泄露:内存没有释放。
程序五
void Test(void)
{
char *str = (char *) malloc(100);
strcpy(str, “hello”);
free(str);
if(str != NULL)
{
strcpy(str, “world”);
printf(str);
}
}
问题:
请问运行Test 函数会有什么样的结果?
答:篡改动态内存区的内容,后果难以预料,非常危险。
在free完str后,str就变成了野指针,执行strcpy后,会改变属于该程序的内存的值,而可能引起未知的错误。
五、编写strcpy函数
已知strcpy 函数的原型是
char *strcpy(char *strDest, const char *strSrc);
其中strDest 是目的字符串,strSrc是源字符串。
(1)不调用C++/C的字符串库函数,请编写函数 strcpy
char *Strcpy(char* strDest, const char* strSrc)
{
assert(strDest != NULL && strSrc != NULL);
char* pCur = strDest;
while (*strSrc)
{
*pCur = *strSrc;
pCur++;
strSrc++;
}
return strDest;
}
(
2
)
strcpy
能把
strSrc
的内容复制到
strDest
,为什么还要
char *
类型的返回值?
答:为了实现链式表达。
cout<<Strcpy(str1,str)<<endl;
六,编写类String 的构造函数,析构函数和赋值函数
class String
{
public:
String(const char* str = NULL);
String(const String& other);
~String();
String& operate=(const String& other);
private:
char* m_data;
};
String::String(const char* str)
{
if (str != NULL)
{
int nLen = strlen(m_data);
m_data = new char[nLen + 1];
strcpy(m_data,str);
}
else
{
m_data = new char[1]; //没有实际值的字符串加了字符串结束符
*m_data = '\0';
}
}
String::String(const String &other)
{
int nLen = strlen(other.m_data);
m_data = new char[nLen + 1];
strcpy(m_data,other.m_data);
}
String::~String()
{
delete[] m_data;
m_data = NULL;
}
String& String::operate=(const String& other)
{
//检测自赋值
if (this == &other)
{
return *this;
}
//释放原有内存
delete m_data;
//申请新内存,并复制内容
int nLen = strlen(other.m_data);
m_data = new char[nLen + 1];
strcpy(m_data,other.m_data);
//返回本对象的引用
return *this;
}
说明:
(1)字符串没有实际值时,使用了字符串结束符填充。
(2)在使用旧String对象对新String对象进行复制时,由于旧String对象的m_data一定是不为空的,则不必写if语句判断为空了。
(3)由于使用new申请空间时,也可以加上if语句,判断申请空间是否成功。
(4)写赋值函数时,需要根据四步走书写。