2006年09月29日

动态联编

面向对象程序设计的基本观点是用程式来仿真大千世界,这使得它的各种根本特性非常人性化,如封装、继承、多态等等,而虚拟函数就是c++中实现多态性的主将。为了实现多态性,c++编译器也革命性地提供了动态联编(或叫晚捆绑)这一特征。

  虚拟函数亦是mfc编程的关键所在,mfc编程主要有两种方法:一是响应各种消息,进行对应的消息处理。二就是重载并改写虚拟函数,来实现自己的某些要求或改变系统的某些默认处理。

  虚函数的地位是如此的重要,对它进行穷根究底,力求能知其然并知其所以然 对我们编程能力的提高大有好处。下面且听我道来。

  多态性和动态联编的实现过程分析

  一、基础略提(限于篇幅,请参阅相应的c++书籍):

  1、多态性:使用基础类的指针动态调用其派生类中函数的特性。

  2、动态联编:在运行阶段,才将函数的调用与对应的函数体进行连接的方式,又叫运行时联编或晚捆绑。

  二、过程描述:

  1、编译器发现一个类中有虚函数,编译器会立即为此类生成虚拟函数表 vtable(后面有对vtable的分析)。虚拟函数表的各表项为指向对应虚拟函数的指针。

  2、编译器在此类中隐含插入一个指针vptr(对vc编译器来说,它插在类的第一个位置上)。

  有一个办法可以让你感知这个隐含指针的存在,虽然你不能在类中直接看到它,但你可以比较一下含有虚拟函数时的类的尺寸和没有虚拟函数时的类的尺寸,你能够发现,这个指针确实存在。

  class cnovirtualfun
   {
    private:
    long lmember;
    public:
    long getmembervalue();
   } class chavevirtualfun
   {
    private:
     long lmember;
    public:
     virtual long getmembervalue();
    }

   cnovirtualfun obj;
   sizeof(obj) -> == 4;
   chavevirtualfun obj;
   sizeof(obj) -> == 8;

  3、在调用此类的构造函数时,在类的构造函数中,编译器会隐含执行vptr与vtable的关联代码,将vptr指向对应的vtable。这就将类与此类的vtable联系了起来。

  4、在调用类的构造函数时,指向基础类的指针此时已经变成指向具体的类的this指针,这样依靠此this指针即可得到正确的vtable,从而实现了多态性。在此时才能真正与函数体进行连接,这就是动态联编。
  三、vtable 分析:

  分析1:虚拟函数表包含此类及其父类的所有虚拟函数的地址。如果它没有重载父类的虚拟函数,vtable中对应表项指向其父类的此函数。反之,指向重载后的此函数。

  分析2:虚拟函数被继承后仍旧是虚拟函数,虚拟函数非常严格地按出现的顺序在 vtable 中排序,所以确定的虚拟函数对应 vtable 中一个固定的位置n,n是一个在编译时就确定的常量。所以,使用vptr加上对应的n,就可得到对应函数的入口地址。

  四、编译器调用虚拟函数的汇编码(参考think in c++):

  push funparam ;先将函数参数压栈

  push si ;将this指针压栈,以确保在当前类上操作

  mov bx,word ptr[si] ;因为vc++编译器将vptr放在类的第一个位置上,所以bx内为vptr

  call word ptr[bx+n] ;调用虚拟函数。n = 所调用的虚拟函数在对应 vtable 中的位置

  纯虚函数:

  一、引入原因:

  1、为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。

  2、在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。

  为了解决上述问题,引入了纯虚函数的概念,将函数定义为纯虚函数(方法:virtual returntype function()= 0;),则编译器要求在派生类中必须予以重载以实现多态性。同时含有纯虚拟函数的类称为抽象类,它不能生成对象。这样就很好地解决了上述两个问题。

  二、纯虚函数实质:

  1、类中含有纯虚函数则它的vtable表不完全,有一个空位,所以,不能生成对象(编译器绝对不允许有调用一个不存在函数的可能)。在它的派生类中,除非重载这个函数,否则,此派生类的vtable表亦不完整,亦不能生成对象,即它也成为一个纯虚基类。

  虚函数与构造、析构函数:

  1、构造函数本身不能是虚拟函数;并且虚机制在构造函数中不起作用(在构造函数中的虚拟函数只会调用它的本地版本)。

  想一想,在基类构造函数中使用虚机制,则可能会调用到子类,此时子类尚未生成,有何后果!?。

  2、析构函数本身常常要求是虚拟函数;但虚机制在析构函数中不起作用。

  若类中使用了虚拟函数,析构函数一定要是虚拟函数,比如使用虚拟机制调用delete,没有虚拟的析构函数,怎能保证delete的是你希望delete的对象。

  虚机制也不能在析构函数中生效,因为可能会引起调用已经被delete掉的类的虚拟函数的问题。

  对象切片:

  向上映射(子类被映射到父类)的时候,会发生子类的vtable 完全变成父类的vtable的情况。这就是对象切片。

  原因:向上映射的时候,接口会变窄,而编译器绝对不允许有调用一个不存在函数的可能,所以,子类中新派生的虚拟函数的入口在vtable中会被强行“切”掉,从而出现上述情况。

  虚拟函数使用的缺点

  优点讲了一大堆,现在谈一下缺点,虚函数最主要的缺点是执行效率较低,看一看虚拟函数引发的多态性的实现过程,你就能体会到其中的原因。

