201205程序员下午真题
第 1 题
阅读以下说明和流程图,填补流程图中的空缺(1)~(5),将解答填入答题纸的对应栏内。
【说明】
已知数组A[1:n]中各个元素的值都是非零整数,其中有些元素的值是相同的(重复)。为删除其中重复的值,可先通过以下流程图找出所有的重复值,并对所有重复值赋0标记。该流程图采用了双重循环。
处理思路:如果数组A某个元素的值在前面曾出现过,则该元素赋标记值0。例如,假设数组A的各元素之值依次为2,5,5,1,2,5,3,则经过该流程图处理后,各元素之值依次为2,5,0,1,0,0,3。
【流程图】
答案与解析
- 试题难度:较难
- 知识点:流程图>流程图
- 试题答案:(1)n-1(2)A[i](3)i+1(4)A[j](5)A[j]
- 试题解析:在处理大批数据记录时,删除重复记录(关键词重复的记录)是常见的操作。本题源自这种应用。删除重复记录算法可分两步进行。第一步将重复出现的多余元素标记为0;第二步再删除所有的0元素。本题流程图只做第一步处理。本流程图采用了对i和j的双重循环,对每个元素A[i],需要查看其后面的各个元素(用A[j]表示)是否与A[i]相同。因此,外层循环应对i=1,n-1进行,从而在(1)处应填“n-1”。内层循环应对j=i+1,n进行,从而在(3)处应填“i+1”。在外循环处理中首先应判断A[i]是否已经标记为0,若是则无需进一步处理。因此,(2)处应填“A[i]”。而在内循环处理中首先应判断A[j]是否已经标记为0,若是则无需进一步处理。因此,(4)处应填“A[j]”。如果发现元素重复(即A[i]=A[j]),则需要再将A[j]赋值为0(标记),因此(5)处应填“A[j]”。
第 2 题
阅读以下说明、C程序代码和问题1至问题3,将解答写在答题纸的对应栏内。
【说明1】
设在某C系统中为每个字符型数据分配1个字节,为每个整型(int)数据分配4个字节,为每个指针分配4个字节,sizeof(x)用于计算为x分配的字节数。
【C代码】
#include <stdio.h>
#include <string.h>
int main()
{ int arr[5]={10, 20, 30};
char mystr[]="JustAtest\n";
char *ptr=mystr;
printf("%d %d %d\n", sizeof(int), sizeof(unsigned int), sizeof(arr));
printf("%d %d\n", sizeof(char), sizeof(mystr);
printf("%d %d %d\n", sizeof(ptr), sizeof(*ptr), strlen(ptr));
return 0;
}
【说明2】
const是C语言的一个关键字,可以用来定义“只读”型变量。
答案与解析
- 试题难度:较难
- 知识点:C程序设计>C程序设计
- 试题答案:【问题1】4 4 201 114 1 10【问题2】(1)const int size=10; 或 int const size=10;(2)int* const ptr=&a;【问题3】static的作用:说明f足内部函数,只能在本文件中调用它。const的作用:在函数f中不能修改数组元素的值,若有修改,编译时会报错。
- 试题解析:本题考查C语言基础及应用。
【问题1】
sizeof是C语言提供的一个关键字,sizeof(x)用于计算为x分配的字节数,其结果与系统或编译器相关。若x是数组名时,用于计算整个数组所占用存储空间的字节数;若x是指针,则无论其指向的目标数据是什么类型,x所占用的存储空间大小都相同(在同一系统或编译环境中);若x是结构体变量或类型,则需要根据系统规定的对齐要求来计算为x所分配空间的字节数。
根据说明,系统为每个字符型数据分配1个字节,为每个整型(int)数据分配4个字节,为每个指针分配4个字节,那么sizeof(int)、sizeof(unsigned int)是计算整型数据和无符号整型数据的存储空间大小,sizeof(arr)是计算数组arr的字节数,它们的值分别为4、4和20。
sizeof(char)计算一个字符数据所占用的字节数,根据说明应为1。sizeof(mystr)计算为字符数组mystr分配的空间大小,该数组的大小由字符串"JustAtest\n"决定,该字符串的长度为10,还有一个串尾结束标志字符'/0',因此sizeof(mystr)的值为11。
ptr是指向字符数组mystr的指针,显然sizeof(ptr)的结果为4。由于ptr指向了一个字符数据,因此sizeof(ptr)的结果为1,函数strlen(ptr)计算ptr所指字符串的长度,结果为10。
【问题2】
在C语言中,const关键字的一个作用是限定一个变量的值不能被改变,使用const可以在一定程度上提高程序的安全性和可靠性。
const int size=10; 或 int const size=10;
以上代码都可以定义一个“只读”型的整型常量size并将其值初始化为10。
当const用于修饰指针时,常见的情形如下:
(1)const修饰的是指针所指向的对象,该对象不可改变,指针变量可改变。
const int p; //或 int const p;
(2)const修饰的是指针,该指针变量不可改变,其指向的对象可改变。
int const p;
(3)const修饰的是指针以及指针所指向的对象,都不可改变。
const int const p;
【问题3】
关键字static用于修饰函数中的局部变量时,是通知编译器将该变量的存储空间安排在全局存储区,这样在下一次调用函数时还保留上一次对该变量的修改结果。
当一个源程序由多个源文件组成时,用static修饰的全局变量和函数,其作用域为当前文件,对其他源文件不可见,即它们不能被其他源文件引用或调用。
当函数的形参用const修饰时,在函数体内部不能被修改。
第 3 题
阅读以下说明和C函数,填补C函数中的空缺(1)~(6),将解答写在答题纸的对应栏内。
【说明】
函数numberOfwords(char message[])的功能是计算存储在message字符数组中的一段英文语句中的单词数目,输出每个单词(单词长度超过20时仅输出其前20个字母),并计算每个英文字母出现的次数(即频数),字母计数时不区分大小写。
假设英文语句中的单词合乎规范(此处不考虑单词的正确性),单词不缩写或省略,即不会出现类似don't形式的词,单词之后都为空格或标点符号。
函数中判定单词的规则是:
(1)一个英文字母串是单词;
(2)一个数字串是单词;
(3)表示名词所有格的撇号(')与对应的单词看作是一个单词。
除上述规则外,其他情况概不考虑。
例如,句子“The 1990's witnessed many changes in people's concepts of conservation.”中有10个单词,输出如下:
The
1990's
witnessed
many
changes
in
people's
concepts
of
conservation
函数numberOfwords中用到的部分标准库函数如下表所述。
【C函数】
int numberOfwords (char message[])
{
char wordbuffer[21], i=0; /* i用作wordbuffer的下标 */
(1) pstr;
int ps[26]={0}; /* ps[0]用于表示字母'A'或'a'的频数 */
/* ps[1]用于表示字母'B'或'b'的频数,依此类推 */
int wordcounter=0;
pstr=message;
while (*pstr) {
if( (2) (*pstr)) {/* 调用函数判定是否为一个单词的开头字符 */
i=0;
do{/* 将一个单词的字符逐个存入wordbuffer[],并进行字母计数 */
wordbuffer[i++]=*pstr;
if (isalpha (*pstr)) {
if( (3) (*pstr)) ps[*pstr-'a']++;
else ps[*pstr-'A']++;
}
(4) ; /* pstr指向下一字符 */
}while (i<20 && (isalnum(*pstr)||*pstr=='\''));
if (i>=20) /* 处理超长单词(含名词所有格形式) */
while (isalnum (*pstr)||*pstr=='\'') { pstr++; }
(5) ='\0'; /* 设置暂存在wordbuffer中的单词结尾 */
wordcounter++; /* 单词计数 */
puts (wordbuffer); /* 输出单词 */
}
(6) ; /* pstr指向下一字符 */
}
return wordcounter;
}
答案与解析
- 试题难度:较难
- 知识点:C程序设计>C程序设计
- 试题答案:(1)char*,或unsigned char(2)isalnum,或isalpha(*pstr)||isdigit(3)islower,或!isupper(4)pstr++,或++pstr,或pst=str+1,或pstr+=1(5)wordbuffer[i],或*(wordbuffer+i)(6)pstr++,或++pstr,或pstr=pstr+1,或pstr+=1
- 试题解析:本题考查C语言裎序设计基本技术。题目中涉及的知识点主要有字符串、字符指针和函数调用等,首先应认真阅读题目的说明部分,以了解函数代码的功能和大致的处理思路,然后理清代码的框架,明确各个变量(或数组元素)所起的作用,并以语句组分析各段代码的功能,从而完成空缺处的代码填充。函数中空(1)处所在语句为定义变量pstr的声明语句,根据下面对pstr的使用方式,可知pstr是一个指向字符的指针变量,因此空(1)处应填入“char*”。显然,“pstr=message;”使pstr指向了英文语句的第一个字符,下面的while循环则用于遍历语句中的每一个字符:while (*patr) {…}对于语句中的一个字符*pstr,它可能是一个单词中的字符、空格、标点符号或其他字符,由于函数的功能是取出单词并进行统计,因此首先考虑该字符是否属于一个单词以及是否是单词的开头(字母或数字字符),结合注释,可知空(2)处用于判定当前字符*pstr是否是单词的开头字符,即是否是字母或数字,由于代码中已给出了(*pstr),因此最合适的做法是直接调用库函数进行处理,即空(2)处应填入“isalnum”,也可以填入“isalpha(*pstr)||isdigit”。得到一个单词的开头字符后就用do-while语句依次取出该单词的每一个字符,直到单词结束为止。根据题目说明,单词中包含的字符为字母、数字或撇号('),因此do-while继续循环的条件之一是表达式“isalnum(*pstr)||*pstr-'\"”的值为“真”,另一个条件是关于单词长度不超过20的限制。分析空(3)所在的语句(如下所示),显然是对单词中的字母进行计数,在*pstr是字母(isalpha(*pstr)的返回值为1)的前提下,“ps[*pstr-'a']++”是对小写字母进行计数,“ps[*pstr-'A']++”是对大写字母进行计数,所以空(3)处应判断*pstr是否为小写字母,应填入“islower”,或者填入“!isupper”。if (isalpha (*pstr)) {if( (3) (*pstr)) ps[*pstr-'a']++;else ps[*pstr-'A']++;}空(4)处是令pstr指向下一字符,因此应填入“pstr++”或其等价形式。空(5)处是设置字符串结尾字符,因此应填入“wordbuffer[i]”或其等价形式。空(6)处是令pstr指向下一字符,因此应填入“pstr++”或其等价形式。
第 4 题
阅读以下说明和C函数,填补C函数中的空缺(1)~(5),将解答写在答题纸的对应栏内。
【说明】
函数SetDiff(LA,LB)的功能是将LA与LB中的共有元素从LA中删除,使得LA中仅保留与LB不同的元素,而LB不变,LA和LB为含头结点的单链表的头指针。
例如,单链表LA、LB的示例如下图中的(a)、(b)所示,删除与LB共有的元素后的LA如下图中的(c)所示。
链表的结点类型定义如下:
typedef struct Node {
int data;
struct Node *next;
}Node, *LinkList;
函数SetDiff(LinkList LA, LinkList LB)的处理思路如下:
(1)从LA的第一个元素结点开始,令LA的第一个元素为当前元素。
(2)在LB中进行顺序查找,查找与LA的当前元素相同者,方法是令LA的当前元素先与LB的第一个元素进行比较,若相等,则结束在LB中的查找过程,否则继续与LB的下一个元素比较,重复以上过程,直到LB中的某一个元素与LA的当前元素相等(表明查找成功),或者到达LB的表尾(表明查找失败)为止。
(3)结束在LB表的一次查找后,若在LB中发现了与LA的当前元素相同者,则删除LA的当前元素,否则保留LA的当前元素。
(4)取LA的下一个元素为当前元素,重复(2)、(3),直到LA的表尾。
【C函数】
void SetDiff (LinkList LA, LinkList LB)
{
LinkList pre, pa, pb;
/* pa用于指向单链表LA的当前元素结点,pre指向pa所指元素的前驱 */
/* pb用于指向单链表LB的元素结点 */
(1) ; /* 开始时令pa指向LA的第一个元素 */
pre=LA;
while (pa) {
pb=LB->next;
/* 在LB中查找与LA的当前元素相同者,直到找到或者到达表尾 */
while( (2) ) {
if (pa->data==pb->data)
break;
(3) ;
}
if (!pb) {
/* 若在LB中没有找到与LA中当前元素相同者,则继续考察LA的后续元素 */
pre=pa;
pa=pa->next;
}
else{
/* 若在LB中找到与LA的当前元素相同者,则删除LA的当前元素 */
pre->next= (4) ;
free (pa);
pa= (5) ;
}
}
}
答案与解析
- 试题难度:较难
- 知识点:C程序设计>C程序设计
- 试题答案:(1)pa=LA->next(2)pb,或pb!=0,或pb!=NULL(3)pb=pb->next(4)pa->next(5)pre->next
- 试题解析:本题考查C程序设计基本技术及指针的应用。题目中涉及的考点主要有链表的查找、删除运算以及程序逻辑,分析程序时首先要明确各个变量所起的作用,并按照语句组分析各段代码的功能,从而完成空缺处的代码填充。根据注释,空(1)处应为指针变量pa赋值,使其指向LA链表的第一个元素结点,由于LA为指向头结点的指针,因此空(1)处应填入“pa=LA->next”。以指针pa的值为循环条件的以下循环语句用于遍历LA的每一个元素。while (pa) {…}对于LA中的每一个元素pa->data,需要在LB中查找是否存在与其相同者,代码段为:pb=LB->next;/* 在LB中查找与LA的当前元素相同者,直到找到或者到达表尾 */while( (2) ) {if (pa->data==pb->data)break;(3) ;}显然,通过“pb=LB->next”已经令pb指向了LB的第一个元素,接下来的while语句就用于和LB的元素pb->data逐个比较,显然,空(2)处应填入“pb”,表明pb为非空指针,使得循环体中进行“pa->data==pb->data”运算时,pb指针是有效的。在该循环中,若找到了两个链表的共有元素,则用break跳出循环,此时pb正指向LB中的该共有元素;否则继续在LB中查找,那就需要在空(3)处填入“pb=pb->next”。在LB结束查找后,如果找到了与pa->data相同的元素,则之前已经令pb指向它;若是没有找到,则pb是空指针。因此,接下来根据pb的值判断是否需要删除LA的当前元素。若不删除,则执行语句组“pre=pa; pa=pa->next;”,继续考察LA的后续元素;若需删除(pa指向的结点),则相关指针的指向如下图所示。显然,pre已经指向pa所指结点的前驱,要删除pa所指结点,只要将pre->next设置为指向pa所指结点的后继结点即可,即令pre->next= pa->next。因此,在free(pa)之后需重新设置pa的指向,即令pa= pre->next,为继续处理后续元素做好准备。对应地,空(4)应填入“pa->next”,空(5)应填入“pre->next”。
第 5 题
阅读以下说明和C++代码,填补C++代码中的空缺(1)~(6),将解答写在答题纸的对应栏内。
【说明】
已知某公司按周给员工发放工资,其工资系统需记录每名员工的员工号、姓名、工资等信息。其中一些员工是正式的,按年薪分周发放(每年按52周计算);另一些员工是计时工,以小时工资为基准,按每周工作小时数核算发放。
下面是实现该工资系统的C++代码,其中定义了四个类:工资系统类PayRoll,员工类Employee,正式工类Salaried和计时工类Hourly,Salaried和Hourly是Employee的子类。
【C++代码】
//头文件和域名空间略
const int EMPLOYEE_NUM=5;
class Employee {
protected:
int empCode; //员工号
string name; //员工姓名
double salary; //周发放工资
public:
Employee(const int empCode, const string &name ) {
this->empCode=empCode; this->name=name;
}
virtual ~Employee(){ }
virtual void pay()=0;
double getSalary() { return this->salary; }
};
class Salaried (1) {
private: double payRate; //年薪
public:
Salaried(const int empCode, const string &namet double payRate):Employee (empCode, name) {
this->payRate=payRate;
}
void pay() {
this->salary= (2) ; //计算正式员工的周发放工资数
cout << this->name << ":" << this->salary<<endl;
};
class Hourly (3) {
private:
double payRate; //小时工资数
int hours; //周工作小时数
public:
Hourly (const int empCode. const string &name, int hours, double payRate)
:Employee(empCode, name) {
this->payRate=payRate; this->hours=hours;
}
void pay() {
this->salary= (4) ; //计算计时工的周发放工资数
cout << this->name << ":" << this->salary << endl;
}
};
class PayRoll {
public:
void pay (Employee*e[]) {
for (int i=0; i<EMPLOYEE_NUM; i++) {
e[i]->pay();
}
}
};
int main() {
PayRoll payRoll=new PayRoll;
(5) employees [EMPLOYEE_NUM]= {
new Salaried (1001, "Zhang San", 58000.00),
//此处省略对其他职工对象的生成
new Hourly (1005, "Li", 12, 50.00),
};
payRoll->pay( (6) );
double total=0.0;
for (int i=0; i<EMPLOYEE_NUM; i++)
{ total+=employees[i]->getSalary();} //统计周发放工资总额
cout<<"总发放额="<<total<<endl;
delete payRoll; return 0;
}
答案与解析
- 试题难度:较难
- 知识点:C++程序设计>C++程序设计
- 试题答案:(1):public Employee(2)payRate/52(3):public Employee(4)payRate*hours(5)Employee*或static Employee*(6)employees
- 试题解析:本题考查C++语言程序设计能力,涉及类、对象、函数及虚函数的定义和相关操作,以及继承关系和多态。要求考生根据给出的案例和执行过程说明,认真阅读理清程序思路,然后完成题目。根据本题中说明中描述,需要记录每名员工的员工号、姓名和工资等信息。公司员工分为正式工和计时工两类,正式工和计时工的工资支付方式不同,根据面向对象设计的概念,这两种员工都是员工,设计时将公有属性和行为封装成抽象类,并使用继承关系设计两种不同员工类作为子类。因此,系统设计了4个类:工资系统类PayRoll、员工类Employee、正式工类Salaried和计时工类Hourly,Salaried和Hourly继承了Employee类。Employee中定义两类员工公有属性和方法,以及支付方式的接口标识,Salaried和Hourly需要在各自的类中有具体支付方式的实现。根据类定义及其之间的关系,将Employee类中支付工资定义为纯虚函数,即virtual void pay()=0;,这样就定义了支付方式的接口,子类中必须实现各自具体的支付方法。这样,在PayRoII中对Salaried和Hourly两类的对象使用同样的调用方式e[i]->pay()达到不同支付效果,也就达到了多态。Salaried和Hodrly均继承了Employee,并在Salaried和Hourly的构造函数中调用父类的构造函数,所以继承的权限为public,其语法为:public后加类名。Salaried的工资发放方式为“按年薪分周发放(每年按52周计算)”,因此在pay()方法的实现中,分周发放工资采用“年薪/52”进行计算。Hourly的工资发放方式是“以小时工资为基准,按每周工作小时数核算发放”,即“小时工资×周工作小时”进行计算。在main()中创建了一组员工,用数组存储,由于数组元素包括Salaried和Hourly两种,因此其声明类型采用父类类型Employee的指针,可以定义为静态数组,对蒙的创建采用new关键字加类名。集中对所有员工进行支付,将数据作为payRoll相同的pay()的参数,即payRoll->pay(employees)。用new关键字创建的数组,在使用完成之后通过delete进行释放。因此空(1)和(3)处添加继承父类,并且权限为public,即:public Employee。空(2)和(4)处补充通过使用计算得到所发放的工资额,空(2)处为计算正式员工的周发放工资数,即payRate/52;空(4)处为计时工的周发放工资数,即payRate*hours。空(5)处定义员工数组的类型,即Employee*或static Employee*。空(6)处将员工数组传递给pay()方法,即employees。
第 6 题
阅读以下说明和Java代码,填补Java代码中的空缺(1)~(6),将解答写在答题纸的对应栏内。
【说明】
已知某公司按周给员工发放工资,其工资系统需记录每名员工的员工号、姓名、工资等信息。其中一些员工是正式的,按年薪分周发放(每年按52周计算);另一些员工是计时工,以小时工资为基准,按每周工作小时数核算发放。
下面是实现该工资系统的Java代码,其中定义了四个类:工资系统类PayRoll,员工类Employee,正式工类Salaried和计时工类Hourly,Salaried和Hourly是Employee的子类。
【Java代码】
abstract class Employee {
protected String name; //员工姓名
protected int empCode; //员工号
protected double salary; //周发放工资
public Employee(int empCode, String name) {
this.empCode=empCode;
this.name=name,
}
public double getSalary(){
return this.salary;
}
public abstract void pay() ;
}
class Salaried (1) Employee {
private double annualSalary;
Salaried(int empCode, String name, double payRate) {
super(empCode, name) ;
this.annualSalary=payRate;
}
public void pay () {
salary= (2) ; //计算正式员工的周发放工资数
System.out.println(this.name+":"+this.salary);
}
}
class Hourly (3) Employee {
private double hourlyPayRate;
private int hours;
Hourly(int empCode, String name, int hours, double payRate) {
super(empCode, name);
this.hourlyPayRate=payRate;
this.hours=hours;
}
public void pay () {
salary= (4) ; //计算计时工的周发放工资数
System.out.println(thisname+":"+this.salary);
}
}
public class PayRoll {
private (5) employees[]={
new Salaried(1001, "Zhang San", 58000.00),
//此处省略对其他职工对象的生成
new Hourly(1005, "Li", 12, 50.00)
};
public void pay(Employee e[]) {
for (int i=0; i<e.length; i++) {
e[i]pay();
}
}
public static void main(String[] args)
{
PayRoll payRoll=new PayRoll();
payRoll.pay( (6) );
double total=0.0;
for (int i=0;i<payRoll.employees.length; i++){ //统计周发放工资总额
total+=payRoll.employees[i].getSalary();
}
System.out.println(total);
}
}
答案与解析
- 试题难度:较难
- 知识点:Java程序设计>Java程序设计案例
- 试题答案:(1)extends(2)annualSalary/52(3)extends(4)hourlyPayRate * hours(5)Employee 或 static Employee(6)employees(空(5)只能为static Employee)或payRoll.employees
- 试题解析:本题考查Java语言程序设计能力,涉及类和抽象类、对象、方法及抽象方法的定义和相关操作,以及继承关系和多态。要求考生根据给出的案例和执行过程说明,认真阅读理清程序思路,然后完成题目。根据本题中说明中描述,需要记录每名员工的员工号、姓名和工资等信息。公司员工分为正式工和计时工两类,正式工和计时工的工资支付方式不同,根据面向对象设计的概念,这两种员工都是员工,设计时将公有属性和行为封装成抽象类,并使用继承关系设计两种不同员工类作为子类。因此,系统设计了四个类:工资系统类PayRoll,员工类Employee,正式工类Salaried和计时工类Hourly,Salaried和Hourly继承了Employee类。Employee中定义两类员工公有属性和方法,以及支付方式的接口标识,Salaried和Hourly需要在各自的类中有具体支付方式的实现。根据类定义及其之间的关系,将Employee类定义为抽象类,其中支付工资定义为抽象方法,public abstract void pay();,这样就定义了支付方式的接口,子类中必须实现各自具体的支付方法。这样,在PayRoll中对Salaried和Hourly两类的对象使用同样的调用方式e[i].pay()达到不同支付效果,也就达到了多态。Salaried和Hourly均继承了Employee,其语法为extends后加类名,在两者的构造方法中调用父类的构造方法,进行相应的员工公共信息初始化操作,所以均在构造方法的第一句(必须是第一句)采用super调用父类构造方法。Salaried的工资发放方式为“按年薪分周发放(每年按52周计算)”,因此在pay()方法的实现中,每周发放工资采用“年薪/52”进行计算。Hourly的工资发放方式是“以小时工资为基准,按每周工作小时数核算发放”,即“小时工资×周工作小时”进行计算。在PayRoII中创建了一组员工,用数组存储,由于数组元素包括Salaried和Hourly两种,因此其声明类型采用父类类型Employee,可以定义为静态数组,对象的创建采用new关键字加类名。在PayRoll中支付时,集中对所有负工进行支付,将数据作为payRoll的pay()的参数,在Java中用数组引用名直接作为参数,即payRoll.pay(payRoll.employees)。因此空(1)和(3)处添加继承父类,即extends。空(2)和(4)处补充通过使用计算得到所发放的工资数,空(2)处为计算正式员工的周发放工资数,即annuaISalary/52;空(4)处为计时工的周发放工资数,即hourlyPayRate*hours。空(5)处定义员工数组的类型,即Employee或static Employee。空(6)处将员工数组的引用传递给pay()方法,即employees(空(5)只能为static Employee)或payRoll.employees。