如何正确的理解指针和结构体指针?

作者一直觉得C语言较其他语言最伟大的地方就是C语言中的指针,有些人认为指针很简单,而有些人认为指针很难,当然这里的对简单和难并不是等价于对指针的理解程度。为此作者在这里对C语言中的指针进行全面的总结,从底层的内存分析,彻底让读者明白指针的本质。

作者认为C指针应该和C语言中的变量放在一起,因为C指针本质上还是一个变量,但现在大部分教材将其单独拿出来讲解,这也使得很多初学者认为指针是一个和变量毫无相关的概念。

一、指针变量

首先读者要明白指针是一个变量,为此作者写了如下代码来验证之:

#include "stdio.h"

int main(int argc, char **argv)
{
    int a=10;
    int *p;
    printf("&a=%d\n",a);
    p=6422216;
    *p=20;
    printf("a=%d\n",a);
    return 0;
}


运行后可以看到a的值被更改了,而代码中仅仅将一个数值赋值给了p指针,只是这个数值正好是变量a的地址值,编译过程中会出现警告,这个这里暂时不管。上面的例子可以清楚的明白指针实质上是一个放置变量地址的特殊变量,其本质仍然是变量。(注意:上面的例子中地址是随机的,每次编译可能都会不一样)

既然指针是变量,那必然会有变量类型,因此这里必须对变量类型做解释。在C语言中,所有的变量都有变量类型,整型、浮现型、字符型、指针类型、结构体、联合体、枚举等,这些都是变量类型。变量类型的出现是内存管理的必然结果,相信读者知道,所有的变量都是保存在计算机的内存中,既然是放到计算机的内存中,那必然会占用一定的空间,问题来了,一个变量会占用多少空间呢,或者说应该分出多少内存空间来放置该变量呢?为了规定这个,类型由此诞生了,对于32位编译器来说,int类型占用4个字节,即32位,long类型占用8字节,即64位。这里简单说了类型主要是为后面引出指针这个特殊性,在计算机中,将要运行的程序都保存在内存中,所有的程序中的变量其实就是对内存的操作。计算机的内存结构较为简单,这里不详细谈论内存的物理结构,只谈论内存模型。将计算机的内存可以想象为一个房子,房子里面居住着人,每一个房间对应着计算机的内存地址,内存中的数据就相当于房子里的人。


既然指针也是一个变量,那个指针也应该被存放在内存中,对于32位编译器来说,其寻址空间为232=4GB,为了能够都操作所有内存(实际上普通用户不可能操作所有内存),指针变量存放也要用32位数即4个字节。这样就有指针的地址&p,指针和变量的关系可以用如下图表示:


从上图可以看到&p是指针的地址,用来存放指针p,而指针p来存放变量a的地址,也就是&a,还有一个*p在C语言中是解引,意思是告诉编译器取出该地址存放的内容。


上面提到过关于指针类型的问题,针对32位编译器而言,既然任何指针都只占用4个字节,那为何还需要引入指针类型呢?仅仅是为了约束相同类型的变量么?实际上这里不得不提到指针操作,先思考如下两个操作:


上面两个操作的意思是不同的,先说下第一种:p+1操作,如下图所示:


对于不同类型指针而言,其p+1所指向的地址不同,这个递增取决于指针类型所占的内存大小,而对于((unsigned int)p)+1,该意思是将地址p所指向的地址的值直接转换为数字,然后+1,这样无论p是何种类型的指针,其结果都是指针所指的地址后一个地址。

从上述可以看到,指针的存在使得程序员可以相当轻松的操作内存,这也使得当前有些人认为指针相当危险,这一观点表现在C#和Java语言中,然而实际上用好指针可以极大的提高效率。下面深入一点来通过指针对内存进行操作,现在我们需要对内存6422216中填入一个数据125,我们可以如下操作:

unsigned int *p=(unsigned int*)(6422216);
*p=125;

当然,上面的代码使用了一个指针,实际上C语言中可以直接利用解引操作对内存进行更方便的赋值,下面说下解引操作*。

二、解引用

所谓解引操作,实际上是对一个地址操作,比如现在想将变量a进行赋值,一般操作是a=125,现在我们用解引操作来完成,操作如下:

*(&a)=125;

上面可以看到解引操作符为*,这个操作符对于指针有两个不同的意义,当在申明的时候是申明一个指针,而当在使用p指针时是解引操作,解引操作右边是一个地址,这样解引操作的意思就是该地址内存中的数据。这样我们对内存6422216中填入一个数据125就可以使用如下操作

*(unsigned int*)(6422216)=125;

上面需要将6422216数值强制转换为一个地址,这个是告诉编译器该数值是一个地址。值得注意的是上面的所有内存地址不能随便指定,必须是计算机已经分配的内存,否则计算机会认为指针越界而被操作系统杀死即程序提前终止。

三、结构体指针

结构体指针和普通变量指针一样,结构体指针只占4个字节(32位编译器),只不过结构体指针可以很容易的访问结构体类型中的任何成员,这就是指针的成员运算符->。


上图中p是一个结构体指针,p指向的是一个结构体的首地址,而p->a可以用来访问结构体中的成员a,当然p->a*(p)是相同的。

四、强制类型转换

为何要在这里提强制类型转换呢,上面的测试代码可以看到编译器会报很多警告,意思是告诉程序员数据类型不匹配,虽然并不影响程序的正确运行,但是很多警告总会让人感到难受。因此为了告诉编译器代码这里没有问题,程序员可以使用强制类型转换来将一段内存转换为需要的数据类型,例如下面有一个数组a,现在将其强制转换为一个结构体类型stu:

#include <stdio.h>

typedef struct STUDENT
{
    int      name;
    int    gender;
}stu;

int a[100]={10,20,30,40,50};

int main(int argc, char **argv)
{
    stu *student;
    student=(stu*)a;
    printf("student->name=%d\n",student->name);
    printf("student->gender=%d\n",student->gender);
    return 0;
}

上面的程序运行结果如下:
 可以看到a[100]被强制转换为stu结构体类型,当然不使用强制类型转换也是可以的,只是编译器会报警报。


上图为程序的示意图,图中数组a[100]的前12个字节被强制转换为了一个struct stu类型,上面仅对数组进行了说明,其它数据类型也是一样的,本质上都是一段内存空间。

五、void指针

为何在这里单独提到空指针类型呢?,主要是因为该指针类型很特殊。void类型很容易让人想到是空的意思,但对于指针而言,其并不是指空,而是指不确定。在很多时候指针在申明的时候可能并不知道是什么类型或者该指针指向的数据类型有多种再或者程序员仅仅是想通过一个指针来操作一段内存空间。这个时候可以将指针申明为void类型。但是问题来了,由于void类型原因,对于确定的数据类型解引时,编译器会根据类型所占的空间来解引相应的数据,例如int p,那么p就会被编译器解引为p指针的地址的4个字节的空间大小。但对于空指针类型来说,编译器如何知道其要解引的内存大小呢?先看一段代码:

#include <stdio.h>

int main(int argc, char **argv)
{
    int a=10;
    void *p;
    p=&a;
    printf("p=%d\n",*p);
    return 0;
}

编译上面的程序会发现,编译器报错,无法正常编译。

 这说明编译器确实是在解引时无法确定p的大小,因此这里必须告诉编译器p的类型或者p的大小,如何告诉呢?很简单,用强制类型转换即可,如下:

*(int*)p

这样上面的程序就可以写为如下:

#include <stdio.h>

int main(int argc, char **argv)
{
    int a=10;
    void *p;
    p=&a;
    printf("p=%d\n",*(int*)p);
    return 0;
}

编译运行后:

可以看到结果确实是正确的,也和预期的想法一致。由于void指针没有空间大小属性,因此void指针也没有++操作。

六、函数指针

函数指针使用

函数指针在Linux内核中用的非常多,而且在设计操作系统的时候也会用到,因此这里将详细讲解函数指针。既然函数指针也是指针,那函数指针也占用4个字节(32位编译器)。下面以一个简单的例子说明:

#include <stdio.h>

int  add(int a,int b)
{
    return a+b;
}

int main(int argc, char **argv)
{
    int (*p)(int,int);
    p=add;
    printf("add(10,20)=%d\n",(*p)(10,20));
    return 0;
}

程序运行结果如下:

可以看到,函数指针的申明为:


函数指针的解引操作与普通的指针有点不一样,对于普通的指针而言,解引只需要根据类型来取出数据即可,但函数指针是要调用一个函数,其解引不可能是将数据取出,实际上函数指针的解引本质上是执行函数的过程,只是这个执行函数是使用的call指令并不是之前的函数,而是函数指针的值,即函数的地址。其实执行函数的过程本质上也是利用call指令来调用函数的地址,因此函数指针本质上就是保存函数执行过程的首地址。函数指针的调用如下:


为了确认函数指针本质上是传递给call指令一个函数的地址,下面用一个简单例子说明:


上面是编译后的汇编指令,可以看到,使用函数指针来调用函数时,其汇编指令多了如下:

0x4015e3    mov    DWORD PTR [esp+0xc],0x4015c0
0x4015eb    mov    eax,DWORD PTR [esp+0xc]
0x4015ef    call   eax

分析:第一行mov指令将立即数0x4015c0赋值给寄存器esp+0xc的地址内存中,然后将寄存器esp+0xc地址的值赋值给寄存器eax(累加器),然后调用call指令,此时pc指针将会指向add函数,而0x4015c0正好是函数add的首地址,这样就完成了函数的调用。细心的读者是否发现一个有趣的现象,上述过程中函数指针的值和参数一样是被放在栈帧中,这样看起来就是一个参数传递的过程,因此可以看到,函数指针最终还是以参数传递的形式传递给被调用的函数,而这个传递的值正好是函数的首地址。

从上面可以看到函数指针并不是和一般的指针一样可以操作内存,因此作者觉得函数指针可以看作是函数的引用申明。

