c语言进阶6-指针

我与影子孤独终老i 提交于 2020-02-22 02:43:27

指针是c语言的一个重要组成部分

是c语言的核心、精髓所在,用好指针可以在c语言编程中起到事半功倍的效果。一方面,可以提高程序的编译效率和执行速度以及实现动态的存储分配;另一方面,使用指针可使程序更灵活,全球表示各种数据结构,编写高质量的程序。

指针是c语言显著的优点之一,其使用起来十分灵活而且能提高某些程序的效率,但是如果使用不当则很容易造成系统错误。许多程序“挂死“往往都是由于错误地使用指针造成的

一、       地址与指针

系统的内存就好比是带有编号的小房间,如果想使用内存就需要得到房间编号。图1定义了一个整型变量i,整型变量需要4个字节,所以编译器为变量i分配的编号为1000~1003.

什么是地址?地址就是内存区中对每个字节的编号,如图1所示的1000/1001/1002和1003就是地址,为了进一步说明来看图2.

内存地址

内容

 

1000

0

变量i

1004

1

变量j

1008

2

 

1012

3

 

1016

4

 

1020

5

 

 

图2所示的1000、1004等就是内存单元的地址,而0、1就是内存单元的内容,换种说法就是基本整型变量i在内存中的地址从1000开始。因为基本整型占4个字节,所以变量j在内存中的起始地址为1004,变量i的内容是0.

那么指针又是什么呢?这里仅将指针看作是内存中的一个地址,多数情况下,这个地址就是内存中另一个变量的位置,如图3所示。

 

在程序中定义了一个变量,在进行编译时就会给该变量在内存中分配一个地址,通过访问这个地址可以找到所需的变量,这个变量的地址称为该变量的“指针”。图3所示的地址1000是变量i的指针。

 

二、       变量与指针


变量的地址是变量和指针二者之间连接的纽带,如果一个变量包含了另一个变量的地址,则可以理解成第一个变量指向第二个变量。
所谓“指向”就是通过地址来体现的,因为指针变量是指向一个变量的地址,所以讲一个变量的地址赋给这个指针变量后,这个指针变量就“指向”了该变量。例如,将变量i的地址存放到指针变量p中,p就指向i

在程序代码中是通过变量名对内存单元进行存取操作的,但是代码经过编译后已经将变量名转换为该变量在内存中的存放地址,对变量值的存取都是通过地址进行的。如对图2所示的变量i和变量j进行如下操作;

其含义时:根据变量名与地址的对应关系,找到变量i的地址1000,然后从1000开始读取4个字节数据放到cpu寄存器中,再找到变量j的地址1004,从1004开始读取4个字节的数据放到cpu的另一个寄存器中,通过cpu的加法中断计算出结果。

在低级语言的汇编语言中都是直接通过地址来方位内存单元的,在高级语言中一般使用变量名访问内存单元,但c语言作为高级语言提供了通过地址来访问内存单元的方式。

一、       指针变量

由于通过地址能访问指定的内存存储单元,可以说地址“指向”该内存单元。地址可以形象地称为指针,意思是通过指针能找到内存单元。一个变量的地址称为该变量的指针。如果一个变量专门用来存放另一个变量的地址,它就是指针变量。在c语言中有专门用来存放内存单元地址的变量类型,即指针类型。下面将针对如何定义一个指针变量,如何为一个指针变量赋值及如何引用指针变量这3方面内容加以介绍。

  1. 1.   指针变量的一般形式

如果一个变量专门用来存放另一变量的地址,则它成为指针变量。图4所示的p就是一个指针变量。如果一个变量包含指针(指针等同于一个变量的地址),则必须对它进行说明。定义指针变量的一般形式如下;

类型说明  *变量名

其中“*”表示该变量是一个指针变量,变量名为定义的指针变量名,类型说明表示本指针变量所指向的变量的数据类型。

  1. 2.   指针变量的赋值

指针变量同普通变量一样,使用之前不仅需要定义,而且必须赋予具体的值,未经赋值的指针变量不能使用。给指针变量所赋的值与给其他变量所赋的值不同,给指针变量的赋值只能赋予地址,而不能赋予任何其他数据,否则将引起错误。c语言中提供了地址运算符&来表示变量的地址。其一般形式为:

&变量名;

如&a表示变量a的地址,&b表示变量b的地址。给一个指针变量赋值可以有以下两种方法。

(1)      定义指针变量的同时就进行赋值,例如:

int  a;

int *p=&a;

(2)      先定义指针变量之后再赋值,例如:

int  a;

int *p;

p=&a;

从键盘中输入两个数,利用指针的方法将这两个数输出

#include "stdio.h"

void main()

{

    int a,b;

    int *ipointer1,*ipointer2;  /*声明两个指针变量*/

    scanf("%d,%d",&a,&b);   /*输入两个数*/

    ipointer1=&a;

    ipointer2=&b;   /*将地址赋给指针变量*/

    printf("the number is :%d,%d\n",*ipointer1,*ipointer2);

}

通过实例1可以发现程序中采用的赋值方式是上述第二种方法,即先定义再赋值。

这里强调一点,即不允许把一个数赋予指针变量,例如:

int *p;

p=1002;

这样写是错误的。

  1. 3.   指针变量的引用

 引用指针变量是对变量进行间接访问的一种形式。对指针变量的引用形式如下;

*指针变量

其含义时引用指针变量所指向的值

利用指针变量实现数据的输入和输出。

#include "stdio.h"

void main()

{

    int *p,q;

    printf("please input:\n");

    scanf("%d",&q);   /*输入一个整型数据*/

    p=&q;

    printf("the number is:\n");

    printf("%d\n",*p);  /*输出变量的值*/

}

可将上述程序修改成如下形式;

#include "stdio.h"

void main()

{

    int *p,q;

    p=&q;

    printf("please input:\n");

    scanf("%d",p);   /*输入一个整型数据*/   

    printf("the number is:\n");

    printf("%d\n",*p);  /*输出变量的值*/

}

 

  1. 4.   “&”和“*”运算符

 在前面介绍指针变量的过程中用到了“&”和“*”两个运算符,运算符&是一个返回操作数地址的单目运算符,叫做取地址运算符,例如:

p=&i;

就是将变量i的内存地址赋给p,这个地址是该变量在计算机内部的存储位置。

运算符“*”是单目运算符,叫做指针运算符,作用是返回指定的地址内的变量的值。如前面提到过p中装有变量i的内存地址,则

q=*p;

就是将变量i的值赋给q,假如变量i的值是5,则q的值也是5.

 

  1. 5.   “&” 和“&”的区别

如果有如下语句:

int a;

p=&a;

 下面通过以上两条语句来分析“&”和“&”的区别,&”和“*”的运算符优先级别相同,按自右向左的方向结合。因此“&*p先进行“*”运算,“*p”相当于变量a;再进行“&”运算,“&*p就相当于取变量a的地址。“*&a”先进行“&”运算,“&a”就是取变量a的地址,然后执行“*”运算,“*&a”就相当于取变量a所在地址的值,实际就是变量a

二、       指针自加自减运算

指针自加自减运算内不同于普通变量的自加自减运算,也就是说并非简单地加1减1,这里面通过下面的实例进行具体分析。

ACM【例5】整型变量地址输出

#include "stdio.h"

void main()

{

    int i;

    int *p;

    printf("please input the number:\n");

    scanf("%d",&i);  

    p=&i;      /*将变量i的地址赋给指针变量*/   

    printf("the result1 is:%d\n",p);

    p++;      /* 地址加1,这里的1并不代表一个字节*/

    printf("the result2 is:%d\n",p);

}

若将实例5改成:

#include "stdio.h"

void main()

{

    short i;

    short *p;

    printf("please input the number:\n");

    scanf("%d",&i);  

    p=&i;      /*将变量i的地址赋给指针变量*/   

    printf("the result1 is:%d\n",p);

    p++;      /* 地址加1,这里的1并不代表一个字节*/

    printf("the result2 is:%d\n",p);

}

