2006年09月28日

拷贝构造函数还是有点难的

拷贝构造函数:具有一般构造函数的所有特性,其形参是本类的对象的引用。

作用:使用一个已经存在的对象(由拷贝构造函数的参数指定的对象)去初始化一个新的同类的对象。
根据实际问题的需要定义特定的拷贝构造函数,以实现同类对象之间数据成员的传递。若没有定义类的拷贝构造函数,系统就会自动生成一个默认函数;

功能:把初始值对象的每个数据成员的值都复制到新建立的对象中,所得到的对象和原对象具有完全相同的数据成员,即完全相同的属性。

普通构造函数是在对象创建时被调用,而拷贝构造函数在以下三种情况下都会被调用:
①当用类的一个对象去初始化该类的另一个对象时。
例如:

②如果函数的形参是类的对象,调用函数时,进行形参和实参结合时。例如:

③如果函数的返回值是类的对象,函数执行完成返回调用者时。例如:

问题:拷贝构造函数与默认的拷贝构造函数功能一样,都是直接将原对象的数据成员值—一赋给新对象中对应的数据成员。这种情况下有必要编写拷贝构造函数吗?
的确,如果情况总是这样,就没有必要特意编写一个拷贝构造函数,用默认的就行。但是,当类的数据成员中有指针类型时,默认的拷贝构造函数实现的只能是浅拷贝,浅拷贝会带来数据安全方面的隐患。要实现正确的拷贝,也就是深拷贝,必须编写拷贝构造函数。

2006年09月26日

       MFC一直在听孙鑫的讲座,对MFC有了一点的认识,绝对是入门的认识,对于MFC最难的就是记住许多函数和类,孙鑫说经常用就会记住。

       要在view 里操作要添加DC,例如CClientDC dc(this),然后对对象dc进行各种操作。

反正很多,也不知道怎么讲,慢慢来““

2006年09月21日

经常有朋友问雷神这样的问题:我在视图画的图象或者文字,当窗口改变后为什么不见了?OnDraw()和OnPaint()两个都是解决上面的问题,有什么不同?

雷神在这里一并解答一下吧。OnDraw()和OnPaint()好象兄弟俩,因为它们的工作类似。

至于不见了的问题简单,因为当你的窗口改变后,会产生无效区域,这个无效的区域需要重画。一般Windows回发送两个消息WM_PAINT(通知客户区有变化)和WM_NCPAINT(通知非客户区有变化)。非客户区的重画系统自己搞定了,而客户区的重画需要我们自己来完成。这就需要OnDraw()或OnPaint()来重画窗口。

OnDraw()和OnPaint()有什么区别呢?首先:我们先要明确CView类派生自CWnd类。而OnPaint()是CWnd的类成员,同时负责响应WM_PAINT消息。OnDraw()是CVIEW的成员函数,并且没有响应消息的功能。这就是为什么你用VC成的程序代码时,在视图类只有OnDraw没有OnPaint的原因。

其次:我们在第《每天跟我学MFC》3的开始部分已经说到了。要想在屏幕上绘图或显示图形,首先需要建立设备环境DC。其实DC是一个数据结构,它包含输出设备(不单指你17寸的纯屏显示器,还包括打印机之类的输出设备)的绘图属性的描述。MFC提供了CPaintDC类和CWindwoDC类来实时的响应,而CPaintDC支持重画。

当视图变得无效时(包括大小的改变,移动,被遮盖等等),Windows 将 WM_PAINT 消息发送给它。该视图的 OnPaint 处理函数通过创建 CPaintDC 类的DC对象来响应该消息并调用视图的 OnDraw 成员函数。通常我们不必编写重写的 OnPaint 处理成员函数。

///CView默认的标准的重画函数void CView::OnPaint(){ CPaintDC dc(this); OnPreparDC(&dc); OnDraw(&dc); //调用了OnDraw}

既然OnPaint最后也要调用OnDraw,因此我们一般会在OnDraw函数中进行绘制。下面是一个典型的程序

///视图中的绘图代码首先检索指向文档的指针,然后通过DC进行绘图调用。void CMyView::OnDraw( CDC* pDC ){ CMyDoc* pDoc = GetDocument(); CString s = pDoc->GetData(); // Returns a CString CRect rect;

GetClientRect( &rect ); pDC->SetTextAlign( TA_BASELINE | TA_CENTER ); pDC->TextOut( rect.right / 2, rect.bottom / 2, s, s.GetLength() );}

最后:现在大家明白这哥俩之间的关系了吧。因此我们一般用OnPaint维护窗口的客户区(例如我们的窗口客户区加一个背景图片),用OnDraw维护视图的客户区(例如我们通过鼠标在视图中画图)。当然你也可以不按照上面规律来,只要达到目的并且没有问题,怎么干都成。

补充:我们还可以利用Invalidate(),ValidateRgn(),ValidateRect()函数强制的重画窗口,具体的请参考MSDN吧。正文完

2006年09月19日

今天和我好友看完了这部片子,总体感觉还是怪怪的,怎么是父亲强奸女儿呢!!

片子中的恶灵其实你女儿被强奸的时候死掉一半的灵魂,她反复提醒Besty记住(Remember)被强奸的事实,而且和她妈妈说你知道真相"you know the truth"。