函数指针应用

在linux驱动面向对象编程思想中用的最多,利用函数指针来实现封装,下面以一个简单的例子说明:

#include <stdio.h>

typedef struct TFT_DISPLAY
{
    int   pix_width;
    int   pix_height;
    int   color_width;
    void (*init)(void);
    void (*fill_screen)(int color);
    void (*tft_test)(void);

}tft_display;

static void init(void)
{
    printf("the display is initialed\n");
}

static void fill_screen(int color)
{
    printf("the display screen set 0x%x\n",color);

}

tft_display mydisplay=
{
    .pix_width=320,
    .pix_height=240,
    .color_width=24,
    .init=init,
    .fill_screen=fill_screen,
};

int main(int argc, char **argv)
{

    mydisplay.init();
    mydisplay.fill_screen(0xfff);
    return 0;
}

上面的例子将一个tft_display封装成一个对象,上面的结构体成员中最后一个没有初始化,这在Linux中用的非常多,最常见的是file_operator结构体,该结构体一般来说只需要初始化常见的函数,不需要全部初始化。上面代码中采用的结构体初始化方式也是在Linux中最常用的一种方式,这种方式的好处在于无需按照结构体的顺序一对一。

回调函数

有时候会遇到这样一种情况,当上层人员将一个功能交给下层程序员完成时,上层程序员和下层程序员同步工作,这个时候该功能函数并未完成,这个时候上层程序员可以定义一个API来交给下层程序员,而上层程序员只要关心该API就可以了而无需关心具体实现,具体实现交给下层程序员完成即可(这里的上层和下层程序员不指等级关系,而是项目的分工关系)。这种情况下就会用到回调函数(Callback Function),现在假设程序员A需要一个FFT算法,这个时候程序员A将FFT算法交给程序员B来完成,现在来让实现这个过程:

#include <stdio.h>

int  InputData[100]={0};
int OutputData[100]={0};

void FFT_Function(int *inputData,int *outputData,int num)
{
    while(num--)
    {

    }
}

void TaskA_CallBack(void (*fft)(int*,int*,int))
{

    (*fft)(InputData,OutputData,100);
}

int main(int argc, char **argv)
{

    TaskA_CallBack(FFT_Function);
    return 0;
}

