《c++编程思想第1卷》第3章练习题答案

3-1

建立一个头文件(扩展名为’.h’)。在该文件中,声明一组函数,具有可变参数,返回值包括void、char、int和float类型。建立一个包含上述头文件的.cpp文件,创建所有这些函数的定义。每个定义应该简单地输出函数名,参数列表,并返回类型以便知道它已经被调用。创建另外一个.cpp文件,它包含头文件且定义int main(),在其中调用已经定义的所有函数。编译和运行这个程序。
创建头文件3-1.h:

#define FUNC(type) type type##Func()
#define CALL_FUNC(type) type##Func()
FUNC(void);
FUNC(char);
FUNC(int);
FUNC(float);

创建3-1.cpp文件:

#include "3-1.h"
#include 
using namespace std;

FUNC(void) {
    cout << "void voidFunc() 被调用" << endl;
}

FUNC(char) {
    cout << "char charFunc() 被调用" << endl;
    return 'a';
}

FUNC(int) {
    cout << "int intFunc() 被调用" << endl;
    return 3;
}

FUNC(float) {
    cout << "float floatFunc() 被调用" << endl;
    return 3.14;
}

创建含有main函数的.cpp文件:

#include "3-1.h"
#include 
using namespace std;

int main() {
    CALL_FUNC(void);
    char a = CALL_FUNC(char);
    cout << "返回值为" << a << endl;
    int b = CALL_FUNC(int);
    cout << "返回值为" << b << endl;
    float c = CALL_FUNC(float);
    cout << "返回值为" << c << endl;
}

输出:

void voidFunc() 被调用
char charFunc() 被调用
返回值为a
int intFunc() 被调用
返回值为3
float floatFunc() 被调用
返回值为3.14

3-2

编写一个程序使用两重for循环和模运算符(%)去寻找和输出质数(只能被1和它本身整除的整数)。

#include 
using namespace std;

int main() {
    cout << "请输入一个整数:";
    int a;
    cin >> a;
    cout << "在2到" << a << "之间有如下质数:" << endl;
    for (int i = 2; i <=a; i++) {
        bool isPrime = true;
        for (int j = 2; j < i; j++) {
            if (i % j == 0) {//能整除,说明不是质数
                isPrime = false;
                break;
            }
        }
        if (isPrime) {
            cout << i << " ";
        }
    }
    cout << endl;
}

输出:

请输入一个整数:20
在2到20之间有如下质数:
2 3 5 7 11 13 17 19

3-3

编写一个程序,使用一个while循环从标准输入(cin)中把单词读入到string中。这是一个“无穷”while循环,可以使用break语句中断(和退出程序)。对于读入的每个单词,先用一系列的if语句把该单词“映射”为一个整数值,然后用该整数值作为一个switch语句的选择条件(这些操作并不意味着良好的设计风格,这仅仅是为练习这些控制流程)。在每个case中,输出一些有意义的信息。判定哪些是“有趣”的单词以及这些单词的意义。同时判定哪个单词是程序结束的标志。用文件作为输入来测试该程序(如果想节省输入,这个文件将作为程序的源文件)。

#include 
#include 
using namespace std;

int main() {
    string word;
    while (cin >> word) {
        if (word == "quit") {
            cout << "退出程序";
            break;
        }

        int type = 0;
        if (word.size() == 5) {
            type = 1;
        } else if (word.size() > 5) {
            type = 2;
        }

        switch (type) {
            case 0:
                cout << word << ":这个单词长度小于5" << endl;
                break;
            case 1:
                cout << word << ":是个有趣的单词,长度等于5" << endl;
                break;
            case 2:
                cout << word << ":这个单词长度大于5" << endl;
                break;
        }
    }
}

输出:

what
what:这个单词长度小于5
are
are:这个单词长度小于5
you
you:这个单词长度小于5
doing
doing:是个有趣的单词,长度等于5
quit
退出程序

3-4

修改Menu.cpp程序,使用switch语句代替if语句。

#include 
using namespace std;

int main() {
  char c; // To hold response
  while(true) {
    cout << "主菜单:" << endl;
    cout << "l: 左, r: 右, q: 退出 -> ";
    cin >> c;
    if (c == 'q') break;
    switch (c) {
      case 'l': {
        cout << "左键菜单:" << endl;
        cout << "选择 a 或 b:";
        cin >> c;
        switch (c) {
          case 'a':
            cout << "你选择了'a'" << endl;
            continue; // Back to main menu
          case 'b':
            cout << "你选择了'b'" << endl;
            continue; // Back to main menu
          default:
            cout << "你没有选择 a 或 b!"
                << endl;
            continue; // Back to main menu
        }
        break;
      }
      case 'r': {
        cout << "右键菜单:" << endl;
        cout << "选择 c 或 d: ";
        cin >> c;
        switch (c) {
          case 'c':
            cout << "你选择了'c'" << endl;
            continue; // Back to main menu
          case 'd':
            cout << "你选择了'd'" << endl;
            continue; // Back to main menu
          default:
            cout << "你没有选择 c 或 d!"
                << endl;
            continue; // Back to main menu
        }
        break;
      }
    }
    cout << "你必须选择 l 或 r 或 q!" << endl;
  }
  cout << "退出菜单..." << endl;
} 