鬼怪的事情可能是真的,但是父亲强奸女儿的事情完全是编剧虚构的,编剧简直太变态了,这种变态的情节都能想的出来,我不建议大家看这部片子““`

如果有人看了这部片子有不懂的可以和我交流““

2006年09月14日

#include <stdio.h>

struct student

{
  int no;
  char name[20];
  int chinese;
  int maths;
  int english;
  int ave;

}stu[20];

 

int i,length=0,n;

     input_stu()

{
  float a;
  printf("Input the number of student:");
  scanf("%d",&n);
  for(i=length;i<length+n;i++)
  {
   printf("the %d student’s no: ",i+1);      scanf("%

d",&stu.no); getchar();
   printf("the %d stdent’s name: ",i+1);     scanf("%

s",stu.name);
   printf("the %d stdent’s chinese: ",i+1);  scanf("%

d",&stu.chinese);
   printf("the %d stdent’s maths: ",i+1);    scanf("%

d",&stu.maths);
   printf("the %d stdent’s english: ",i+1);  scanf("%

d",&stu.english);
   stu.ave=(stu.chinese+stu.maths+stu.english)/3;
   printf("\n");
   }
   length=length+n;
   menu2();

}

         display_stu()

{
   textcolor(9);
   printf

("\tNo\tName\tChinese\tMaths\tEnglish\tAveage\n\n");

   for(i=0;i<length;i++)
   {
    printf("  %8d%10s%8d%8d%8d%

8d\n",stu.no,stu.name,stu.chinese,stu.maths,stu.english,

stu.ave);
   }
   printf("\nInput any key to return main menu…");
   getch();
   menu2();

}

          search_stu()

{
  int k;   char ch=’y';
  textcolor(2);
  while (ch==’y')
 {
  printf("Input the student’s No:");
  scanf("%d",&k);
  for(i=0;i<length;i++)
  {
    if(stu.no==k)
    { printf("the student’s record is:\n");
      printf

("\tNo\tName\tChinese\tMaths\tEnglish\tAveage\n\n");
      printf("  %8d%10s%8d%8d%8d%

8d\n",stu.no,stu.name,stu.chinese,stu.maths,stu.english,

stu.ave);
    }
  }
  printf("\nDo you want search again(y or n)?  ");
  getchar();
  scanf("%c",&ch);
 }
  printf("\nInput any key to return main menu…");
  getch();
  menu2();

}

         modify_stu()

{
  int m,k,pos;   char ch=’y';
  textcolor(3);
  while (ch==’y')
 {
  printf("Input the student’s No to modify:");
  scanf("%d",&k);
  for(i=0;i<length;i++)
  {
    if(stu.no==k)
    { printf("the student’s record is:\n");
      printf

("\tNo\tName\tChinese\tMaths\tEnglish\tAveage\n\n");
      printf("  %8d%10s%8d%8d%8d%

8d\n",stu.no,stu.name,stu.chinese,stu.maths,stu.english,

stu.ave);
      pos=i;
     }
  }
  printf("\n\nChoose you want to modify(1-5):\n");
  printf("\t1. No  2. Name  3. Chinese  4. Maths 

5.English\n");
  scanf("%d",&m);
  switch(m)
  {
   case 1: printf("Input new No:");      scanf("%

d",&stu[pos].no);      break;
   case 2: printf("Input new Name:");    scanf("%s",stu

[pos].name);     break;
   case 3: printf("Input new chinese:"); scanf("%

d",&stu[pos].chinese); break;
   case 4: printf("Input new maths:");   scanf("%

d",&stu[pos].maths);   break;
   case 5: printf("Input new english:"); scanf("%

d",&stu[pos].english); break;
  }
  stu[pos].ave=(stu[pos].chinese+stu[pos].maths+stu

[pos].english)/3;

  printf("\nDo you want modify again(y or n)?  ");
  getchar();
  scanf("%c",&ch);
 }
  printf("\nInput any key to return main menu…");
  getch();
  menu2();

}

         sort_stu()

{
  int j,t; char ch[20];
  textcolor(13);
  printf("\n\n\t\tthe sort by aveage!\n\n");
  for(i=1;i<length;i++)
 for(j=0;j<length-i;j++)
 if(stu[j].ave<stu[j+1].ave)
 {
   t=stu[j].no;      stu[j].no=stu[j+1].no;           

stu[j+1].no=t;
   strcpy(ch,stu[j].name); strcpy(stu[j].name,stu

[j+1].name); strcpy(stu[j+1].name,ch);
   t=stu[j].chinese; stu[j].chinese=stu[j+1].chinese; 

stu[j+1].chinese=t;
   t=stu[j].maths;   stu[j].maths=stu[j+1].maths;     

stu[j+1].maths=t;
   t=stu[j].english; stu[j].english=stu[j+1].english; 

stu[j+1].english=t;
   t=stu[j].ave;     stu[j].ave=stu[j+1].ave;         

stu[j+1].ave=t;
 }
  display_stu();

}

         menu1()

{
 clrscr();  printf("\n\n\n\n");
 textcolor(9);
 printf

("\t\t**********************************************\n")

;
 printf("\t\t**                                         

**\n");
 printf("\t\t**          Welcom to Use the System!      

**\n");
 printf("\t\t**               (C语言版)                 

 **\n");
 printf("\t\t**                                         

**\n");
 printf("\t\t**           编制:

www.DesignTide.com**\n");
 printf("\t\t**                                         

**\n");
 printf("\t\t**              Copyright    2003          

**\n");
 printf("\t\t**                                         

**\n");
 printf

("\t\t**********************************************\n\n

");
 printf("\t\t Input any key to enter…");
 getch();
 menu2();

}

         menu2()

{
 int i;
 clrscr();  printf("\n\n");
 textcolor(10);
 printf

("\t\t**********************************************\n")

;
 printf("\t\t**            Main   Menu                  

**\n");
 printf("\t\t**                                         

**\n");
 printf("\t\t**       1. Input Records                  

**\n");
 printf("\t\t**       2. Display all Records            

**\n");
 printf("\t\t**       3. Search                         

**\n");
 printf("\t\t**       4. Modify                         

**\n");
 printf("\t\t**       5. Sort                           

**\n");
 printf("\t\t**       6. Exit                           

**\n");
 printf("\t\t**                                         

**\n");
 printf

("\t\t**********************************************\n\n

");
 printf("Input your choose(1-6):");
 scanf("%d",&i);

 while (i<0 || i>6)
 { printf("Your choose is error! Input again!\n");
   printf("Input your choose(1-6):");
   scanf("%d",&i);
 }

   switch(i)
   {
     case 1:    input_stu();    break;
     case 2:    display_stu();  break;
     case 3:    search_stu();   break;
     case 4:    modify_stu();   break;
     case 5:    sort_stu();     break;
     case 6:    menu3();        exit();
    }

}

         menu3()

{
 clrscr();  printf("\n\n\n\n");
 textcolor(12);
 printf

("\t\t**********************************************\n")

;
 printf("\t\t**                                         

**\n");
 printf("\t\t**                  Thanks !               

**\n");
 printf("\t\t**                                         

**\n");
 printf("\t\t**              Copyright    2003          

**\n");
 printf("\t\t**                                         

**\n");
 printf

("\t\t**********************************************\n\n

");
 getch();

}

     main()

{
 textcolor(6);
 menu1();
 getch();

}

 

 

中文修改版:

 

#define N 4

/*—-学生信息管理系统:编制 www.designtide.com—–*/

{
 clrscr();  printf("\n\n\n\n");
 textcolor(9);
 printf

("\t\t**********************************************\n")

;
 printf("\t\t**                                         

**\n");
 printf("\t\t**         欢迎使用学生信息管理系统        

**\n");
 printf("\t\t**                                         

**\n");
 printf("\t\t**               (C语言版本)               

**\n");
 printf("\t\t**                                         

**\n");
 printf("\t\t**          编制: www.DesignTide.com 

**\n");
 printf("\t\t**                                         

**\n");
 printf("\t\t**           Copyright  @   2004           

**\n");
 printf("\t\t**                                         

**\n");
 printf

("\t\t**********************************************\n\n

");
 printf("\t\t 按任意键继续……");
 getch();
 menu2();

}

 

/*—————-定义相关变量———————*/

struct student

{ char name[10];
  int num;
  int score1;
  int score2;
  float ave;
  int class;

}stu[N];

 

/*————相关数据的处理—————*/

void setup_stu_info()

{ int i;
  for(i=0;i<N;i++)
  { printf("请输入学生姓名:");
    scanf("%s",stu.name);
    printf("请输入学生学号:");
    scanf("%d",&stu.num);
    printf("请输入班级代号:");
    scanf("%d",&stu.class);
    printf("请输入第一门课程成绩:");
    scanf("%d",&stu.score1);
    printf("请输入第二门课程成绩:");
    scanf("%d",&stu.score2);
    stu.ave=(stu.score1+stu.score2)/2.0;
    printf("ave:%4.2f\n",stu.ave); }
    printf("\n\n\n");
    getch();
    ma();
 }

 

/*———–查找学生的相关信息——————*/

void search_stu_info()

{ char name[10];
  int num;
  int i,flag;
  printf("\n");
  printf("\t\t**********查找菜单***************\n");
  printf("\t\t       1.选择学号查找方式\n");
  printf("\t\t       2.选择姓名查找方式\n");

  printf("请选择查找的方式:");
  scanf("%d",&flag);
  switch(flag)
  { case 1:{printf("\t\t请输入你查找的学号:");
    scanf("%d",&num);
    printf("name\tNo\tclass\tscore1\tscore2\tave\n");
    printf

("_____________________________________________________\

n");
    printf("\n");
    for(i=0;i<N;i++)
    { if(stu.num==num)
    printf("%s\t%d\t%d\t%d\t%d\t%

4.2f\n",stu.name,stu.num,stu.class,
    stu.score1,stu.score2,stu.ave);} }
    break;

    case 2:{printf("\t\t请输入你查找的姓名:");
    scanf("%s",name);
    printf("name\tNo.\tclass\tscore1\tscore2\tave\n");
    printf

("______________________________________________________

__\n");
    printf("\n");
    for(i=0;i<N;i++)
    { if (strcmp(stu.name,name)==0)
      printf("%s\t%d\t%d\t%d\t%d\t%

4.2f\n",stu.name,stu.num,stu.class,
     stu.score1,stu.score2,stu.ave);}  }
     break;
     default:printf("\t\t输入出错!");}
   getch();
   ma();
 }

 

/*—————显示学生的相关信息——————-*/

void ouput_stu_info()

{ int i;
  printf("\n");
  printf("\t\t该学生的相关记录:\n");
  printf("\t\tNo.\tname\tclasss\tscore1\tscore2

\tave\n");
  printf("———————————————

——————–\n");
  printf("\n");
  for(i=0;i<N;i++)
  printf("%d\t%s\t%d\t%d\t%d\t%

4.2f\n",stu.num,stu.name,stu.class,
    stu.score1,stu.score2,stu.ave);
  getch();
  ma();

}

 

/*————学生总成绩排名——————*/

void stu_total_score_info()

{ int i,t,n;
  float j;
  char as[10];
  printf("\n");
  printf("\n\n\n");
  printf("No.\tname\tscore1\tscore2\tclass\tave\n");
  printf

("======================================================

==========\n");
  for(n=1;n<N;n++)
  for(i=0;i<N-n;i++)
  { if(stu.ave>stu[i+1].ave)
    {t=stu.num;stu.num=stu[i+1].num;stu[i+1].num=t;
     strcpy(as ,stu.name);strcpy(stu.name,stu

[i+1].name);strcpy(stu[i+1].name,as );
     t=stu.score1;stu.score1=stu[i+1].score1;stu

[i+1].score1=t;
     t=stu.score2;stu.score2=stu[i+1].score2;stu

[i+1].score2=t;
     t=stu.class;stu.class=stu[i+1].class;stu

[i+1].class=t;
     j=stu.ave;stu.ave=stu[i+1].ave;stu[i+1].ave=j;}
  }
  for(i=0;i<N;i++)
  printf("%d\t%s\t%d\t%d\t%d\t%

4.2f\n",stu.num,stu.name,stu.score1,
  stu.score2,stu.class,stu.ave);
  getch();
  ma();

}

 

/*—————-学生获取奖学金的情况—————–

*/

void stu_jiangxuejin_info()

{ int i;
 printf("\n\n\n");
 printf("奖学金的相关情况:\n");
 printf

("_____________________________________________________\

n");
 for(i=0;i<N;i++)
 { if(stu.ave>=90.0)
     printf("%s %d number %d class can win the teden

jinagxuejin.\n",stu.name,stu.num,stu.class);
   else if(stu.ave>=85.0)
    printf("%s %d number %d class can win the frist

jiangxuejin.\n",stu.name,stu.num,stu.class);
   else if(stu.ave>=75.0)
    printf("%s %d number %d class can win the second or

third jiangxuejin.\n",stu.name,stu.num,stu.class);
      else printf("The else person can’t win the

jiangxuejin.\n");
   printf("\n"); }
 getch();
 ma();

}

/*———————–退出学生信息管理系统———-

————-*/

go_out()

{ clrscr();
  printf("\n\n\n\n");
  printf

("\t====================================================

======\n");
  printf("\n");
  printf("\t              谢谢您使用学生信息管理系统!

\n");
  printf("\n");
  printf("\t                ***      

**************\n");
  printf("\n");
  printf

("\t====================================================

======\n");
  printf("\n");
  printf("如果你想退出,请按Alt+X.\n");
  printf("\n");
  printf("如果你想继续,请按enter键.\n");
  getch();

}

main()

{clrscr();
 printf("\n\n\n\n\n\n");
 printf

("\t====================================================

====\n");
 printf("\n");
 printf("\t                学生信息管理系统\n");
 printf("\n");
 printf

("\t====================================================

====\n");
 printf("\n\n");
 ma();

}

ma()

{ int i;
 printf("\n");
 printf

("\t****************************************************

****\n");
 printf("\n");
 printf("\t            选 择 菜 单   \n");
 printf("\n");
 printf("\t         1.输入学生信息\n");
 printf("\t         2.查找学生信息\n");
 printf("\t         3.显示学生信息\n");
 printf("\t         4.总分排名\n");
 printf("\t         5.奖学金情况\n");
 printf("\t         6.退出系统\n");
 printf("\n");
 printf("\n");
 printf

("\t****************************************************

*****\n");
 printf("\n");
 printf("选择菜单(1-6):");
 scanf("%d",&i);
 switch(i)
  { case 1:setup_stu_info();break;
    case 2:search_stu_info();break;
    case 3:ouput_stu_info();break;
    case 4:stu_total_score_info();break;
    case 5:stu_jiangxuejin_info();break;
    case 6:go_out();break;
    default: printf("输入出错!\n");
  }

}

#include <stdio.h>

struct student

{
  int no;
  char name[20];
  int chinese;
  int maths;
  int english;
  int ave;

}stu[20];

 

int i,length=0,n;

     input_stu()

{
  float a;
  printf("Input the number of student:");
  scanf("%d",&n);
  for(i=length;i<length+n;i++)
  {
   printf("the %d student’s no: ",i+1);      scanf("%

d",&stu.no); getchar();
   printf("the %d stdent’s name: ",i+1);     scanf("%

s",stu.name);
   printf("the %d stdent’s chinese: ",i+1);  scanf("%

d",&stu.chinese);
   printf("the %d stdent’s maths: ",i+1);    scanf("%

d",&stu.maths);
   printf("the %d stdent’s english: ",i+1);  scanf("%

d",&stu.english);
   stu.ave=(stu.chinese+stu.maths+stu.english)/3;
   printf("\n");
   }
   length=length+n;
   menu2();

}

         display_stu()

{
   textcolor(9);
   printf

("\tNo\tName\tChinese\tMaths\tEnglish\tAveage\n\n");

   for(i=0;i<length;i++)
   {
    printf("  %8d%10s%8d%8d%8d%

8d\n",stu.no,stu.name,stu.chinese,stu.maths,stu.english,

stu.ave);
   }
   printf("\nInput any key to return main menu…");
   getch();
   menu2();

}

          search_stu()

{
  int k;   char ch=’y';
  textcolor(2);
  while (ch==’y')
 {
  printf("Input the student’s No:");
  scanf("%d",&k);
  for(i=0;i<length;i++)
  {
    if(stu.no==k)
    { printf("the student’s record is:\n");
      printf

("\tNo\tName\tChinese\tMaths\tEnglish\tAveage\n\n");
      printf("  %8d%10s%8d%8d%8d%

8d\n",stu.no,stu.name,stu.chinese,stu.maths,stu.english,

stu.ave);
    }
  }
  printf("\nDo you want search again(y or n)?  ");
  getchar();
  scanf("%c",&ch);
 }
  printf("\nInput any key to return main menu…");
  getch();
  menu2();

}

         modify_stu()

{
  int m,k,pos;   char ch=’y';
  textcolor(3);
  while (ch==’y')
 {
  printf("Input the student’s No to modify:");
  scanf("%d",&k);
  for(i=0;i<length;i++)
  {
    if(stu.no==k)
    { printf("the student’s record is:\n");
      printf

("\tNo\tName\tChinese\tMaths\tEnglish\tAveage\n\n");
      printf("  %8d%10s%8d%8d%8d%

8d\n",stu.no,stu.name,stu.chinese,stu.maths,stu.english,

stu.ave);
      pos=i;
     }
  }
  printf("\n\nChoose you want to modify(1-5):\n");
  printf("\t1. No  2. Name  3. Chinese  4. Maths 

5.English\n");
  scanf("%d",&m);
  switch(m)
  {
   case 1: printf("Input new No:");      scanf("%

d",&stu[pos].no);      break;
   case 2: printf("Input new Name:");    scanf("%s",stu

[pos].name);     break;
   case 3: printf("Input new chinese:"); scanf("%

d",&stu[pos].chinese); break;
   case 4: printf("Input new maths:");   scanf("%

d",&stu[pos].maths);   break;
   case 5: printf("Input new english:"); scanf("%

d",&stu[pos].english); break;
  }
  stu[pos].ave=(stu[pos].chinese+stu[pos].maths+stu

[pos].english)/3;

  printf("\nDo you want modify again(y or n)?  ");
  getchar();
  scanf("%c",&ch);
 }
  printf("\nInput any key to return main menu…");
  getch();
  menu2();

}

         sort_stu()

{
  int j,t; char ch[20];
  textcolor(13);
  printf("\n\n\t\tthe sort by aveage!\n\n");
  for(i=1;i<length;i++)
 for(j=0;j<length-i;j++)
 if(stu[j].ave<stu[j+1].ave)
 {
   t=stu[j].no;      stu[j].no=stu[j+1].no;           

stu[j+1].no=t;
   strcpy(ch,stu[j].name); strcpy(stu[j].name,stu

[j+1].name); strcpy(stu[j+1].name,ch);
   t=stu[j].chinese; stu[j].chinese=stu[j+1].chinese; 

stu[j+1].chinese=t;
   t=stu[j].maths;   stu[j].maths=stu[j+1].maths;     

stu[j+1].maths=t;
   t=stu[j].english; stu[j].english=stu[j+1].english; 

stu[j+1].english=t;
   t=stu[j].ave;     stu[j].ave=stu[j+1].ave;         

stu[j+1].ave=t;
 }
  display_stu();

}

         menu1()

{
 clrscr();  printf("\n\n\n\n");
 textcolor(9);
 printf

("\t\t**********************************************\n")

;
 printf("\t\t**                                         

**\n");
 printf("\t\t**          Welcom to Use the System!      

**\n");
 printf("\t\t**               (C语言版)                 

 **\n");
 printf("\t\t**                                         

**\n");
 printf("\t\t**           编制:

www.DesignTide.com**\n");
 printf("\t\t**                                         

**\n");
 printf("\t\t**              Copyright    2003          

**\n");
 printf("\t\t**                                         

**\n");
 printf

("\t\t**********************************************\n\n

");
 printf("\t\t Input any key to enter…");
 getch();
 menu2();

}

         menu2()

{
 int i;
 clrscr();  printf("\n\n");
 textcolor(10);
 printf

("\t\t**********************************************\n")

;
 printf("\t\t**            Main   Menu                  

**\n");
 printf("\t\t**                                         

**\n");
 printf("\t\t**       1. Input Records                  

**\n");
 printf("\t\t**       2. Display all Records            

**\n");
 printf("\t\t**       3. Search                         

**\n");
 printf("\t\t**       4. Modify                         

**\n");
 printf("\t\t**       5. Sort                           

**\n");
 printf("\t\t**       6. Exit                           

**\n");
 printf("\t\t**                                         

**\n");
 printf

("\t\t**********************************************\n\n

");
 printf("Input your choose(1-6):");
 scanf("%d",&i);

 while (i<0 || i>6)
 { printf("Your choose is error! Input again!\n");
   printf("Input your choose(1-6):");
   scanf("%d",&i);
 }

   switch(i)
   {
     case 1:    input_stu();    break;
     case 2:    display_stu();  break;
     case 3:    search_stu();   break;
     case 4:    modify_stu();   break;
     case 5:    sort_stu();     break;
     case 6:    menu3();        exit();
    }

}

         menu3()

{
 clrscr();  printf("\n\n\n\n");
 textcolor(12);
 printf

("\t\t**********************************************\n")

;
 printf("\t\t**                                         

**\n");
 printf("\t\t**                  Thanks !               

**\n");
 printf("\t\t**                                         

**\n");
 printf("\t\t**              Copyright    2003          

**\n");
 printf("\t\t**                                         

**\n");
 printf

("\t\t**********************************************\n\n

");
 getch();

}

     main()

{
 textcolor(6);
 menu1();
 getch();

}

 

 

中文修改版:

 

#define N 4

/*—-学生信息管理系统:编制 www.designtide.com—–*/

{
 clrscr();  printf("\n\n\n\n");
 textcolor(9);
 printf

("\t\t**********************************************\n")

;
 printf("\t\t**                                         

**\n");
 printf("\t\t**         欢迎使用学生信息管理系统        

**\n");
 printf("\t\t**                                         

**\n");
 printf("\t\t**               (C语言版本)               

**\n");
 printf("\t\t**                                         

**\n");
 printf("\t\t**          编制: www.DesignTide.com 

**\n");
 printf("\t\t**                                         

**\n");
 printf("\t\t**           Copyright  @   2004           

**\n");
 printf("\t\t**                                         

**\n");
 printf

("\t\t**********************************************\n\n

");
 printf("\t\t 按任意键继续……");
 getch();
 menu2();

}

 

/*—————-定义相关变量———————*/

struct student

{ char name[10];
  int num;
  int score1;
  int score2;
  float ave;
  int class;

}stu[N];

 

/*————相关数据的处理—————*/

void setup_stu_info()

{ int i;
  for(i=0;i<N;i++)
  { printf("请输入学生姓名:");
    scanf("%s",stu.name);
    printf("请输入学生学号:");
    scanf("%d",&stu.num);
    printf("请输入班级代号:");
    scanf("%d",&stu.class);
    printf("请输入第一门课程成绩:");
    scanf("%d",&stu.score1);
    printf("请输入第二门课程成绩:");
    scanf("%d",&stu.score2);
    stu.ave=(stu.score1+stu.score2)/2.0;
    printf("ave:%4.2f\n",stu.ave); }
    printf("\n\n\n");
    getch();
    ma();
 }

 

/*———–查找学生的相关信息——————*/

void search_stu_info()

{ char name[10];
  int num;
  int i,flag;
  printf("\n");
  printf("\t\t**********查找菜单***************\n");
  printf("\t\t       1.选择学号查找方式\n");
  printf("\t\t       2.选择姓名查找方式\n");

  printf("请选择查找的方式:");
  scanf("%d",&flag);
  switch(flag)
  { case 1:{printf("\t\t请输入你查找的学号:");
    scanf("%d",&num);
    printf("name\tNo\tclass\tscore1\tscore2\tave\n");
    printf

("_____________________________________________________\

n");
    printf("\n");
    for(i=0;i<N;i++)
    { if(stu.num==num)
    printf("%s\t%d\t%d\t%d\t%d\t%

4.2f\n",stu.name,stu.num,stu.class,
    stu.score1,stu.score2,stu.ave);} }
    break;

    case 2:{printf("\t\t请输入你查找的姓名:");
    scanf("%s",name);
    printf("name\tNo.\tclass\tscore1\tscore2\tave\n");
    printf

("______________________________________________________

__\n");
    printf("\n");
    for(i=0;i<N;i++)
    { if (strcmp(stu.name,name)==0)
      printf("%s\t%d\t%d\t%d\t%d\t%

4.2f\n",stu.name,stu.num,stu.class,
     stu.score1,stu.score2,stu.ave);}  }
     break;
     default:printf("\t\t输入出错!");}
   getch();
   ma();
 }

 

/*—————显示学生的相关信息——————-*/

void ouput_stu_info()

{ int i;
  printf("\n");
  printf("\t\t该学生的相关记录:\n");
  printf("\t\tNo.\tname\tclasss\tscore1\tscore2

\tave\n");
  printf("———————————————

——————–\n");
  printf("\n");
  for(i=0;i<N;i++)
  printf("%d\t%s\t%d\t%d\t%d\t%

4.2f\n",stu.num,stu.name,stu.class,
    stu.score1,stu.score2,stu.ave);
  getch();
  ma();

}

 

/*————学生总成绩排名——————*/

void stu_total_score_info()

{ int i,t,n;
  float j;
  char as[10];
  printf("\n");
  printf("\n\n\n");
  printf("No.\tname\tscore1\tscore2\tclass\tave\n");
  printf

("======================================================

==========\n");
  for(n=1;n<N;n++)
  for(i=0;i<N-n;i++)
  { if(stu.ave>stu[i+1].ave)
    {t=stu.num;stu.num=stu[i+1].num;stu[i+1].num=t;
     strcpy(as ,stu.name);strcpy(stu.name,stu

[i+1].name);strcpy(stu[i+1].name,as );
     t=stu.score1;stu.score1=stu[i+1].score1;stu

[i+1].score1=t;
     t=stu.score2;stu.score2=stu[i+1].score2;stu

[i+1].score2=t;
     t=stu.class;stu.class=stu[i+1].class;stu

[i+1].class=t;
     j=stu.ave;stu.ave=stu[i+1].ave;stu[i+1].ave=j;}
  }
  for(i=0;i<N;i++)
  printf("%d\t%s\t%d\t%d\t%d\t%

4.2f\n",stu.num,stu.name,stu.score1,
  stu.score2,stu.class,stu.ave);
  getch();
  ma();

}

 

/*—————-学生获取奖学金的情况—————–

*/

void stu_jiangxuejin_info()

{ int i;
 printf("\n\n\n");
 printf("奖学金的相关情况:\n");
 printf

("_____________________________________________________\

n");
 for(i=0;i<N;i++)
 { if(stu.ave>=90.0)
     printf("%s %d number %d class can win the teden

jinagxuejin.\n",stu.name,stu.num,stu.class);
   else if(stu.ave>=85.0)
    printf("%s %d number %d class can win the frist

jiangxuejin.\n",stu.name,stu.num,stu.class);
   else if(stu.ave>=75.0)
    printf("%s %d number %d class can win the second or

third jiangxuejin.\n",stu.name,stu.num,stu.class);
      else printf("The else person can’t win the

jiangxuejin.\n");
   printf("\n"); }
 getch();
 ma();

}

/*———————–退出学生信息管理系统———-

————-*/

go_out()

{ clrscr();
  printf("\n\n\n\n");
  printf

("\t====================================================

======\n");
  printf("\n");
  printf("\t              谢谢您使用学生信息管理系统!

\n");
  printf("\n");
  printf("\t                ***      

**************\n");
  printf("\n");
  printf

("\t====================================================

======\n");
  printf("\n");
  printf("如果你想退出,请按Alt+X.\n");
  printf("\n");
  printf("如果你想继续,请按enter键.\n");
  getch();

}

main()

{clrscr();
 printf("\n\n\n\n\n\n");
 printf

("\t====================================================

====\n");
 printf("\n");
 printf("\t                学生信息管理系统\n");
 printf("\n");
 printf

("\t====================================================

====\n");
 printf("\n\n");
 ma();

}

ma()

{ int i;
 printf("\n");
 printf

("\t****************************************************

****\n");
 printf("\n");
 printf("\t            选 择 菜 单   \n");
 printf("\n");
 printf("\t         1.输入学生信息\n");
 printf("\t         2.查找学生信息\n");
 printf("\t         3.显示学生信息\n");
 printf("\t         4.总分排名\n");
 printf("\t         5.奖学金情况\n");
 printf("\t         6.退出系统\n");
 printf("\n");
 printf("\n");
 printf

("\t****************************************************

*****\n");
 printf("\n");
 printf("选择菜单(1-6):");
 scanf("%d",&i);
 switch(i)
  { case 1:setup_stu_info();break;
    case 2:search_stu_info();break;
    case 3:ouput_stu_info();break;
    case 4:stu_total_score_info();break;
    case 5:stu_jiangxuejin_info();break;
    case 6:go_out();break;
    default: printf("输入出错!\n");
  }

}

2006年09月12日

空间数据挖掘技术理论及方法
葛继科
(西南农业大学信息学院 400716)
 
摘要  本文简要论述了空间数据库技术及空间数据挖掘技术的理论及特点,分析了空间数据挖掘技术的层次、方法,并重点介绍了当前常用的分类、聚类、关联规则等空间数据挖掘方法,指出了当前空间数据挖掘技术中尚需解决的问题、发展趋势及方向。
关键词 空间数据挖掘 分类 聚类 关联规则 
0 引言
地理信息系统(Geographic Information System,简称GIS)是计算机科学、地理学、测量学、地图学等多门学科综合的技术[1]。GIS的基本技术是空间数据库、地图可视化及空间分析,而空间数据库是GIS的关键。空间数据挖掘技术作为当前数据库技术最活跃的分支与知识获取手段,在GIS中的应用推动着GIS朝智能化和集成化的方向发展。
1 空间数据库与空间数据挖掘技术的特点
随着数据库技术的不断发展和数据库管理系统的广泛应用,数据库中存储的数据量也在急剧增大,在这些海量数据的背后隐藏了很多具有决策意义的信息。但是,现今数据库的大多数应用仍然停留在查询、检索阶段,数据库中隐藏的丰富的知识远远没有得到充分的发掘和利用,数据库中数据的急剧增长和人们对数据库处理和理解的困难形成了强烈的反差,导致“人们被数据淹没,但却饥饿于知识”的现象。
空间数据库(数据仓库)中的空间数据除了其显式信息外,还具有丰富的隐含信息,如数字高程模型〔DEM或TIN〕,除了载荷高程信息外,还隐含了地质岩性与构造方面的信息;植物的种类是显式信息,但其中还隐含了气候的水平地带性和垂直地带性的信息,等等。这些隐含的信息只有通过数据挖掘才能显示出来。空间数据挖掘(Spatial Data Mining,简称SDM),或者称为从空间数据库中发现知识,是为了解决空间数据海量特性而扩展的一个新的数据挖掘的研究分支,是指从空间数据库中提取隐含的、用户感兴趣的空间或非空间的模式和普遍特征的过程[2]。由于SDM的对象主要是空间数据库,而空间数据库中不仅存储了空间事物或对象的几何数据、属性数据,而且存储了空间事物或对象之间的图形空间关系,因此其处理方法有别于一般的数据挖掘方法。SDM与传统的地学数据分析方法的本质区别在于SDM是在没有明确假设的前提下去挖掘信息、发现知识,挖掘出的知识应具有事先未知、有效和可实用3个特征。
空间数据挖掘技术需要综合数据挖掘技术与空间数据库技术,它可用于对空间数据的理解,对空间关系和空间与非空间关系的发现、空间知识库的构造以及空间数据库的重组和查询的优化等。
2 空间数据挖掘技术的主要方法及特点
常用的空间数据挖掘技术包括:序列分析、分类分析、预测、聚类分析、关联规则分析、时间序列分析、粗集方法及云理论等。本文从挖掘任务和挖掘方法的角度,着重介绍了分类分析、聚类分析和关联规则分析三种常用的重要的方法。
2.1、分类分析
分类在数据挖掘中是一项非常重要的任务,目前在商业上应用最多。分类的目的是学会一个分类函数或分类模型(也常常称作分类器),该模型能把数据库中的数据项映射到给定类别中的某一个。分类和我们熟知的回归方法都可用于预测,两者的目的都是从历史数据纪录中自动推导出对给定数据的推广描述,从而能对未来数据进行预测。和回归方法不同的是,分类的输出是离散的类别值,而回归的输出则是连续的数值。二者常表现为一棵决策树,根据数据值从树根开始搜索,沿着数据满足的分支往上走,走到树叶就能确定类别。空间分类的规则实质是对给定数据对象集的抽象和概括,可用宏元组表示。
要构造分类器,需要有一个训练样本数据集作为输入。训练集由一组数据库记录或元组构成,每个元组是一个由特征(又称属性)值组成的特征向量,此外,训练样本还有一个类别标记。一个具体样本的形式可为:( v1, v2, …, vn; c );其中vi表示字段值,c表示类别。
分类器的构造方法有统计方法、机器学习方法、神经网络方法等等。统计方法包括贝叶斯法和非参数法(近邻学习或基于事例的学习),对应的知识表示是判别函数和原型事例。机器学习方法包括决策树法和规则归纳法,前者对应的表示为决策树或判别树,后者则一般为产生式规则。神经网络方法主要是反向传播(Back-Propagation,简称BP)算法,它的模型表示是前向反馈神经网络模型(由代表神经元的节点和代表联接权值的边组成的一种体系结构),BP算法本质上是一种非线性判别函数[3]。另外,最近又兴起了一种新的方法:粗糙集(rough set),其知识表示是产生式规则。
不同的分类器有不同的特点。有三种分类器评价或比较尺度:1) 预测准确度;2) 计算复杂度;3) 模型描述的简洁度。预测准确度是用得最多的一种比较尺度,特别是对于预测型分类任务,目前公认的方法是10番分层交叉验证法。计算复杂度依赖于具体的实现细节和硬件环境,在数据挖掘中,由于操作对象是海量的数据库,因此空间和时间的复杂度问题将是非常重要的一个环节。对于描述型的分类任务,模型描述越简洁越受欢迎。例如,采用规则归纳法表示的分类器构造法就很有用,而神经网络方法产生的结果就难以理解。
另外要注意的是,分类的效果一般和数据的特点有关。有的数据噪声大,有的有缺值, 有的分布稀疏,有的字段或属性间相关性强,有的属性是离散的而有的是连续值或混合式的。目前普遍认为不存在某种方法能适合于各种特点的数据。
分类技术在实际应用非常重要,比如:可以根据房屋的地理位置决定房屋的档次等。
2. 2 聚类分析
聚类是指根据“物以类聚”的原理,将本身没有类别的样本聚集成不同的组,并且对每一个这样的组进行描述的过程。它的目的是使得属于同一个组的样本之间应该彼此相似,而不同组的样本应足够不相似。与分类分析不同,进行聚类前并不知道将要划分成几个组和什么样的组,也不知道根据哪些空间区分规则来定义组。其目的旨在发现空间实体的属性间的函数关系,挖掘的知识用以属性名为变量的数学方程来表示。聚类方法包括统计方法、机器学习方法、神经网络方法和面向数据库的方法。基于聚类分析方法的空间数据挖掘算法包括均值近似算法[4]、CLARANS、BIRCH、DBSCAN等算法。目前,对空间数据聚类分析方法的研究是一个热点。
对于空间数据,利用聚类分析方法,可以根据地理位置以及障碍物的存在情况自动地进行区域划分。例如,根据分布在不同地理位置的ATM机的情况将居民进行区域划分,根据这一信息,可以有效地进行ATM机的设置规划,避免浪费,同时也避免失掉每一个商机。
2.3 关联规则分析
关联规则分析主要用于发现不同事件之间的关联性,即一事物发生时,另一事物也经常发生。关联分析的重点在于快速发现那些有实用价值的关联发生的事件。其主要依据是:事件发生的概率和条件概率应该符合一定的统计意义。空间关联规则的形式是X->Y[S%,C%],其中X、Y是空间或非空间谓词的集合,S%表示规则的支持度,C%表示规则的置信度。空间谓词的形式有3种:表示拓扑结构的谓词、表示空间方向的谓词和表示距离的谓词[5]。各种各样的空间谓词可以构成空间关联规则。如,距离信息(如Close_to(临近)、Far_away(远离))、拓扑关系(Intersect(交)、Overlap(重叠)、Disjoin(分离))和空间方位(如Right_of(右边)、West_of(西边))。实际上大多数算法都是利用空间数据的关联特性改进其分类算法,使得它适合于挖掘空间数据中的相关性,从而可以根据一个空间实体而确定另一个空间实体的地理位置,有利于进行空间位置查询和重建空间实体等。大致算法可描述如下:(1)根据查询要求查找相关的空间数据;(2)利用临近等原则描述空间属性和特定属性;(3)根据最小支持度原则过滤不重要的数据;(4)运用其它手段对数据进一步提纯(如OVERLAY);(5)生成关联规则。
关联规则通常可分为两种:布尔型的关联规则和多值关联规则。多值关联规则比较复杂,一种自然的想法是将它转换为布尔型关联规则,由于空间关联规则的挖掘需要在大量的空间对象中计算多种空间关系,因此其代价是很高的。—种逐步求精的挖掘优化方法可用于空间关联的分析,该方法首先用一种快速的算法粗略地对一个较大的数据集进行一次挖掘,然后在裁减过的数据集上用代价较高的算法进一步改进挖掘的质量。因为其代价非常高,所以空间的关联方法需要进一步的优化。
对于空间数据,利用关联规则分析,可以发现地理位置的关联性。例如,85%的靠近高速公路的大城镇与水相邻,或者发现通常与高尔夫球场相邻的对象是停车场等。
3 空间数据挖掘技术的研究方向
3.1 处理不同类型的数据
绝大多数数据库是关系型的,因此在关系数据库上有效地执行数据挖掘是至关重要的。但是在不同应用领域中存在各种数据和数据库,而且经常包含复杂的数据类型,例如结构数据、复杂对象、事务数据、历史数据等。由于数据类型的多样性和不同的数据挖掘目标,一个数据挖掘系统不可能处理各种数据。因此针对特定的数据类型,需要建立特定的数据挖掘系统。
3.2  数据挖掘算法的有效性和可测性
海量数据库通常有上百个属性和表及数百万个元组。GB数量级数据库已不鲜见,TB数量级数据库已经出现,高维大型数据库不仅增大了搜索空间,也增加了发现错误模式的可能性。因此必须利用领域知识降低维数,除去无关数据,从而提高算法效率。从一个大型空间数据库中抽取知识的算法必须高效、可测量,即数据挖掘算法的运行时间必须可预测,且可接受,指数和多项式复杂性的算法不具有实用价值。但当算法用有限数据为特定模型寻找适当参数时,有时也会导致物超所值,降低效率。
3.3 交互性用户界面
数据挖掘的结果应准确地描述数据挖掘的要求,并易于表达。从不同的角度考察发现的知识,并以不同形式表示,用高层次语言和图形界面表示数据挖掘要求和结果。目前许多知识发现系统和工具缺乏与用户的交互,难以有效利用领域知识。对此可以利用贝叶斯方法和演译数据库本身的演译能力发现知识。
3.4 在多抽象层上交互式挖掘知识
很难预测从数据库中会挖掘出什么样的知识,因此一个高层次的数据挖掘查询应作为进一步探询的线索。交互式挖掘使用户能交互地定义一个数据挖掘要求,深化数据挖掘过程,从不同角度灵活看待多抽象层上的数据挖掘结果。
3.5  从不同数据源挖掘信息
局域网、广域网以及Internet网将多个数据源联成一个大型分布、异构的数据库,从包含不同语义的格式化和非格式化数据中挖掘知识是对数据挖掘的一个挑战。数据挖掘可揭示大型异构数据库中存在的普通查询不能发现的知识。数据库的巨大规模、广泛分布及数据挖掘方法的计算复杂性,要求建立并行分布的数据挖掘。
3.6  私有性和安全性
数据挖掘能从不同角度、不同抽象层上看待数据,这将影响到数据挖掘的私有性和安全性。通过研究数据挖掘导致的数据非法侵入,可改进数据库安全方法,以避免信息泄漏。
3.7  和其它系统的集成
方法、功能单一的发现系统的适用范围必然受到一定的限制。要想在更广泛的领域发现知识,空间数据挖掘系统就应该是数据库、知识库、专家系统、决策支持系统、可视化工具、网络等技术的集成。
4 有待研究的问题
我们虽然在空间数据挖掘技术的研究和应用中取得了很大的成绩,但在一些理论及应用方面仍存在急需解决的问题。
4.1 数据访问的效率和可伸缩性
空间数据的复杂性和数据的大量性,TB数量级的数据库的出现,必然增大发现算法的搜索空间,增加了搜索的盲目性。如何有效的去除与任务无关的数据,降低问题的维数,设计出更加高效的挖掘算法对空间数据挖掘提出了巨大的挑战。
4.2 对当前一些GIS软件缺乏时间属性和静态存储的改进
由于数据挖掘的应用在很大的程度上涉及到时序关系,因此静态的数据存储严重妨碍了数据挖掘的应用。基于图层的计算模式、不同尺度空间数据之间的完全割裂也对空间数据挖掘设置了重重障碍。空间实体与属性数据之间的联系仅仅依赖于标识码,这种一维的连接方式无疑将丢失大量的连接信息,不能有效的表示多维和隐含的内在连接关系,这些都增加了数据挖掘计算的复杂度,极大地增加了数据准备阶段的工作量和人工干预的程度。
4.3 发现模式的精炼
当发现空间很大时会获得大量的结果,尽管有些是无关或没有意义的模式,这时可利用领域的知识进一步精炼发现的模式,从而得到有意义的知识。
在空间数据挖掘技术方面,重要的研究和应用的方向还包括:网络环境上的数据挖掘、栅格矢量一体化的挖掘、不确定性情况下的数据挖掘、分布式环境下的数据挖掘、数据挖掘查询语言和新的高效的挖掘算法等。
5  小结
随着GIS与数据挖掘及相关领域科学研究的不断发展,空间数据挖掘技术在广度和深度上的不断深入,在不久的将来,一个集成了挖掘技术的GIS、GPS、RS集成系统必将朝着智能化、网络化、全球化与大众化的方向发展。
参考文献:
[1] 邬 伦 等 地理信息系统――原理、方法和应用 .科学出版社.2001.
[2] 邸凯昌.空间数据挖掘和知识发现的理论与方法[D].武汉:武汉测绘科技大学,1999.
[3] 蔡自兴,徐光祐. 人工智能及其应用. 清华大学出版社. 1999. 206~216.
[4] Sheikholeslami G, Chatterjee S, Zhang A. Wave-Cluster: A multi-resolution clustering approach for very large spatiall databases. In:Proceedings of the 24th International Conference on Very Large Databases. New York, 1998. 428~439.
[5] 朱建秋,张晓辉,菇伟杰,朱杨勇.数据挖掘语言浅析[Z].
http://www.sqlmine.com/warehouse/htm/40.htm.
 
The Technology and Methods of Spatial Data Mining
Ge Ji-Ke
(Information College  South West Agricultural University  Chongqing  400716)
Abstract: This paper introduces the theory and characteristic of spatial database and spatial data mining, analyses the hierarchy method and knowledge’s classification of spatial data mining, introduces spatial classification rules , spatial clustering rules and spatial association rules, points out unsolved question, trend and direction.
Key words: spatial data mining, classification, clustering, association rules

2006年09月11日

窗口创建步骤:

1、设计一个窗口类

2、注册窗口类

3、创建窗口

4、显示及更新窗口–小心循环、消息处理

创建链表

1、动态分配

2、初始化新节点Next指针(NULL)

3、若为空表:头=尾=新

       若不为空:尾的Next =新

                            尾=新

 

#include<stdio.h>
struct Node
{
 int data;
 Node *next;
};
void main()
{
 Node *pHead=NULL;
 Node *pTail=NULL;
 Node *pNew=NULL;
 while(1)
 {
  pNew=new Node;    //动态分配
  pNew->next=NULL;//初始化新节点Next指针(NULL)
  
  printf("请输入数据:");
  scanf("%d",&pNew->data);
  
  if(pNew->data==0)
  {
   delete pNew;
   break;
  }
  
  if(pHead==NULL)
  {
   pHead=pTail=pNew;  //若为空表:头=尾=新
  }
  
  else
  {
   pTail->next=pNew;  //   若不为空:尾的Next =新 ;尾=新
   pTail=pNew;
  }
  
 }
 Node *pMove=pHead;
 while(pMove!=NULL)
 {
  printf("%d\t",pMove->data);
  pMove=pMove->next;
 }
 
}

 

2006年09月08日

1指针的概念

指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址。 要搞清一个指针需要搞清指针的四方面的内容:指针的类型,指针所指向的 类型,指针的值或者叫指针所指向的内存区,还有指针本身所占据的内存区。让我们分别说明。

先声明几个指针放着做例子:

例一:

(1)int*ptr;

(2)char*ptr;

(3)int**ptr;

(4)int(*ptr)[3];

(5)int*(*ptr)[4];

如果看不懂后几个例子的话,请参阅我前段时间贴出的文章<<如何理解c和c ++的复杂类型声明>>。

指针的类型

从语法的角度看,你只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型。这是指针本身所具有的类型。让我们看看例一中各个指针的类型:

(1)int*ptr;//指针的类型是int*

(2)char*ptr;//指针的类型是char*

(3)int**ptr;//指针的类型是int**

(4)int(*ptr)[3];//指针的类型是int(*)[3]

(5)int*(*ptr)[4];//指针的类型是int*(*)[4]

怎么样?找出指针的类型的方法是不是很简单?

指针所指向的类型

当你通过指针来访问指针所指向的内存区时,指针所指向的类型决定了编译器将把那片内存区里的内容当做什么来看待。

从语法上看,你只须把指针声明语句中的指针名字和名字左边的指针声明符*去掉,剩下的就是指针所指向的类型。例如:

(1)int*ptr;//指针所指向的类型是int

(2)char*ptr;//指针所指向的的类型是char

(3)int**ptr;//指针所指向的的类型是int*

(4)int(*ptr)[3];//指针所指向的的类型是int()[3]

(5)int*(*ptr)[4];//指针所指向的的类型是int*()[4]

在指针的算术运算中,指针所指向的类型有很大的作用。

指针的类型(即指针本身的类型)和指针所指向的类型是两个概念。当你对C越来越熟悉时,你会发现,把与指针搅和在一起的"类型"这个概念分成"指针的类型"和"指针所指向的类型"两个概念,是精通指针的关键点之一。我看了不少书,发现有些写得差的书中,就把指针的这两个概念搅在一起了,所以看起书来前后矛盾,越看越糊涂。
指针的值,或者叫指针所指向的内存区或地址

指针的值是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。在32位程序里,所有类型的指针的值都是一个32位整数,因为32位程序里内存地址全都是32位长。 指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为si zeof(指针所指向的类型)的一片内存区。以后,我们说一个指针的值是XX,就相当于说该指针指向了以XX为首地址的一片内存区域;我们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。

指针所指向的内存区和指针所指向的类型是两个完全不同的概念。在例一中,指针所指向的类型已经有了,但由于指针还未初始化,所以它所指向的内存区是不存在的,或者说是无意义的。

以后,每遇到一个指针,都应该问问:这个指针的类型是什么?指针指的类型是什么?该指针指向了哪里?

指针本身所占据的内存区

指针本身占了多大的内存?你只要用函数sizeof(指针的类型)测一下就知道了。在32位平台里,指针本身占据了4个字节的长度。

指针本身占据的内存这个概念在判断一个指针表达式是否是左值时很有用。 指针的算术运算 指针可以加上或减去一个整数。指针的这种运算的意义和通常的数值的加减运算的意义是不一样的。例如:

例二:

1、chara[20];

2、int*ptr=a;

3、ptr++;

在上例中,指针ptr的类型是int*,它指向的类型是int,它被初始化为指向整形变量a。接下来的第3句中,指针ptr被加了1,编译器是这样处理的:它把指针ptr的值加上了sizeof(int),在32位程序中,是被加上了4。由于地址是用字节做单位的,故ptr所指向的地址由原来的变量a的地址向高地址方向增加了4个字节。
由于char类型的长度是一个字节,所以,原来ptr是指向数组a的第0号单元开始的四个字节,此时指向了数组a中从第4号单元开始的四个字节。

我们可以用一个指针和一个循环来遍历一个数组,看例子:

例三:
intarray[20];
int*ptr=array;

//此处略去为整型数组赋值的代码。

for(i=0;i<20;i++)
{
(*ptr)++;
ptr++;
} 这个例子将整型数组中各个单元的值加1。由于每次循环都将指针ptr加1,所以每次循环都能访问数组的下一个单元。

再看例子:

例四:

1、chara[20];

2、int*ptr=a;


3、ptr+=5;

在这个例子中,ptr被加上了5,编译器是这样处理的:将指针ptr的值加上5乘sizeof(int),在32位程序中就是加上了5乘4=20。由于地址的单位是字节,故现在的ptr所指向的地址比起加5后的ptr所指向的地址来说,向高地址方向移动了20个字节。在这个例子中,没加5前的ptr指向数组a的第0号单元开始的四个字节,加5后,ptr已经指向了数组a的合法范围之外了。虽然这种情况在应用上会出问题,但在语法上却是可以的。这也体现出了指针的灵活性。

如果上例中,ptr是被减去5,那么处理过程大同小异,只不过ptr的值是被减去5乘sizeof(int),新的ptr指向的地址将比原来的ptr所指向的地址向低地址方向移动了20个字节。

总结一下,一个指针ptrold加上一个整数n后,结果是一个新的指针ptrnew,ptrnew的类型和ptrold的类型相同,ptrnew所指向的类型和ptrold所指向的类型也相同。ptrnew的值将比ptrold的值增加了n乘sizeof(ptrold所指向的类型)个字节。就是说,ptrnew所指向的内存区将比ptrold所指向的内存区向高地址方向移动了n乘sizeof(ptrold所指向的类型)个字节。

一个指针ptrold减去一个整数n后,结果是一个新的指针ptrnew,ptrnew的类型和ptrold的类型相同,ptrnew所指向的类型和ptrold所指向的类型也相同。ptrnew的值将比ptrold的值减少了n乘sizeof(ptrold所指向的类型)个字节,就是说,ptrnew所指向的内存区将比ptrold所指向的内存区向低地址方向移动了n乘sizeof(ptrold所指向的类型)个字节运算符&和* 这里&是取地址运算符,*是…书上叫做"间接运算符"。

&a的运算结果是一个指针,指针的类型是a的类型加个*,指针所指向的类型是a的类型,指针所指向的地址嘛,那就是a的地址。

*p的运算结果就五花八门了。总之*p的结果是p所指向的东西,这个东西有这些特点:它的类型是p指向的类型,它所占用的地址是p所指向的地址。

例五:
inta=12;
intb;
int*p;
int**ptr;
p=&a;
//&a的结果是一个指针,类型是int*,指向的类型是int,指向的地址是a的地址。
*p=24;
//*p的结果,在这里它的类型是int,它所占用的地址是p所指向的地址,显然,*p就是变量a。
ptr=&p;
//&p的结果是个指针,该指针的类型是p的类型加个*,在这里是int **。该指针所指向的类型是p的类型,这里是int*。该指针所指向的地址就是指针p自己的地址。
*ptr=&b;
//*ptr是个指针,&b的结果也是个指针,且这两个指针的类型和所指向的类型是一样的,所以用&b来给*ptr赋值就是毫无问题的了。
**ptr=34;
//*ptr的结果是ptr所指向的东西,在这里是一个指针,对这个指针再做一次*运算,结果就是一个int类型的变量。 指针表达式 一个表达式的最后结果如果是一个指针,那么这个表达式就叫指针表式。

下面是一些指针表达式的例子:

例六:
inta,b;
intarray[10];
int*pa;
pa=&a;//&a是一个指针表达式。
int**ptr=&pa;//&pa也是一个指针表达式。
*ptr=&b;//*ptr和&b都是指针表达式。
pa=array;
pa++;//这也是指针表达式。 例七:
char*arr[20];
char**parr=arr;//如果把arr看作指针的话,arr也是指针表达式
char*str;
str=*parr;//*parr是指针表达式
str=*(parr+1);//*(parr+1)是指针表达式
str=*(parr+2);//*(parr+2)是指针表达式 由于指针表达式的结果是一个指针,所以指针表达式也具有指针所具有的四个要素:指针的类型,指针所指向的类型,指针指向的内存区,指针自身占据的内存。

好了,当一个指针表达式的结果指针已经明确地具有了指针自身占据的内存的话,这个指针表达式就是一个左值,否则就不是一个左值。

在例七中,&a不是一个左值,因为它还没有占据明确的内存。*ptr是一个左值,因为*ptr这个指针已经占据了内存,其实*ptr就是指针pa,既然pa已经在内存中有了自己的位置,那么*ptr当然也有了自己的位置。

数组和指针的关系 如果对声明数组的语句不太明白的话,请参阅我前段时间贴出的文章<<如何理解c和c++的复杂类型声明>>。

数组的数组名其实可以看作一个指针。看下例:

例八:
intarray[10]={0,1,2,3,4,5,6,7,8,9},value;


value=array[0];//也可写成:value=*array;
value=array[3];//也可写成:value=*(array+3);
value=array[4];//也可写成:value=*(array+4); 上例中,一般而言数组名array代表数组本身,类型是int[10],但如果把array看做指针的话,它指向数组的第0个单元,类型是int*,所指向的类型是数组单元的类型即int。因此*array等于0就一点也不奇怪了。同理,array+3是一个指向数组第3个单元的指针,所以*(array+3)等于3。其它依此类推。

例九:
char*str[3]={
"Hello,thisisasample!",
"Hi,goodmorning.",
"Helloworld"
};
chars[80];
strcpy(s,str[0]);//也可写成strcpy(s,*str);
strcpy(s,str[1]);//也可写成strcpy(s,*(str+1));
strcpy(s,str[2]);//也可写成strcpy(s,*(str+2)); 上例中,str是一个三单元的数组,该数组的每个单元都是一个指针,这些指针各指向一个字符串。把指针数组名str当作一个指针的话,它指向数组的第0号单元,它的类型是char**,它指向的类型是char*。
*str也是一个指针,它的类型是char*,它所指向的类型是char,它指向的地址是字符串"Hello,thisisasample!"的第一个字符的地址,即’H'的地址。 str+1也是一个指针,它指向数组的第1号单元,它的类型是char**,它指向的类型是char*。

*(str+1)也是一个指针,它的类型是char*,它所指向的类型是char,它指向 "Hi,goodmorning."的第一个字符’H',等等。

下面总结一下数组的数组名的问题。声明了一个数组TYPEarray[n],则数组名称array就有了两重含义:第一,它代表整个数组,它的类型是TYPE[n];第二 ,它是一个指针,该指针的类型是TYPE*,该指针指向的类型是TYPE,也就是数组单元的类型,该指针指向的内存区就是数组第0号单元,该指针自己占有单独的内存区,注意它和数组第0号单元占据的内存区是不同的。该指针的值是不能修改的,即类似array++的表达式是错误的。

在不同的表达式中数组名array可以扮演不同的角色。

在表达式sizeof(array)中,数组名array代表数组本身,故这时sizeof函数测出的是整个数组的大小。
在表达式*array中,array扮演的是指针,因此这个表达式的结果就是数组第0号单元的值。sizeof(*array)测出的是数组单元的大小。

表达式array+n(其中n=0,1,2,….。)中,array扮演的是指针,故array+n的结果是一个指针,它的类型是TYPE*,它指向的类型是TYPE,它指向数组第n号单元。故sizeof(array+n)测出的是指针类型的大小。

例十:
intarray[10];
int(*ptr)[10];
ptr=&array; 上例中ptr是一个指针,它的类型是int(*)[10],他指向的类型是int[10] ,我们用整个数组的首地址来初始化它。在语句ptr=&array中,array代表数组本身。

本节中提到了函数sizeof(),那么我来问一问,sizeof(指针名称)测出的究竟是指针自身类型的大小呢还是指针所指向的类型的大小?答案是前者。例如:

int(*ptr)[10];

则在32位程序中,有:
sizeof(int(*)[10])==4
sizeof(int[10])==40
sizeof(ptr)==4 实际上,sizeof(对象)测出的都是对象自身的类型的大小,而不是别的什么类型的大小。指针和结构类型的关系 可以声明一个指向结构类型对象的指针。

例十一:
structMyStruct
{
inta;
intb;
intc;
}
MyStructss={20,30,40};
//声明了结构对象ss,并把ss的三个成员初始化为20,30和40。
MyStruct*ptr=&ss;
//声明了一个指向结构对象ss的指针。它的类型是MyStruct*,它指向的类型是MyStruct。
int*pstr=(int*)&ss;
//声明了一个指向结构对象ss的指针。但是它的类型和它指向的类型和ptr是不同的。请问怎样通过指针ptr来访问ss的三个成员变量?

答案:

ptr->a;
ptr->b;
ptr->c;

又请问怎样通过指针pstr来访问ss的三个成员变量?

答案:

*pstr;//访问了ss的成员a。
*(pstr+1);//访问了ss的成员b。
*(pstr+2)//访问了ss的成员c。

虽然我在我的MSVC++6.0上调式过上述代码,但是要知道,这样使用pstr来访问结构成员是不正规的,为了说明为什么不正规,让我们看看怎样通过指针来访问数组的各个单元:

例十二:

intarray[3]={35,56,37};
int*pa=array;

通过指针pa访问数组array的三个单元的方法是:

*pa;//访问了第0号单元
*(pa+1);//访问了第1号单元
*(pa+2);//访问了第2号单元

从格式上看倒是与通过指针访问结构成员的不正规方法的格式一样。

所有的C/C++编译器在排列数组的单元时,总是把各个数组单元存放在连续的存储区里,单元和单元之间没有空隙。但在存放结构对象的各个成员时,在某种编译环境下,可能会需要字对齐或双字对齐或者是别的什么对齐,需要在相邻两个成员之间加若干个"填充字节",这就导致各个成员之间可能会有若干个字节的空隙。

所以,在例十二中,即使*pstr访问到了结构对象ss的第一个成员变量a,也不能保证*(pstr+1)就一定能访问到结构成员b。因为成员a和成员b之间可能会有若干填充字节,说不定*(pstr+1)就正好访问到了这些填充字节呢。这也证明了指针的灵活性。要是你的目的就是想看看各个结构成员之间到底有没有填充字节,嘿,这倒是个不错的方法。

通过指针访问结构成员的正确方法应该是象例十二中使用指针ptr的方法。

指针和函数的关系 可以把一个指针声明成为一个指向函数的指针。
intfun1(char*,int);
int(*pfun1)(char*,int);
pfun1=fun1;
….
….
inta=(*pfun1)("abcdefg",7);//通过函数指针调用函数。 可以把指针作为函数的形参。在函数调用语句中,可以用指针表达式来作为实参。

例十三:
intfun(char*);
inta;
charstr[]="abcdefghijklmn";
a=fun(str);


intfun(char*s)
{
intnum=0;
for(inti=0;i{
num+=*s;s++;
}
returnnum;
} 这个例子中的函数fun统计一个字符串中各个字符的ASCII码值之和。前面说了,数组的名字也是一个指针。在函数调用中,当把str作为实参传递给形参s后,实际是把str的值传递给了s,s所指向的地址就和str所指向的地址一致,但是str和s各自占用各自的存储空间。在函数体内对s进行自加1运算,并不意味着同时对str进行了自加1运算。

指针类型转换 当我们初始化一个指针或给一个指针赋值时,赋值号的左边是一个指针,赋值号的右边是一个指针表达式。在我们前面所举的例子中,绝大多数情况下,指针的类型和指针表达式的类型是一样的,指针所指向的类型和指针表达式所指向的类型是一样的。

例十四:

1、floatf=12.3;

2、float*fptr=&f;

3、int*p;

在上面的例子中,假如我们想让指针p指向实数f,应该怎么搞?是用下面的语句吗?

p=&f;

不对。因为指针p的类型是int*,它指向的类型是int。表达式&f的结果是一个指针,指针的类型是float*,它指向的类型是float。两者不一致,直接赋值的方法是不行的。至少在我的MSVC++6.0上,对指针的赋值语句要求赋值号两边的类型一致,所指向的类型也一致,其它的编译器上我没试过,大家可以试试。为了实现我们的目的,需要进行"强制类型转换":
p=(int*)&f; 如果有一个指针p,我们需要把它的类型和所指向的类型改为TYEP*TYPE, 那么语法格式是:

(TYPE*)p;

这样强制类型转换的结果是一个新指针,该新指针的类型是TYPE*,它指向的类型是TYPE,它指向的地址就是原指针指向的地址。而原来的指针p的一切属性都没有被修改。

一个函数如果使用了指针作为形参,那么在函数调用语句的实参和形参的结合过程中,也会发生指针类型的转换。

例十五:
voidfun(char*);
inta=125,b;
fun((char*)&a);


voidfun(char*s)
{
charc;
c=*(s+3);*(s+3)=*(s+0);*(s+0)=c;
c=*(s+2);*(s+2)=*(s+1);*(s+1)=c;
}
} 注意这是一个32位程序,故int类型占了四个字节,char类型占一个字节。函数fun的作用是把一个整数的四个字节的顺序来个颠倒。注意到了吗?在函数调用语句中,实参&a的结果是一个指针,它的类型是int*,它指向的类型是int。形参这个指针的类型是char*,它指向的类型是char。这样,在实参和形参的结合过程中,我们必须进行一次从int*类型到char*类型的转换。结合这个例子,我们可以这样来想象编译器进行转换的过程:编译器先构造一个临时指针char*temp, 然后执行temp=(char*)&a,最后再把temp的值传递给s。所以最后的结果是:s的类型是char*,它指向的类型是char,它指向的地址就是a的首地址。

我们已经知道,指针的值就是指针指向的地址,在32位程序中,指针的值其实是一个32位整数。那可不可以把一个整数当作指针的值直接赋给指针呢?就象下面的语句:
unsignedinta;
TYPE*ptr;//TYPE是int,char或结构类型等等类型。


a=20345686;
ptr=20345686;//我们的目的是要使指针ptr指向地址20345686(十进制

ptr=a;//我们的目的是要使指针ptr指向地址20345686(十进制) 编译一下吧。结果发现后面两条语句全是错的。那么我们的目的就不能达到了吗?不,还有办法:
unsignedinta;
TYPE*ptr;//TYPE是int,char或结构类型等等类型。


a=某个数,这个数必须代表一个合法的地址;
ptr=(TYPE*)a;//呵呵,这就可以了。 严格说来这里的(TYPE*)和指针类型转换中的(TYPE*)还不一样。这里的(TYPE*)的意思是把无符号整数a的值当作一个地址来看待。上面强调了a的值必须代表一个合法的地址,否则的话,在你使用ptr的时候,就会出现非法操作错误。

想想能不能反过来,把指针指向的地址即指针的值当作一个整数取出来。完 全可以。下面的例子演示了把一个指针的值当作一个整数取出来,然后再把这个整数当作一个地址赋给一个指针:

例十六:
inta=123,b;
int*ptr=&a;
char*str;
b=(int)ptr;//把指针ptr的值当作一个整数取出来。
str=(char*)b;//把这个整数的值当作一个地址赋给指针str。 现在我们已经知道了,可以把指针的值当作一个整数取出来,也可以把一个整数值当作地址赋给一个指针。 指针的安全问题 看下面的例子:

例十七:
chars=’a';
int*ptr;
ptr=(int*)&s;
*ptr=1298;
指针ptr是一个int*类型的指针,它指向的类型是int。它指向的地址就是s的首地址。在32位程序中,s占一个字节,int类型占四个字节。最后一条语句不但改变了s所占的一个字节,还把和s相临的高地址方向的三个字节也改变了。这三个字节是干什么的?只有编译程序知道,而写程序的人是不太可能知道的。也许这三个字节里存储了非常重要的数据,也许这三个字节里正好是程序的一条代码,而由于你对指针的马虎应用,这三个字节的值被改变了!这会造成崩溃性的错误。

让我们再来看一例:

例十八:

1、chara;

2、int*ptr=&a;


3、ptr++;

4、*ptr=115;

该例子完全可以通过编译,并能执行。但是看到没有?第3句对指针ptr进行自加1运算后,ptr指向了和整形变量a相邻的高地址方向的一块存储区。这块存储区里是什么?我们不知道。有可能它是一个非常重要的数据,甚至可能是一条代码。而第4句竟然往这片存储区里写入一个数据!这是严重的错误。所以在使用指针时,程序员心里必须非常清楚:我的指针究竟指向了哪里。在用指针访问数组的时候,也要注意不要超出数组的低端和高端界限,否则也会造成类似的错误。

在指针的强制类型转换:ptr1=(TYPE*)ptr2中,如果sizeof(ptr2的类型)大于sizeof(ptr1的类型),那么在使用指针ptr1来访问ptr2所指向的存储区时是安全的。如果sizeof(ptr2的类型)小于sizeof(ptr1的类型),那么在使用指针ptr1来访问ptr2所指向的存储区时是不安全的。至于为什么,读者结合例十七来想一想,应该会明白的。