资源描述
17
第五章 数组与指针习题
二、编程与综合练习题
5.3 打印杨辉三角形(10行)。使用二维数组并利用每个系数等于其肩上两系数之和。
解:好的算法无特例,二维数组共用11列,第1列全0,方便计算
#include<iostream>
using namespace std;
int main(){
int a[10][11]={0,1},i,j; //初始化时写好第1行,其余各行全0
for(i=1;i<10;i++) //为了全部算法无特例,共用11列,第1列全0,方便计算
for(j=1;j<=i+1;j++) a[i][j]=a[i-1][j-1]+a[i-1][j];
for(i=0;i<10;i++){
for(j=1;j<=i+1;j++) cout<<a[i][j]<<'\t';
cout<<endl;
}
return 0;
}
5.4 将[例5.5]改用一维数组,附加行、列参数,实现通用算法。
解:用一维数组,附加行、列参数,实现通用算法难度大。
#include <iostream>
#include <iomanip>
using namespace std;
void inverse(int [], int [],int,int);//注意数组最高维可缺省,例5.5因初学未省
void multi(int [], int [], int [],int,int,int);
void output(int [],int,int);
int main(){
int middle[6*3], result[6*4];//注意写作6*3等可清楚看出矩阵的行列
int matrix1[3*6]={8,10,12,23,1,3,5,7,9,2,4,6,34,45,56,2,4,6};
int matrix2[3*4]={3,2,1,0,-1,-2,9,8,7,6,5,4};
output(matrix1,3,6);
inverse(matrix1,middle,3,6);
output(middle,6,3);
output(matrix2,3,4);
multi(middle,matrix2,result,6,3,4);
output(result,6,4);
return 0;
}
void inverse(int matrix1_1[],int middle_1[],int a,int b){
int i,j;
for (i=0;i<a;i++)
for (j=0;j<b;j++)
middle_1[i+j*a]=matrix1_1[i*b+j];
return;
}
void multi(int middle_1[],int matrix2_1[],int result_1[],int a,int b,int c){
int i,j,k;
for (i=0;i<a;i++){
for (j=0;j<c;j++){
result_1[i*c+j] = 0;
for (k=0;k<b;k++)
result_1[i*c+j]+=middle_1[i*b+k]*matrix2_1[k*c+j];
}
}
return;
}
void output(int max_1[],int a,int b){
for (int i=0;i<a;i++){
for (int j=0;j<b;j++)
cout <<setw(4)<<max_1[i*b+j]<<" ";
cout<<'\n';
}
cout<<endl;
return;
}
5.5 编写函数int atoi(char s[ ]),将字符串s转化为整型数返回。注意负数处理方法。
解:用指针处理字符串非常方便。使用符号位来处理负数。
#include<iostream>
using namespace std;
int atoi(char s[]){
int temp=0,f=1,i=0;
while(s[i]!='\0'&&s[i]!='-'&&(s[i]<'0'||s[i]>'9')) i++;//去除串前部无效字符
if(s[i]=='-'){//读负号
f=-1;
i++;
}
if(s[i]<'0'||s[i]>'9') cout<<"error!"<<endl;//串非法时,输出提示,返回0
while(s[i]>='0'&&s[i]<='9'){//转换数字串
temp=temp*10+s[i]-48;
i++;
}
return f*temp;
}
int main(){
char num[20];
cin.getline(num,19);
cout<<atoi(num)<<'\n';
return 0;
}
5.6 有如下定义:
int ival=60021;
int *ip;
double *dp;
下面哪些赋值非法或可能带来错误,并加以讨论。
ival=*ip; ival=ip; *ip=ival; ip=ival; *ip=&ival;
ip=&ival; dp=ip; dp=*ip; *dp=*ip;
解:ival=*ip; 错,未确定指针ip初值,用随机内存地址中的数据给ival赋值是危险的。但语法对。
ival=ip; 错,赋值类型错,指针型不能赋给整型。
*ip=ival; 错,未确定指针ip初值,用ival给随机内存赋值是危险的。但语法对。
ip=ival; 错,赋值类型错,整型不能赋给指针型。
*ip=&ival; 错,赋值类型错,地址(指针型)不能赋给整型。
ip=&ival; 对,地址赋给指针型。
dp=ip; 错,整型指针不能赋给双精度指针。
dp=*ip; 错,赋值类型错,整型不能赋给指针型。
*dp=*ip; 对,赋值类型转换
5.7 编程定义一个整型、一个双精度型、一个字符型的指针,并赋初值,然后显示各指针所指目标的值与地址,各指针的值与指针本身的地址及各指针所占字节数(长度)。
*其中地址用十六进制显示。
解:注意:字符指针输出是字符串,必须强制转换为无类型指针
#include<iostream>
using namespace std;
int main(){
int *ip,ival=100;
double *dp,dval=99.9;
char *cp,cval='A';
ip=&ival;
dp=&dval;
cp=&cval;
cout<<*ip<<'\t'<<&*ip<<'\t'<<sizeof(*ip)<<endl;
cout<<*dp<<'\t'<<&*dp<<'\t'<<sizeof(*dp)<<endl;
cout<<*cp<<'\t'<<(void*)&*cp<<'\t'<<sizeof(*cp)<<endl;
//字符指针输出是字符串,必须强制转换为无类型指针
cout<<*cp<<'\t'<<&*cp<<'\t'<<sizeof(*cp)<<endl;
//输出A开头的字符串
cout<<ip<<'\t'<<&ip<<'\t'<<sizeof(ip)<<endl;
cout<<dp<<'\t'<<&dp<<'\t'<<sizeof(dp)<<endl;
cout<<(void*)cp<<'\t'<<&cp<<'\t'<<sizeof(cp)<<endl;
return 0;
}
一个典型的运行结果:
变量
内容
首地址
长度(字节)
cval
‘A’
0x0012ff64
1
cp
0x0012ff64
0x0012ff68
4
dval
99.9
0x0012ff6c
8
dp
0x0012ff6c
0x0012ff74
4
ival
100
0x0012ff78
4
ip
0x0012ff78
0x0012ff7c
4
内存分配解释:速度优化时通常以字(4字节)为单位(开始地址可被4整除)给变量安排内存。cval仅用一个字节,也安排了4个字节。
5.8 分别编写下列字符串处理函数
(1)char *strcat1(char *s,const char *ct);
将串ct接到串s的后面,形成一个长串。【例6.7】以数组为参数,现用指针为参数。
(2)int strlen1(const char * s);
求字符串长度的函数,返回串长(不包括串结束符)。
(3)char * reverse (char *);
反置字符串s,即可将“break”成为“kaerb”。
(4)char * strchr( const char *cs,char c);
查找字符c在串cs中第一次出现的位置,返回指向该字符的指针,若没有出现则返回NULL。
(5)char *strstr (const char *cs1,const char *cs2);
返回串cs2作为子串在cs1中第一次出现的位置,若没有出现则返回NULL。
解:为了函数的通用性,有些可不要返回值的函数,也保留返回值。反置字符串函数,从串两头的指针同时向中间移动,重合或交错时停止。查找子串,先找子串的第一个字符,再核对子串其他字符。
#include<iostream>
using namespace std;
char* strcat1(char* s,const char* ct){
char* st=s;
while(*s) s++;//*s作为条件,等效*s!=0
while(*s++=*ct++);
return st;
}
int strlen1(const char* s){
int i=0;
while(*s++) i++;
return i;
}
char* reverse (char* s){
char temp,* temp1=s,* temp2=s;
while(*temp2) temp2++;
temp2--; //指针移回串尾
while(temp2-temp1>0){//注意此处,从串两头的指针同时向中间移动,重合或交错时停止
temp=*temp1;
*temp1=*temp2;
*temp2=temp;
temp1++;
temp2--;
}
return s;
}
char* strchr( const char*cs,char c){
while(*cs!=c&&*cs) cs++;
if(*cs==0) cs=NULL; //未找到返回NALL
return (char*)cs;
}
char *strstr (const char *cs1,const char *cs2){
char *temp;
char *temp1=(char*)cs2;
while(*cs1){ //只要主串还有字符未查,则继续
while(*cs1!=*cs2&&*cs1) cs1++;
//找到主串含有子串的第一个字符,或主串查完停止
temp=(char*)cs1;
temp1=(char*)cs2;
if(*cs1){ //核对子串其他字符
while(*cs1++==*temp1++||*temp1);
if(*temp1==0) return temp; //找到子串返回
}
}
return NULL; //未找到返回NAL
}
int main(){
char a[40]="李明";
char b[20]="是东南大学学生";
char c[40]="Southeast University";
char *cp;
cout<<a<<endl;
cout<<b<<endl;
strcat1(a,b);
cout<<"字符串连接后:"<<endl;
cout<<a<<endl; //打印字符数组a
cout<<"字符串长度为:"<<strlen1(a)<<endl;
cout<<c<<endl;
cp=strchr(c,'U');
if(cp==NULL) cout<<"未找到"<<endl;
else cout<<cp<<endl; //找到输出由该字符开始的剩余串
cp=strchr(c,'A');
if(cp==NULL) cout<<"未找到"<<endl;
else cout<<cp<<endl;
cout<<reverse(c)<<endl;
cp=strstr(a,"东南");
if(cp!=NULL) cout<<cp<<endl; //找到输出由该字符串开始的剩余串
else cout<<"未找到"<<endl;
cp=strstr(a,"西北");
if(cp==NULL) cout<<"未找到"<<endl;
else cout<<cp<<endl;
return 0;
}
5.9 使用递归和非递归的两种方法编写函数
char *itoa (int n,char *string);
将整数n转换为十进制表示的字符串。(在非递归方法中,可使用reverse()函数。)
解:递归方法分析。难度大,可用图解法:
每次调用除以10,以去除最后一位,以n=3657为例。
由此图可见,由string指向应填入的字符数组的相应位置。
由调用的最底层开始,回归时填入,每次回归,指针后移一位,由此得
char * itoal(int n,char *string){
if(n/10) string=itoal(n/10,string);
*string++=n%10+48;//字符,ASCII码
return string;
}
考虑,数字串结束符和负数,得完整的函数。
char * itoal(int n, char *string){
if(n<0){
*string++=’_’;
n=-n;
}
if(n/10)string=itoal(n/10,string);
*string++=n%10+48;
*string=’\0’;
return string;
}
源代码:
#include<iostream>
using namespace std;
char* reverse (char* s){
char temp,* temp1=s,* temp2=s;
while(*temp2) temp2++;
temp2--;//指针移回串尾
while(temp2-temp1>0){//注意此处,从串两头的指针同时向中间移动,重合或交错时停止
temp=*temp1;
*temp1=*temp2;
*temp2=temp;
temp1++;
temp2--;
}
return s;
}
char *itoa (int n,char *string){
char *temp=string;
if(n<0){
*temp++='-';
n=-n;
}
do{//注意个位放在前了
*temp++=n%10+48;
}while(n=n/10);//显式的循环
*temp='\0';
if(*string=='-') temp=string+1;//有负号仅反转数字部分
else temp=string;
reverse(temp);//个位放到后面
return string;
}
char *itoa1 (int n,char *string){
if(n<0){
*string++='-';
n=-n;
}
if(n/10) string=itoa1(n/10,string);//隐式循环
*string++=n%10+48;
//第一次是数字最高位放串的最前面的字符(不含符号),注意指针移动在后
*string='\0';
return string;//注意返回的指针已后移一字符
}
char *itoa0 (int n,char *string){
itoa1(n,string);
return string;
}
int main(){
int num;
char st[20];
cin>>num;
cout<<"输出数字串:"<<itoa(num,st)<<endl;
cin>>num;
cout<<"输出数字串:"<<itoa0(num,st)<<endl;
return 0;
}
5.10 头文件<ctime>中定义一个日期时间的结构:
struct tm{
int tm_sec; //秒
int tm_min; //分
int tm_hour; //时
int tm_mday; //日
int tm_mon; //月
int tm_year; //年,实际放的是与1970年的差,如1990年为20
int tm_wday; //星期
int tm_yday; //一年中的第几天
int tm_isdst; //是否夏时制
};
函数 time_t time(time_t *tp)是提取当前时间,time_t即长整型,代表从1970年1月1日00:00:00开始计算的秒数(格林尼治时间),放在首地址为tp的单元内。
函数 tm *localtime(const time_t *tp) 将tp地址单元中的时间转换为日期时间结构的当地时间;(函数 tm *gmtime(const time_t *tp)转换为日期时间结构的格林尼治时间;)
函数 char *asctime(tm *tb)将tb地址单元中的tm结构的日期时间转换为字符串(供显示),它有固有格式,如:
Sun Sep 16 01:03:52 1973
利用以上资源,重新设计一个日期时间类(DataTime),要求定义对象时取当前时间进行初始化,显示时重取显示时刻的时间并显示出来。
解:
#include<iostream>
#include<ctime>
using namespace std;
class datatime{
tm *timedata;
long allsecond;
char *tmp;
public:
datatime(){
time(&allsecond);
timedata=localtime(&allsecond);
tmp=asctime(timedata);
cout<<tmp<<endl;
}
void gettime(){
allsecond=time(NULL);//time有两种用法
timedata=localtime(&allsecond);
tmp=asctime(timedata);
cout<<tmp<<endl;
}
};
int main(){
char ch;
datatime dt;
cout<<"需要知道现在的日期和时间吗?(Y或N)"<<endl;
cin>>ch;
if(ch=='y'||'Y') dt.gettime();
return 0;
}
5.11 完善自定义字符串类mystring,函数包括:构造函数、拷贝构造函数、析构函数,并重载运算符[ ],=(分别用mystring和C字符串拷贝),+(strcat),+=,<,==(strcmp)。
解: 此例既是对第4章的复习也是一个提高。拷贝构造函数的应用请参阅4.4.2节末尾两项说明,本例形参使用引用,仅在返回时调用了拷贝构造函数。运算符的重载请参阅4.5节。
#include<iostream>
using namespace std;
const int n=256;
class mystring{
char str[n]; //存放字符串的数组容器
int maxsize; //最大可用元素数,可防止数组出界,提高健壮性
int last; //已用元素最大下标
public:
mystring(){
last=0;
maxsize=n;
str[0]='\0';
cout<<"缺省构造函数"<<endl;
}
mystring(char *s){//当C字符串过长,初始化时采用截尾处理
last=-1;
maxsize=n;
do{
last++;
str[last]=s[last];
}while(s[last]!='\0'&&last<maxsize-1);
str[last] ='\0'; //截尾处理时,必须加串结束符
cout<<"构造函数"<<endl;
}
mystring(mystring & ms){
last=-1;
maxsize=n;
do{
last++;
str[last]=ms.str[last];
}while(last<ms.last);
cout<<"拷贝构造函数"<<endl;
}
~mystring(){
cout<<"析构函数"<<endl;
}
void show(){//如需重载<<,则请参见9.3.3节,暂时未学到,替代方法是用show()函数
cout<<str<<endl;
}
char & operator[](int i){ //返回引用,可读可写
if(i>last) last=i; //下标运算符,可添加长度但不查边界
return str[i];
}
mystring operator=(mystring &);
mystring & operator=(char * ms);//这里重载的=是把C风格字符串赋给mystring
mystring operator+(mystring &);
mystring operator+=(mystring &);
bool operator<(mystring &);
bool operator==(mystring &);
};
mystring mystring::operator=(mystring & ms){
last=-1;
do{
last++;
str[last]=ms.str[last];
}while(last<ms.last);
return *this;
}
mystring & mystring::operator=(char* ms){ //这里返回值为引用,不调用拷贝构造函数
last=-1;
do{
last++;
str[last]=ms[last];
}while(ms[last]!='\0'&&last<maxsize-1);
str[last] ='\0'; //截尾处理时,必须加串结束符
return *this;
}
mystring mystring::operator+(mystring & ms){//注意+和+=的不同
mystring temp(*this);//+必须在一份拷贝上进行
int i=-1;
temp.last--;//串的结尾是结束符,连接时要覆盖掉
do{
temp.last++;
i++;
temp.str[temp.last]=ms.str[i];
}while(i<ms.last&& temp.last<maxsize-1);
temp.str[temp.last] ='\0'; //截尾处理时,必须加串结束符
return temp;//拷贝的临时变量生命期在调用它的表达式中
}
mystring mystring::operator+=(mystring & ms){//+=在对象自身进行
int i=-1;
last--;//串的结尾是结束符,连接时要覆盖掉
do{
last++;
i++;
str[last]=ms.str[i];
}while(i<ms.last&&last<maxsize-1);
str[last] ='\0'; //截尾处理时,必须加串结束符
return *this;
}
bool mystring::operator<(mystring & ms){ //重载<运算符
int i=0,k;
do{
k=str[i]-ms.str[i];
i++;
}while(k==0&&i<last&&i<ms.last);
if(k<0) return true;
if(i==last&&i!=ms.last) return true;
return false;
}
bool mystring::operator==(mystring & ms){
int i=0,k;
if(last!=ms.last) return false;
do{
k=str[i]-ms.str[i];
i++;
}while(k==0&&i<last);
if(k!=0) return false;
else return true;
}
int main(){
int i;
char *sp1="东南大学",*sp2="交通学院",*sp3="学生",*sp4="教师";
mystring ms1(sp1),ms2(sp2),ms3(sp3);//ms1,ms2,ms3是用构造函数生成
mystring ms4(ms3),ms5=ms3,ms6; //ms4,ms5用拷贝构造函数生成;ms6用缺省构造函数
ms6=sp4; //ms6赋值是返回引用,不用拷贝构造函数
ms1.show();
ms2.show();
ms3.show();
ms4.show();
ms5.show();
ms6.show();
ms4=ms1+ms2+ms6;//注意temp和临时变量由拷贝构造函数生成
ms4.show();
ms1+=ms2+=ms3;
ms1.show();
if(ms1<ms4) {ms1.show();cout<<"应排在"<<endl;ms4.show();cout<<"之前"<<endl;}
else {ms1.show();cout<<"应排在"<<endl;ms4.show();cout<<"之后"<<endl;}
ms6=ms1;//ms6赋值不是返回引用,必须调用拷贝构造函数建立临时对象
if(ms1==ms6) cout<<"串ms1与串ms6相同"<<endl;
ms1="C++ programming language";
i=0;
while(ms1[i]!='\0') cout<<ms1[i++];//读出
cout<<endl;
ms1[i++]='.';//写入
ms1[i]='\0';
i=0;
ms1.show();
return 0;
}
5.12 将习题5.8中的字符串处理函数移植到mystring类中,其中strcat已重载为+运算符,
请将其它4个转为成员函数。对比成员函数与独立函数构造上有何不同?
解:这四个函数因mystring内部有串长,又要求下标索引,再加上str字符数组可直接使用,构造大不相同。
#include<iostream>
using namespace std;
const int n=256;
class mystring{
char str[n]; //存放字符串的数组容器
int maxsize; //最大可用元素数,可防止数组出界,提高健壮性
int last; //已用元素最大下标
public:
mystring(){
last=-1;
maxsize=n;
str[0]='\0';
cout<<"缺省构造函数"<<endl;
}
mystring(char *s){//当C字符串过长,初始化时采用截尾处理
last=-1;
maxsize=n;
do{
last++;
str[last]=s[last];
}while(s[last]!='\0'&&last<maxsize-1);
str[last] ='\0'; //截尾处理时,必须加串结束符
cout<<"构造函数"<<endl;
}
mystring(mystring & ms){
last=-1;
maxsize=n;
do{
last++;
str[last]=ms.str[last];
}while(last<ms.last);
cout<<"拷贝构造函数"<<endl;
}
~mystring(){
cout<<"析构函数"<<endl;
}
void show(){//如需重载<<,则参见9.3.3节,暂时未学到,替代方法是用show()函数
cout<<str<<endl;
}
int strlen(){return last;};
void reverse();
int strchr(char c);
int strstr (mystring str1);
char & operator[](int i){ //返回引用,可读可写
if(i>last) last=i; //下标运算符,可添加长度但不查边界
return str[i];
}
mystring & operator=(mystring &);
mystring & operator=(char * ms);//这里重载的=是把C风格字符串赋给mystring
mystring operator+(mystring &); //这里返回不能用引用
mystring & operator+=(mystring &);
bool operator<(mystring &);
bool operator==(mystring &);
};
void mystring::reverse(){
int i=0,j=last-1;
char temp;
while(j>i){//注意此处,从串两头同时向中间移动,重合或交错时停止
temp=str[i]; //头尾交换
str[i]=str[j];
str[j]=temp;
i++;
j--;
}
}
int mystring::strchr(char c){
int i;
for(i=0;i!=last;i++) if(str[i]==c) return i;
return -1; //未找到返回-1
}
int mystring::strstr(mystring str1){
int i=0,k=1;
while(str[i]!='\0'){//只要主串还有字符未查,则继续
while(str[i]!=str1[0]&&str[i]!='\0') i++;//找到主串含有子串的第一个字符,或主串查完停止
if(str[i]!='\0'){//核对子串其他字符
while(str[i+k]==str1.str[k]&&k<str1.last) k++;//字符串结束符不比较
if(k==str1.last) return i;//找到子串返回
k=1;
i++;
}
}
return -1; //未找到返回-1
}
mystring & mystring::operator=(mystring & ms){//这里返回值改为引用,不调用拷贝构造函数
last=-1;
do{
last++;
str[last]=ms.str[last];
}while(last<ms.last);
return *this;
}
mystring & mystring::operator=(char* ms){ //这里返回值为引用,不调用拷贝构造函数
last=-1;
do{
last++;
str[last]=ms[last];
}while(ms[last]!='\0'&&last<maxsize-1);
str[last] ='\0'; //截尾处理时,必须加串结束符
return *this;
}
mystring mystring::operator+(mystring & ms){//注意+和+=的不同
mystring temp(*this);//+必须在一份拷贝上进行
int i=-1;
temp.last--;//串的结尾是结束符,连接时要覆盖掉
do{
temp.last++;
i++;
temp.str[temp.last]=ms.str[i];
}while(i<ms.last&& temp.last<maxsi
展开阅读全文