基本整型变量i在内存中占4个字节,指针p是指向变量i的地址的,这里的p++不是简单地在地址上加1,而是指向下一个存放基本整型数的地址。图9所示的结果是因为变量i是基本整型,所以执行p++后,p的值增加4(4个字节);图10所示的结果是因为i被定义成了短整型,所以执行p++后,p的值增加了2(两个字节)。

指针都按照它所指向的数据包类型的直接长度进行增或减。

三、       数组与指针

系统须要提供一定量连续的内存来存储数组中的各元素,内存都有地址,指针变量就是存放地址的变量,如果数组的地址赋给指针变量,就可以通过指针变量引用数组。下面就介绍如何用指针来引用一对数组元素。

一维数组与指针

当定义一个一对数组时,系统会在内存中为该数组分配一个存储空间,其数组的名称就是数组在内存中的首地址。若再定义一个指针变量,并将数组的首地址传给指针变量,则该指针就指向了这个一维数组。

例如:

int *p,a[10];

p=a;

这里a是数组名,也就是数组的首地址,将它赋给指针变量p,也就是将数组a的首地址赋给p,也可以写成如下形式;

int *p,a[10];

p=&a[0];

上面的语句是讲数组a中的首个元素的地址赋给指针变量p。由于a[0]的地址就是数组的首地址,因此两条赋值操作效果完全相同,如实例6所示。

ACM【例 6】 输入数组中的元素。

#include "stdio.h"

void main()
{
    int *p,*q,a[5],b[5];
    int i;
    p=&a[0];
    q=b;
    printf("please input array a:\n");
    for(i=0;i<5;i++)
    scanf("%d",&a[i]);
    printf("please input array b:\n");
    for(i=0;i<5;i++)
    scanf("%d",&b[i]);
    printf("array a is :\n");
    for(i=0;i<5;i++)
    printf("%5d",*(p+i));
    printf("\n");
    printf("array b is :\n");
    for(i=0;i<5;i++)
    printf("%5d",*(q+i));
    printf("\n");
}

实例6中有如下两条语句:

p=&a[0];

q=b;

分别表示输出数组a和数组b中对应的元素。

这两种表示方法都是讲数组首地址赋给指针变量。

那么如何通过指针的方式来引用一对数组中的元素呢?有以下语句:

int *p,a[5];

p=&a;

针对上面的语句将通过以下几方面进行介绍

p+n与a+n表示数组元素a[n]的地址,即&a[n]。对整个a数组来说,共有5个元素,n的取值为0~4,则数组元素的地址就可以表示为p+0~p+4或a+0~a+4。

表示数组中的元素用到了前面介绍的数组元素的地址,用*(p+n)和*(a+n)来表示数组中的个元素。

实例6中的语句:

printf("%5d",*(p+i));

和语句:

printf("%5d",*(q+i));

分别表示输出数组a和数组b中对应的元素。

实例6中使用指针指向一对数组及通过指针引用数组元素的过程可以通过图13和图14来表示。

 

前面提到可以用a+n表示数组元素的地址,*(a+n)表示数组元素,那么就可以将实例6的程序代码改成如下形式:

#include "stdio.h"

void main()

{

    int *p,*q,a[5],b[5];

    int i;

    p=&a[0];

    q=b;

    printf("please input array a:\n");

    for(i=0;i<5;i++)

    scanf("%d",&a[i]);

    printf("please input array b:\n");

    for(i=0;i<5;i++)

    scanf("%d",&b[i]);

    printf("array a is :\n");

    for(i=0;i<5;i++)

    printf("%5d",*(a+i));

    printf("\n");

    printf("array b is :\n");

    for(i=0;i<5;i++)

    printf("%5d",*(b+i));

    printf("\n");

}

程序运行的结果与实例6的运行结果一样。

表示指针的移动可以使用“++”和“——”这两个运算符

利用“++”运算符可将程序改写成如下形式。

#include "stdio.h"

void main()