3-5

编写一个程序计算在“优先级”一节中的两个表达式的值。

#include 
using namespace std;

#define PRINT(X) \
    cout << #X " = " << X << endl;

int main() {
    int X = 1, Y = 2, Z = 3;
    PRINT(X + Y - 2/2 + Z);
    PRINT(X + (Y - 2)/(2 + Z));
}

输出:

X + Y - 2/2 + Z = 5
X + (Y - 2)/(2 + Z) = 1

3-6

修改YourPets2.cpp程序以使用不同的数据类型(char、int、float、double和这些类型的变型)。运行该程序并画出结果内存分布图。如果能在多种机器、操作系统或者编译器上运行该程序,用尽可能多的变化进行这个试验。

#include 
using namespace std;

#define PRINT(type) cout << #type " 大小:" << sizeof(type) << endl;
#define PRINT_ADDR(x, y, z) cout << &x << " " << &y << " " << &z << endl;

int main() {
  char ci, cj, ck;
  short int si, sj, sk;
  int i, j, k;
  long int li, lj, lk;
  long long lli, llj, llk;
  float fi, fj, fk;
  double di, dj, dk;
  long double ldi, ldj, ldk;
  PRINT(char);
  // PRINT_ADDR(ci, cj, ck); 不可以直接使用cout << &ci 这种形式打印地址,因为
  // &ci,会变成一个char*指针,而使用cout << (char *) 会被认为要打印一个字符数组,
  // 所以为了打印,可能把它转成void*来打开地址。
  cout << (void*)&ci << " " << (void*)&cj << " " << (void*)&ck << endl;
  PRINT(short int);PRINT_ADDR(si, sj, sk);
  PRINT(int);PRINT_ADDR(i, j, k);
  PRINT(long int);PRINT_ADDR(li, lj, lk);
  PRINT(long long);PRINT_ADDR(lli, llj, llk);
  PRINT(float);PRINT_ADDR(fi, fj, fk);
  PRINT(double);PRINT_ADDR(di, dj, dk);
  PRINT(long double);PRINT_ADDR(ldi, ldj, ldk);
}

输出:

char 大小:1
0xbce13ff83f 0xbce13ff83e 0xbce13ff83d
short int 大小:2
0xbce13ff83a 0xbce13ff838 0xbce13ff836
int 大小:4
0xbce13ff830 0xbce13ff82c 0xbce13ff828
long int 大小:4
0xbce13ff824 0xbce13ff820 0xbce13ff81c
long long 大小:8
0xbce13ff810 0xbce13ff808 0xbce13ff800
float 大小:4
0xbce13ff7fc 0xbce13ff7f8 0xbce13ff7f4
double 大小:8
0xbce13ff7e8 0xbce13ff7e0 0xbce13ff7d8
long double 大小:16
0xbce13ff7c0 0xbce13ff7b0 0xbce13ff7a0

3-7

创建两个函数,一个接受一个string*参数,另一个接受一个string&参数。每个函数必须用它特有的方式去改变外部的string对象。在main()中,创建和初始化一个string对象,输出它,然后把它传给每个函数,输出结果。

#include 
#include 
using namespace std;

void modify(string* ps) {
    *ps = "string* " + *ps;
}

void modify(string& rs) {
    rs = "string& " + rs;
}

int main() {
    string a("hello");
    cout << "原始字符串:" << a << endl;
    modify(&a);
    cout << "用指针改变:" << a << endl;
    modify(a);
    cout << "用引用改变:" << a << endl;
}

输出:

原始字符串:hello
用指针改变:string* hello
用引用改变:string& string* hello

3-8

编写一个使用所有三个图形字符(trigraph)的程序,看看你的编译器是否支持它们。
trigraph是什么,看一下这个文章。

#include 
using namespace std;

int main() {
    cout << "??=" << endl;//如果支持,应该输出#
    cout << "??(" << endl;//如果支持,应该输出[
    cout << "??)" << endl;//如果支持,应该输出]
    cout << "??<" << endl;//如果支持,应该输出{
    cout << "??>" << endl;//如果支持,应该输出}
    cout << "??/" << endl;//如果支持,应该输出/
    cout << "??!" << endl;//如果支持,应该输出|
    cout << "??'" << endl;//如果支持,应该输出^
    cout << "??-" << endl;//如果支持,应该输出~
}