2006年09月28日

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

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

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

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

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

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

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

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

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月11日

创建链表

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所指向的存储区时是不安全的。至于为什么,读者结合例十七来想一想,应该会明白的。

2006年09月07日

         以前学习过指针方面的东西,以为自己比较了解指针了,在千人学习中才发现掌握指针我还差的远呢,从指针开始我要好好的努力了,不要再贪玩了。

 

1。直接引用和间接应用:

int a,b;

int *p;

p=&b;

a=3;   //直接引用变量a

*p=5; //间接引用变量b

2.指向变量的指针变量:

指针变量作为函数参数

将两调换2个整数的值

3.指针与一维数组:

指向数组的指针是数组的首地址,指向数组元素的指针是数组元素的地址。

int a[100];

int * iptr=a;

 表示第i个元素:

a[i]  <=> *(a+i) <=> iptr[i] <=> *(iptr+i)

表示第i个元素的地址:

&a[i] <=> a+i <=> iptr+i <=>&iptr[i]

数组名做参数:

一旦把数组作为参数传递到函数中,则在栈上定义了指针,可以对该指针进行递增、递减操作。

4.指针与字符串

指向字符串的指针变量

用指针变量实现字符串的操作

#include<stdio.h>

void main()

{

    char * string="hello";

    printf("%s\n",string);

}

用指针法引用字符串中的字符:

#include<stdio.h>

void main()
{
 char *s1,*s2,str1[30],str2[10];

 printf("请输入第一个字符串:");
 gets(str1);

 printf("\n请输入第二个字符串:");
 gets(str2);

 for(s1=str1;*s1!=’\0′;s1++);
 for(s2=str2;*s2!=’\0′;)
 {
  *(s1++)=*(s2++);
 }
  *s1=’\0′;
  printf("%s\n",str1);
}

5.返回值为指针型数据的函数

定义:

int *func(inx,int y);

6.指向函数的指针

定义:

int(*p)();

表示p是一个指向函数的指针变量,此函数的返回值为int型。注意,*p两侧的括号不能省略。

定义指向函数的指针变量后,就可以令它等于函数的入口地址。当指向函数的指针变量等于一个函数名的时候,表示该指针变量指向函数。此时可以通过指向函数的指针变量调用该函数。

调用时:(*p)(实参表);

7.指针数组:

定义:

int *a[5];

表示指针数组a的每个元素都是指向int型变量的指针变量。