{

    int *p,*q,a[5],b[5];

    int i;

    p=&a[0];

    q=b;

    printf("please input array a:\n");

    for(i=0;i<5;i++)

    scanf("%d",&a[i]);

    printf("please input array b:\n");

    for(i=0;i<5;i++)

    scanf("%d",&b[i]);

    printf("array a is :\n");

    for(i=0;i<5;i++)

    printf("%5d",*p++);
    printf("\n");
    printf("array b is :\n");
    for(i=0;i<5;i++)
    printf("%5d",*q++);
    printf("\n");
}

 

还可将上面程序再进一步改写,运行结果仍与实例6的运行结果相同,改写后的程序代码如下:

#include "stdio.h"

void main()
{
    int *p,*q,a[5],b[5];
    int i;
    p=&a[0];
    q=b;
    printf("please input array a:\n");
    for(i=0;i<5;i++)
    scanf("%d",p++);
    printf("please input array b:\n");
    for(i=0;i<5;i++)
    scanf("%d",q++);
    printf("array a is :\n");
    for(i=0;i<5;i++)
    printf("%5d",*p++);
    printf("\n");
    printf("array b is :\n");
    for(i=0;i<5;i++)
    printf("%5d",*q++);
    printf("\n");
}

 

比较上面两个程序会发现,如果在给数组元素赋值时使用了如下语句:

  printf("please input array a:\n");

    for(i=0;i<5;i++)

    scanf("%d",p++);

    printf("please input array b:\n");

    for(i=0;i<5;i++)

    scanf("%d",q++);

而且在输出数组元素时需要使用指针变量,则需加上如下语句:

p=a;

q=b;

这两个语句的作用是讲指针变量p和q重新指向数组a和数组b在内存中的起始位置,若没有该语句,而直接使用*p++的方法进行输出,则此时将会产生错误。

四、       字符串与指针

访问一个字符中可以通过两种方式,第一种方式就是前面讲过的使用字符数组来存放一个字符串,从而实现对字符串的操作;另一种方式就是下面将要介绍的使用字符指针指向一个字符串,此时可不定义数组。

ACM【例 10】 字符型指针引用。

#include "stdio.h"

void main()

{

    char *string="hello word";

    printf("%s",string);

}

程序运行结果如图19所示。

实例10中定义了字符型指针变量string,用字符串敞亮“hello mingri”为其赋初值,注意这里并不是把“hello mingri”中的所有字符存放到string中,只是把该字符串中的第一个字符的地址赋给指针变量string,如图20所示。

char *string="hello word";

等价于下面两条语句:

char *string;

string="hello word";

ACM【例 11】  输入两个字符串a和b,将字符串a和b连接起来。

#include "stdio.h"

#include "string.h"

void main()

{

    char str1[]="you are beautiful",str2[30],*p1,*p2;

    p1=str1;

    p2=str2;

    while(*p1!='\0')

    {

        *p2=*p1;

        p1++;      /*指针移动*/

        p2++;

    }

    *p2='\0';      /*在字符串的结尾加结束符*/

    printf("now the string2 is:\n");

    puts(str1);    /*输出字符串*/

}

程序运行结果如图21所示。

实例11中定义了两个指向字符型数据的指针变量。首先让p1和p2分别指向字符串a和字符串b的第一个字符的地址。将p1所指向的内容赋给p2所指向的元素,然后p1和p2分别加1,指向下一个元素,直到*p1的值为“\0”为止。

这里有一点需要注意,就是p1和p2的值是同步变化的,如图22所示。若p1处在p11的位置,p2就出在p21的位置:若p1处在p12的位置,p2就出在p2的位置。

 

五、       指向指针的指针

 

一个指针变量可以指向整型变量、实型变量、字符类型变量,当然也可以指向指针类型变量。当这种指针变量用于指向指针类型变量时,则称之为指向指针的指针变量。这种双重指针如图24所示。

整型变量i的地址就是&i,将其值传递给指针变量p1,则p1指向i:同事,将p1的地址&p1传递给p2,则p2指向p1.这里的p2就是前面讲到的指向指针变量的指针变量,即指针的指针。指向指针的指针变量定义如下:

类型标识符 **指针变量名;

例如:

int **p;

其含义为定义一个指针变量p,它指向另一个指针变量,该指针变量又指向一个基本整型变量。由于指针运算符*是自右至左结合,所以上述定义相当于:

