Q&高质量C&C++编程指南中的试题 | 爱恨如写意山水画
Cobb 822 1

一、请填写 BOOL , float, 指针变量 与“零值”比较的 if 语句。(10 分)

(注意:第二个精度比较一一易错)

0.1、 请写出 BOOL flag 与“零值”比较的 if 语句。(3 分) 标准答案: if ( flag ) if ( !flag ) 如下写法均属不良风格,不得分。 if (flag == TRUE) if (flag == 1 ) if (flag == FALSE) if (flag == 0)

0.2、 // 注意精度比较方式 请写出 float x 与“零值”比较的 if 语句。(4 分) 标准答案示例: const float EPSINON = 0.00001; if ((x >= - EPSINON) && (x <= EPSINON) 不可将浮点变量用“==”或“!=”与数字 比较,应该设法转化成“>=”或“<=”此 类形式。 如下是错误的写法,不得分。 if (x == 0.0) if (x != 0.0)

0.3、 请写出 char *p 与“零值”比较的 if 语句。(3 分) 标准答案: if (p == NULL) if (p != NULL) 如下写法均属不良风格,不得分。 if (p == 0) if (p != 0) if (p) if (!)

二、以下为 Windows NT 下的 32 位 C++程序,请计算 sizeof 的值(10 分)

char str[] = “Hello” ; char *p = str ; int n = 10; 请计算 sizeof (str ) = 6 (2 分) sizeof ( p ) = 4 (2 分) sizeof ( n ) = 4 (2 分)

void Func ( char str[100]) { 请计算 sizeof( str ) = 4 (2 分) }

void *p = malloc( 100 ); 请计算 sizeof ( p ) = 4 (2 分

分析:sizeof是计算所占内存大小

三、简答题(25 分)

1、头文件中的 ifndef/define/endif 干什么用?(5 分) 答:防止该头文件被重复展开。

2、#include <filename.h> 和 #include “filename.h” 有什么区别?(5 分) 答:#include <> 只在系统路径搜索 #include "" 先在当前工程目录搜索,然后在系统路径搜索

3、const 有什么用途?(请至少说明两种)(5 分) 答:(1)可以定义 const 常量 (2)const 可以修饰函数的参数、返回值,甚至函数的定义体。被 const 修饰的东 西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。

4、在 C++ 程序中调用被 C 编译器编译后的函数,为什么要加 extern “C”? (5 分) 答:C++语言支持函数重载,C 语言不支持函数重载。函数被 C++编译后在库中的符号 与 C 语言的不同。 假设某个函数的原型为: void foo(int x, int y); 该函数被 C 编译器编译后在库中的符号为 _foo, 而 C++编译器则会产生像_foo_int_int 之类的符号。 C++提供了 C 连接交换指定符号 extern“C”来解决符号匹配问题

5、请简述以下两个 for 循环的优缺点(5 分) for (i=0; i<N; i++) { if (condition) DoSomething(); else DoOtherthing(); } 优点:逻辑在一个循环中,统一处理,代码紧凑,无重复代码 缺点:每次都要逻辑判断,效率低,

if (condition) { for (i=0; i<N; i++) DoSomething(); } else { for (i=0; i<N; i++) DoOtherthing(); } 优点:循环的效率更高,for没有进程额外判断,循环能够持续进行, 不被打断。 缺点:代码不紧凑,逻辑分开处理,有重复代码

四、有关内存的思考题(每小题 5 分,共 20 分)

1、 void GetMemory(char *p) { p = (char *)malloc(100); } void Test(void) { char *str = NULL; GetMemory(str);
strcpy(str, "hello world"); printf(str); } 请问运行 Test 函数会有什么样的结果? 答:程序段错误 因为 对GetMemory传值了, 所以Test 函数中的 str 一直都是 NULL。 strcpy(str, "hello world");向0地址处拷贝 将使程序段错误

2、 char *GetMemory(void) { char p[] = "hello world"; return p; } void Test(void) { char *str = NULL; str = GetMemory(); printf(str); } 请问运行 Test 函数会有什么样的结果? 答:可能是乱码。 返回了函数局部栈上的内存,函数运行完,栈内存归还 但调用了printf(str)函数,重新开了栈帧,覆盖其 原现的内容,新内容不可知。

3、 void GetMemory2(char **p, int num) { *p = (char *)malloc(num); } void Test(void) { char *str = NULL; GetMemory(&str, 100); strcpy(str, "hello"); printf(str); } 请问运行 Test 函数会有什么样的结果? 答: 正常输出 hello

4、 void Test(void) { char *str = (char *) malloc(100); strcpy(str, “hello”); free(str); if(str != NULL) { strcpy(str, “world”); printf(str); } } 请问运行 Test 函数会有什么样的结果? 答:有可能输出world,有可能不输出, 前提是刚释放的内存没有被再分配出去。但是堆内存非法访问了。 因为 free(str);之后,str 成为野指针,但是此时释放的内存没有被其它程序使用,仍然可以操作。 free之后,堆内存没有被覆盖 if(str != NULL)语句不起作用

五、编写 strcpy 函数(10 分)

已知 strcpy 函数的原型是 char *strcpy(char *strDest, const char *strSrc); 其中 strDest 是目的字符串,strSrc 是源字符串。 (1)不调用 C++/C 的字符串库函数,请编写函数 strcpy

strcpy函数的实现:
char *strcpy(char *strdest,const char* strsrc)
{
        char*p = strdest;
        while(*strdest != '/0' && *strsrc != '/0')
        {
            *strdest = *strsrc
            strdest++;
            strsrc++;
        }
       if(*strdest != '/0')
       {
            *strdst = '/0';
       }
       return p;
}

(2)strcpy 能把 strSrc 的内容复制到 strDest,为什么还要 char * 类型的返回值? 分析:便与连续操作,例如: strlen(strcpy(str1, str2));

六、编写类 String 的构造函数、析构函数和赋值函数(25 分)

已知类 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 的上述 4 个函数

    // str参数接收的就是外部传进来的字符串   String str;
    String::String(const char* str = nullptr)
    {
        if (str != nullptr)
        {
            // new开辟内存失败不是返回空指针,而是抛异常,此处省略处理
            this->m_data = new char[strlen(str) + 1];
            strcpy(this->m_data, str);
        }
        else
        {
            //this->m_data = NULL;
            this->m_data = new char('\0');
        }
    }

    String::~String()
    {
        delete[]this->m_data;
        this->m_data = nullptr;
    }

    String::String(const String& str) // str m_data
    {
        this->m_data = new char[strlen(str.m_data) + 1];
        strcpy(this->m_data, str.m_data);
    }

    // 带右值引用参数的拷贝构造函数
    String::String(String&& str)
    {
        // 做资源转移的  this->对象是新创建的对象    str引用的对象马上就完蛋了
        this->m_data = str.m_data;
        str.m_data = nullptr;
    }

    String::String& operator=(const String& str) // str m_data
    {
        // 防止自赋值
        if (this == &str)
            return *this;

        delete[]this->m_data;

        this->m_data = new char[strlen(str.m_data) + 1];
        strcpy(this->m_data, str.m_data);
        return *this;
    }

    // 带右值引用参数的赋值函数
    String::String& operator=(String&& str)
    {
        // this->是要左值对象    str引用的是临时对象
        delete[]this->m_data;

        this->m_data = str.m_data;
        str.m_data = nullptr;
        return *this;
    }
评论区

索引目录