《重学C++》前七章习题

第一章:

一、基础题

  1. 请说出C++语言的优点,缺点,和主要用途?(涉及知识点:1-4节 C++特点, 1-5 C++作用

优点:

  • 强大的抽象封装能力:这让C++语言具备了强大的开发工程能力,在封装的同时C++最大程度的保留了高性能;
  • 高性能:运行快,快并且占用资源少一直是C++语言的追求;
  • 低功耗:特别适合在各种微型的嵌入式设备中运行高效的程序;

缺点:

  • 语法相对复杂,细节比较多,学习曲线比较陡;
  • 需要一些好的规范和范式,否则代码很难维护;

二、提高题

  1. 请参考课程演示代码”CPPDemo1”中的C++面向对象方式,思考C面向过程方式中如何实现trace功能在开关打开状态下写入到文件中,并想想这两种方式各自的优缺点?(涉及知识点:1-3节 C++vsC,面向对象vs面向过程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>
static bool trigger;
void turnOn()
{
trigger = true;
}
void turnOff()
{
trigger = false;
}
void write(char *s,int n)
{
FILE *fp = fopen("test.txt","a+");
if(trigger)
{
fwrite(s,1,n,fp);
}
fclose(fp);
}
int main()
{
turnOn();
write("First",5);
turnOff();
write("Second",6);
}

c语言需要用全局变量和全局函数来实现,在稍微大一些的项目中,将会产生难以维护,难以扩展,难以阅读的问题,而C++封装对象的方式就可以解决这些问题。

c语言在内存方面占用更小。在微型项目中编写更加方便。

第二章

一、基础题

  1. 下面标识符是合法的有哪些(BEF ) (涉及知识点: 2-5 标识符与关键字
    A.float
    B.ipad
    C.1button
    D. A#BC
    E.my_button
    F. button_1_ok
  2. 请给一个退出按钮命一个好变量名( C)(涉及知识点: 2-5 标识符
    A. 1button
    B. button1
    C.buttonQuit
    D.button_tuichu
  3. 下面整数常量合法的是( D)(涉及知识点: 2-6 常量
    A.078
    B.03UU
    C.0x9AHX
    D.0xFFAA00

二、提高题

  1. 下面程序输出结果是 (8)(涉及知识点: 2-6 常量的宏定义

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #define MA(x) x*(x-1)
    void main()
    {
    int a=1, b=2;
    cout << MA(1+a+b) << endl;
    } // 8
    //ma(1+a+b) = 1+a+b*(1+a +b-1)
    // = 1+1+2*(1+1+2-1)
    // = 8

第三章:

一、填空

1.

1
2
3
4
5
cout <<sizeof(char) << endl;   //1
cout << sizeof(short) << endl; //2
cout << sizeof(int) << endl; //4
cout << sizeof(float) << endl; //4
cout << sizeof(double) << endl; //8

  1. 1
    2
    a = (x=10,y=20,z=x+y);
    cout << a << endl; //30 逗号返回最后一个值
  2. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    typedef struct{
    short Sunday = 0;
    short Monday = 1;
    short Tuesday = 2;
    short Wednesday = 3;
    short Thursday = 4;
    short Friday = 5;
    short Saturday = 6;
    } Week;
    Week w;
    cout << sizeof(w.Sunday); //2
    cout << sizeof(w) << endl; //14

二、编程题

  1. 使用#define 编写一段代码,来实现“标准”宏MIN

    1
    #define MIN(a,b) (((a)>=(b))?(b):(a))         //a,b可能为式子,故需单独加括号
  2. 分别写出bool 、int、 float、与“零值”比较,表达式返回值等于1的代码片段;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #设变量为flag
    ////////////////////////////////////////////////////////////////////////
    bool:
    if(flag)
    int:
    if(flag == 0)
    double:
    const double EPSINON = 0.00001;
    if ((flag >= - EPSINON) && (flag <= EPSINON))
    char *:
    if (flag == NULL)

第四五六章:

一、程序运行题

  1. 请说出下列问号处的结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
       char str[] = “Hello” ;
    char *p = str ;
    int n = 10;
    //请计算
    sizeof (str ) = ?
    sizeof ( p ) = ?
    sizeof ( n ) = ?
    void Func ( char str[100])
    {
    //请计算
    sizeof( str ) = ?
    }

    答:

    1
    2
    3
    4
    5
    6
    7
    sizeof (str ) = 6
    sizeof ( p ) = 4
    sizeof ( n ) = 4
    void Func ( char str[100])
    {
    sizeof( str ) = 4
    }

    如果定义如下代码,那么该str2变量大小(sizeof(str2))应该是多少?
    char* str2 = (char*)malloc(sizeof(char)*3);

  2. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    void GetMemory(char *p)
    {
    p = new char[100];
    }
    void Test(void)
    {
    char *str = NULL;
    GetMemory(str);
    strcpy(str, "hello world");
    printf(str);
    }

    问运行Test 函数会有什么样的结果?
    答:程序崩溃。因为GetMemory 并不能传递动态内存,
    Test 函数中的 str 一直都是 NULL。strcpy(str, “hello world”);将使程序崩溃。

  3. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    char *GetMemory(void)
    {
    char p[] = "hello world";
    return p;
    }
    void Test(void)
    {
    char *str = NULL;
    str = GetMemory();
    printf(str);
    }

    请问运行Test 函数会有什么样的结果?
    答:可能是乱码;因为GetMemory 返回的是指向“栈内存”的指针,该指针的地址不是 NULL,但其原现的内容已经被清除,新内容不可知。

  4. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    void GetMemory2(char **p, int num)
    {
    *p = new char[num];
    }
    void Test(void)
    {
    char *str = NULL;
    GetMemory2(&str, 100);
    strcpy(str, "hello");
    printf(str);
    }

请问运行Test 函数会有什么样的结果?
**答:能够输出hello,但内存泄漏 **

5.

1
2
3
4
5
6
7
8
9
10
11
12
# include <string.h>
void Test(void)
{
char *str = new char[100];
strcpy(str, "hello");
delete[ ] str;
if (str != NULL)
{
strcpy(str,"world");
printf(str);
}
}

请问运行Test 函数会有什么样的结果?
答:篡改动态内存区的内容,后果难以预料,非常危险。因为 delete[ ]str;之后,str成为野指针(需要str = NULL;)if(str != NULL)语句不起作用。

二、编程题

  1. char *strcpy(char *strDest, const char *strSrc)
    请在不调用C++/C 的字符串库函数的情况下,编写函数 strcpy;
    1
    2
    3
    4
    5
    6
    7
    8
    char *strcpy(char *strDest, const char *strSrc);
    {
    assert((strDest!=NULL) && (strSrc !=NULL));
    char *address = strDest;
    while( (*strDest++ = * strSrc++) != ‘\0’ )
    ;
    return address ;
    }

第七章:

一、编程题

编写自定义类String 的构造函数、析构函数和赋值函数。
已知类String 的原型为:

1
2
3
4
5
6
7
8
9
10
11
12
class String
{
public:
String(const char *str = NULL); // 普通构造函数
String(const String &other); // 拷贝构造函数
String(String&& other); // 移动构造函数
~String(void); // 析构函数
String& operator= (const String& other); // 赋值函数
String& operator=(String&& rhs)noexcept; // 移动赋值运算符
private:
char *m_data; // 用于保存字符串
};

请编写String 的上述几个函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
// String 的析构函数
String::~String(void)
{
if (m_data != NULL)
{
delete[] m_data;
}
}

// String 的普通构造函数
String::String(const char *str)
{
if (str == NULL)
{
m_data = new char[1];
if (m_data != NULL)
{
*m_data = '\0';
}
else
{
exit(-1); // new有可能失败,失败后返回错误退出, 最好能有日志
}
}
else
{
int length = strlen(str);
m_data = new char[length + 1];
if (m_data != NULL)
{
*m_data = '\0';
}
else
{
exit(-1); // new有可能失败,失败后返回错误退出, 最好能有日志
}
strcpy(m_data, str);
}
}
// 拷贝构造函数
String::String(const String &other)
{
int length = strlen(other.m_data);
m_data = new char[length + 1];
if (m_data != NULL)
{
*m_data = '\0';
}
else
{
exit(-1); // new有可能失败,失败后返回错误退出, 最好能有日志
}
strcpy(m_data, other.m_data);
}

String::String(String&& other)
{
if (other.m_data != NULL)
{
m_data=other.m_data;
other.m_data = NULL;
}
}


// 赋值函数
String& String::operator= (const String &other)
{
// 检查自赋值
if (this == &other)
return *this;
// 释放原有的内存资源
delete[] m_data;
// 分配新的内存资源,并复制内容
int length = strlen(other.m_data);
m_data = new char[length + 1];
if (m_data != NULL)
{
*m_data = '\0';
}
else
{
exit(-1); // new有可能失败,失败后返回错误退出, 最好能有日志
}
strcpy(m_data, other.m_data);
// 返回本对象的引用
return *this;
}

String& String::operator=(String&& rhs)noexcept
{
if (this != &rhs)
{
delete[] m_data;
m_data = rhs.m_data;
rhs.m_data = NULL;
}
return *this;
}