结构体类型完全归纳

2023-05-16

结构体类型
目录

基本概述
一、结构体类型变量的定义方法及其初始化
1. 定义结构体类型变量的方法
2. 结构体变量的初始化
二、结构体变量的引用
三、结构体数组
1. 定义结构体数组
2. 结构体数组应用举例
四、 指向结构体变量的指针
1.类型一
2. 类型二
五、 结构体类型数据作为函数参数
1.方法一
2.方法二
3.方法三
六、动态分配和撤销内存的new和delete
基本概述
一、结构体类型变量的定义方法及其初始化
1. 定义结构体类型变量的方法
2. 结构体变量的初始化
二、结构体变量的引用
三、结构体数组
1. 定义结构体数组
2. 结构体数组应用举例
四、 指向结构体变量的指针
1.类型一
2. 类型二
五、 结构体类型数据作为函数参数
1.方法一
2.方法二
3.方法三
六、动态分配和撤销内存的new和delete

七、使用实例

1、结构体中的指针函数以及“结构体”的继承

2、结构体中指向函数的指针

  C++提供了许多种基本的数据类型(如int、float、double、char等)供用户使用。但是由于程序需要处理的问题往往比较复杂,而且呈多样化,已有的数据类型显得不能满足使用要求。因此C++允许用户根据需要自己声明一些类型,用户可以自己声明的类型还有结构体类型(structure)、共用体类型(union)、枚举类型(enumeration)、类类型(class )等,这些统称为用户自定义类型(user-defined type,UDT)。
  在一个组合项中包含若干个类型不同(当然也可以相同)的数据项。C和C++允许用户自己指定这样一种数据类型,它称为结构体。它相当于其他高级语言中的记录(record)。例如,可以通过下面的声明来建立数据类型。

  struct Student//声明一个结构体类型Student

  {

  int num;//包括一个整型变量num

  char name[20];//包括一个字符数组name,可以容纳20个字符

  char ***;//包括一个字符变量***

  int age;//包括一个整型变量age

  float score;//包括一个单精度型变量

  char addr[30];//包括一个字符数组addr,可以容纳30个字符

  };//最后有一个分号

  这样,程序设计者就声明了一个新的结构体类型Student(struct是声明结构体类型时所必须使用的关键字,不能省略),它向编译系统声明: 这是一种结构体类型,它包括num, name, ***, age, score, addr等不同类型的数据项。应当说明Student是一个类型名,它和系统提供的标准类型(如int、char、float、double 一样,都可以用来定义变量,只不过结构体类型需要事先由用户自己声明而已。

  声明一个结构体类型的一般形式为

  struct 结构体类型名{成员表列};

  结构体类型名用来作结构体类型的标志。上面的声明中Student就是结构体类型名。大括号内是该结构体中的全部成员(member),由它们组成一个特定的结构体。上例中的num,name,***,score等都是结构体中的成员。在声明一个结构体类型时必须对各成员都进行类型声明即类型名成员名;每一个成员也称为结构体中的一个域(field)。成员表列又称为域表。

  成员名的定名规则与变量名的定名规则相同

  声明结构体类型的位置一般在文件的开头,在所有函数(包括main函数)之前,以便本文件中所有的函数都能利用它来定义变量。当然也可以在函数中声明结构体类型。

  在C语言中,结构体的成员只能是数据(如上面例子中所表示的那样)。

  C++对此加以扩充,结构体的成员既可以包括数据(即数据成员),又可以包括函数(即函数成员),以适应面向对象的程序设计。

编辑本段一、结构体类型变量的定义方法及其初始化
  前面只是指定了一种结构体类型,它相当于一个模型,但其中并无具体数据,系统也不为之分配实际的内存单元为了能在程序中使用结构体类型的数据,应当定义结构体类型的变量,并在其中存放具体的数据。
1. 定义结构体类型变量的方法
  (1) 先声明结构体类型再定义变量名如上面已定义了一个结构体类型Student,可以用它来定义结构体变量。如
  Student student1, student2;

  以上定义了student1和student2为结构体类型Student的变量,即它们具有Student类型的结构。在定义了结构体变量后,系统会为之分配内存单元。例如student1和student2在内存中各占63个字节

  (4+20+1+4+4+30=63)。

  (2) 在声明类型的同时定义变量

  例如:

  struct Student//声明结构体类型Student

  {

  int num;

  char name[20];

  char ***;

  int age;

  float score;

  char addr[30];

  }student1,student2;//定义两个结构体类型Student的变量student1,student2

  这种形式的定义的一般形式为

  struct 结构体名

  {

  成员表列

  }变量名表列;

  (3) 直接定义结构体类型变量

  其一般形式为

  struct//注意没有结构体类型名

  {

  成员表列

  } 变量名表列;

  这种方法虽然合法,但很少使用。提倡先定义类型后定义变量的第(1)种方法。

  在程序比较简单,结构体类型只在本文件中使用的情况下,也可以用第(2)种方法。

  关于结构体类型,有几点要说明:

  (1) 不要误认为凡是结构体类型都有相同的结构。实际上,每一种结构体类型都有自己的结构,可以定义出许多种具体的结构体类型。

  (2) 类型与变量是不同的概念,不要混淆。只能对结构体变量中的成员赋值,而不能对结构体类型赋值。在编译时,是不会为类型分配空间的,只为变量分配空间。

  (3) 对结构体中的成员(即“域”),可以单独使用,它的作用与地位相当于普通变量。

  (4) 成员也可以是一个结构体变量。

  如

  struct Date //声明一个结构体类型Date

  {

  int month;

  int day;

  int year;

  };

  struct Student

  //声明一个结构体类型Student

  {

  int num;

  char name[20];

  char ***;

  int age;

  Date birthday;

  char addr[30];

  }student1,student2;

  //定义student1和student2为结构体类型Student的变量。

  (5) 结构体中的成员名可以与程序中的变量名相同,但二者没有关系。

  例如,程序中可以另定义一个整型变量num,它与student中的num是两回事,互不影响。

2. 结构体变量的初始化
  和其他类型变量一样,对结构体变量可以在定义时指定初始值。如
  struct Student

  {

  int num;

  char name[20];

  char ***;

  int age;

  float score;

  char addr[30];

  }student1={10001,"Zhang Xin",'M',19,90.5,"Shanghai"};

  这样,变量student1中的数据如图7.2中所示。也可以采取声明类型与定义变量分开的形式,在定义变量时进行初始化:

  student2= student1;

编辑本段二、结构体变量的引用
  在定义了结构体变量以后,当然可以引用这个变量。
  (1) 可以将一个结构体变量的值赋给另一个具有相同结构的结构体变量。

  如上面的student1和student2都是student类型的变量,可以这样赋值:

  student1= student2;

  (2) 可以引用一个结构体变量中的一个成员的值。

  例如, student1.num表示结构体变量student1中的成员的值,如果student1的值如图7.2所示,

  则student1.num的值为10001。

  引用结构体变量中成员的一般方式为

  结构体变量名.成员名

  例如可以这样对变量的成员赋值:

  student1.num=10010;

  (3) 如果成员本身也是一个结构体类型,则要用若干个成员运算符,一级一级地找到最低一级的成员。

  例如,对上面定义的结构体变量student1, 可以这样访问各成员:

  student1.num (引用结构体变量student1中的num成员)

  如果想引用student1变量中的birthday成员中的month成员,不能写成student1.month,

  必须逐级引用,即

  student1.birthday.month=12;

  (引用结构体变量student1中的birthday成员中的month成员)

  (4) 不能将一个结构体变量作为一个整体进行输入和输出。

  例如,已定义student1和student2为结构体变量,并且它们已有值。不能企图这样输出结构体变量中的各成员的值:

  cin>>student1;

  只能对结构体变量中的各个成员分别进行输入和输出。

  (5) 对结构体变量的成员可以像普通变量一样进行各种运算(根据其类型决定可以进行的运算种类)。例如

  student2.score=student1.score;

  sum=student1.score+student2.score;

  student1.age++;

  ++student1.age;

  由于“.”运算符的优先级最高,student1.age++相当于(student1.age)++ 。++是对student1.age进行自加运算,而不是先对age进行自加运算。

  (6) 可以引用结构体变量成员的地址,也可以引用结构体变量的地址。如

  cout<<&student1;//输出student1的首地址

  cout<<&student1.age;//输出student1.age的地址

  结构体变量的地址主要用作函数参数,将结构体变量的地址传递给形参。

  例 引用结构体变量中的成员。

  #include <iostream>

  using namespace std;

  struct Date//声明结构体类型Date

  {int month;

  int day;

  int year;

  };

  struct Student//声明结构体类型Student

  {int num;

  char ***;

  Date birthday;//声明birthday为Date类型的成员

  float score;

  }student1,student2={10002,″Wang Li″,′f′,5,23,1982,89.5};

  //定义Student 类型的变量student1,student2,并对student2初始化

  int main( )

  { student1=student2;//将student2各成员的值赋予student1的相应成员

  cout<<student1.num<<endl;//输出student1中的num成员的值

  cout<<student1.***<<endl;//输出student1中的***成员的值cout<<student1.birthday.month<<′/′<<student1.birthday.day<<′/′ <<student1.birthday.year<<endl;//输出student1中的birthday各成员的值

  cout<<student1.score<<endl;

  return 0; }

  运行结果如下:

  10002 Wang Li f 5/23/1982 89.5 7.1.4

编辑本段三、结构体数组
  一个结构体变量中可以存放一组数据(如一个学生的学号、姓名、成绩等数据)。如果有10个学生的数据需要参加运算,显然应该用数组,这就是结构体数组。结构体数组与以前介绍过的数值型数组的不同之处在于:
  每个数组元素都是一个结构体类型的数据,它们都分别包括各个成员项。

1. 定义结构体数组
  定义结构体数组和定义结构体变量的方法相仿,定义结构体数组时只需声明其为数组即可。如
  struct Student//声明结构体类型Student

  { int num;

  char name[20];

  char ***;

  int age;

  float score;

  char addr[30];

  };

  Student stu[3];//定义Student类型的数组stu

  也可以直接定义一个结构体数组,如

  struct Student

  { int num;

  char name[20];

  char ***;

  int age;

  float score;

  char addr[30];

  }stu[3];

  或

  struct

  { int num;

  char name[20];

  char ***;

  int age;

  float score;

  char addr[30];

  }stu[3];

  结构体数组的初始化与其他类型的数组一样,对结构体数组可以初始化。如

  struct Student

  {

  int num;

  char name[20];

  char ***;

  int age;

  float score;

  char addr[30];

  }stu[3]={

  {10101,″Li Lin″, ′M′, 18,87.5, ″103 Beijing Road″},

  {10102,″Zhang Fun″,′M′,19,99, ″130 Shanghai Road″},

  {10104,″Wang Min″,′F′, 20,78.5, ″1010 Zhongshan Road″}};

  定义数组stu时,也可以不指定元素个数,即写成以下形式:

  stu[ ]={{…},{…},{…}};

  编译时,系统会根据给出初值的结构体常量的个数来确定数组元素的个数。

  一个结构体常量应包括结构体中全部成员的值。当然,数组的初始化也可以用以下形式:

  Student stu[ ]={{…},{…},{…}};//已事先声明了结构体类型Student

  由上可以看到,结构体数组初始化的一般形式是在所定义的数组名的后面加上 ={初值表列};

2. 结构体数组应用举例
  下面举一个简单的例子来说明结构体数组的定义和引用。
  例7.2对候选人得票的统计程序。设有3个候选人,最终只能有1人当选为领导。今有10个人参加投票,从键盘先后输入这10个人所投的候选人的名字,要求最后输出这3个候选人的得票结果。可以定义一个候选人结构体数组,包括3个元素,在每个元素中存放有关的数据。

  程序如下:

  #include <iostream>

  struct Person//声明结构体类型Person

  {

  char name[20];

  int count;

  };

  int main( )

  {

  Person leader[3]={″Li″,0,″Zhang″,0,″Fun″,0};

  //定义Person类型的数组,内容为3个候选人的姓名和当前的得票数

  int i,j;

  char leader_name[20];

  //leader_name为投票人所选的人的姓名

  for(i=0;i<10;i++) {cin>>leader_name;

  //先后输入10张票上所写的姓名

  for(j=0;j<3;j++)//将票上姓名与3个候选人的姓名比较

  if(strcmp(leader_name,leader[j].name)==0) leader[j].count++;

  //如果与某一候选人的姓名相同,就给他加一票

  }

  cout<<endl;

  for(i=0;i<3;i++)//输出3个候选人的姓名与最后得票数

  {cout<<leader[i].name<<″:″<<leader[i].count<<endl;}

  return 0;

  }

  运行情况如下:

  Zhang↙ (每次输入一个候选人的姓名)

  Li↙

  Fun↙

  Li↙

  Zhang↙

  Li↙

  Zhang↙

  Li↙

  Fun↙

  Wang↙

  Li:4 (输出3个候选人的姓名与最后得票数)

  Zhang:3

  Fun:2

  程序定义一个全局的结构体数组leader,它有3个元素,每一元素包含两个成员,即name(姓名)和count(得票数)。在定义数组时使之初始化,使3位候选人的票数都先置零。

  在这个例子中,也可以不用字符数组而用string方法的字符串变量来存放姓名数据,程序可修改如下:

  #include <iostream>

  #include <string>

  using namespace std;

  struct Person

  {

  string name;//成员name为字符串变量

  int count;

  };

  int main( )

  {

  Person leader[3]={″Li″,0,″Zhang″,0,″Fun″,0};

  int i,j;

  string leader_name;// leader_name为字符串变量

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

  {

  cin>>leader_name;

  for(j=0;j<3;j++)

  if(leader_name==leader[j].name) leader[j].count++

  //用“==”进行比较

  }

  cout<<endl;

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

  {cout<<leader[i].name<<″:″<<leader[i].count<<endl;}

  return 0;

  }

编辑本段四、 指向结构体变量的指针
  一个结构体变量的指针就是该变量所占据的内存段的起始地址。可以设一个指针变量,用来指向一个结构体变量,此时该指针变量的值是结构体变量的起始地址。
  指针变量也可以用来指向结构体数组中的元素。

1.类型一
  指向结构体变量的指针引用结构体变量中的成员
  下面通过一个简单例子来说明指向结构体变量的指针变量的应用。

  例 指向结构体变量的指针的应用。

  #include <iostream>

  #include <string>

  using namespace std;

  int main( )

  {

  struct Student

  //声明结构体类型student

  { int num;

  char ***;

  float score;

  };

  Student stu;

  //定义Student类型的变量stu

  Student *p=&stu;

  //定义p为指向Student类型数据的指针变量并指向stu

  stu.num=10301;

  //对string变量可以直接赋值

  stu.***=′f′;

  stu.score=89.5;

  cout<<stu. num<<″ ″<<″ ″<<stu.***<<″ ″<<

  stu.score<<endl;

  cout<<p -> num<<″ ″<<(*p).name<<″ ″<<(*p).***<<″ ″<<

  (*p).score<<endl;

  return 0;

  }

  程序运行结果如下:

  10301 Wang Fun f 89.5 (通过结构体变量名引用成员)

  10301 Wang Fun f 89.5 (通过指针引用结构体变量中的成员)

  两个cout语句输出的结果是相同的。

  为了使用方便和使之直观,C++提供了指向结构体变量的运算符->,

  例如p->num表示指针p当前指向的结构体变量中的成员num。

  p->num 和(*p).num等价。

  同样,p->name等价于(*p).name。

  也就是说,以下3种形式等价:

  ① 结构体变量.成员名。如stu.num。

  ② (*p).成员名。如(*p).num。

  ③ p->成员名。如p->num。

  “->”称为指向运算符。

  请分析以下几种运算:

  p->n 得到p指向的结构体变量中的成员n的值。

  p->n++ 得到p指向的结构体变量中的成员n的值,用完该值后使它加1。

  ++p->n 得到p指向的结构体变量中的成员n的值,并使之加1,然后再使用它。

2. 类型二
  用结构体变量和指向结构体变量的指针构成链表
  链表是一种常见的重要的数据结构。

  链表有一个“头指针”变量,以head表示,它存放一个地址。该地址指向一个元素。

  链表中的每一个元素称为“结点”,每个结点都应包括两个部分:

  一是用户需要用的实际数据,

  二是下一个结点的地址。

  可以看到链表中各元素在内存中的存储单元可以是不连续的。要找某一元素,可以先找到上一个元素,根据它提供的下一元素地址找到下一个元素。

  可以看到,这种链表的数据结构,必须利用结构体变量和指针才能实现。

  可以声明一个结构体类型,包含两种成员,一种是用户需要用的实际数据,另一种是用来存放下一结点地址的指针变量。

  例如,可以设计这样一个结构体类型:

  struct Student

  {

  int num;

  float score;

  Student *next;//next指向Student结构体变量

  };

  其中成员num和score是用户需要用到的数据,相当于图7.8结点中的A,B,C,D。next是指针类型的成员,它指向Student类型数据(就是next所在的结构体类型)。用这种方法就可以建立链表。

  每一个结点都属于Student类型,在它的成员next中存放下一个结点的地址,程序设计者不必知道各结点的具体地址,只要保证能将下一个结点的地址放到前一结点的成员next中即可。

  下面通过一个例子来说明如何建立和输出一个简单链表。

  例 建立一个简单链表,它由3个学生数据的结点组成。输出各结点中的数据。

  #define NULL 0

  #include <iostream>

  struct Student

  {

  long num;

  float score;

  struct Student *next;

  };

  int main( )

  {

  Student a,b,c,*head,*p;

  a. num=31001;

  a.score=89.5;//对结点a的num和score成员赋值

  b. num=31003;

  b.score=90;//对结点b的num和score成员赋值

  c. num=31007;

  c.score=85;//对结点c的num和score成员赋值

  head=&a;//将结点a的起始地址赋给头指针head

  a.next=&b; //将结点b的起始地址赋给a结点的next成员

  b.next=&c; //将结点c的起始地址赋给b结点的next成员

  c.next=NULL; //结点的next成员不存放其他结点地址

  p=head; //使p指针指向a结点

  do

  {

  cout<<p->num<<″ ″<<p->score<<endl; //输出p指向的结点的数据

  p=p->next; //使p指向下一个结点

  } while (p!=NULL); //输出完c结点后p的值为NULL

  return 0;

  }

  请读者考虑:

  ①各个结点是怎样构成链表的。

  ②p起什么作用?

  本例是比较简单的,所有结点(结构体变量)都是在程序中定义的,不是临时开辟的,也不能用完后释放,这种链表称为静态链表。对各结点既可以通过上一个结点的next指针去访问,也可以直接通过结构体变量名a,b,c去访问。

  动态链表则是指各结点是可以随时插入和删除的,这些结点并没有变量名,只能先找到上一个结点,才能根据它提供的下一结点的地址找到下一个结点。只有提供第一个结点的地址,即头指针head,才能访问整个链表。如同一条铁链一样,一环扣一环,中间是不能断开的。

编辑本段五、 结构体类型数据作为函数参数
  将一个结构体变量中的数据传递给另一个函数,有下列3种方法:
  (1) 用结构体变量名作参数。一般较少用这种方法。

  (2) 用指向结构体变量的指针作实参,将结构体变量的地址传给形参。

  (3) 用结构体变量的引用变量作函数参数。

  下面通过一个简单的例子来说明,并对它们进行比较。

  例有一个结构体变量stu,内含学生学号、姓名和3门课的成绩。要求在main函数中为各成员赋值,在另一函数print中将它们的值输出。

1.方法一
  用结构体变量作函数参数
  #include <iostream>

  #include <string>

  using namespace std;

  struct Student//声明结构体类型Student

  { int num;

  float score[3];

  };

  int main( )

  {

  void print(Student);//函数声明,形参类型为结构体Student

  Student stu;//定义结构体变量

  stu.num=12345;//以下5行对结构体变量各成员赋值

  stu.score[0]=67.5;

  stu.score[1]=89;

  stu.score[2]=78.5;

  print(stu);//调用print函数,输出stu各成员的值

  return 0;

  }

  void print(Student st)

  {

  cout<<st.num<<″ ″<<″ ″<<st.score[0]

  <<″ ″ <<st.score[1]<<″ ″<<st.score[2]<<endl;

  }

  运行结果为

  12345 67.5 89 78.5

2.方法二
  用指向结构体变量的指针作实参

  #include <iostream>

  #include <string>

  using namespace std;

  struct Student

  {

  int num; string name;//用string类型定义字符串变量

  float score[3];

  }stu={12345,″Li Fung″,67.5,89,78.5};//定义结构体student变量stu并赋初值

  int main( )

  {

  void print(Student *);//函数声明,形参为指向Student类型数据的指针变量

  Student *pt=&stu;//定义基类型为Student的指针变量pt,并指向stu

  print(pt);//实参为指向Student类数据的指针变量

  return 0;

  }

  //定义函数,形参p是基类型为Student的指针变量

  void print(Student *p)

  {

  cout<<p->num<<″ ″<<p->name<<″ ″<<p->score[0]<<″ ″ <<

  p->score[1]<<″ ″<<p->score[2]<<endl;

  }

  调用print函数时,实参指针变量pt将stu的起始地址传送给形参p(p也是基类型为student的指针变量)。这样形参p也就指向stu,见图7.10。

  在print函数中输出p所指向的结构体变量的各个成员值,它们也就是stu的成员值。在main函数中也可以不定义指针变量pt,而在调用print函数时以&stu作为实参,把stu的起始地址传给实参p。

3.方法三
  用结构体变量的引用作函数参数
  #include <iostream>

  #include <string>

  using namespace std;

  struct Student

  {

  int num;

  string name;

  float score[3];

  }stu={12345,″Li Li″,67.5,89,78.5};

  void main( )

  {

  void print(Student &);

  //函数声明,形参为Student类型变量的引用

  print(stu);

  //实参为结构体Student变量

  }

  //函数定义,形参为结构体Student变量的引用

  void print(Student &stud)

  {

  cout<<stud.num<<″ ″<<″ ″<<stud.score[0]

  <<″ ″ <<stud.score[1]<<″ ″<<stud.score[2]<<endl;

  }

  程序(1)用结构体变量作实参和形参,程序直观易懂,效率是不高的。

  程序(2)采用指针变量作为实参和形参,空间和时间的开销都很小,效率较高。但程序(2)不如程序(1)那样直接。

  程序(3)的实参是结构体Student类型变量,而形参用Student类型的引用,虚实结合时传递的是stu的地址,因而效率较高。它兼有(1)和(2)的优点。

  引用变量主要用作函数参数,它可以提高效率,而且保持程序良好的可读性。在本例中用了string方法定义字符串变量,在某些C++系统中目前不能运行这些程序,读者可以修改程序,使之能在自己所用的系统中运行。

编辑本段六、动态分配和撤销内存的new和delete
  在软件开发过程中,常常需要动态地分配和撤销内存空间,例如对动态链表中结点的插入与删除。
  在C语言中是利用库函数malloc和free来分配和撤销内存空间的。

  sizeof

  C++提供了较简便而功能较强的运算符new和delete来取代malloc和free函数。

  注意: new和delete是运算符,不是函数,因此执行效率高。

  虽然为了与C语言兼容,C++仍保留malloc和free函数,但建议用户不用malloc和free函数,而用new和delete运算符。

  new运算符的例子:

  new int;//开辟一个存放整数的存储空间,返回一个指向该存储空间的地址(即指针)

  new int(100);//开辟一个存放整数的空间,并指定该整数的初值为100,返回一个指向该存储空间的地址

  new char[10];//开辟一个存放字符数组(包括10个元素)的空间,返回首元素的地址

  new int[5][4];//开辟一个存放二维整型数组(大小为5*4)的空间,返回首元素的地址

  float *p=new float (3.14159);//开辟一个存放单精度数的空间,并指定该实数的初值为//3.14159,将返回的该空间的地址赋给指针变量p

  new运算符使用的一般格式为

  new 类型 [初值]

  用new分配数组空间时不能指定初值。如果由于内存不足等原因而无法正常分配空间,则new会返回一个空指针NULL,用户可以根据该指针的值判断分配空间是否成功。

  delete运算符使用的一般格式为

  delete [ ] 指针变量

  例如要撤销上面用new开辟的存放单精度数的空间(上面第5个例子),应该用

  delete p;

  前面用“new char[10];”开辟的字符数组空间,如果把new返回的指针赋给了指针变量pt,则应该用以下形式的delete运算符撤销该空间:

  delete [] pt;//在指针变量前面加一对方括号,表示是对数组空间的操作

  例 开辟空间以存放一个结构体变量。

  #include <iostream>

  #include <string>

  using namespace std;

  struct Student //声明结构体类型Student

  { string name;

  int num;

  char ***;

  };

  int main( )

  { Student *p; //定义指向结构体类型Student的数据的指针变量

  p=new Student; //用new运算符开辟一个存放Student型数据的空间

  p->name=″Wang Fun″; //向结构体变量的成员赋值

  p->num=10123;

  p->***='m';

  cout<<p->name<<endl<<p->num

  <<endl<<p->***<<endl;//输出各成员的值

  delete p;//撤销该空间

  return 0;

  }

  运行结果为

  Wang Fun 10123 m

  在动态分配/撤销空间时,往往将这两个运算符和结构体结合使用,是很有效的。可以看到:

  要访问用new所开辟的结构体空间,无法直接通过变量名进行,只能通过指针p进行访问。如果要建立一个动态链表,必须从第一个结点开始,逐个地开辟结点并输入各结点数据,通过指针建立起前后相链的关系。

七、使用实例

1、结构体中的指针函数以及“结构体”的继承
#include <stdio.h>
#include <stdlib.h>
struct Fruit
{
void (*output)(void);
int i;
};
struct Apple
{
struct Fruit n;
void (*output)(void);
};
void output_base(void);
void output_driven(void);
void output_apple(void);
int main()
{
struct Fruit *p;
struct Apple *q;
struct Fruit base;
struct Apple driven;
base.i = 0;
base.output = output_base;
driven.n.i = 1;
driven.n.output = output_driven;
driven.output = output_apple;
p = &base;
printf("base's i is %d\n",p->i);
p->output();
p = (Fruit *)&driven;
printf("driven's i is %d\n",p->i);
q = &driven;
q->output();
getchar();
return 0;
}
void output_base(void)
{
printf("this is the base\n");
}
void output_driven(void)
{
printf("this is the driven\n");
}
void output_apple()
{
printf("this is apple's output\n");
}
******************************************************************
2、结构体中指向函数的指针
C语言中的struct是最接近类的概念,但是在C语言的struct中只有成员,不能有函数,但是可以有指向函数的指针,这也就
方便了我们使用函数了。举个例子,如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct student
{
int id;
char name[50];
void (*initial)();
void (*process)(int id, char *name);
void (*destroy)();
}stu;
void initial()
{
printf("initialization...\n");
}
void process(int id, char *name)
{
printf("process...\n%d\t%s\n",id, name);
}
void destroy()
{
printf("destroy...\n");
}
int main()
{
stu *stu1;
//在VC和TC下不需要malloc也可以正常运行,但是linux gcc下就会出错,为段错误,必须malloc
stu1=(stu *)malloc(sizeof(stu));
// 使用的时候必须要先初始化
stu1->id=1000;
strcpy(stu1->name,"xufeng");
stu1->initial=initial;
stu1->process=process;
stu1->destroy=destroy;
printf("%d\t%s\n",stu1->id,stu1->name);
stu1->initial();
stu1->process(stu1->id, stu1->name);
stu1->destroy();
free(stu1);
return 0;
}
------------------------------------
c语言中,如何在结构体中实现函数的功能?把结构体做成和类相似,让他的内部有属性,也有方法,这样的结构体一般称为协议类,提供参考:
struct {
int funcid;
char *funcname;
int (*funcint)(); /* 函数指针 int 类型*/
void (*funcvoid)(); /* 函数指针 void类型*/
};
每次都需要初始化,比较麻烦
******************************************************************
函数中使用结构体指针改变成员变量的值怎么不能传递到主函数中去
程序代码如下:
#include <stdlib.h>

#include <iostream.h>


struct TNode
{
int data ;
TNode *lchild ;
TNode *rchild ;
} ;
void CreateTree(TNode *T , int A[] , int begin , int end)
{
if (begin > end)
{
T = NULL ;
return ;
}
int index = 0.5 * (begin + end) ;
T = (TNode*)malloc(sizeof(TNode)) ;
T->data = A[index] ;
CreateTree(T->lchild , A , begin , index - 1) ;
CreateTree(T->rchild , A , index + 1 , end) ;
}
void main()
{
int A[10] ;
for (int i = 0 ; i < 10 ; i++)
{
A[i] = i ;
}
TNode *T ;
CreateTree(T , A , 0 , 9) ;
cout << T->data << endl ;
}
程序运行不太对,错误就出在CreateTree函数中创建的T节点的的成员变量值没有传递出去,但是CreateTree函数的形参是指向结构体的指针啊,为什么会出现这种问题呢?
----------------------------------------------------------------------
void CreateTree(TNode* &T , int A[] , int begin , int end)
----------
改成对指针的引用...
--------------------------------------------------------
使用引用
--------------------------------------------------------
为什么要加引用呢?T已经是指向结构体的指针了,应该可以将改变后的信息传递到主函数中啊
-------------------------------------------------------
尽管加引用后运行结果是正确的
--------------------------------------------------------
因为需要将指针自身传出去
如果在函数外面分配空间就不需要引用了
--------------------------------------------------------
如果需要改变变量的值,那么就应该传变量的指针(地址),
如果需要改变指针的值,那么就应该传指针的指针(地址)。
--------------------------------------------------------
不用引用可以用指针的指针
void CreateTree(TNode **T , int A[] , int begin , int end)
{
TNode* newT;
if (begin > end)
{
T = NULL;
return;
}
int index = 0.5 * (begin + end);
newT = (TNode*)malloc(sizeof(TNode));
*T = newT;
newT->data = A[index] ;
CreateTree(&newT->lchild , A , begin , index - 1) ;
CreateTree(&newT->rchild , A , index + 1 , end) ;
}
void main()
{
int A[10] ;
for (int i = 0 ; i < 10 ; i++)
{
A[i] = i ;
}
TNode *T ;
CreateTree(&T , A , 0 , 9) ;
cout << T->data << endl ;
}
--------------------------------------------------------
哦,我明白了,谢谢各位了,结帖
******************************************************************************
函数给作为参数传递的结构体指针的结构体成员赋值
char func(struct compABC *ABC) 正确
{
ABC->member1 = 12;
ABC->member2 = 34;
}
char func(struct compABC *ABC) 错误,根据 K&R C 相关章节的说明,-> 运算符也比 * 优先
{
*ABC->member1 = 12;
*ABC->member2 = 34;
}
char func(struct compABC *ABC) 错误,点号(.)比星号(*)优先
{
*ABC.member1 = 12;
*ABC.member2 = 34;
}
char func(struct compABC *ABC) 正确
{
(*ABC).member1 = 12;
(*ABC).member2 = 34;
}

我的实例操作:

#include <iostream>
#include<stdio.h>

struct Database_Param //数据库参数
{
char *http;
char host[20]; //主机名
char user[10]; //用户名
char password[20]; //密码
char db[20]; //数据库名
unsigned int port; //端口,一般为0
char unix_socket[10]; //套接字,一般为NULL
unsigned int client_flag; //一般为0
} *dataparam;


int main()
{
dataparam = new (struct Database_Param );
dataparam->http = new char[20];
strcpy_s(dataparam->http,20," ");
strcpy_s(dataparam->host,sizeof("localhost"),"localhost");
strcpy_s(dataparam->user,sizeof("root"),"root");
strcpy_s(dataparam->password,20,"111");
strcpy_s(dataparam->db,sizeof("mysql"),"mysql");
dataparam->port = 3306;
strcpy_s(dataparam->unix_socket,sizeof("NULL"),"NULL");
dataparam->client_flag = 0;

std::cout<<"Hello world 你好!"<<std::endl;
std::cout<<"http:"<<dataparam->http<<std::endl;
std::cout<<"host:"<<dataparam->host<<"\nuser:"<< dataparam->user<<"\nclient_flag: "<<dataparam->client_flag<<std::endl;
delete dataparam->http;
delete db_param;
delete dataparam;
return 0;
}

输出结果:

关于结构体变量清零

typedef struct student
{
int i;
char c;
void* p;
} Stu;

Stu a,*p;
memset(&a,0,sizeof(Stu));

p = new Stu;//指针先分配,再使用

memset(p,0,sizeof(Stu));

关于两个相同的结构体变量直接赋值

比如结构体A,里面有结构体,有指针,有各种int char 变量。如果包含指针,赋值只能实现浅拷贝。被赋值的结构体变量的指针成员与原结构体变量的指针成员指向一个地址。C里用memcpy(),c++里重载了赋值运算符,可以直接用 =

#include <stdio.h>

struct s
{
int i;
char c;
void* p;
};

int main()
{
struct s s1, s2; char * s = "rabbit is cute";
s1.i
= 345;
s1.c
= 'y';
s1.p
= (void*)s;

s2
= s1;

printf(
"s2: %d, %c, %s\n", s2.i, s2.c, s2.p);
printf(
"s1 ptr: %d; s2 ptr: %d\n", s1.p, s2.p);

return 0;
}
输出结果是s1.p的值和s2.p的值是一样的。可见字符串没有被拷贝,两个指针指向同一个字符串。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

结构体类型完全归纳 的相关文章

  • 图形化的调试工具 j-scope systemview

    2022 03 01 当调试传感器 AD值时 xff0c 特别想把转换值直观的展示出来 xff0c 就用到了下面几咱方法 通常的解决办法是用串口上位机 xff0c USB接口上位机或者MDK的逻辑分析仪功能 xff0c 使用这三种方式都比较
  • 移远ec20模式与切换

    移远EC20支持4种模式 0 rmnet模式 通过QMI工具发的QMI命令 xff0c 获取公网IP 这种模式可以配合usb ecm驱动或高通GobiNet驱动使用 1 ecm模式 通过标准的CDC ECM发起data call xff0c
  • STM32开发必备知识篇:串口DMA空闲中断

    随着撰写博客的深入 xff0c 笔者先初步打算把博客细分为四大板块 xff1a 1 FPGA基础知识篇 xff1b 2 FPGA 20个例程篇 xff1b 3 STM32开发必备知识篇 xff1b 4 STM32 10个项目篇 xff0c
  • 大端小端(Big- Endian和Little-Endian)

    字节序 xff08 Endian xff09 xff0c 大端 xff08 Big Endian xff09 xff0c 小端 xff08 Little Endian xff09 图文并茂 http www cppblog com tx7d
  • STM32程序设计规范浅析

    这篇博客写到 STM32基础知识篇 里 xff0c 一方面是一个很好地对过往工作的总结 xff0c 另一方面也是整个专栏撰写计划的开端 xff0c 古人云 xff1a 良好的开端是成功的一半 xff0c 在文章的最后详细地规划了整个专栏后期
  • C语言编程规范(头文件规范)

    C语言的规范使用 xff0c 有利于提高代码的清晰 简洁 可测试 安全 效率 可移植 xff0c 因此必须规范使用C语言编程 代 码 总 体 原
  • C语言变量和常量命名规则

    变量命名规则 原则 1 一个变量只有一 个功能 xff0c 不能把一个变量用作多个用途 2 结构单一 xff0c 不能设计面面俱到的数据结构 xff1b 结构的定义应该明确的描述一个对象 xff0c 去掉相关相不强的数据 xff1b 3 不
  • ROS+Gazebo----Unable to find uri[model:// ]

    基于ROS 43 Gazebo环境 xff0c 用roslaunch把sdf模型加载到gazebo仿真世界 目录结构如下 输入命令roslaunch my simulation my world launch 报错 xff1a 1 不接入网
  • 最完整介绍Visual C++ 6.0和Visual Studio 2022中的编译、生成和运行(CTRL+F7、F7、CTRL+F5)

    我是荔园微风 xff0c 作为一名在IT界整整25年的老兵 xff0c 经常被Visual C 43 43 6 0和Visual Studio 2022初学者问到程序写好后怎么使用编译调试菜单以及怎么使用CTRL 43 F7 F7 CTRL
  • 判断大小端的方法(java和c++)

    首先我们给出大小端的定义 小端 xff1a 较高的有效字节存放在较高的的存储器地址 xff0c 较低的有效字节存放在较低的存储器地址 大端 xff1a 较高的有效字节存放在较低的存储器地址 xff0c 较低的有效字节存放在较高的存储器地址
  • vscode配置c++代码提示补全

    vscode配置c 43 43 代码提示补全 在网上找了大半天 xff0c 说的方式都试过了 xff0c 都没有适合我的 xff0c 还是自己找stackoverflow靠谱点 34 editor rulers 34 80 一行限制80字符
  • 解决头文件相互包含问题的方法

    解决头文件相互包含问题的方法 所谓超前引用是指一个类型在定义之前就被用来定义变量和声明函数 一般情况下 xff0c C C 43 43 要求所有的类型必须在使用前被定义 xff0c 但是在一些特殊情况下 xff0c 这种要求无法满足 xff
  • C++ 中头文件相互包含问题的解决办法

    我们在写C 43 43 程序的时候 xff0c 常常要把不同的类的声明放置与不同的头文件中 xff0c 以提高代码的整洁性 xff0c 如此一来 xff0c 就难免会遇到头文件相互包含的问题 xff0c 也就是说 xff0c 假设我们有两个
  • Pixhawk_nuttx启动过程和启动文件

    lt span style 61 34 font family Arial Helvetica sans serif background color rgb 255 255 255 34 gt Pixhawk nuttx启动过程 lt s
  • 施密特触发器原理图解

    施密特触发器原理图解详细分析 重要特性 xff1a 施密特触发器具有如下特性 xff1a 输入电压有两个阀值VL VH xff0c VL施密特触发器通常用作缓冲器消除输入端的干扰 施密特波形图 施密特触发器也有两个稳定状态 xff0c 但与
  • Delphi 类库(DLL)动态调用与静态调用示例讲解

    在Delphi或者其它程序中我们经常需要调用别人写好的DLL类库 下面直接上示例代码演示如何进行动态和静态的调用方法 DLL动态调用与静态调用的例子 编译环境 Delphi XE 转载或编译请不要修改此文件
  • HTML中的Hack手段之条件注释

    通常WEB 的好处就是可以跨平台 但这个世界偏偏有个另类 就是IE 浏览器 在平常做HTML 设计时 xff0c 有时需要为IE 的表示差异而不得不使用一些Hack 手段 条件注释就是这类手段之一 条件注释是IE 浏览器的 专利 也就是说我
  • JavaScript函数中的arguments对象

    ECMAScript标准中 xff0c 每个函数都有一个特殊的内置对象arguments arguments对象是一个类Array对象 object 用以保存函数接收到的实参副本 一 内置特性 说它是一个内置对象是因为我们在创建函数时并没有
  • JavaScript函数之高阶函数

    高阶函数 xff08 higher order function xff09 如果一个函数接收的参数为或返回的值为函数 xff0c 那么我们可以将这个函数称为高阶函数 众所周知 xff0c JavaScript是一种弱类型的语言 JavaS
  • 前端优化建议:合理利用JavaScript的条件运算符

    在最近的项目中要使用到一个格式化文件大小的算法 xff0c 于是不假思索写了如下代码 function formatSize size if size lt 1024 return size 43 34 B 34 else if size

随机推荐

  • 了解python之面向对象

    了解python之面向对象 面向对象概念 xff1a 面向对象编程 xff08 Object Oriented Programming xff0c 简称OOP xff09 是一种程序涉及思想 OOP把对象作为程序的基本单元 xff0c 一个
  • 了解python之进程与线程

    了解python之进程与线程 本文虽然叫作 了解python进程与线程 xff0c 但还是有点难度的 可以先查阅另外一篇文字 xff0c 快速入门 Python快速入门多线程与多进程 1 进程 进程 xff08 Process xff0c
  • Python快速入门多线程与多进程

    Python快速入门多线程与多进程 多线程 多线程的含义 进程我们可以理解为是一个可以独立运行的程序单位 xff0c 比如打开一个浏览器 xff0c 这就开启了一个浏览器进程 xff1b 打开一个文本编辑器 xff0c 这就开启了一个文本编
  • C++中strcmp的头文件问题

    C 43 43 中strcmp的头文件问题 今天在写程序时遇到的一个问题 include lt stdio h gt include lt string gt using std string int main char str STEL
  • strlen()函数详解

    头文件 xff1a include lt string h gt strlen 函数用来计算字符串的长度 xff0c 其原型为 xff1a unsigned int strlen char s strlen 用来计算指定的字符串s 的长度
  • 阿里云物联网平台基本设置-物模型

    陈拓 2019 12 14 2020 01 15 1 概述 如何让设备连接上云 xff1f 参考如下路径 本文以一个温度传感器为例 xff0c 演示创建产品 定义物模型 创建设备 虚拟设备调试 xff0c 这几部分 2 阿里云开通 2 1
  • Make与CMake

    1 Make与CMake 首先先来了解一下gcc xff0c gcc是GNU Compiler Collection 就是GNU编译器套件 xff0c 也可以简单认为是编译器 xff0c 它可以编译很多种编程语言 包括C C 43 43 O
  • C++学习(23)

    1 分析下述代码运行 xff1a include lt iostream gt using namespacestd int main int a 10 61 0 1 2 3 4 5 6 7 8 9 int p 61 a cout lt l
  • 史上最全最丰富的“最长公共子序列”、“最长公共子串”问题的解法与思路

    花了一天时间把一直以来的 最大子序列 最大递增子序列 最大公共子序列 最长公共子串 等问题总结了一下 其中参考了若干博文 xff0c 都备注引用 首先子序列是指一个一个序列中 xff0c 由若个数 xff08 字母 xff09 组成 xff
  • TCP协议拥塞控制算法(Reno、HSTCP、BIC、Vegas、Westwood)

    TCP协议拥塞控制算法 xff08 Reno HSTCP BIC Vegas Westwood xff09 一 TCP拥塞控制的研究框架 二 现有TCP拥塞控制的算法 xff08 Reno HSTCP Vegas Westwood xff0
  • C# Convert类

    Convert类常用的类型转换方法 方法说明Convert ToInt32 转换为整型 int Convert ToChar 转换为字符型 char Convert ToString 转换为字符串型 string Convert ToDat
  • try catch里面try catch嵌套

    try catch里面try catch嵌套 点击打开链接 try 与catch的作用 首先要清楚 xff0c 如果没有try的话 xff0c 出现异常会导致程序崩溃 而try则可以保证程序的正常运行下去 xff0c 比如说 xff1a t
  • mysql 中使用 where 1=1和 1=1 的作用

    Mysql中where 1 61 1 和count 0 使用小技巧 mysql中使用 where 1 61 1和 1 61 1 的作用
  • 面试题1:OS或者编译器怎么识别是全局变量还是局部变量

    OS或者编译器怎么识别是全局变量还是局部变量 操作系统内根本不关心你是什么变量 xff0c 它只管代理运行程序 xff0c 也就是进程 xff0c 负责这些进程之间的调度 xff0c 不过如果要说操作系统本身也是进程 xff0c 那倒可以理
  • 面试题4:数组、指针、引用的联系区别

    数组和指针 xff1f xff1f xff1f 从两个方面来看 xff0c 一是作为一个语言 xff0c 数组是必须要支持的一种数组类型 xff0c 原因很简单 xff0c 数组是线性表的直接体现 而从编译器设计者的角度来看 xff0c 如
  • c++ 容器类 概括性介绍

    C 43 43 中的容器类包括 顺序存储结构 和 关联存储结构 xff0c 前者包括vector xff0c list xff0c deque等 xff1b 后者包括set xff0c map xff0c multiset xff0c mu
  • 海康摄像头使用RTSP

    1 协议格式 海康威视IP摄像头rtsp协议地址如下 xff1a rtsp username passwd 64 ip port codec channel subtype av stream 主码流 xff1a rtsp admin 12
  • 树莓派串口连接ESP8266

    陈拓 chentuo 64 ms xab ac cn 2020 03 12 2020 03 12 1 概述 ESP8266是物联网行业广泛使用的WiFi模块 xff0c 小巧 功能强大 xff0c 而且价格低廉 通常用电脑进行ESP8266
  • Linux 创建TCP连接流程

    文章目录 Linux创建TCP的步骤服务端客户端TCP建立流程示例代码 Linux创建TCP的步骤 TCP编程需要客户端和服务器两套编码 xff0c 其创建TCP的流程也是不完全一致的 服务端 使用socket函数创建一个套接字使用sets
  • 结构体类型完全归纳

    结构体类型 目录 基本概述 一 结构体类型变量的定义方法及其初始化 1 定义结构体类型变量的方法 2 结构体变量的初始化 二 结构体变量的引用 三 结构体数组 1 定义结构体数组 2 结构体数组应用举例 四 指向结构体变量的指针 1 类型一