int *(*p);

既然知道了如何定义指向指针的指针,那么可以将它形象地表示出来。

 

指向指针的指针应用过程这里就不做过多讲解了,也希望同学们可以利用业余时间,查询资料充实自己。

五子棋代码3

#include <conio.h>
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>



int ChessData[15][15] ={0};
int GuangbiaoData[2]={8,8};
int ChessStepData[255][2]={0};



void gotoxy(int x, int y) //指定y行,x列
{ 
 COORD c; 
 c.X=x-1; 
 c.Y=y-1; 
 SetConsoleCursorPosition (GetStdHandle(STD_OUTPUT_HANDLE), c); 
}


void GotoChess(int x,int y)
{
 x=3*x-2;y=2*y-1;
 gotoxy(x,y);
}

void color(short x)    //自定义函根据参数改变颜色 
{
    if(x>=0 && x<=15)//参数在0-15的范围颜色
        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), x);    //只有一个参数,改变字体颜色 
    else//默认的颜色白色
        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);
}

 

void Move(int MoveData) //输入参数为用户输入的方向(1表示上,2表示下,3表示左,4表示右)
{
 switch (MoveData)
 {
 case 1:GuangbiaoData[1]-=1;break;
 case 2:GuangbiaoData[1]+=1;break;
 case 3:GuangbiaoData[0]-=1;break;
 case 4:GuangbiaoData[0]+=1;break;
 default:printf("Move函数出错");
 }
 GotoChess(GuangbiaoData[0],GuangbiaoData[1]);
}


int Get(int *data) 
/*该函数的功能是用户的按键,
*并转化为01234567
(0表示输入错误,1表示上,2表示下,3表示左,
4表示右,5表示落子,6表示悔棋,7表示退出。)*/
{          //并返回输入的用户号码(共同键返回3),错误则返回0
 int temp;


B: temp=getch();
   if (temp==224) 
   {
    temp=getch();
    switch (temp) 
    {
    case 72:*data=1;break;
    case 80:*data=2;break;
       case 75:*data=3;break;
       case 77:*data=4;break;
       default:goto B;
    }
       return 2;
}
   else 
   {
    switch (temp)
    {
    case 'w':
    case 'W':*data=1;return 1;break;
    case 's':
       case 'S':*data=2;return 1;break;
       case 'a':
       case 'A':*data=3;return 1;break;
       case 'd':
       case 'D':*data=4;return 1;break;
       case 13 :*data=5;return 2;break;
       case 32 :*data=5;return 1;break;
       case 8 :*data=6;break;
       case 27 :*data=7;break;
       default:*data=0 ;return 0;break;
    }
    return 3;
   }
}
void MoveToEnd()
{
 gotoxy(1,30);
}
int LogicBeOut(int a,int b)
{
 if (a==-1||a==15||b==-1||b==15) return 1;
 else return 0;
}
int win(int v)
{
 int i=1,j=1,a=0,b=0;
 while (ChessData[a=GuangbiaoData[0]-i-1][b=GuangbiaoData[1]-i-1]==v*2&&!LogicBeOut(a,b)) i++;
 while (ChessData[a=GuangbiaoData[0]+j-1][b=GuangbiaoData[1]+j-1]==v*2&&!LogicBeOut(a,b)) j++;
 if (i+j-1>=5) return 1;
 i=1,j=1,a=0,b=0;
 while (ChessData[a=GuangbiaoData[0]+i-1][b=GuangbiaoData[1]-i-1]==v*2&&!LogicBeOut(a,b)) i++;
 while (ChessData[a=GuangbiaoData[0]-j-1][b=GuangbiaoData[1]+j-1]==v*2&&!LogicBeOut(a,b)) j++;
 if (i+j-1>=5) return 1;
 i=1,j=1,a=0,b=0;
 while (ChessData[a=GuangbiaoData[0]-i-1][b=GuangbiaoData[1]-1]==v*2&&!LogicBeOut(a,b)) i++;
 while (ChessData[a=GuangbiaoData[0]+j-1][b=GuangbiaoData[1]-1]==v*2&&!LogicBeOut(a,b)) j++;
 if (i+j-1>=5) return 1;
 i=1,j=1,a=0,b=0;
 while (ChessData[a=GuangbiaoData[0]-1][b=GuangbiaoData[1]-i-1]==v*2&&!LogicBeOut(a,b)) i++;
 while (ChessData[a=GuangbiaoData[0]-1][b=GuangbiaoData[1]+j-1]==v*2&&!LogicBeOut(a,b)) j++;
 if (i+j-1>=5) return 1;
 return 0;
}


