《C++编程思想》第二章 数 据 抽 象(原书代码+习题+答案)

相关代码例如以下:

1.

<span style="font-size:18px;">/*声明与定义的差别*/

#include <iostream>
using namespace std; extern int i;//声明
extern float f(float);//声明 float b;//定义+声明
float f(float a)//定义
{ return a + 1.0;
} int i;//定义
int h(int x)//定义+声明
{
return x + 1;
} int main()
{
b = 1.0;
i = 2;
cout<<f(b)<<endl;
cout<<h(i)<<endl; return 0;
}</span>

2.

<span style="font-size:18px;">/*test.h*/
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
/* 一个袖珍C库 C */
typedef struct STASHtag
{
int size; //Size of each space
int quantity; //Number of storage spaces
int next; //Next empty space
unsigned char* storage;//storage指针是一个unsigned char*。 这是 C 编译器支持的最小的存储片。虽然在某些机器
//上它可能与最大的一般大,这依赖于详细实现。 storage指向的内存从堆中分配
}Stash; void initialize(Stash* S, int Size);//initialize()完毕对 struct stash 的必要的设置。即设置内部变量为适当的值。 最初,设置
//storage指针为零。设置size 指示器也为零,表示初始存储未被分配。 void cleanup(Stash* S); //清除
int add(Stash* S, void* element); //add()函数在stash的下一个可用位子上插入一个元素。首先,它检查是否有可用空间,如
//果没有,它就用后面介绍的 inflate() 函数扩展存储空间。
void* fetch(Stash* S, int index); //fetch()首先看索引是否越界,假设没有越界,返回所希望的变量地址,地址的计算採用与
//add()中同样的方法
int count(Stash* S); //返回所存储空间大小
void inflate(Stash* S, int increase);
/*inflate()函数使用realloc()为stash得到更大的空间块。 realloc()把已经分配而又希望重分配
的存储单元首地址作为它的第一个參数(假设这个參数为零,比如 initialize()刚刚被调用时。
realloc()分配一个新块)。第二个參数是这个块新的长度,假设这个长度比原来的小。这个块
将不须要作拷贝。简单地告诉堆管理器剩下的空间是空暇的。 假设这个长度比原来的大。在堆
中没有足够的相临空间,所以要分配新块,而且要拷贝内存。 assert()检查以确信这个操作成
功。(假设这个堆用光了。 malloc()、 calloc()和realloc()都返回零。 )*/</span>
<span style="font-size:18px;">/*test.c*/

/*如果有一个程序设计工具,当创建时它的表现像一个数组。但它的长度能在执行时建
立。我称它为stash*/ #include "test.h" void initialize(Stash* S, int Size)
{
S->size = Size;
S->quantity = 0;
S->storage = 0;
S->next = 0;
} void cleanup(Stash* S)
{
if(S->storage)
{
puts("freeing storage");
free(S->storage);
}
} int add(Stash* S, void* element)
{
if(S->next >= S->quantity)
{
inflate(S,100);
}
memcpy(&(S->storage[S->next * S->size]),element,S->size );
/*我们必须用标准 C 库函数memcpy( )一个字节一个字节地拷贝这个变量,第一个參数是 memcpy()
開始拷贝字节的目的地址,由以下表达式产生:
&(S->storage[S->next * S->size])
它指示从存储块開始的第 next个可用单元结束。这个数实际上就是已经用过的单元号加一
的计数,它必须乘上每一个单元拥有的字节数,产生按字节计算的偏移量。 这不产生地址。而是
产生处于这个地址的字节。为了产生地址,必须使用地址操作符 &。
memcpy()的第二和第三个參数各自是被拷贝变量的開始地址和要拷贝的字节数。 n e x t计数
器加一。并返回被存值的索引。 这样。程序猿能够在后面调用 fetch( )时用它来取得这个元素。 */
S->next ++;
return (S->next - 1);
} void* fetch(Stash* S, int index)
{
if(index >= S->next || index < 0)
{
return 0;
}
return &(S->storage[index * S->size]);
} int count(Stash* S)
{
return S->next;
} void inflate(Stash* S, int increase)
{
void* v = realloc(S->storage,(S->quantity + increase)*S->size );
assert(v);
S->storage = v;//在 C 库中的 inflate()中,能够将void *赋给其它不论什么指针。比如S->storage = v;并且编译器能够通过。
S->quantity += increase;
}</span>
<span style="font-size:18px;">/*main.c*/