上面的代码中TaskA_CallBack是回调函数,该函数的形参为一个函数指针,而FFT_Function是一个被调用函数。可以看到回调函数中申明的函数指针必须和被调用函数的类型完全相同。

  • 21
    点赞
  • 61
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Select在Socket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如connect、accept、recv或recvfrom这样的阻塞程序(所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件的发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回)。可是使用Select就可以完成非阻塞(所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高)方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。下面详细介绍一下! Select的函数格式(我所说的是Unix系统下的伯克利socket编程,和windows下的有区别,一会儿说明): int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout); 先说明两个结构体: 第一,struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符(file descriptor),即文件句柄,这可以是我们所说的普通意义的文件,当然Unix下任何设备、管道、FIFO等都是文件形式,全部包括在内,所以毫无疑问一个socket就是一个文件,socket句柄就是一个文件描述符。fd_set集合可以通过一些宏由人为来操作,比如清空集合FD_ZERO(fd_set *),将一个给定的文件描述符加入集合之中FD_SET(int ,fd_set *),将一个给定的文件描述符从集合中删除FD_CLR(int ,fd_set*),检查集合中指定的文件描述符是否可以读写FD_ISSET(int ,fd_set* )。一会儿举例说明。 第二,struct timeval是一个大家常用的结构,用来代表时间值,有两个成员,一个是秒数,另一个是毫秒数。 具体解释select的参数: int maxfdp是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数的值无所谓,可以设置不正确。 fd_set *readfds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。 fd_set *writefds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。 fd_set *errorfds同上面两个参数的意图,用来监视文件错误异常。 struct timeval* timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态,第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。 返回值: 负值:select错误 正值:某些文件可读写或出错 0:等待超时,没有可读写或错误的文件
一:需求分析 二:总体设计 三:详细设计 四:调试与测试 五;测试结果 六:本次课程设计的心得体会 七:附录 本程序设计亮点介绍(及)改进方面 1.在修改函数中,可以实现单条记录修改(运用switch) 2.查询函数中,能返回该函数继续查询(运用了递归) 3.使用变量少,仅通过变量i的递加和表示成员数的变量n就实现了数据结构体之间的联 系 4.界面美观,使用方便 5.允许编号输入为字符 6.在显示函数上能够分屏显示(换页) 缺点 在输入数据时,未输完一个成员的所有信息不能退出输入 一:需求分析 A.需要实现的功能有:输入功能、输出功能、显示功能、查找功能、以及增加、删 除、修改功能。输入功能要求一次完成若干信息的输入;显示功能要能完成所有信息的 显示,并且自动分页;查找可以通过多种方式进行。 按需求的功能,应该设计出输入函数、输出函数、显示函数、查找函数、修改函数。另 需要一个标准界面。 B.数据存储问题 这个系统数据庞大复杂,需要需要适当应用数组,循环,结构体,文件操作等基本 语句及稍有难度的语句。因此要求我们熟练掌握结构化程序设计的基本思路和方法,在 所掌握的基本知识和技能的基础上,进一步提高自学能力和查找资料的能力,解决一定 程度的复杂的结构化程序设计问题,加深对所学知识的理解与掌握,利用自己所学知识 解决实际问题的能力,为以后的程序开发打下基础。 二:总体设计 主要结构图示 本程序由主函数和多个子函数组成,可执行多种数据管理的功能。具体通过c语言中 结构体数组来实现。主函数主体为定义的结构体,和一个switch函数,以提供多种程序 操作功能。 主要需运用到结构体数组、文件、函数等内容知识。 子函数有六个,分别负责添加、显示、删除、查询、修改、保存的功能。通过变量i使结 构体数组之间达成数据链表的功能,利用该方法主要考虑到成员中对于结构体指针尚未 熟练掌握,而这种方法更加便捷易懂。 子函数功能介绍 1. 添加:输入成员信息,依次输入编号、姓名、QQ、电话号码、单位、电子邮箱、身 份。 2. 显示:以表格形式输出所有通讯记录 3. 删除:只需输入该成员姓名,便删除其所有记录 4. 查询:可选用姓名、电话、单位身份4种方式来查询 5. 修改:输入该成员姓名后,再依次输入更新信息完成修改 6. 保存:输入文件名(带后缀)后保存。C-free中会默认以txt文件保存至c- free standard/temp文件夹中 三:详细设计 1. 主函数 完成变量的初始化与函数的定义之后,运用for( ; ; )来开启一个无限循环。循环内有switch函数来实现函数的选择功能,case1、case2等分 别执行不同的函数操作,每个函数中调用了相应的函数,并采用函数system("pause") ;暂停等待按键。在case6中采用函数exit(0)跳出循环。 下面在讲一下主函数中定义的n的作用,n的初值是1,当执行完case1中的函数input ( )后返回值为n+i,i为输入的成员数,返回值重新赋给n,n变为成员数,从而确定了之后 display()等函数循环中的终止条件。 2. 分函数 【1】添加 【2】显示 【3】删除 【4】查询 【5】修改 保存函数:【1】定义一个文件指针fp 【2】输入想创建的文件名称,如filename 【3】开辟文件,用fopen以"写"的方式打开,并把fp指针指向该文件 【4】在文件中输入信息 【5】保存(即fclose(filename)) 查询函数:运用了if来实现选择,并通过递归返回继续查询。 注:身份以及单位的查询方式有所不同,单位只能查找一条符合的记录,而按身份查询 可显示所有符合的信息。若查找不到,则返回继续查询,选择5返回则退出查询。 四:调试与测试 我们编写的函数具有一定的容错能力。具体表现为 【1】如select函数中输入0-6以外的数,则循环继续进行,直到输入0-6为止 【2】sign!='n'&&sign!='N'语句保证了大小写不会影响结果 【3】while(m!='1'&&m!='2'&&m!='3'&&m!='4') { printf("输入错误,请重新选择:"); scanf("%c",&m); }保证了输入正确 【4】当输入的成员信息错误时,可通过修改函数进行改动 五:测试结果 1.主函数运行正常。通过for语句是界面循环,并有switch及system函数进行配合调节 达到目的。(界面显示如下) 2.测试结果:运行正常,达到了预订的目标。 此处为通过保存函数保存后的txt文本中的学生信息 六:心得 课程设计是一个不断学习、思考、编写、修改的过程。它对我们的能力提出了挑战。 我不仅意识到c语言基础的重要性,更意识到了自学能力的重要性,同学学习书本,查找 资料等多种途径来构建思
C语言程序设计》 实验指导书 山东水利职业学院 目 录 实验一 C语言程序初步 2 实验二 数据类型、运算符和表达式 3 实验四 循环结构(1) 6 实验五 循环结构(2) 9 实验六 函数(1) 12 实验七 函数(2) 14 实验八 数组(1) 16 实验九 数组(2) 19 实验十 指 针 (1) 22 实验十一 指针(二) 24 实验十二 有关文件的程序设计 27 实验十三 结构体和共用体 30 实验十四 位运算 32 实验十五 综合应用举例 33 实验一 C语言程序初步 [实验目的和要求] 1、 熟悉C语言运行环境。 2、 掌握C语言程序的书写格式和C语言程序的结构。 3、 掌握C语言上机步骤,了解运行一个C程序的方法。 4、 本实验可在学习完教材第一章后进行。 [实验内容和步骤] 1、输入一个简单的C语言程序:输入矩形的两条边,求矩形的面积。 [分析与讨论] 1、记下在调试过程中所发现的错误、系统给出的出错信息和对策。分析讨论对策成功或失败的原因。 2、总结C程序的结构和书写规则。 实验二 数据类型、运算符和表达式 [实验目的] 1.熟练表达式的表示方法 2.了解表达式的运行结果 3.了解C语言中数据类型的意义 [实验内容和步骤] 1. 用printf函数来输入表达式运行结果 2. 程序试图计算由键盘输入的任意两个整数的平均值: [问题讨论] 1.“=”和“==”有什么区别? 2.“&”和“&&”、“|”和“||”有什么区别? 3. 如何正确地选用数据类型? 实验三 选择结构 [实验目的] 1、了解条件与程序流程的关系 2、了解用不同的数据使程序的流程覆盖不同的语句、分支和路径。 [实验内容和步骤] 1、题目 有如下程序段: {…… if (a>1&&b= =0) x=x/a; if(a==2||x>1) x=x+1; } 为了更容易明白程序的逻辑结构,要求用流程图来加以描述。 要求增加一些输入语句和输出语句,以便使上述程序能在不同的a,b和x值下运行,并且能观察程序流程经过(覆盖)了哪些语句、哪些分支及哪些路径。 [分析讨论] (1) 用哪一组数据就可使程序中的每个处理语句都执行一次?为了找出程序中各条处理语句中的错误,应该使用什么样的数据对程序进行测试?请上机验证自己的结论。 (2) 用哪两组数据就可以使程序段中的每个分支都运行一次?这种情形与上面的讨论有何不同?如果为了找出程序中积压分支中的错误,应该使用什么样的数据对程序进行测试?请上机验证自己的结论。 (3)用哪两组数据就可以把判定框中的每个条件运算都进行一次?如果为了测试出判定条件中的错误,应使用哪些数据对程序进行测试?请上机验证自己的结论。 (4)用哪四组数据才可以把各种条件的组合都检测一遍? 如果为了测试各种条件的组合的情形,应该使用什么样的测试数据?请上机验证自己的结论。 (5)用哪四组数据才可以把起始到终止的各条路径都覆盖一次?如果为了测试出程序在不同路径下的错误,应该使用什么样的测试数据?请上机验证自己的结论。 实验四 循环结构(1) [目的和要求] 1、掌握在程序设计条件型循环结构时,如何正确地设定循环条件,以及如何控制循环的次数。 2、了解条件型循环结构的基本测试方法。 [实验内容与步骤] 1、下面是一个计算e的近似值(使误差小于给定的δ)的程序。 main() {double e=1.0,x=1.0,y,detax; int i=1; printf(“\n please input enter a error:”); scanf(“%lf”,&detax); y=1/x; while(y>=detax) { x=x*I; y=1/x; e=e+y; ++i; } printf(“%12.10lf”,e); } [分析讨论] (1)、阅读上面的程序,写出程序所依据的计算公式。 (2)、当输入的detax各是什么值时,能分别使程序按下面的要求运行: .不进入循环; .只循环一次; .只循环两次; .进入死循环(程序将永远循环下去)。 为了能知道程序循环了多少次,应该在程序中增加一条什么样的语句? (3)、原程序中while语句中的y>=detax,分别换成y>detax,y=detax,y<detax,y<=detax,观察程序运行将会有什么变化。 假如不知道机器内的程序中的各语句实际上是什么,分别输入什么样的detax来测试出while语句的循环条件写错了。 (4)、把原程序中while语句之前的y=1/x语句去掉,观察程序的运行将会发生什么样的变化。 假如不知道机器内的程序实际上是怎么写的,输入什么样的detax就能测试出少了上述这条语句。 (5)、若把原程序中的++i换成i++,观察程序的运行发生了什么变化? 假如不知道这条语句到底是怎么写的,输入什么样的detax就能测试出这条语句写错了. (6)、把原程序中的while结构改写成do—while结构,再分别按上述的(2)、(3)两步进行实验。 实验五 循环结构(2) [ 目的和要求] 1、 掌握如何正确地控制计数型循环结构的次数 2、 了解对计数型循环结构进行测试的基本方法 3、 了解在嵌套循环结构中,提高程序效率的方法 4、 本实验应在学习了教材第3.3.4进行 [实验内容与步骤] 实验题目:下面是一个循环结构 的C程序。 main() { int i ,j; long sum=0; for(i=1,i<=20,i++) for(j=1;j<=3;j++) sum=sum+(i+1)*(i+1)*(j+2) printf(“sum is :%d\n”,sum); } [ 分析讨论] (1) 将上述程序中的和分别做什么样修改,能使程序按如下要求运行: .不循环; .外循环1次,不进入内循环; .外循环,内循环各1次; .外循环1次,内循环2次; .外循环 2次, 内循环1次; .外循环19次,内循环3次; .外循环20次,内循环2次; .外循环21次,内循环3次。 (2) 试修改程序,以便能知道控制表达式 i=1;j<=20;i++ 和 j=1;j<3;j+= 各执行了多少次? (3)内循环与外循环的控制表达式进行对换,观察程序的执行结果有无变化,并且观 察对换后的外循环与内循环控制表达式执行的总次数有无变化? (4)将(3)中程序写为 main() { int i,j,k; long sum=0; for (j=1.j<=3.j++) { k=j+2; for(i=1;i<=20;i++) sum=sum+(i+1)*(i+1)**k; } printf(“sum is :%d\n”,sum); } 观察程序的执行结果有无变化? (5)将(3)中程序改写为 main() { int I,j,m,n; long sum=0; for(j=1;j<=3;j++) { m=j+2; for(I=1;j<=20;I++) { n=I+1; sum=sum+m*m*k; } } printf(“sum is :%d\n”,sum); } 观察程序的执行结果有无变化? (1) 实验六 函数(1) [实验目的] 1.理解和掌握多模块的程序设计与调试的方法。 2.掌握函数的定义和调用的方法。 3.学会使用递归方法进行程序设计。 [实验内容和步骤] 1. 编写一个函数,判断一个数是不是素数。在主函数中输入一个整数,输出是否是素数的信息。 实验步骤与要求: (1) 编写一个函数prime(n),返回给定整数n是否为素数。 (2) 编写一个主函数,输入一个整数,调用(1)中的函数,判断此整数是否为素数,并输出结果。 (3) 对于属于多函数程序,可以采用每个函数分别进行编辑、编译的方法,然后再连接、运行。如果编译有错时,可分别修改,这样便于调试。实验2和实验3也可以使用这样的调试方法。 2. 用梯形法求函数的定积分。 实验步骤与要求: (1) 编制一个函数sab(a,b,n),其功能是求函数f(x)在[a,b]上的定积分,其中n为区间[a,b]的等分数。要求该函数在一个独立的文件中。 (2) 编制一个主函数以及计算被积函数值的函数f(x),在主函数中调用(1)中的函数计算并输出下列积分值。要求主函数与函数f(x)在同一个文件中。 (3) 编制另一个主函数以及计算被积函数值的函数f(x),在主函数中调用(1)中的函数计算并输出下列积分值。要求主函数与函数f(x)在同一个文件中。 说明: 用梯形法求定积分,梯形公式为 s=h[f(a)+f(b)]/2+hf(a+kh)其中,h=(b-a)/n 3. 用递归方法编写程序,求n阶勒让德多项式的值,递归公式为 1 (n=0) pn(x)=x (n=1) ((2n-1)xpn-1(x)-(n-1)pn-2(x))/n (n>1) , [ 分析讨论] 1. 小结函数的定义及调用方法。 2. 小结函数中形参和实参的结合规则。 实验七 函数(2) [目的与要求] 1、 掌握C语言函数定义及调用的规则。 2、 理解参数传递的过程。 [ 实验内容与步骤] 1、 上机调试下面的程序,记录系统给出的出错信息,并指出出错原因。 main() { int,y; printf(“%d\n”,sum(x+y)); int sum(a,b) { int a,b; return(a+b); } } 2、 编写并调试一个求(n为整数)的递归函数,希望能在程序运行过程中动态地显示递归函数被调用的轨迹。 [分析讨论] 1、 针对以上实验内容写出相应的参数传递过程并分析结果。 2、 讨论参数的传递的几种形式。 实验八 数组(1) [实验目的] 掌握有关数组和字符串的程序设计方法。 [实验内容和步骤] 1 已知一组数据如下: 6,3,42,23,35,71,98,67,56,38 编写程序,把它们按从小到大的次序排列起来。 程序如下: /*EX3-1*/ # include<stdio.h> # define N 10 main() { int a[N]={6, 3, 42, 23, 35, 71, 98, 67, 56, 38}; int i, j, t; printf(“The array before sorted:”); for(i=0; i<N; i++) printf(“%4d”, a[i]); for(i=0; i<9; i++) { for(j=i+1; j<10; j++) { if(a[i]>a[j]) { t=a[i]; a[i]=a[j]; a[j]=t; } } } for(i=0; i<N; i++) printf(“%4d”, a[i]); return o; } [ 分析讨论] 1. 定义数组时下标的用法和注意事项. 2.什么情况下出现下标越界.  实验九 数组(2) [目的和要求] 1、掌握数组定义的规则。 2、掌握C语言数组的基本用法。 [实验内容与步骤] 1、编写程序,测试下列数组的定义方式是否正确 (1) main() { int n; scanf(“%d”,&n); int a[n]; …… } (2) main() { const int n=10; int a[n]; } (3) #include <stdio.h> #define M 10 main() { int a[M]; } (4) main() { int a[2+2*4]; } (5) #include <stdio.h> #define M 2 #define N 8 main() { int a[M+N]; } 通过这一实验,可以说明什么问题? 2、运行下面的C程序,根据运行结果,可以说明什么? main( ) { int num[5]={1,2,3,4,5}; inti i; for(i=0;i<=5;i++) printf(“%d”,num[i]); } 3、操作符&用以求一个变量的地址,这在函数scanf中已经使用过了。现在要你设计一个程序,返回一个3 * 5 的二维数组各元素的地址,并由此说明二维数组中各元素是按什么顺序存诸的。 4、为一个起泡排序程序设计测试用例,并测试之。 [分析讨论] 通过实验,分析定义与引用数组的区别。 实验十 指 针 (1) [目的和要求] 1. 熟悉指针正确用法。 [实验内容和步骤] 1、指出下面程序错误的原因。 main( ) { int x=10,y=5,*px,*py; px=py; px=&x; py=&y; printf(“*px=%d,*py=%d,*px,*py); } 2.下面的程序能获得上述运行结果吗? main() { char *S=”COMPUTER”; char c; printf(“which style you want to \n”); printf(“capital (c) or uncapital(u);”); c=getchar(); if(c=’c’) put(s); else { s=”computer”; puts(s); } } 分析出错原因。 3.设计一个C程序,用以测试下列各表达式的运算。 (1) y=*px++ (2) y=*++py (3) y=(*py)++ (4) y=--*ppx++ [分析讨论] 数组与指针的联系。 实验十一 指针(二) [目的和要求] 1、了解指针参数的特殊性。 2、掌握函数、指针、数组的用法。 [实验内容与步骤] 1、想使指针变量pt1指向a 和b 中的大者,pt2指向小者,以下程序能否实现此目的? swap(int *p1,int *p2) { int *p; p=p1;p1=p2;p2=p; } main() { int a,b; scanf(“%d,%d”,&a,&b); pt1=&a;pt2=&b; if(a<b)swap(pt1.pt2); printf(“%d,%d\n”,*pt1,*pt2); } 上机调试此程序。如果不能实现题目要求,指出原因,并修改之。 2、 下面的程序。注意其中的复杂声明语句; int (*function[4])(); 和各标记符的含义,并记录执行结果。 main() { int fun1(); int fun2(); int fun3(); int fun4(); int (*function[4])(); int a=10,b=5,i; function[0]=fun1; function[1]=fun2; function[2]=fun3; function[3]=fun4; for(I=0;I<4;i++) printf(“fun no.%d->%d\n”,i+1,execute(a,b,function[i])); } execute(int x,int y,int *fun()) { return(*fun)(x,y);} fun1(int x, int y) { return(x+y);} fun2(int x,int y) { return(x-y);} fun3(int x,int y) { return(x*y);} fun4(int x,int y) { return(x/y);} [分析讨论] 指针、数组、函数的联系。 实验十二 有关文件的程序设计 [实验目的] 1. 掌握文件建立的方法。 2. 掌握包含文件操作的程序设计和调试方法。 [实验内容和步骤 ] 1 建立一个磁盘文件,其内容是0~90°之间每隔5°的正弦值。 程序如下: //EX8-1 #include<iostream.h> #include<fstream.h> #include<math.h> #define PI 3.14159 main() { float S[19]; int i, a; ofstream out(“fsin.bny”); if(!out) { cout<<”Cannot open file.”<<endl; return 1; } for(i=0,a=-5; i<19; i++) { a+=5; S[i]=sin(a*PI/180.0); } out.write((unsigned char*)&s, sizeof s); out.close( ); return 0; } [ 分析讨论] 文件操作中各函数的功能,具体使用方法. 实验十三 结构体和共用体 [目的和要求] 1、掌握结构体类型方法以及结构体变量的定义和引用。 2、掌握指向结构体变量的指针变量的应用,特别是链表的应用。 3、掌握运算符”.”和”->”的应用。 4、共用体的概念和应用。 [实验内容和步骤] 1、编写程序:有4名学生,每个学生的数据包括学号、姓名、成绩,要求找出成绩最高者的姓名和成绩,上机运行程序。 2、建立一个链表每个结点包括的成员为:职工号、工资。用malloc打印出来。用一个creat 来建立链表,用list来输出数据。5个职工号为101,103,105,107,109。 3、在上题基础上,新增加一个职工的数据,按职工号的顺序插入链表,新插入的职工号为106。写一函数insert来插入新结点。 4、在上面的基础上,写一函数delete,用来删除一个结点。要求删除职工号为103的结点。打印出删除后的链表. 5、你有无办法测试构造链表时,内存空间的动态分配情形? [分析讨论] 怎样引用结构体变量成员? 实验十四 位运算 [目的和要求] 1. 掌握位运算的概念和方法。 2. 掌握位运算(&,|,^,~)的使用方法。 3. 掌握有关位运算的用法。 [实验内容和步骤] 1. 按习题8.2的要求,编一个程序,将一个整数的高字节和低字节分别输出(用位运算方法)。上机运行。 2. 按习题8.6的要求,编一个程序,使一个整数的低4位翻转。上机运行,用十六进制数输入和输出。 3. 按习题8.10的要求,将一个整数i的各位循环左移4位,设i的值为十六进制数fe13。 4. 按习题8.13的要求,设计一个函数,当给定一个整数后,能得到该数的补码(应考虑该整数是正数或负数)。 [分析讨论] 位运算时应注意什么问题? 实验十五 综合应用举例 [目的与要求] 1、熟悉C语言的端口及硬件控制的方法与原理。 2、了解用C语言实现发声程序所要访问的端口。 [实验内容和步骤] 1、调试课本中例10.8程序,注意调出不同的效果。 2、调试并总结程序中所用的知识点,写出乐谱文件播放的是什么乐谱? 乐谱实例: 6000 h5 1 h3 2 h2 0.5 h3 0.5 h1 4 h2 1 m7 2 m6 0.5 h1 0.5 m5 4 m3 2 m5 1.5 m6 0.5 h1 1.5 h2 0.5 m6 0.5 h1 0.5 m5 1 h5 1.5 h1 0.5 h6 0.5 h5 0.5 h3 0.5 h5 0.5 h2 4 h2 1.5 h3 0.5 m7 1 m6 1 m5 1.5 m6 0.5 h1 1 h2 1 m3 1 h1 1 m6 0 m5 0.5 m6 0.5 h1 0.5 m5 4 h3 1.5 h5 0.5 m7 1 h2 1 m6 0.5 h1 0.5 m5 3 m3 0. m5 0.5 m3 0.5 m5 0.5 m5 0.5 m6 0.5 m7 0.5 h2 0.5 m6 3 m5 0.5 m6 0.5 h1 1.5 h2 0.5 h5 1 h3 1 h2 1 h3 0.5 h2 0.5 h1 1 m6 0.5 m5 0.5 m3 2 h1 2 m6 0.5 h1 0.5 m6 0.5 m5 0.5 m3 0.5 m5 0.5 m6 0.5 h1 0.5 m5 3 h3 0.5 h5 0.5 h2 0.5 h3 0.5 h2 0.5 h1 0.5 m7 1 m6 1 h5 4## void play_music(char *filename) /*filename为音乐文件名格式见上*/ { FILE *fp; int rate; char sound_high[3]; float sound_long; register int i=0,j; int sign=0; float str[1000][2]; if ((fp=fopen(filename,"r"))==NULL) { printf("Open file music.doc Errors!\n"); exit(1); } fscanf(fp,"%d",&rate); while(!feof(fp)&&!sign) { fscanf(fp,"%s%f",&sound_high,&sound_long); str[i][1]=rate*sound_long; switch(sound_high[0]) { case 'h': switch(sound_high[1]) { case '1': str[i++][0]=1046.5; break; case '2': str[i++][0]=1174.7; break; case '3': str[i++][0]=1318.5; break; case '4': str[i++][0]=1396.9; break; case '5': str[i++][0]=1568; break; case '6': str[i++][0]=1760; break; case '7': str[i++][0]=1975.5; break; default: printf("\n Error Music.doc\n"); break; } break; case 'm': switch(sound_high[1]) { case '1': str[i++][0]=523.3; break; case '2': str[i++][0]=587.3; break; case '3': str[i++][0]=659.3; break; case '4': str[i++][0]=698.5; break; case '5': str[i++][0]=784.0; break; case '6': str[i++][0]=880; break; case '7': str[i++][0]=987.8; break; default: printf("\n Error music.doc.\n"); break; } break; case 'l': switch(sound_high[1]) { case '1': str[i++][0]=262; break; case '2': str[i++][0]=296; break; case '3': str[i++][0]=329.6; break; case '4': str[i++][0]=349.2; break; case '5': str[i++][0]=392; break; case '6': str[i++][0]=440; break; case '7': str[i++][0]=493.9; break; default: printf("\n Error music.doc.\n"); break; } break; case '*': switch(sound_high[1]) { case '1': str[i++][0]=131; break; case '2': str[i++][0]=147; break; case '3': str[i++][0]=165; break; case '4': str[i++][0]=176; break; case '5': str[i++][0]=196; break; case '6': str[i++][0]=220; break; case '7': str[i++][0]=247; break; default: printf("\n Error music.doc\n"); break; } break; case '#': if (sound_high[1]=='#') sign=1; break; case 'x': if (sound_high[1]=='0') str[i++][0]=20; break; default:printf("\n ERRor music.doc\n"); exit(1); } } for(j=0;j<=i-1;j++) { sound(str[j][0]); delay(str[j][1]); } nosound(); } [分析讨论] 1.c语言怎样对数据库进行管理? 2.分析程序设计过程中怎样对基础知识进行灵活运用。
C、C++语言是IT行业的主流编程语言,也是很多程序员必备的软件基本功,是软件开发行业招聘考查的重点。本书以流行的面试题讲解为主要内容,介绍了C、C++语言基本概念,包括保留字、字符串、指针和引用、结构体、库函数等各个方面的基础知识,介绍了面向对象编程基本概念,包括如何实现继承、多态和封装等。还介绍了排序算法及数据结构的实现,包括链表、堆栈、队列和树。此外,本书开始用两章篇幅详细介绍了中英文面试的注意事项、常见问题及程序员的职业规划等软件工程师的常识。最后四章详细讲解了现在流行的智力测试题。 第一篇 求职 第1章 应聘求职 1.1 企业与人才 1.1.1 企业需要什么样的人才 1.1.2 如何成为企业需要的人才 1.2 做好面试的准备 1.2.1 面试衣着 1.2.2 简历 1.3 面试 1.3.1 面试注意事项 1.3.2 面试问题分析 问题一:“请自我介绍一下。” 问题二:“谈谈你的家庭情况。” 问题三:“你有什么业余爱好?” 问题四:“你最崇拜谁?” 问题五:“谈谈你的缺点。” 问题六:“谈一谈你的一次失败经历。” 问题七:“你为什么选择我们公司?” 问题八:“对这项工作,你可预见到哪些困难?” 问题九:“如果我们录用你,你将怎样开展工作?” 问题十:“与上级意见不一致,你将怎么办?” 问题十一:“我们为什么要录用你?” 问题十二:“你能为我们做什么?” 问题十三:“你是应届毕业生,缺乏经验,如何能胜任这项工作?” 问题十四:“你希望与什么样的上级共事?” 问题十五:“你在前一家公司的离职原因是什么?” 1.4 为明天做好计划 第2章 英文面试 2.1 英文电话面试 2.1.1 英文电话面试注意事项 2.1.2 英文电话面试常见   问题 问题一:When will you graduate? 问题二:How do you normally handle criticism? 问题三:Why should we hire you? 问题四:Please tell me something unreflected at your resume/about yourself/ your experience/your activities. 2.1.3 英文电话面试常用词汇 2.2 英文面试 2.2.1 英文简历 2.2.2 英文面试流程 2.2.3 英文面试注意事项 2.2.4 英文面试常见问题 问题一:What is your strongest trait? 问题二:How would your friends or colleagues describe you? 问题三:What personality traits do you admire? 问题四:What leadership qualities did you develop as an administrative personnel? 问题五:How do you normally handle criticism? 问题六:What do you find frustrating in a work situation? 问题七:How do you handle your failure? 问题八:What kinds of people do you like to work with? 2.2.5 英文面试常用词汇 2.3 计算机专业英语面试常用词汇 第二篇 C/C++面试题 第3章 C/C++程序基础 3.1 基本概念 面试题1:什么是C语言语句 面试题2:变量的声明和定义有什么区别 面试题3:下列字符中,哪些不是C语言关键字 面试题4:下列变量定义中,哪些是合法的 面试题5:如何以最简单的方式让电脑蜂鸣器发出声音 3.2 编程规范 面试题6:谈谈你对编程规范的理解或认识 面试题7:函数、变量等命名都有哪些规则 面试题8:写出bool、int、float、指针变量与“零值”比较的if语句 3.3 数据类型 面试题9:写出代码的输出结果 面试题10:C语言中不合法的整型常数 面试题11:short i = 0; i = i + 1L;这两句有错吗 面试题12:char x[] = {"abcd"}和 char y[] = {'a','b','c','d'} 有不同吗 面试题13:char型数据在内存中的存储形式 3.4 运算符 面试题14:请写出下列代码的输出内容 面试题15:运算符的优先级问题 面试题16:&&和&,||和|有什么区别 面试题17:什么是左值,什么是右值 面试题18:请写出程序的运行结果 面试题19:sizeof和strlen的区别 3.5 结构体 面试题20:结构体是什么样的数据类型 面试题21:结构体可以直接赋值吗 面试题22:组织WAV文件头,并解析WAV格式的各项信息 面试题23:计算学生不及格的人数打印他们的性别、姓名和成绩 面试题24:结构体内存对齐问题 3.6 C和C++区别 面试题25:关键字static在C和C++中的区别 面试题26:C语言的结构体和C++的有什么区别 面试题27:C中的malloc和C++中的new有什么区别 面试题28:C++的引用和C语言指针有什么区别 第4章 预处理、保留字 4.1 预处理 面试题1:简述#ifdef、#else、#endif和#ifndef的作用 面试题2:宏定义和函数 面试题3:用#define声明一个常数 面试题4:写一个“标准”宏MIN 面试题5:typedef和define有什么区别 面试题6:#define CHAR char*和typedef char* CHAR各有什么优劣 面试题7:谈谈你对typedef的认识 4.2 const(常量) 面试题8:关键字const是什么 面试题9:说明以下a声明的含义 面试题10:const、define定义常量的区别 4.3 static(静态)和extern 面试题11:static有什么作用 面试题12:extern有什么作用 面试题13:简述变量存储类型 4.4 volatile 面试题14:volatile有什么作用 面试题15:一个参数可以既是const又是volatile吗 面试题16:一个指针可以是volatile吗 第5章 引用和指针 5.1 引用 面试题1:什么是引用 面试题2:常引用有什么作用 面试题3:流操作符重载为什么返回引用 5.2 指针 面试题4:说明以下声明的含义 面试题5:简述指针常量与常量指针区别 面试题6:写出以下代码的输出结果 面试题7:找出代码的错误 5.3 指针和数组 面试题8:写出代码的输出结果 面试题9:请问这段程序有问题吗 面试题10:a和&a有什么区别 面试题11:请问代码有什么问题 面试题12:数组名和指针区别 5.4 函数指针 面试题13:请解析(*(void (*)())0)()的含义 面试题14:指出程序的错误 5.5 “野指针” 面试题15:如何避免“野指针” 面试题16:程序是否正确 面试题17:指出程序的错误 5.6 动态内存 面试题18:简述C、C++程序编译的内存分配情况 面试题19:以下四段代码中哪段没有错误 第6章 字符串 6.1 数字字符串 面试题1:编码实现数字转化为字符串 面试题2:编码实现字符串转化为数字 6.2 字符串函数 面试题3:编写一个标准strcpy函数 面试题4:简述strcpy、sprintf与memcpy的区别 6.3 字符串与数组 面试题5:找出程序的错误之处 面试题6:判断程序会出现什么问题 第7章 嵌入式编程 面试题1:编码实现某一变量某位清或置 面试题2:用C编写一个死循环程序 面试题3:用变量a给出下面的定义 面试题4:设置地址为0x67a9的整型变量的值为0xaa66 面试题5:评论下面这个中断函数 面试题6:评价一个代码片段 第8章 面向对象 8.1 面向对象的基本概念 面试题1:谈谈你对面向对象的认识 面试题2:面向对象的三大特征 面试题3:面向过程和面向对象有什么区别 8.2 类的成员变量和成员函数 面试题4:简述类public、protected、private的作用 面试题5:写出代码的打印结果 面试题6:写出程序的打印结果 面试题7:C++的空类有哪些成员函数 8.3 构造函数和析构函数 面试题8:构造函数能否为虚函数 面试题9:简述子类与父类的析构、构造函数的调用顺序 面试题10:编写类String 的构造函数、析构函数和赋值函数 8.4 拷贝构造函数 面试题11:谈谈对拷贝构造函数和赋值运算符的认识 面试题12:写出当定义#define _INMAIN 0和不定义时代码打印结果 第9章 继承与多态 9.1 继承 面试题1:指出程序的错误 面试题2:用C++设计一个不能被继承的类 9.2 虚函数和纯虚函数 面试题3:下面说法中正确的是哪个 面试题4:写出程序的打印结果 面试题5:访问基类的私有虚函数 9.3 多态 面试题6:简述类成员函数的重写、重载和隐藏的区别 面试题7:简述多态实现的原理 第10章 数据结构 10.1 链表 面试题1:链表和数组有什么区别 面试题2:寻找单链表中间结点 面试题3:怎样把一个单链表反序 10.2 单循环链表 面试题4:根据需求建立一个单向循环链表 面试题5:检测一个较大的单向链表是否带环 10.3 双向链表 面试题6:按要求构造一个双向链表 面试题7:编程实现双链表插入新结点 面试题8:编程实现双链表删除指定结点 10.4 栈和队列 面试题9:简述队列和栈的异同 面试题10:建立一个链式栈 面试题11:建立一个链式队列 面试题12:能否用两个栈实现一个队列的功能 10.5 二叉树 面试题13:建立一个二叉树 面试题14:计算一棵二叉树的深度 面试题15:在二元树中找出和为某一值的所有路径 第11章 排序 11.1 插入排序 面试题1:编码实现直接插入排序 面试题2:编码实现希尔(Shell)排序 11.2 交换排序 面试题3:编码实现冒泡排序 面试题4:编码实现快速排序 11.3 选择排序 面试题5:编码实现直接选择排序 面试题6:编程实现堆排序 11.4 基数排序 面试题7:编程实现基数排序 第三篇 智力测试 第12章 基本方法 面试题1:斯密斯夫妇握手问题 面试题2:5个强盗分100颗宝石 面试题3:分牛 面试题4:谁在说谎 面试题5:是亏了还是赚了 面试题6:小虫分裂问题 面试题7:飞机绕地球环行问题 第13章 数学能力 面试题1:用一笔画出经过9个点的4条直线 面试题2:在9个点上画10条线 面试题3:100盏灯 面试题4:找出不同的球 面试题5:时针、分针和秒针重合问题 面试题6:可以喝多少瓶汽水 面试题7:怎样拿到第100号球 面试题8:烧绳计时 面试题9:分金条 面试题10:至少有多少人及格 面试题11:如何取3升水 面试题12:将16升水平均分给四个人 面试题13:如何将140克的盐分成50、90克各一份 面试题14:蜗牛几天能爬到井口 面试题15:100美元的差额到哪里去了 面试题16:点击鼠标比赛 面试题17:小猴最多能运回多少根香蕉 面试题18:算出小张买了几瓶啤酒、几瓶饮料 面试题19:牧场有多少匹马 面试题20:找出不同的苹果 面试题21:如何穿越沙漠 第14章 推理能力 面试题1:怎么少了100元 面试题2:村里有多少条病狗 面试题3:他们都在做什么 面试题4:躯体与灵魂 面试题5:小明一家能否安全过桥 面试题6:过河   问题 面试题7:这是张什么牌 面试题8:说谎岛上的两个部落 面试题9:谁是特尔斐城的预言家 面试题10:哪个政党获胜 面试题11:每个护士星期几休息 面试题12:每个人系的圆牌都是什么颜色的 面试题13:帽子问题 面试题14:谁是凶手 面试题15:他们的头发是什么颜色的 面试题16:谁是漂亮的青年 面试题17:哪个袋子里有金子 面试题18:他们星期几在说谎 面试题19:剩下的是什么牌 面试题20:老李的儿子们是做什么的 面试题21:史密斯家的门牌号 面试题22:尤克利地区的电话 面试题23:乡村庙会的15点游戏 面试题24:各家的孩子得了第几名 面试题25:经理应该带谁出差 面试题26:法官的判决 面试题27:张老师的生日是哪一天 面试题28:谁是M小姐的情人 面试题29:他们分别是哪国人 面试题30:他们分别是做什么的 面试题31:他们都会说什么语言 面试题32:怎么把马匹从甲村拉到乙村 面试题33:谁打碎了花瓶 面试题34:分机票 面试题35:石头有多重 面试题36:该释放谁 面试题37:谁打碎的玻璃 面试题38:谁是最优秀的医生 面试题39:今天星期几 面试题40:五个人进行汽车竞赛 面试题41:下一行是什么 面试题42:三筐水果各是什么 面试题43:最后剩下的是谁 第15章 反应能力 面试题1:下水道的井盖 面试题2:30秒答题 面试题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:女儿的错
目 录 1.课程设计目的 2 2.需求分析 2 2.1要求 2 2.2任务描述 2 2.3运行环境 3 2.4开发工具 3 3.总体设计 3 3.1系统流设计 3 3.2系统模块设计 4 4.详细设计 5 4.1数据结构设计 5 4.2模块接口设计 6 4.3 以下是几个主要模块的流程图: 6 5.运行结果 9 5.1主菜单界面: 10 5.2信息录入界面: 11 5.3信息浏览界面: 12 5.4 信息查询界面: 14 5.5信息修改界面: 15 6.程序调试与测试 16 6.1程序调试 16 6.2程序测试 16 7.问题与不足 17 8.建议 17 9.编程体会 17 10.程序源代码: 17 11.参考文献 32 12.用户使用手册 33 1.课程设计目的 1) 通过课程设计,加深对结构化程序设计思想的理解,能对系统功能进行分析,并设计合 理的模块化结构。 2) 通过课程设计,进一步掌握C语言中重要的数据结构。掌握并熟练运用指针、链表、结 构体、文件等数据结构。 3) 通过课程设计,提高程序开发能力,能运用合理的控制流编写清晰高效的程序。 4) 通过课程设计,培养C语言知识运用和自学能力,真正的把课堂上获得的知识运用起来 ,培养自己对程序编写的兴趣,并能独立设计和实现一个小型系统。 5) 通过课程设计,加强了自己的程序分析能力。 2.需求分析 2.1要求 1) 遵循软件开发过程的基本规范;能灵活应用,初步具备运用C程序解决实际问题的能力 。 2) 运用结构化程序设计方法,按照课程设计题目要求,完成包括需求分析、数据结构设计 、概要设计、详细设计、编写代码、调试和测试C程序等过程。 3) 学会利用数组、结构体、链表进行相关信息表示。 4) 功能模块划分清晰,系统结构图符合功能操作要求,且有详尽的功能描述 5) 各主要功能模块有详尽的流程图及描述,用函数的形式实现,且调用入口、功能、出口 等关系正确。 6) 所设计的C程序较好的可读性和可靠性;程序书写风格规范,注释清晰。 7) 界面友好(良好的人机互交),凡需要输入信息时,都有较详尽的提示信息,并对输入 信息有较完善的值域分析。 8) 程序经上机调试通过,不应出现语法型、功能型错误,并获得满意的结果。 9) 学会通过各功能性测试用例测试程序的能力,尽可能避免原则性错误的出现。 10) 掌握编写、调试和测试C语言程序的基本技巧,充分理解结构化程序设计的基本方法。 11) C语言课程设计报告的内容反应实际设计过程,且符合格式要求,无抄袭现象。 2.2任务描述 假定公司有职工10人,职工有职工号、姓名、以周为记录时长的考勤情况(迟到次数 、早退次数、旷工天数)三项记录,一周上班五天,每天上午上班时间为8:00,下班时 间为11:30、下午2:00上班,5:30下班。 试设计一考勤管理系统,使之能提供以下功能: (1) 系统以菜单方式工作(用键盘输入1~5之间的数来选择功能,其中6为退出) (2) 职工上班、下班录入功能(职工号、姓名任意给一个即可,上班时间(自动获取系统时 间)、下班时间(自动获取系统时间)) (3) 职工出勤情况整体浏览功能(职工号、姓名、(迟到、早退、旷工)、无前项则显示全 勤) (4) 查询功能,按职工号、姓名、迟到、早退、旷工、全勤查询(至少一种查询方式) (5) 职工考勤信息的删除(给定密码后可按职工号、姓名、考勤情况任一项进行删除) (6) 考勤信息的修改(给定密码后可按职工号、姓名、考勤情况任一项进行删除) (7) 所有数据符合实际应用要求 2.3运行环境 1) WindowsXP/7系统 2) VC++6.0编译环境 2.4开发工具 C语言 3.总体设计 3.1系统流设计 本系统的功能模块如图1所示,主要有以下模块: 图1系统功能模块图 3.2系统模块设计 1) 主菜单界面:构造函数void menu(),实现系统主菜单的显示。 2) 删除职工资料:构造函数void Del(Link l),实现职工资料的删除。并且要密码输入的要求(可以修改密码)。 3) 职工资料录入:构造函数void Qur1(Link l)和void Qur2(Link l)。分别对应上班时间和下班时间的录入。 4) 查询职工资料:构造函数void find(Link l),实现对需要修改资料的学生的查询,并提供姓名和工号两种查询方式。 5) 修改职工资料:构造函数void Modify(Link l),实现职工对工号和姓名的修改。 6) 保存职工资料:构造函数void Save(Link l),实现资料修改,删除等功能之后的保存。 7) 整体浏览:构造函数void Disp(Link l),实现对全部职工的全部信息的浏览。 4.详细设计 4.1数据结构设计 (根据具体的题目
1基础题_2.由计算机生成简单的四则运算题 1.1 需求分析: 本题主要是要求设计一个可以自动生成四则运算的测试器,并且完全由用户决定出加、减、乘、除哪一种运算题,以及出一位数还是两位数的运算题,同时还要对用户给出的答案的对错进行判断。在程序运行过程中,用户可以选择何时结束程序,并在结束程序时给出一个某种形式的成绩。 ///////////////////////////////////////////// 程序执行的结果://///////////////////////////////////////////////// 1.2 概要设计: 在对题目理解的基础上,并针对几个特别的技术环节,我认为程序可分为三个部分: 1) 程序的欢迎界面,主要通过一些特殊制表符来完成。其中运行,退出程序可以通过一个while循环来判定同时还要考虑用户输入信号量的正误; 2) 出题函数,也是本程序最关键的一个函数,通过使用“rand()%10”或“rand()%100”来获得一个0到9的一位整数随机值或得到0到99的两位整数随机值来为用户出题,并判断用户答案的对错; 3) 评分系统,是在用户选择退出后对用户所答题情况给出的成绩评价。 /////////////////////////////////////////////////// 程序流程图: 1.3 详细设计与编码: 为了使程序更加简洁与工整,且容易修改和阅读,我采用头文件的方式将Exam()函数放在了Exam .h中。Exam()函数主要负责程序的出题和结果的判断,其输入接口为运算符号,位数,即只需向其输入四则运算的一种符号和运算的位数,函数便自动生成题目并自动判断结果的正误,结果以1,0返回。而主程序则是完成了程序的开始、结束,用户成绩的判定。 /////////////////////////////////////////////////// 具体源程序如下: ---------------------------------------------------------------------------------------------------------------------- int Exam(int figure, int sign) {//本函数负责给用户出题 if (figure!=1&&figure!=2&&sign<1&&sign>4) return 0; //判断函数的输入是否符合要求 int a, b; if (figure==1) a=rand()%10; b=rand()%10; if (figure==2) a=rand()%100; b=rand()%100; switch(sign) { case(1): { cout<<" "<<a<<'+'<<b<<'='; int r; cin>>r; if(r!=a+b) { cout<<" "<<"╳ 很遗憾,回答错误! X﹏X "<<endl; return -1; } else { cout<<" "<<"√ 恭喜你回答正确!↘(^ω^)↙"<<endl; return 1; } } case(2): { cout<<" "<<a<<'-'<<b<<'='; int r; cin>>r; if(r!=a-b) { cout<<" "<<"╳ 很遗憾,回答错误! X﹏X "<<endl; return -1; } else { cout<<" "<<"√ 恭喜你回答正确!↘(^ω^)↙"<<endl; return 1; } } case(3): { cout<<" "<<a<<'*'<<b<<'='; int r; cin>>r; if(r!=a*b) { cout<<" "<<"╳ 很遗憾,回答错误! X﹏X "<<endl; return -1; } else { cout<<" "<<"√ 恭喜你回答正确!↘(^ω^)↙"<<endl; return 1; } } case(4): { while(b==0) b=rand()%10; cout<<" "<<a<<'/'<<b<<'='; int r; cin>>r; if(r!=a/b) { cout<<" "<<"╳很遗憾,回答错误! X﹏X "<<endl; return -1; } else { cout<<" "<<"√ 恭喜你回答正确!↘(^ω^)↙"<<endl; return 1; } } } return 0; } ---------------------------------------------------------------------------------------------------------------------- main() 见上传程序 Arithmetic . cpp文件 1.4 调试分析: 为了使程序的欢迎界面更加的友好,我采用了制表符来美化程序界面,让整个程序不会显得那么的单调和无趣。在调试过程中,为了使界面好看确实下了很大的功夫。 在调试过程中程序使用"rand()%10"或"rand()%100" 获得的随机数往往不随机,即获得的随机数不变,经过查阅资料和详细分析,原来它需要初始化。 1.5 用户使用说明: 友好的程序界面给人一种亲切感,整个程序使用起来并不是十分的复杂,只需要根据每步的提示进行即可。至于在进行除法运算时,面对无法除尽的数用户只需要保留小数点后一位数字即可。 1.6 设计心得: 设计制作类似的程序已经不是第一次了,但这次却是比以前各次都下了大功夫。虽然整个题目并不是很难,出题函数也比较容易实现,但我抓住了程序界面的设计,让整个程序变得友好而吸引人。然原本枯燥的四则运算测试变得有意思。而且通过这次的编程,使我对设计一个程序的步骤更加的熟悉,为后面的加强题打下了坚实的基础。 运行程序后,看到自己完全独立设计制作的程序,一种无比的自豪感油然而生,内心掩饰不住的喜悦。特别是听到在同学们看到我的程序界面时发出的感叹声时,心里也是无比的开心。 2基础题_6.钱币兑换问题(贪心算法) 2.1 需求分析: 本题主要是要求设计一个程序,让用户输入正整数m ,它代表一个人民币钱数(元数),由程序计算一个最有方法,使人民币纸币的张数最少,并凑成上述的钱数m 。 ///////////////////////////////////////////// 程序执行的结果://///////////////////////////////////////////////// 2.2 概要设计: 在对题目理解的基础上,以及题目中所给出的要求,我认为此问题最好还是选用贪心算法会比较方便。而且由于此问题也恰巧是贪心算法的最典型应用,即所求问题的整体最优解可以通过一系列局部最优的选择来达到。 /////////////////////////////////////////////////// 程序流程图: 2.3 详细设计与编码: 根据上面的流程图可以看到如果是一步一步的写程序,势必会让程序变得冗长且不易阅读,因而我想到使用循环的方法,将流程图中类似的结构体做成一个循环体来实现,使程序源代码变得十分的简洁,且容易被阅读和修改。 /////////////////////////////////////////////////// 具体源程序如下: ---------------------------------------------------------------------------------------------------------------------- void main() { cout<<"请输入总钱数 m"; int m=0; cin>>m; int N[7]={0,0,0,0,0,0,0}; //用来存储每种面值纸币所需的数目 int Money[7]={100,50,20,10,5,2,1}; //存放7种纸币的面值 for (int i=0; i<7; i++) { while (m>=Money[i]) { m-=Money[i]; N[i]++; } } cout<<"最少使用"<<endl; for (i=0; i<7; i++) { cout<<"\t面值"<<setw(3)<<Money[i] <<"的纸币"<<N[i]<<"张"<<endl; } } ---------------------------------------------------------------------------------------------------------------------- 2.4 调试分析: 本程序的调试工作显得非常的简单,只需要对几个特殊的值进行检查就行了。程序输出也是十分的清晰,容易查错。这主要是由于问题并不是十分复杂,再加上贪心算法的简便性。使得整个程序浑然一体。 2.5 用户使用说明: 在程序运行后需要用户输入需要兑换的总钱数,回车后系统便自动将各种纸币所需要的张数输出来。供用户参考。且输出的结果是张数最少的情况。 2.6 设计心得: 钱币兑换问题是个非常简单的题目,完成本题所需的编程技巧并不多,但却巩固了我在算法设计与分析课上所学到的很多知识。特别是对于贪心算法,不但让我对其有了更进一步的了解,而且使我能够更好的掌握在分析问题时,首先对问题的解决算法进行分析的能力。 3基础题_9.约瑟环问题(使用数组存放人员编号) 3.1 需求分析: 本题主要是要求设计一种算法,使用数组来存放n个人,而后从1号人员开始报数(顺时针方向),当数到k时(其中k>1由用户通过cin输入指定),则该号人员被“淘汰出局”;接着仍沿顺时针方向从被淘汰出局者的下一人员又重新从1开始数起,数到k后,淘汰第2个人;如此继续,直到最后剩下一个人时停止。请输出最后所剩那一个人的编号,并输出淘汰过程的某种“中间结果数据”。 ///////////////////////////////////////////// 程序执行的结果://///////////////////////////////////////////////// 3.2 概要设计: 在对题目理解的基础上,以及题目中所给出的要求,我认为此问题可以通过模拟指针循环查找的方法来实现题目所给的限定。在淘汰人员时,我准备利用一个布尔数组来存放这n个人的状态(是否被淘汰),然后通过一个point"指针"对其进行循环查找。而另定义一个j变量来进行报数操作。不但可以实现在时下最后一个人时输出这个人的编号,还可以在每次淘汰人员时,输出被淘汰人员的编号。 /////////////////////////////////////////////////// 程序流程图: 3.3 详细设计与编码: 因为总人数是由用户所给定的,所以主函数内的所有涉及n的数组都需要使用动态数组来进行定义。整个报数环节在主函数中体现在一个while循环上,而跳出这个循环的条件便是对队列中人数的判断,即当队列中的人数只剩下一个时,跳出此循环。 /////////////////////////////////////////////////// 具体源程序如下: ---------------------------------------------------------------------------------------------------------------------- void main() { int n, k; cout<<"请输入总人数 n:"; cin>>n; cout<<"请输入报数的最大值 k:"; cin>>k; bool *p=new bool[n+1]; //布尔 p 数组用来存放 n 个人的状态 for (int i=1; i<=n; i++) p[i]=1;//1 状态在队列里 0 被淘汰了 int point=0, j=0, m=n; //point 编号指针 j 报数 m 队列里剩有的人数 while (true) { j++; point++; if (point==n+1) point=1; //point 指向 n+1 是转到1 while (p[point]==0) { point++; if (point==n+1) point=1; } if (j==k) //报数报到 k { p[point]=0; cout<<"编号为 "<<point<<" 的人被淘汰了。"<<endl; m--; j=0; } if (m==1) break; //队列只剩下1个人时跳出循环 } point=1; while (p[point]==0) point++; cout<<"最后剩下的人的编号为 "<<point<<" 。"<<endl; } ---------------------------------------------------------------------------------------------------------------------- 3.4 调试分析: 本程序的调试并不怎么复杂,但却有点麻烦。因为为了测得程序运行的正确性,在输入n之前,我总是要先把正确的答案计算出来,再对程序的结果进行比较。其中也难免自己计算错误的时候,况且n的值越大,其产生的结果也就越繁多,因而比较耗时。 在整体的调试下,本程序并没有出现错误,在对每步输出上也是比较完美的,达到了预先设定的效果。 3.5 用户使用说明: 在程序运行后需要用户输入排成一圈的总人数n,接着会让用户输入报数的最大值k,这两个数理论上在大小上没有限制,但输入的必须是整型正整数,否则系统会报错。在程序输出时,每一步所淘汰的成员编号都会被输出,因而用户可以清晰地看到整个的淘汰过程。并可由最后输出的结果知道最后会剩下哪个编号的成员。 3.6 设计心得: 约瑟环问题是一个比较老的问题了,在刚接触C++语言时就做过类似的题目,因而完成起来并没有太大难度。虽然在题目中对设计时所使用的数据结构进行了限制,但这并不是本题的难点,我认为关键在于超越自我,设计出与我过去编程时所设计出的程序有所不同。我觉得我做到了,这次的程序是我完全重新设计的,从算法到数据结构的使用均是独立完成,感到很有成就感。 4基础题_11.猜扑克牌问题 4.1 需求分析: 本题主要是要求设计一个程序,能让计算机来猜测用户“暗记”的某张扑克牌:计算机从一副扑克牌(54张)中任意抽出27张,摆放在不同的三行上(每行9张),用户“暗记”某张纸牌,而后告诉计算机所“暗记”的那张纸牌处于哪一行中;之后计算机再两次将纸牌重新摆放,并让用户再回答两次相同的提问(那张纸牌在重新摆放后又处在哪一行上);此时计算机会将用户所“暗记”的那张纸牌给挑出来。 ///////////////////////////////////////////// 程序执行的结果://///////////////////////////////////////////////// 4.2 概要设计: 在对题目理解的基础上,以及题目中所给出的要求,我认为此问题有以下两个重点: 1)要从一副54张的扑克牌中任意抽出27张,可通过“rand()%54”所产生的随机值来确定。但注意,一旦随机抽走哪张,下次牌中就没有这张了。 2)程序总按照一种策略将三行纸牌重新“摆放”,而后进一步让用户进行指定。上述所谓的策略指的是,总将纸牌“一分为三”:第一次要将每一行的9张分散到不同的3行上(每行仅“剩”3张),而第二次则要将上次“确定”出的某3张进一步分散到不同的3行上(每行只“剩”1张。此时靠用户再指定一次行号则可唯一确定所“暗记”的那张纸牌)。 /////////////////////////////////////////////////// 程序流程图: 4.3 详细设计与编码: 题目中有很多地方都涉及到无序排列的问题,再加上一旦随机抽走哪张,下次牌中就没有这张了,而且在分组完成后数据的存放都是一个问题。所以我采用布尔变量数组来存放牌的状态(抽走了为0,未抽走为1)。而使用string数组来存放每一张牌。在输出的问题上,我将无须输出和有序输出函数单独做成一个头文件:OutWay.h ,以方便函数的调用于阅读。为解决题目的最终问题,在流程图中的交换操作需要下一些功夫才能很好的完成。 /////////////////////////////////////////////////// 具体源程序如下: ---------------------------------------------------------------------------------------------------------------------- void main() { srand(GetTickCount()); //使rand()函数每次所取得随机数不同 string squeezer[54]={"\3- A","\3- 2","\3- 3","\3- 4","\3- 5","\3- 6","\3- 7","\3- 8","\3- 9", "\3-10","\3- J","\3- Q","\3- K","\4- A","\4- 2","\4- 3","\4- 4","\4- 5", "\4- 6","\4- 7","\4- 8","\4- 9","\4-10","\4- J","\4- Q","\4- K","\5- A", "\5- 2","\5- 3","\5- 4","\5- 5","\5- 6","\5- 7","\5- 8","\5- 9","\5-10", "\5- J","\5- Q","\5- K","\6- A","\6- 2","\6- 3","\6- 4","\6- 5","\6- 6", "\6- 7","\6- 8","\6- 9","\6-10","\6- J","\6- Q","\6- K","KING1","KING2"}; //顺序列出54张牌 cout<<"The 54 cards aring in proper order is arranged as follows: "<<endl; Order_Out(squeezer, 54); string draw[27]; //存放随机抽取出来的27张牌 string line1[9], line2[9], line3[9]; //分别存放从27张牌中抽取出的9张牌 bool a1[54], a2[27]; //状态参数,a1用来存放第一次随机抽取时54张牌的状态 //a2用来存放第二次随机抽取时27张牌的状态 for (int i=0; i<54; i++) a1[i]=true;//状态初始化 for (i=0; i<27; i++) a2[i]=true;//状态初始化 int b; //用来产生随机数 for (i=0; i<27; i++) { b=rand()%54; //产生0~53之间的随机数 while(a1[b]==false) b=rand()%54; //产生0~53之间的随机数 a1[b]=false; draw[i]=squeezer[b]; } cout<<"The 27 cards sampled are arranged as follows: "<<endl; Order_Out(draw, 27); //顺序列出抽取出的27张牌 for (i=0; i<9; i++) { b=rand()%27; //产生0~27之间的随机数 while(a2[b]==false) b=rand()%27; //产生0~27之间的随机数 a2[b]=false; line1[i]=draw[b]; } for (i=0; i<9; i++) { b=rand()%27; //产生0~27之间的随机数 while(a2[b]==false) b=rand()%27; //产生0~27之间的随机数 a2[b]=false; line2[i]=draw[b]; } for (i=0; i<9; i++) { b=rand()%27; //产生0~27之间的随机数 while(a2[b]==false) b=rand()%27; //产生0~27之间的随机数 a2[b]=false; line3[i]=draw[b]; } cout<<"-------------------------------------"<<endl <<"line1:"; Orderless_Out(line1, 9); //无序输出line1 cout<<"line2:"; Orderless_Out(line2, 9); //无序输出line2 cout<<"line3:"; Orderless_Out(line3, 9); //无序输出line3 cout<<"-------------------------------------"<<endl <<"Remember a card, and tell me what line it reside in (1/2/3): "; int c; //用户输入的信号量 cin>>c; while (c<1||c>3) { cout<<"Sorry, your importation contain mistake, please reinput: "; cin>>c; } switch(c) {//将用户选出的一行分成3份,分布到每一行 case 1: { for (i=3; i<6; i++) swap(line1[i], line2[i]); for (i=6; i<9; i++) swap(line1[i], line3[i]); break; } case 2: { for (i=0; i<3; i++) swap(line2[i], line1[i]); for (i=6; i<9; i++) swap(line2[i], line3[i]); break; } case 3: { for (i=0; i<3; i++) swap(line3[i], line1[i]); for (i=3; i<6; i++) swap(line3[i], line2[i]); break; } } cout<<"-------------------------------------"<<endl <<"line1:"; Orderless_Out(line1, 9); //无序输出line1 cout<<"line2:"; Orderless_Out(line2, 9); //无序输出line2 cout<<"line3:"; Orderless_Out(line3, 9); //无序输出line3 cout<<"-------------------------------------"<<endl <<"What line the card you remenbered reside in now (1/2/3): "; cin>>c; while (c<1||c>3) { cout<<"Sorry, your importation contain mistake, please reinput: "; cin>>c; } switch(c) {//将用户选出的一行中的3个可能值,分布到每一行的首位 case 1: { swap(line1[1], line2[0]); swap(line1[2], line3[0]); break; } case 2: { swap(line2[3], line1[0]); swap(line2[4], line2[0]); swap(line2[5], line3[0]); break; } case 3: { swap(line3[6], line1[0]); swap(line3[7], line2[0]); swap(line3[8], line3[0]); break; } } cout<<"-------------------------------------"<<endl <<"line1:"; Orderless_Out(line1, 9); //无序输出line1 cout<<"line2:"; Orderless_Out(line2, 9); //无序输出line2 cout<<"line3:"; Orderless_Out(line3, 9); //无序输出line3 cout<<"-------------------------------------"<<endl <<"What line the card you remenbered reside in now (1/2/3): "; cin>>c; while (c<1||c>3) { cout<<"Sorry, your importation contain mistake, please reinput: "; cin>>c; } //用户所输入的行数的第一张牌即用户一开始就“暗记”的牌 if (c==1) cout<<"Your remenber card is: "<<line1[0]<<endl; else if (c==2) cout<<"Your remenber card is: "<<line2[0]<<endl; else cout<<"Your remenber card is: "<<line3[0]<<endl; } ---------------------------------------------------------------------------------------------------------------------- 其余函数请查看头文件OutWay.h ---------------------------------------------------------------------------------------------------------------------- 4.4 调试分析: 本程序是一个比较有意思的程序,在调试的过程中,确认结果比较容易,但要想完全检测程序是否有误,还是比较繁的。不过整个程序调试下来,还是挺好玩的,看到自己心里“暗记”的牌最后可以被计算机找出来。不过我的程序还有很大的改进空间,如将switch语句进行调整,或合并成一个大循环等。 4.5 用户使用说明: 在程序运行后需要用户根据程序的提示,在抽出的27张牌中“暗记”一张牌,然后3次回答程序“暗记”的那张牌的行数,程序便能够自动将用户所“暗记”的牌显示出来。 4.6 设计心得: 这一题是这么多题目中最有意思的一道题,向变魔术一般,将用户“暗记”的牌展示出来。整个程序还是比较复杂的,特别是我在设计输出函数时,无序排列确实让我费了一番周折。而且在这3个switch语句的case语句中的交换也是比较容易弄混的,我就在编程序是忘了在case语句后加上break而导致程序一直调试不成功,好不容易才改正过来。 5基础题_13. 找出整数的前第n位和后第n位 5.1 需求分析: 本题主要是要求设计具有如下原型的函数:int f(unsigned long x, int n, int& Lxn); 它负责将整数x的第n位(从左边数第n位,n>0)的数值放到引用Lxn之中(将作为结果返回到主调函数的对应实参变量中),并将倒数第n位(从右边数第n位,n>0)的数值作为函数结果返回去。并编制主函数对它进行调用以验证其正确性。 ///////////////////////////////////////////// 程序执行的结果://///////////////////////////////////////////////// 5.2 概要设计: 在对题目理解的基础上,以及题目中所给出的要求,我认为此问题有以下两个重点: 1)将用户输入的这个长整型的数按位存储在一个数组里; 2)通过循环输出这个数组的第n-1项和倒数第n-1项。 /////////////////////////////////////////////////// 程序流程图: 5.3 详细设计与编码: 当x=123456789,n=7时,执行语句“Rxn=f(x, n, Lxn);”将使返回的Lxn为7,并使Rxn变为3;而执行语句“Rxn=f(12345, 6, Lxn);”将使Lxn与Rxn都变为为0(超出数的“长度”即总位数时返回0)。 /////////////////////////////////////////////////// 具体源程序如下: ---------------------------------------------------------------------------------------------------------------------- int f(unsigned long x, int n, int&Lxn) {//题目所要求的函数f int N=1; //用来记录无符长整数x的位数 unsigned long IM=x/10; while (IM!=0) { IM=IM/10; N++; } if (n>N) {//用来判断n是否超出x的长度 Lxn=0; return 0; } int *p=new int[N+1]; //用来存放x的每一位数 for (int i=N; i>0; i--) {//将x的每一位数字倒序的输入到数组p中 int j=pow(10,(N-i+1)); p[i]=(x%j-x%(j/10))/(j/10); } Lxn=p[n]; //返回引用Lxn的值 return p[N-n+1]; //函数返回的值 } ---------------------------------------------------------------------------------------------------------------------- main()函数见test_Main .cpp ---------------------------------------------------------------------------------------------------------------------- 5.4 调试分析: 本程序在调试的过程中,可能会遇到由于长整型数位数的限制,用户如果输入的x太大的话便会产生程序的溢出错误,造成程序进入死循环。所以在输入时,x的大小因有所限制。 5.5 用户使用说明: 在程序运行后需要用户根据程序的提示输入一个长整型的x,在输入要显示的位数n即可让程序将x的第n位和从后数第n位显示出来。 5.6 设计心得: 这道题重点便是怎样将用户输入的长整型数分位存储,当然,我们也可以选用getchar()的方式,但我个人认为还是使用数组来通过取余的方式将x的每一位村与数组中比较好。 在完成程序后,调试是发现了很多的错误,其中取余的地方就一直通不过,在反复调试后才最终将程序改好。通过这次编程使我懂得即使是非常有把握的程序,如果不细心的话,一样会碰钉子。 6基础题_14.整数组前n项是否按降序排列 6.1 需求分析: 本题主要是要求编出两种算法,一种递归一种非递归两种函数f,负责判断数组a的前n个元素是否从大到小完全有序了,是则返回true,否则返回false。 ///////////////////////////////////////////// 程序执行的结果://///////////////////////////////////////////////// 6.2 概要设计: 1)非递归函数中只需逐对地判断各a[i] 与a[i+1]是否都已从大到小有序排列(i = 0,1,…,n-2)。 2)递归函数中将问题分解处理为:若n=1(即只有1个元素时)则返回true而递归出口;n>1时,若最后一对元素不顺序则返回false,否则进行递归调用(传去实参a与 n-1,去判断前n-1个元素的顺序性),并返回递归调用的结果(与前n-1个元素的是否顺序性相同)。 /////////////////////////////////////////////////// 程序流程图:

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值