void NewShow() //新棋局的开始
{
 int i,j;
 for (i=0;i<15;i++)
  for (j=0;j<15;j++)
   ChessData[i][j]=0;
  system("cls");
  for (i=1;i<=29;i++)
  {
   for (j=1;j<=43;j++)
    if(i%2==1) printf("-");
    else if (j%3==1) printf("|");
    else printf(" ");
    printf("\n");
  }
  GuangbiaoData[0]=8;GuangbiaoData[1]=8;
  MoveToEnd();
  printf("现在请用户1下棋         \n");
  printf("用户1使用 w,s,a,d移动光标,空格键落子\n");
  printf("用户2使用各方向键移动光标,回车键落子\n");
  printf("按下Backspace键悔棋,按下esc返回主菜单\n");
  GotoChess(8,8);
}


int BeOut(int data)
{
 int Xiuzheng[2],New[2];
 switch (data)
 {
 case 1:Xiuzheng[0]=0;Xiuzheng[1]=-1 ;break;
 case 2:Xiuzheng[0]=0;Xiuzheng[1]=1;break;
 case 3:Xiuzheng[0]=-1;Xiuzheng[1]=0;break;
 case 4:Xiuzheng[0]=1;Xiuzheng[1]=0 ;break;
 case 5:Xiuzheng[0]=0;Xiuzheng[1]=0 ;break;
 default:printf("BeOut函数出错");
 }
 New[0]=GuangbiaoData[0]+Xiuzheng[0];
 New[1]=GuangbiaoData[1]+Xiuzheng[1];
 if (New[0]>15||New[0]<1||New[1]>15||New[1]<1) return 1;
 else return 0; 
}


void UserChoose(int * choice)
{  
    int temp=0,i=0;
    system("cls");
    printf("_________________________________________________\n");
    printf("_________________________________________________\n");
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),3 | 8 | 224 | 64);
    printf("                1.单人游戏                       \n");
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),5 | 8 | 224 | 64);
    printf("                2.双人游戏                       \n");
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),6 | 8 | 224 | 64);
    printf("                3.退出游戏                       \n");
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),4 | 8 | 224 | 64);
    printf("                4.游戏帮助                       \n");
    printf("_________________________________________________\n");
    printf("_________________________________________________\n");
    printf("                                                 \n");
    printf("                开心五子棋                     \n");
    printf("                                                 \n");
    printf("                拷贝者:Tim                    \n");
    printf("_________________________________________________\n");
 do
 { 
  if ((temp=getch())==224) 
  { 
   temp=getch();
   if (temp==72&&i!=0)
   {
    gotoxy(34,3+i);
    printf(" ");
    i--;
    gotoxy(34,3+i);
    printf("$");
    gotoxy(0,0);
   }
   else if(temp==80&&i!=3)
   {
    gotoxy(34,3+i);
    printf(" ");
    i++;
    gotoxy(34,3+i);
    printf("$");
    gotoxy(0,0);
   }   
  }
  else if (temp==13) {*choice=i+1;return;}
  else if (temp==27) {*choice=3;return;}
  else if (temp=='1'||temp=='2'||temp=='3'||temp=='4') {*choice=temp-48;return;}
 }while(1);
}
int CannotDo(int v1,int v2,int MoveData,int choice) //第一个输入值为按键的用户号,第二个是本应该按键的用户号,第三个为按下键的对应值,第四个键代表游戏模式。
{
 if (v1==3) return 0; //如果用户输入的为共用按键,则CannotDo为假


 else if (v1==0) return 1;//如果用户输入错误,则CannotDo为真
 else if (v1!=v2&&choice==2) return 1; //如果不该此用户输入,而用户进行了输入,则CannotDo为真


 if (BeOut(MoveData)) return 1; //如果移动出边界则CannotDo为真
 return 0;
}