#include "test.h"

#define BUFSIZE 80

int main()
{
Stash intStash,stringStash;
int i;
FILE* file;
char buf[BUFSIZE];
char* cp; initialize(&intStash,sizeof(int));
for(i = 0;i < 100;++i)
{
add(&intStash,&i);
} initialize(&stringStash,sizeof(char)*BUFSIZE);
file = fopen("main.c","r");
assert(file);
while(fgets(buf, BUFSIZE, file))
{
add(&stringStash, buf);
}
fclose(file); for(i = 0;i < count(&intStash);++i)
{
printf("fetch(&intStash, %d) = %d\n",i ,
*(int*)fetch(&intStash,i));
}
i = 0;
while((cp = fetch(&stringStash,i++)) != 0)
{
printf("fetch(&stringStash, %d) = %s",
i-1,cp);
}
putchar('\n');
cleanup(&intStash);
cleanup(&stringStash); return 0;
}</span>

3.

<span style="font-size:18px;">#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
//C++
struct stash
{
int size; //Size of each space
int quantity; //Number of storage spaces
int next; //Next empty space
unsigned char* storage; //storage指针是一个unsigned char*。这是 C 编译器支持的最小的存储片,虽然在某些机器
//上它可能与最大的一般大,这依赖于详细实现。 storage指向的内存从堆中分配
void initialize(int Size);//initialize()完毕对 struct stash 的必要的设置,即设置内部变量为适当的值。最初,设置
//storage指针为零。设置size 指示器也为零,表示初始存储未被分配。
void cleanup(); //清除
int add(void* element); //add()函数在stash的下一个可用位子上插入一个元素。首先,它检查是否有可用空间,如
//果没有,它就用后面介绍的 inflate() 函数扩展存储空间。 void* fetch(int index); //fetch()首先看索引是否越界,假设没有越界,返回所希望的变量地址,地址的计算採用与
//add()中同样的方法
int count(); //返回所存储空间大小
void inflate(int increase);
/*inflate()函数使用realloc()为stash得到更大的空间块。 realloc()把已经分配而又希望重分配
的存储单元首地址作为它的第一个參数(假设这个參数为零,比如 initialize()刚刚被调用时。
realloc()分配一个新块)。第二个參数是这个块新的长度,假设这个长度比原来的小。这个块
将不须要作拷贝,简单地告诉堆管理器剩下的空间是空暇的。假设这个长度比原来的大,在堆
中没有足够的相临空间。所以要分配新块,而且要拷贝内存。 assert()检查以确信这个操作成
功。 (假设这个堆用光了。 malloc()、 calloc()和realloc()都返回零。 )*/
};
</span>
<span style="font-size:18px;">/*test.cpp*/