使用如下makefile编译:

CPP = g++
OFLAG = -o
.SUFFIXES : .o .cpp .c
.cpp.o :
	$(CPP) $(CPPFLAGS) -c $<
.c.o :
	$(CPP) $(CPPFLAGS) -c $<

3-8: 3-8.o
	$(CPP) $(OFLAG) $@ $^
	rm $^
	./$@
3-8.o: 3-8.cpp

输出:

3-8.cpp:5:14: warning: trigraph ??= ignored, use -trigraphs to enable [-Wtrigraphs]
    5 |     cout << "??=" << endl;//如果支持,应该输出#
      |
3-8.cpp:6:14: warning: trigraph ??( ignored, use -trigraphs to enable [-Wtrigraphs]
    6 |     cout << "??(" << endl;//如果支持,应该输出[
      |
3-8.cpp:7:14: warning: trigraph ??) ignored, use -trigraphs to enable [-Wtrigraphs]
    7 |     cout << "??)" << endl;//如果支持,应该输出]
      |
3-8.cpp:8:14: warning: trigraph ??< ignored, use -trigraphs to enable [-Wtrigraphs]
    8 |     cout << "??<" << endl;//如果支持,应该输出{
      |
3-8.cpp:9:14: warning: trigraph ??> ignored, use -trigraphs to enable [-Wtrigraphs]
    9 |     cout << "??>" << endl;//如果支持,应该输出}
      |
3-8.cpp:10:14: warning: trigraph ??/ ignored, use -trigraphs to enable [-Wtrigraphs]
   10 |     cout << "??/" << endl;//如果支持,应该输出/
      |
3-8.cpp:11:14: warning: trigraph ??! ignored, use -trigraphs to enable [-Wtrigraphs]
   11 |     cout << "??!" << endl;//如果支持,应该输出|
      |
3-8.cpp:12:14: warning: trigraph ??' ignored, use -trigraphs to enable [-Wtrigraphs]
   12 |     cout << "??'" << endl;//如果支持,应该输出^
      |
3-8.cpp:13:14: warning: trigraph ??- ignored, use -trigraphs to enable [-Wtrigraphs]
   13 |     cout << "??-" << endl;//如果支持,应该输出~
      |
g++ -o 3-8 3-8.o
rm 3-8.o
./3-8
??=
??(
??)
??<
??>
??/
??!
??'
??-

按照提示,使用-trigraphs来编译,修改makefile:

CPP = g++
OFLAG = -o
#加一个CPPFLAGS
CPPFLAGS = -trigraphs 
.SUFFIXES : .o .cpp .c
.cpp.o :
	$(CPP) $(CPPFLAGS) -c $<
.c.o :
	$(CPP) $(CPPFLAGS) -c $<

把cpp文件第10行转义一下:

cout << "\??/" << endl;//如果支持,应该输出/

输出:

#
[
]
{
}
\
|
^
~

3-9

编译和运行Static.cpp程序。从代码中删除static关键词,再次编译和运行,解释发生的现象。

#include 
using namespace std;

void func() {
  int i = 0;
  cout << "i = " << ++i << endl;
}

int main() {
  for(int x = 0; x < 10; x++)
    func();
} 

输出:

i = 1
i = 1
i = 1
i = 1
i = 1
i = 1
i = 1
i = 1
i = 1
i = 1

删除了static关键字之后,i变成了一个普通的局部变量,每次调用func函数时,都会初始化为0,输出时加1,所以每次调用都输出1。

3-10

试编译FileStatic.cpp和FileStatic2.cpp程序并把它们连接起来。得到的错误消息的含义是什么?
错误消息是:

D:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/10.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: FileStatic2.o:FileStatic2.cp:(.rdata$.refptr.fs[.refptr.fs]+0x0): undefined reference to `fs'

未定义fs引用,也就是说fs定义找不到。因为在FileStatic.cpp中,fs是static的,只能文件内可见,所以在FileStatic2.cpp中虽然声明了fs,但是在连接步骤,连接器是找不到的。

3-11

修改Boolean.cpp程序,用double值代替int值。

#include 
using namespace std;

int main() {
  double i,j;
  cout << "Enter an double: ";
  cin >> i;
  cout << "Enter another double: ";
  cin >> j;
  cout << "i > j is " << (i > j) << endl;
  cout << "i < j is " << (i < j) << endl;
  cout << "i >= j is " << (i >= j) << endl;
  cout << "i <= j is " << (i <= j) << endl;
  cout << "i == j is " << (i == j) << endl;
  cout << "i != j is " << (i != j) << endl;
  cout << "i && j is " << (i && j) << endl;
  cout << "i || j is " << (i || j) << endl;
  cout << " (i < 10) && (j < 10) is "
       << ((i < 10) && (j < 10))  << endl;
}

3-12

修改Boolean.cpp和Bitwise.cpp程序,使用显示运算符(如果你的编译器与C++标准兼容,那么它会支持这些运算符)。
Boolean.cpp

#include 
using namespace std;

int main() {
  int i,j;
  cout << "Enter an integer: ";
  cin >> i;
  cout << "Enter another integer: ";
  cin >> j;
  cout << "i > j is " << (i > j) << endl;
  cout << "i < j is " << (i < j) << endl;
  cout << "i >= j is " << (i >= j) << endl;
  cout << "i <= j is " << (i <= j) << endl;
  cout << "i == j is " << (i == j) << endl;
  cout << "i != j is " << (i not_eq j) << endl;
  cout << "i && j is " << (i and j) << endl;
  cout << "i || j is " << (i or j) << endl;
  cout << " (i < 10) && (j < 10) is "
       << ((i < 10) and (j < 10))  << endl;
}

Bitwise.cpp

#include "printBinary.h"
#include 
using namespace std;

// A macro to save typing:
#define PR(STR, EXPR) \
  cout << STR; printBinary(EXPR); cout << endl;  

int main() {
  unsigned int getval;
  unsigned char a, b;
  cout << "Enter a number between 0 and 255: ";
  cin >> getval; a = getval;
  PR("a in binary: ", a);
  cout << "Enter a number between 0 and 255: ";
  cin >> getval; b = getval;
  PR("b in binary: ", b);
  PR("a | b = ", a bitor b);
  PR("a & b = ", a bitand b);
  PR("a ^ b = ", a xor b);
  PR("~a = ", compl a);
  PR("~b = ", compl b);
  // An interesting bit pattern:
  unsigned char c = 0x5A; 
  PR("c in binary: ", c);
  a or_eq c;
  PR("a |= c; a = ", a);
  b and_eq c;
  PR("b &= c; b = ", b);
  b xor_eq a;
  PR("b ^= a; b = ", b);
} 

3-13

使用在Rotation.cpp程序中的函数去修改Bitwise.cpp程序。确保用这种方式能清楚地显示在旋转过程中的结果。

#include "printBinary.h"
#include 
using namespace std;

// A macro to save typing:
#define PR(STR, EXPR) \
  cout << STR; printBinary(EXPR); cout << endl;  

extern unsigned char rol(unsigned char val);
extern unsigned char ror(unsigned char val);

int main() {
  unsigned char a;
  cout << "输入一个 0 到 255 之间的数: ";
  cin >> a;
  cout << (int)a << endl;
  PR("当前数的二进制: ", a);
  cout << "开始左转:" << endl;
  for (int i = 0; i < 8; i++) {
    a = rol(a);
    PR("左转1位:", a);
  }

  PR("当前数的二进制:", a)
  cout << "开始右转:" << endl;
  for (int i = 0; i < 8; i++) {
    a = ror(a);
    PR("右转1位:", a);
  }
} 

输出:

当前数的二进制: 01100001
开始左转:
左转1位:11000010
左转1位:10000101
左转1位:00001011
左转1位:00010110
左转1位:00101100
左转1位:01011000
左转1位:10110000
左转1位:01100001
当前数的二进制:01100001
开始右转:
右转1位:10110000
右转1位:01011000
右转1位:00101100
右转1位:00010110
右转1位:00001011
右转1位:10000101
右转1位:11000010
右转1位:01100001

3-14

修改Ifthen.cpp程序,使用三重if-else运算符(?:)。

#include 
using namespace std;

int main() {
  int i;
  cout << "type a number and 'Enter'" << endl;
  cin >> i;
  i > 5 ? cout << "It's greater than 5" << endl :
  i < 5 ? cout << "It's less than 5 " << endl :
  cout << "It's equal to 5 " << endl;  

  cout << "type a number and 'Enter'" << endl;
  cin >> i;
  i < 10 ? i > 5 ? cout << "5 < i < 10" << endl :
  cout << "i <= 5" << endl :
  cout << "i >= 10" << endl;  
}

3-15

创建一个含有两个string对象和一个int对象的struct。使用typedef为该struct命名。创建struct的一个实例,初始化实例的三个值,然后输出它们。获得实例的地址,然后赋值给定义的struct类型的指针。改变实例的三个值,然后通过指针把它们打印出来。

#include 
#include 
using namespace std;

typedef struct {
    string a;
    string b;
    int c;
} MyStruct;

int main() {
    MyStruct a;
    a.a = "hello";
    a.b = "world";
    a.c = 100;
    cout << a.a << " " << a.b << " " << a.c << endl;

    MyStruct *pa;
    pa = &a;
    pa->a = "Hello";
    pa->b = "World";
    pa->c = 200;
    cout << pa->a << " " << pa->b << " " << pa->c << endl;
}

输出:

hello world 100
Hello World 200

3-16

编制一个使用颜色枚举类型的程序。创建一个enum类型的变量,然后用for循环输出与颜色名对应的数字。

#include 
using namespace std;

enum Color {
    COLOR_MIN,
    BLACK,
    WHITE,
    RED,
    GREEN,
    BLUE,
    COLOR_MAX
};

int main() {
    Color color = RED;
    for (int i = COLOR_MIN + 1; i < COLOR_MAX; i++) {
        if (i == color) {
            cout << i << endl;
            break;
        }
    }
}

3-17

用Union.cpp程序做一个试验,删除各种union元素,观察对union大小的影响。试给该union的一个元素赋值(属于某一类型),然后通过不同的元素(属于不同的类型)输出它的值,看看发生了什么情况。

#include 
using namespace std;

union Packed { // Declaration similar to a class
  char i;
  short j;
  int k;
  long l;
  float f;
  double d;  
  // The union will be the size of a 
  // double, since that's the largest element
};  // Semicolon ends a union, like a struct

int main() {
  cout << "sizeof(Packed) = " 
       << sizeof(Packed) << endl;
  Packed x;
  x.i = 'c';
  cout << x.i << endl;
  cout << x.j << endl;
  cout << x.k << endl;
  cout << x.l << endl;
  cout << x.f << endl;
  cout << x.d << endl;
}

因为short, int , long与char存储格式一致,所以都能够正确输出char 'c’的十进制数,但是由于浮点数的格式不一样,所以无法正确输出’c’对应十进制数99的浮点数。
输出:

sizeof(Packed) = 8
c
99
99
99
1.38729e-43
4.89125e-322

union大小是元素里类型字节占用最大的大小,在本cpp中就是double,占用8个字节,如果你删除l, f, d元素,那么大小就会变成int k的字节大小,也就是4个字节。

3-18

编制一个程序,连续定义两个int数组。第二个数组的开始下标紧接第一个数组的结束下标。给两个数组赋值。打印出第二个数组观察由此引起的变化。再在两个数组定义之间定义一个char变量,重复上述操作。可以创建一个数组输出函数以简化程序。
NND,我愣是没看懂啥意思

3-19

修改ArrayAddresses.cpp程序,使之能处理char、long、int、float以及double类型数据。

#include 
using namespace std;

int main() {
  char a[10];
  cout << "sizeof(char) = "<< sizeof(char) << endl;
  for(int i = 0; i < 10; i++)
    cout << "&a[" << i << "] = " 
         << (void*)&a[i] << endl; // 打印char的地址必须转换成别的类型,否则会当成字符串
  long b[10];
  cout << "sizeof(long) = "<< sizeof(long) << endl;
  for(int i = 0; i < 10; i++)
    cout << "&a[" << i << "] = " 
         << &b[i] << endl;
  // float, double忽略了
}

输出:

sizeof(char) = 1
&a[0] = 0xb0651ff89e
&a[1] = 0xb0651ff89f
&a[2] = 0xb0651ff8a0
&a[3] = 0xb0651ff8a1
&a[4] = 0xb0651ff8a2
&a[5] = 0xb0651ff8a3
&a[6] = 0xb0651ff8a4
&a[7] = 0xb0651ff8a5
&a[8] = 0xb0651ff8a6
&a[9] = 0xb0651ff8a7
sizeof(long) = 4
&a[0] = 0xb0651ff870
&a[1] = 0xb0651ff874
&a[2] = 0xb0651ff878
&a[3] = 0xb0651ff87c
&a[4] = 0xb0651ff880
&a[5] = 0xb0651ff884
&a[6] = 0xb0651ff888
&a[7] = 0xb0651ff88c
&a[8] = 0xb0651ff890
&a[9] = 0xb0651ff894

3-20

运用ArrayAddresses.cpp程序中的技术,输出在StructArray.cpp程序中定义的struct的大小以及数组元素的地址。

#include 
using namespace std;

typedef struct {
  int i, j, k;
} ThreeDpoint;

int main() {
  ThreeDpoint a[10];
  cout << "sizeof(ThreeDpoint) = "<< sizeof(ThreeDpoint) << endl;
  for(int i = 0; i < 10; i++)
    cout << "&a[" << i << "] = " 
         << &a[i] << endl;
}

输出:

sizeof(ThreeDpoint) = 12
&a[0] = 0xceb6dffbb0
&a[1] = 0xceb6dffbbc
&a[2] = 0xceb6dffbc8
&a[3] = 0xceb6dffbd4
&a[4] = 0xceb6dffbe0
&a[5] = 0xceb6dffbec
&a[6] = 0xceb6dffbf8
&a[7] = 0xceb6dffc04
&a[8] = 0xceb6dffc10
&a[9] = 0xceb6dffc1c

3-21

创建一个string对象数组且对每一个元素赋一个字符串。用for循环输出该数组。

#include 
#include 
using namespace std;

int main() {
    string strArr[3];
    strArr[0] = "hello"; strArr[1] = "csdn"; strArr[2] = "!";
    for (int i = 0; i < 3; i++) {
        cout << strArr[i] << endl;
    }
}

3-22

在ArgsToInts.cpp的基础上,编制两个新程序,它们各自使用atol()和atof()函数。

#include 
#include 
using namespace std;

int main(int argc, char* argv[]) {
  if (argc < 3) {
      cout << "请输入2个参数" << endl;
      return 0;
  }
  cout << atol(argv[1]) << endl;
  cout << atof(argv[2]) << endl;
}

3-23

修改PointerIncrement2.cpp程序,其中用union代替struct。

#include 
using namespace std;

typedef union {
  char c;
  short s;
  int i;
  long l;
  float f;
  double d;
  long double ld;
} Primitives;

int main() {
  Primitives p[10];
  Primitives* pp = p;
  cout << "sizeof(Primitives) = " 
       << sizeof(Primitives) << endl;
  cout << "pp = " << pp << endl;
  pp++;
  cout << "pp = " << pp << endl;
}

输出:

sizeof(Primitives) = 16
pp = 0x9dc2bffbe0
pp = 0x9dc2bffbf0

3-24

修改PointerArithmetic.cpp程序,其中使用long和long double。
这个比较简章,就是替换一下变量。

#include 
using namespace std;

#define P(EX) cout << #EX << ": " << EX << endl;

int main() {
  long a[10];
  for(int i = 0; i < 10; i++)
    a[i] = i; // Give it index values
  long* ip = a;
  P(*ip);
  P(*++ip);
  P(*(ip + 5));
  long* ip2 = ip + 5;
  P(*ip2);
  P(*(ip2 - 4));
  P(*--ip2);
  P(ip2 - ip); // Yields number of elements
}

输出:

*ip: 0
*++ip: 1
*(ip + 5): 6
*ip2: 6
*(ip2 - 4): 2
*--ip2: 5
ip2 - ip: 4

3-25

定义一个float变量。获得它的地址,把地址转化为unsigned char,赋值给一个unsigned char指针。使用指针和[ ]符号引用float变量中的下标,并用本章中定义的printBinary()函数输出该float的内存映像。(从0到sizeof(float))。改变该float变量的值看看是否能推算出下一步的情况(float包含编码的数据)。

#include "printBinary.h"
#include 
using namespace std;

int main() {
    float f = 3.5;//根据IEEE标准,实际二进制格式为01000000 01100000 00000000 00000000
    unsigned char *p = (unsigned char*)&f;
    int len = sizeof(float)/sizeof(unsigned char);
    for (int i = 0; i < len; i++) {
        printBinary(p[i]);cout << " ";
    }
    cout << endl;
    for (int i = 0; i < len; i++) {
        printBinary(p[len - 1 - i]);cout << " ";
    }
}

输出:

00000000 00000000 01100000 01000000
01000000 01100000 00000000 00000000

第二种输出顺序与IEEE标准的格式完全一致,第二种输出是先输出高位,然后是低位,分别对应了float字节的高位和低位,说明存储的字节序是小端字节序,即低位字节码存储在低地址上。

3-26

定义一个int数组。获得该数组的起始地址,使用static_cast把它转化为void*。写一个带以下参数的函数:一个void*、一个数字(表明字节的数目)和一个值(表明每个字节需要设定的值)。该函数必须为特定范围内的每个字节设定特定的值。在这个int数组上试验函数。

#include 
using namespace std;

void setArray(void *p, int size, int val) {
    unsigned char * pch = static_cast<unsigned char*>(p);
    for (int i = 0; i < size; i++) {
        pch[i] = val;
    }
}

int main() {
    const int LEN = 10;
    int a[LEN];
    setArray(static_cast<void*>(a), sizeof(a), 1);
    for (int i = 0; i < LEN; i++) {
        cout << "a[" << i << "] = 0x" << hex << a[i] << endl;
    }
}

输出:

a[0] = 0x1010101
a[1] = 0x1010101
a[2] = 0x1010101
a[3] = 0x1010101
a[4] = 0x1010101
a[5] = 0x1010101
a[6] = 0x1010101
a[7] = 0x1010101
a[8] = 0x1010101
a[9] = 0x1010101

3-27

建立一个const double类型数组和一个volatile double类型数组。通过引用每个数组的下标且用const_cast把每个元素分别转换为non-const和non-volatile,然后对每个元素赋值。

#include 
using namespace std;

int main() {
    const double a[10] = {0};
    volatile double b[10];
    for (int i = 0; i < 10; i++) {
        const_cast<double&>(a[i]) = i;
        const_cast<double&>(b[i]) = i;
    }
    for (int i = 0; i < 10; i++) {
        cout << a[i] << " ";
    }
    cout << endl;
    for (int i = 0; i < 10; i++) {
        cout << b[i] << " ";
    }
    cout << endl;
}

输出:

0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9

3-28

建立一个函数,该函数接受一个指向double类型数组的指针和一个表明该数组大小的值。该函数应该输出数组中的每个元素值。现在建立一个double类型的数组,且初始化每个元素的值为0,然后使用你的函数输出该数组。接着使用reinterpret_cast关键字把数组的起始地址转化为unsigned char*,把每个元素设置为1(提示:必须用sizeof运算符计算一个double类型变量包含的字节数)。现在使用你的数组输出函数输出结果。想想为什么每个元素值不设成1.0?

#include 
using namespace std;

void displayArray(double *pa, int len) {
    for (int i = 0; i < len; i++) {
        cout << pa[i] << " ";
    }
    cout << endl;
}

int main() {
    double a[10] = {0};
    unsigned char *p = reinterpret_cast<unsigned char*>(a);
    for (int i = 0, size = sizeof(a); i < size; i++) {
        p[i] = 1;
    }
    displayArray(a, 10);
}

输出:

7.7486e-304 7.7486e-304 7.7486e-304 7.7486e-304 7.7486e-304 7.7486e-304 7.7486e-304 7.7486e-304 7.7486e-304 7.7486e-304

可以把1.0赋值为每个元素,但是unsigned char*只接受0~255之间的数,即使你给它赋值为1.0,也会被截断成1赋值给每个元素。

3-29

(带有挑战性)修改FloatingAsBinary.cpp程序以便能够以单独的二进制位组输出double类型数据。为实现目标,必须用自己的特殊代码(可以从printBinary()函数中衍生)去替换对printBinary()的调用,还必须查阅并理解自己的编译器的浮点数字节格式(这是具有挑战性的部分)。
单独的二进制位应该指的是分别输出符号位、尾数、指数。

3-30

创建makefile文件,可以把编译YourPets1.cpp和YourPets2.cpp程序(用你特定的编译器)以及执行这两个程序作为默认的目标,确保使用后缀规则。
3-30.makefile

CPP = g++
OFLAG = -o
.SUFFIXES : .o .cpp
.cpp.o :
	$(CPP) $(CPPFLAGS) -c $<

all:  \
	YourPets1 \
	YourPets2

YourPets1: YourPets1.o 
	$(CPP) $(OFLAG)$@ $<
	rm $<
	./$@

YourPets2: YourPets2.o 
	$(CPP) $(OFLAG)$@ $<
	rm $<
	./$@

YourPets1.o: YourPets1.cpp
YourPets2.o: YourPets2.cpp

执行输出:

$ make -f 3-30.makefile
g++  -c YourPets1.cpp
g++ -oYourPets1 YourPets1.o
rm YourPets1.o
./YourPets1
g++  -c YourPets2.cpp
g++ -oYourPets2 YourPets2.o
rm YourPets2.o
./YourPets2
char 大小:1
0x932b5ffc2f 0x932b5ffc2e 0x932b5ffc2d
short int 大小:2
0x932b5ffc2a 0x932b5ffc28 0x932b5ffc26
int 大小:4
0x932b5ffc20 0x932b5ffc1c 0x932b5ffc18
long int 大小:4
0x932b5ffc14 0x932b5ffc10 0x932b5ffc0c
long long 大小:8
0x932b5ffc00 0x932b5ffbf8 0x932b5ffbf0
float 大小:4
0x932b5ffbec 0x932b5ffbe8 0x932b5ffbe4
double 大小:8
0x932b5ffbd8 0x932b5ffbd0 0x932b5ffbc8
long double 大小:16
0x932b5ffbb0 0x932b5ffba0 0x932b5ffb90

3-31

修改StringizingExpressions.cpp程序,通过设置一个命令行标志,使用P(A)能用条件#ifdef与调试代码分离开。需要参数编译器文档,了解在命令行上怎样定义和取消定义预处理的值。
cpp文件:

#include 
using namespace std;

#ifdef LOG
#define P(A) cout << #A << ": " << (A) << endl;
#else
#define P(A) cout << (A) << endl;
#endif

int main() {
  int a = 1, b = 2, c = 3;
  P(a); P(b); P(c);
  P(a + b);
  P((c - a)/b);
} 

编译命令:

$ make -f gcc.makefile StringizingExpressions
g++  -c StringizingExpressions.cpp
g++ -oStringizingExpressions StringizingExpressions.o
rm StringizingExpressions.o
./StringizingExpressions
1
2
3
3
1

编译命令上添加宏定义标志,需要修改makefile文件,在后缀规则前面添加下面一行,表示定义一个宏:

CPPFLAGS = -DLOG

再看输出:

$ make -f gcc.makefile StringizingExpressions
g++ -DLOG -c StringizingExpressions.cpp
g++ -oStringizingExpressions StringizingExpressions.o
rm StringizingExpressions.o
./StringizingExpressions
a: 1
b: 2
c: 3
a + b: 3
(c - a)/b: 1

3-32

定义一个函数,该函数接受一个double型参数且返回一个int值。创建和初始化一个指向该函数的指针,通过这个指针调用这个函数。

#include 
using namespace std;

int f(double d) {
    cout << "int f(double d) 被调用" << endl;
    return 0;
}

int main() {
    int (*pf)(double);
    pf = f;
    pf(0);
}

输出:

int f(double d) 被调用

3-33

声明一个函数,该函数接受一个int参数且返回指向另一个函数的指针,这个函数接受一个char变量且返回一个float值。

#include 
using namespace std;

float f(char a) {
    cout << "float f(char a) 被调用" << endl;
    return 0;
}

float (*ff(int))(char) {
    return f;
}

typedef float (*pf)(char);
//或者如下声明ff也可以更简单
pf ff1(int) {
    return f;
}

int main() {
    pf a = ff(0);
    a(1);
    a = ff1(0);
    a(1);
}

输出:

float f(char a) 被调用
float f(char a) 被调用

3-34

修改FunctionTable.cpp程序使每个函数返回一个string(而不是输出一个消息)以便在main()函数中输出。

#include 
#include 
using namespace std;

// A macro to define dummy functions:
#define DF(N) string N() { \
   return "function " #N " called..."; }

DF(a); DF(b); DF(c); DF(d); DF(e); DF(f); DF(g);

string (*func_table[])() = { a, b, c, d, e, f, g };

int main() {
  while(1) {
    cout << "press a key from 'a' to 'g' "
      "or q to quit" << endl;
    char c;
    cin >> c;
    if ( c == 'q' ) 
      break; // ... out of while(1)
    if ( c < 'a' || c > 'g' ) 
      continue;
    cout << (*func_table[c - 'a'])() << endl;
  }
}

输出:

press a key from 'a' to 'g' or q to quit
a
function a called...
press a key from 'a' to 'g' or q to quit
b
function b called...
press a key from 'a' to 'g' or q to quit
c
function c called...
press a key from 'a' to 'g' or q to quit
d
function d called...
press a key from 'a' to 'g' or q to quit
e
function e called...
press a key from 'a' to 'g' or q to quit
f
function f called...
press a key from 'a' to 'g' or q to quit
g
function g called...
press a key from 'a' to 'g' or q to quit
h
press a key from 'a' to 'g' or q to quit
q

3-35

为前面某个练习(自己选择)建立一个makefile文件,允许键入make以构建这个程序,并且键入make debug以构建带有调试信息的程序。
3-35.cpp

#include 
using namespace std;

int main() {
    #ifdef DEBUG
    cout << "debug info" << endl;
    #endif
}

3-35.makefile:

CPP = g++
OFLAG = -o
#CPPFLAGS = -DLOG
.SUFFIXES : .o .cpp .c
.cpp.o :
	$(CPP) $(CPPFLAGS) -c $<

all: \
	3-35

debug: \
	3-35-debug

3-35: 3-35.o
	$(CPP) $(OFLAG)$@ $<
	rm $<
	./$@

3-35-debug: 3-35-debug.o
	$(CPP) $(OFLAG)$@ $<
	rm $<
	./$@
3-35.o:3-35.cpp
3-35-debug.o: 3-35.cpp
	$(CPP) -DDEBUG $(OFLAG)$@ -c $<

默认目标是all,所以编译不带debug信息的命令如下:

$ make -f 3-35.makefile
g++  -c 3-35.cpp
g++ -o3-35 3-35.o
rm 3-35.o
./3-35

编译debug信息的,需要使用debug目标:

$ make -f 3-35.makefile debug
g++ -DDEBUG -o3-35-debug.o -c 3-35.cpp
g++ -o3-35-debug 3-35-debug.o
rm 3-35-debug.o
./3-35-debug
debug info

你可能感兴趣的:(C++,c++,C++编程思想)