int CannotLuozi() //判断是否可以落子。
{
 if (ChessData[GuangbiaoData[0]-1][GuangbiaoData[1]-1])
  return 1;
 else return 0;
}



int luozi(int v)    //玩家v落子。
{ 
 int i;
 ChessData[GuangbiaoData[0]-1][GuangbiaoData[1]-1]+=v*2;
 if (v==1) printf("O");
 else if (v==2) printf("X");
 else printf("luozi函数出错");
 if (win(v)) {
     MoveToEnd();printf("玩家%d获得了胜利!         \n",v);
     for (i=1;i<=240;i++) printf(" ");
     GotoChess(GuangbiaoData[0],GuangbiaoData[1]);
     getch();
     return 1;
     }
 MoveToEnd();
 printf("现在请用户%d下棋             ",v%2+1);
 GotoChess(GuangbiaoData[0],GuangbiaoData[1]);
 return 0;
}


void HuiQi(int step) //输入的是当前的要悔的棋是第几步
{
 GuangbiaoData[0]=ChessStepData[step-1][0];
 GuangbiaoData[1]=ChessStepData[step-1][1];
 ChessData[GuangbiaoData[0]-1][GuangbiaoData[1]-1]=0;


 GotoChess(GuangbiaoData[0],GuangbiaoData[1]);
 printf("-");
 MoveToEnd();
 printf("现在请用户%d下棋         ",(step+1)%2+1);
 GotoChess(GuangbiaoData[0],GuangbiaoData[1]);
}


int DataGetAndChoose(int choice) 
{ 
 int MoveData=0,i=0,temp; //MoveData 0表示不可移动,1表示上,2表示下,3表示左,4表示右,5表示落子,6表示悔棋,7表示退出。
 while(1)
{
loop: while (temp=Get(&MoveData),CannotDo(temp,i%2+1,MoveData,choice));
   switch (MoveData)
   {
      case 1:
      case 2:
      case 3:
      case 4:Move(MoveData);break;
      case 7:return 0;
      case 6:
    if (i==0) {
    MoveToEnd();printf("现在无法悔棋                         ");
    GotoChess(GuangbiaoData[0],GuangbiaoData[1]);}
    else HuiQi(i--);
    break;
   case 5:
    if (CannotLuozi()) goto loop;
    if(luozi(i%2+1)) return 0;
    ChessStepData[i][0]=GuangbiaoData[0];
    ChessStepData[i][1]=GuangbiaoData[1];
    i++;
    break;
   default:printf("DataGetAndChoose函数出错");break;
   }
 }
 return 1;  
}


void ShowHelp()
{
 system("cls");
 printf("********************************************************************************");
    printf("********************************************************************************");
    printf("******          单人游戏供用户一个人自己与自己下棋研究棋局之用            ******");
    printf("******          双人游戏中,用户1使用wsad控制方向,按空格落子             ******");
    printf("******               用户2按方向键控制方向,回车键落子                    ******");
    printf("******                   游戏过程中按esc返回主菜单                        ******");
    printf("******                    游戏过程中退格键悔棋                            ******");
    printf("******        双人模式中某人下棋时,另一个用户无法控制光标与落子          ******");
    printf("******                                                                    ******"); 
    printf("******                             帮助                                   ******"); 
    printf("******                          按任意键返回                              ******"); 
    printf("********************************************************************************"); 
    printf("********************************************************************************");
    getch();
}



int main()
{
   int choice=0;
 /*  system ( "mode con cols=80 lines=28" );*/
   system("color E0");
   choose: UserChoose(&choice);
   if (choice<1||choice>4) goto choose;
   if (choice==3) {gotoxy(1,18);
   printf("\n谢谢您的使用,再见        "); getch();return 0;}
   if (choice==4) {ShowHelp(); goto choose;}
   NewShow();
   DataGetAndChoose(choice);
   main();
   return 0; 
}

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!