/*如果有一个程序设计工具。当创建时它的表现像一个数组,但它的长度能在执行时建
立。我称它为stash*/ #include "test.h" void stash::initialize(int Size)
{
size = Size;
quantity = 0;
storage = 0;
next = 0;
} void stash::cleanup()
{
if(storage)
{
puts("freeing storage");
free(storage);
}
} int stash::add(void* element)
{
if(next >= quantity)
{
inflate(100);
}
memcpy(&(storage[next * size]),element,size );
/*我们必须用标准 C 库函数memcpy( )一个字节一个字节地拷贝这个变量,第一个參数是 memcpy()
開始拷贝字节的目的地址,由以下表达式产生:
&(S->storage[S->next * S->size])
它指示从存储块開始的第 next个可用单元结束。这个数实际上就是已经用过的单元号加一
的计数,它必须乘上每一个单元拥有的字节数,产生按字节计算的偏移量。这不产生地址,而是
产生处于这个地址的字节,为了产生地址。必须使用地址操作符 &。
memcpy()的第二和第三个參数各自是被拷贝变量的開始地址和要拷贝的字节数。 n e x t计数
器加一,并返回被存值的索引。这样,程序猿能够在后面调用 fetch( )时用它来取得这个元素。*/
next ++;
return (next - 1);
} void* stash::fetch(int index)
{
if(index >= next || index < 0)
{
return 0;
}
return &(storage[index * size]);
} int stash::count()
{
return next;
} void stash::inflate( int increase)
{
void* v = realloc(storage,(quantity + increase)*size );
assert(v);
storage = (unsigned char*)v;
quantity += increase;
}</span>
<span style="font-size:18px;">#include "test.h"
#define BUFSIZE 80 int main()
{
stash intStash,stringStash;
int i;
FILE* file;
char buf[BUFSIZE];
char* cp; intStash.initialize(sizeof(int));
for(i = 0;i < 100;++i)
{
intStash.add(&i);
} stringStash.initialize(sizeof(char)*BUFSIZE);
file = fopen("main.cpp","r");
assert(file);
while(fgets(buf, BUFSIZE, file))
{
stringStash.add(buf);
}
fclose(file); for(i = 0;i < intStash.count();++i)
{
printf("intStash.fetch(%d) = %d\n",i,
*(int*)intStash.fetch(i));
}
i = 0;
while((cp = (char*)stringStash.fetch(i++)) != 0)
{
printf("stringStash.fetch(%d) = %s",
i-1,cp);
}
putchar('\n');
intStash.cleanup();
stringStash.cleanup();
return 0;
}</span>

4.

<span style="font-size:18px;">/*1.h     C     */
typedef struct STASHtag
{
int size; //Size of each space
int quantity; //Number of storage spaces
int next; //Next empty space
unsigned char* storage;//storage指针是一个unsigned char*。这是 C 编译器支持的最小的存储片,虽然在某些机器
//上它可能与最大的一般大,这依赖于详细实现。 storage指向的内存从堆中分配
}Stash; void initialize(Stash* S, int Size);//initialize()完毕对 struct stash 的必要的设置,即设置内部变量为适当的值。最初,设置
//storage指针为零,设置size 指示器也为零,表示初始存储未被分配。 void cleanup(Stash* S); //清除
int add(Stash* S, void* element); //add()函数在stash的下一个可用位子上插入一个元素。首先,它检查是否有可用空间。如
//果没有,它就用后面介绍的 inflate() 函数扩展存储空间。 void* fetch(Stash* S, int index); //fetch()首先看索引是否越界,假设没有越界,返回所希望的变量地址,地址的计算採用与
//add()中同样的方法
int count(Stash* S); //返回所存储空间大小
void inflate(Stash* S, int increase);
/*inflate()函数使用realloc()为stash得到更大的空间块。 realloc()把已经分配而又希望重分配
的存储单元首地址作为它的第一个參数(假设这个參数为零。比如 initialize()刚刚被调用时,
realloc()分配一个新块)。第二个參数是这个块新的长度。假设这个长度比原来的小,这个块
将不须要作拷贝,简单地告诉堆管理器剩下的空间是空暇的。假设这个长度比原来的大,在堆
中没有足够的相临空间。所以要分配新块,而且要拷贝内存。 assert()检查以确信这个操作成
功。(假设这个堆用光了。 malloc()、 calloc()和realloc()都返回零。 )*/</span>
<span style="font-size:18px;">//C++2.h
struct stash
{
int size; //Size of each space
int quantity; //Number of storage spaces
int next; //Next empty space
unsigned char* storage; //storage指针是一个unsigned char*。这是 C 编译器支持的最小的存储片,虽然在某些机器
//上它可能与最大的一般大。这依赖于详细实现。 storage指向的内存从堆中分配
void initialize(int Size);//initialize()完毕对 struct stash 的必要的设置,即设置内部变量为适当的值。 最初,设置
//storage指针为零,设置size 指示器也为零。表示初始存储未被分配。
void cleanup(); //清除
int add(void* element); //add()函数在stash的下一个可用位子上插入一个元素。 首先,它检查是否有可用空间,如
//果没有,它就用后面介绍的 inflate() 函数扩展存储空间。
void* fetch(int index); //fetch()首先看索引是否越界。假设没有越界。返回所希望的变量地址,地址的计算採用与
//add()中同样的方法
int count(); //返回所存储空间大小
void inflate(int increase);
/*inflate()函数使用realloc()为stash得到更大的空间块。 realloc()把已经分配而又希望重分配
的存储单元首地址作为它的第一个參数(假设这个參数为零,比如 initialize()刚刚被调用时,
realloc()分配一个新块)。第二个參数是这个块新的长度,假设这个长度比原来的小。这个块
将不须要作拷贝。简单地告诉堆管理器剩下的空间是空暇的。假设这个长度比原来的大,在堆
中没有足够的相临空间,所以要分配新块,而且要拷贝内存。 assert()检查以确信这个操作成
功。(假设这个堆用光了, malloc()、 calloc()和realloc()都返回零。)*/
};
</span>
<span style="font-size:18px;">#include "1.h"
#include "2.h"
#include <stdio.h> struct A
{
int I[100];
}; struct B
{
void f();
}; void B::f()
{} int main()
{
printf("sizeof struct A = %d bytes\n",
sizeof(A));//每一个 int 占二个字节
printf("sizeof struct B = %d bytes\n",
sizeof(B));//struct B 是神秘的,由于它是没有数据成员的 struct
printf("sizeof Stash in C = %d bytes\n",
sizeof(Stash));
printf("sizeof stash in C++ = %d bytes\n",
sizeof(stash));
/*最后两个 sizeof 语句表明在 C++ 中的结构长度与 C 中等价版本号的长度同样。 C++ 尽力不
添加不论什么花费*/ return 0;
}</span>

5.

<span style="font-size:18px;">#ifndef NESTED_H_
#define NESTED_H_ struct stack//这个嵌套 struct 称为 link。它包含指向这个表中的下一个 link 的指针和指向存放在 link 中的数据的指针。假设 next 指针是零。意味着表尾。
{
struct link
{
void* data;
link* next;
void initialize(void* Data, link* Next);
}*head;
void initialize();
void push(void* Data);
void* peek();
void* pop();
void cleanup();//cleanup 去除每一个栈元素,并释放data 指针
};
#endif</span>
<span style="font-size:18px;">#include "nested.h"
#include <stdlib.h>
#include <assert.h> void stack::link::initialize(void* Data, link* Next)
//简单地两次使用范围分解运算符,以指明这个嵌套 struct 的名字。 stack::link::initialize( ) 函数取參数并把參数赋给它的成员们
{
data = Data;
next = Next;
} void stack::initialize()
//stack::initialize( ) 函数置 head 为零。使得这个对象知道它有一个空表
{
head = 0;
} void stack::push(void* Data)
//stack::push( ) 取參数,也就是一个指向希望用这个 stack 保存的一块数据的指针。而且把
//这个指针放在栈顶
{
link* newlink = (link*)malloc(sizeof(link));
assert(newlink);
newlink->initialize(Data, head);
head = newlink;
} void* stack::peek()
//返回head的值
{
return head->data;
} void* stack::pop()
//stack::pop( )取出当前在该栈顶部的 data 指针,然后向下移 head 指针,删除该栈老的栈顶元素
{
if(head == 0)
{
return 0;
}
void* result = head->data;
link* oldHead = head;
head = head->next;
free(oldHead);
return result;
} void stack::cleanup()
//stack::cleanup()创建cursor 在整个栈上移动。用free()释放每一个link的data和link 本身
{
link* cursor = head;
while(head)
{
cursor = cursor->next;
free(head->data);
free(head);
head = cursor;
}
}</span>
<span style="font-size:18px;">#include "nested.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h> #define BUFSIZE 100 int main(int argc, char** argv)
{
stack textlines;
FILE* file;
char* s;
char buf[BUFSIZE];
assert(argc == 2);
textlines.initialize();
file = fopen(argv[1],"r");
assert(file);
while(fgets(buf, BUFSIZE, file))
{
char* string = (char*)malloc(strlen(buf)+1);
assert(string);
strcpy(string, buf);
textlines.push(string);
} while((s = (char*)textlines.pop()) != 0 )
{
printf("%s",s);
free(s);
}
textlines.cleanup(); return 0;
}</span>

6.

<span style="font-size:18px;">#include <iostream>
using namespace std;
//假设在 S::f() 中没有范围分解。
//编译器会缺省地选择 f() 和 A 的成员版本号
int A;
void f()
{} struct S
{
int A;
void f();
}; void S::f()
{
::f();
::A++;
A--;
}
int main()
{
return 0;
}</span>

1) 创建一个 struct 声明,它有单个成员函数。然后对这个成员函数创建定义。创建这个新数据类型的对象,再调用这个成员函数。

#include <iostream>
using namespace std; struct Student
{
void play();
}; void Student::play()
{
cout<<"Funny!"<<endl;
} int main()
{
Student s;
s.play();
return 0;
}

2) 编写而且编译一段代码,这段代码完毕数据成员选择和函数调用。

#include <iostream>
#include <string>
using namespace std; struct Student
{
string address;
void play(string address);
}; void Student::play(string address)
{
cout<<address<<" "<<"is Funny!"<<endl;
} int main()
{
Student s;
s.play("Xi'an");
return 0;
}

3) 写一个在还有一个结构中的被声明结构的样例(嵌套结构)。并说明怎样定义这个结构的成员。

#include <iostream>
#include <string>
using namespace std; #ifndef TEST_H_
#define TSET_H_ struct family
{
struct school
{
string address;
void schools(string address);
}*addr;
int number;
void families(int number);
}; #endif
#include "test.h"

void family::school::schools(string address)//取參数并输出
{
cout<<"The school's address: "<<address<<endl;
}
void family::families(int number)//取參数并输出
{
cout<<"The family's people number: "<<number<<endl;
}
#include "test.h"

int main()
{
family f;
f.families(5); return 0;
}

4) 结构有多大?写一段能打印各个结构的长度的代码。创建一些结构。它们仅仅有数据成员。还有一些有数据成员和函数成员。然后创建一个结构,它根本没有成员。打印出全部这些结构的长度。

对于根本没有成员的结构的结果作出解释。

#include <stdio.h>

struct A
{
char a;
int b;
}; struct B
{
void f();
double d;
}; struct C
{ }; void B::f()
{} int main()
{
printf("sizeof struct A = %d bytes\n",
sizeof(A));//每一个 int 占四个字节,char占一个字节,可是要是四的倍数即为八
printf("sizeof struct B = %d bytes\n",
sizeof(B));//每一个double占八个字节,函数字节数为八
printf("sizeof struct C = %d bytes\n",
sizeof(C));//struct C 是神秘的,由于它是没有数据成员的 struct return 0;
}

5) C++对于枚举、联合和 struct 自己主动创建 typedef 的等价物,正如在本章中看到的。

写一个能说明这一点的小程序。

#include <iostream>
#include <string>
using namespace std; typedef struct
{
int age;
string address;
}student; typedef union
{
int a;
double d;
}number; typedef enum
{
white,black,blue,green
}colour; int main()
{
student s;
s.address = "Xi'an";
s.age = 18;
cout<<"student's address: "<<s.address<<endl;
cout<<"student's age: "<<s.age<<endl; number n;
n.a = 5;
cout<<"people number:"<<n.a<<endl; colour c = white;
cout<<"number of enum: "<<c<<endl; return 0;
}
对于上述答案仅供參考,如有错误希望大家指出,谢谢大家。
上一篇:洛谷P2446 大陆争霸


下一篇:loadrunner socket协议问题归纳(3)