结构体详解
2020-03-22 00:20

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct node{
    char name[10];
    char num[25];
    char age[5];
    float money;
}gon;//l
//类型定义
int main()
{
    gon *p;//变量定义
    p=(gon *) malloc(sizeof(gon));//结果提要动态分配内存
    scanf("%s%s%s%f",p->name,p->num,p->age,&p->money);
    printf("%sn%sn%sn%.2fn",p->name,p->num,p->age,p->money);
    return 0;
}

例如 student1.num 表示 student1 变量中的 num 成员,即 student1 的 num 项,可以对变量的成员赋值。例如:
  student1.num = 10010;
"." 是成员(分量)运算符,它在所有的运算符中优先级最高,因此可以把 student1.num 作为一个整体来看待。上面的赋值语句作用是将整数 10010赋给 student1 变量中的成员 num。

  printf("NO. :%ldnname: %snsex:%cnscore:%fn", stu_1.num, stu_1.name, stu_1.sex, stu_1.score);
  printf("NO. :%ldnname: %s澳门新葡亰平台网址大全,nsex:%cnscore:%fn", (*p).num, (*p).name, (*p).sex, (*p).score);
  system("pause");
}
  在主函数中声明了 struct student 类型,然后定义了一个 struct student 类型的变量stu_1, 同时又定义一个指针变量 p ,它指向一个 struct student 类型的数据,在函数的执行部分将结构体变量 stu_1 的起始地址赋给指针变量 p ,也就是使 p 指向 stu_1 然后对 stu_1 的各成员赋值,第二个 printf 函数也是用来输出 stu_1 各成员的值,但使用的是 (*p).num 这样的形式, (*p) 表示 p 指向的结构体变量,(*p).num 是 p 指向的结构体变量中的成员 num 。注意 *p 两侧的括弧不可省略,因为成员运算符 '.' 优先于 '*' 运算符,*p.num 就等价于 *(p.num)
运行结果如下:
NO. :89101
name: Li Lin
sex:M
score:89.500000
NO. :89101
name: Li Lin
sex:M
score:89.500000
  可以看到两个 printf 输出的结果相同。
  在C语言中,为了使用方便和使之直观,可以把 (*p).num 改用 p->num 来代替,它表示 *p 所指向的结构体变量中的 num 成员,同样,(*p).name 等价于 p->name。
就是说以下三种形式等价:
  a. 结构体变量.成员名
  b. (*p).成员名
  c. p->成员名

注意,不能用 student1.birthday 来访问 student1 变量中的成员 birthday,因为 birthday 本身是一个结构体变量。

  定义了 student1, student2 为 struct student 类型的变量。

3 结构体变量的引用

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

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

6 指向结构体类型数据的指针
  一个结构体变量的指针就是该变量所占据的内存段的起始地址,可以设一个指针变量,用来指向一个结构体变量,此时该指针变量的值是结构体变量的起始地址。指针变量也可以用来指向结构体数组中的元素。
6.1 指向结构体变量的指针
澳门新葡亰553311b,  指向结构体变量的指针的应用:
#include <string.h>
#include <stdio.h>
#include <stdlib.b>
struct student
澳门新葡亰网站所有平台,{
  long num;
  char name[20];
  char sex;
  float score;
};
void main()
{
  struct student stu_1;
  struct student *p;
  p = &stu_1;
  stu_1.num = 89101;
  strcpy(stu_1.name, "Li Lin");
  stu_1.sex = 'M';
  stu_1.score = 89.5;

  {成员列表};

 

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

 p = &stu[1].name
编译时将出错。千万不要认为反正 p 是存放地址的,可以将任何地址赋给它。如果地址类型不相同,可以用强制类型转换。例如:
  p = (struct student *)&stu[1].name;
此时,在 p 中存放 stu[1] 元素的 name 成员的起始地址。
6.3 用结构体变量和指向结构体的指针作函数参数
  将一个结构体变量的值传递给另一个函数,有3个方法:
  (1)用结构体变量的成员作参数,例如:用 stu[1].num 或 stu[2].name 作函数实参,将实参值传给形参。用法和用普通变量作实参是一样的,属于 值传递 方式。应当注意实参与形参的类型保持一致。
  (2)用结构体变量作参数。老版本的C系统不允许用结构体变量作实参,ANSI C取消了这一限制。但是用结构体变量作实参时,采取的是 值传递 的方式,将结构体变量所占的内存单元全部顺序传递给形参。形参也必须是同类型的结构体变量。在函数调用期间形参也要占用内存单元。这种传递方式在空间和时间上开销较大,如果结构体的规模很大时,开销是很可观的,此外由于采用值传递方式,如果在执行被调用函数期间改变了形参(也是结构体变量)的值,该值不能返回主调函数,这往往造成使用上的不便。因此一般较少用这种方法。
  (3)用指向结构体变量(或数组)的指针作实参,将结构体变量(或数组)的地址传给形参。
用结构体变量作函数参数。
#include <stdio.h>
#define FORMAT "%dn%sn%fn%fn%fn"
struct student
{
  int num;
  char name[20];
  float score[3];
};
void print(struct student stu)
{
  printf(FORMAT, stu.num, stu.score[0], stu.score[1], stu.score[2]);
  printf("n");
}
void main()
{
  struct student stu;
  stu.num = 12345;
  strcpy(stu.name, "Li Li");
  stu.score[0] = 67.5;
  stu.score[1] = 89;
  stu.score[2] = 78.6;
  printf(stu);
}
将上面改用指向结构体变量的指针作实参
#include <stdio.h>
#define FORMAT "%dn%sn%fn%fn%fn"
struct student
{
 int num;
 char name[20];
 float score[3];
}stu = {12345, "Li Li", 67.5, 89, 78.6};
void print(struct student *p)
{
  printf(FORMAT, p->num, p->name, p->score[0], p->score[1], p->score[2]);
  printf("n");
}
void main()
{
  print(&stu);
}
7 用指针处理链表
7.1 链表概述
 链表是一种常见的重要的数据结构。它是动态地进行存储分配的一种结构。
 链表有一个 头指针 变量,它存放一个地址,该地址指向一个元素,链表中每一个元素称为 结点,每个结点都应包括两个部分,一为用户需要用的实际数据,二为下一个结点的地址。可以看出,头指针 head 指向第一个元素,第一个元素又指向第二个元素,。。。。直到最后一个元素,该元素不再指向其他元素,它称为 表尾,它的地址部分放一个 NULL(表示 空地址)链表到此结束。
  可以看到链表中各元素在内存中可以不是连续存放的,要找某一元素,必须先找到上一个元素,根据它提供的下一元素地址才能找到下一个元素。如果不提供 头指针 head 则整个链表无法访问。
  可以看到。这种链表的数据结构,必须利用指针变量才能实现,即一个结点中应包含一个指针变量,用它存放下一结点的地址。
  前面介绍了结构体变量,用它作链表中的结点是最合适的,一个结构体变量包含若干成员,这些成员可以是数值类型,字符类型,数组类型,也可以是指针类型,我们用这个指针类型成员来存放下一个结点的地址。例如可以设计这样一个结构体类型:
  struct student
 {
   int num;
   float score;//数据域
   struct student *next;//指针域
 };
  其中成员 num 和 score 用来存放结点中的有用数据(用户需要用到的数据),next 是指针类型成员,它指向 struct student 类型数据(这是 next 所在结构体类型)。一个指针类型的成员既可以指向其他类型的结构体数据,也可以指向自己所在的结构体类型的数据。现在 next 是 struct student 类型中的一个成员,它又指向 struct student 类型的数据。用这种方法就可以建立链表。
  请注意:只是定义一个 struct student 类型,并未实际分配存储空间,只有定义了变量才分配内存单元。

例如:结构体变量 student1 可以这样访问各成员:
student1.num
student1.birthday.month

 1 struct student *add(struct student *head, int index, int num, float score) {
 2     printf("add.n");
 3     struct student *p1, *p2, *p3;
 4     if (head == NULL) {
 5         printf("The List is NULL.n");
 6     } else {
 7         p1 = p2 = head;
 8         while (p1->next != NULL && p1->num != index) {
 9             p1 = p1->next;
10             p2 = p1;
11         }
12         if (p1->num == index) {
13             p3 = (struct student *) malloc(LEN);
14             p3->num = num;
15             p3->score = score;
16 
17             if (p2->next == NULL) {
18                 p2->next = p3;
19                 p3->next = NULL;
20             } else {
21                 p3->next = p2->next;
22                 p2->next = p3;
23             }
24         } else
25             printf("Can not find list index.n");
26     }
27     return head;
28 }

  应当注意,将一个变量定义为标准类型(基本数据类型)与定义为结构体类型不同之处在于后者不仅要求指定变量为结构体类型,而且要求指定为某一特定的结构体类型(例如 struct student 类型),因为可以定义出许多种具体的结构体类型。而在定义变量为整形时,只需指定为 int 型即可。

  结构体名,用作结构体类型的标志,它又称 结构体标记,大括号内是该结构体中的各个成员,由它们组成一个结构体,对各成员都应进行类型声明如:

  (3)对结构体变量的成员可以像普通变量一样进行各种运算(根据其类型决定可以进行的运算)。
student2.score = student1.score;
Sum = student1.score + student2.score;
student1.age ++;
++ student1.age;

运行结果如下:
LI
Li
Fun
Zhang
Zhang
Fun
Li
Fun
Zhang
Li
   Li: 3
Zhang: 3
  Fun: 3

  ++p -> n 得到 p 指向的结构体变量中的成员 n 的值使之加 1 (先加)

也可以把 成员列表称为 域表,第一个成员也称为结构体中的一个域。成员名定名规则写变量名同。

 

7.2 简单链表
 下面通过一个例子来说明如何建立和输出一个简单链表
#include <stdio.h>
#include <stdlib.h>
#define NULL 0
struct student
{
  long num;
  float score;
  struct student *next;
};
void main()
{
  struct student a, b, c, *head, *p;
  a.num = 99101; a.score = 89.5;
  b.num = 99103; b.score = 90;
  c.num = 99107; c.score = 85;//对结点的 num 和 score 成员赋值
  head = &a;//将结点 a 的起始地址赋给头指针 head
  a.next = &b;//将结点 b 的起始地址赋给 a 结点的 next 成员
  b.next = &c;
  c.next = NULL;// c 结点的 next 成员不存放其他结点地址
  p = head;//使 p 指针指向 a 结点
  do
  {
    printf("%ld %5.1fn", p->num, p->score);// 输出 p 指向的结点的数据
    p = p->next;//使 p 指向下一结点
  }while(p != NULL);//输出完 c 结点后 p 的值为 NULL
  system("pause");
}
运行结果
99101  89.5
99103  90.0
99107  85.0
7.3 处理动态链表所需的函数
  (1)malloc 函数
  void *malloc(unsigned int size);
  作用是在内存的动态存储区中分配一个长度为 size 的连接空间。些函数的值(即返回值)是一个指向分配空间起始地址的指针(基类型为 void)。如果些函数未能成功地执行(例如内存空间不足)则返回空指针 NULL。
 (2)calloc 函数
  void *calloc(unsigned n, unsigned size);
  其作用是在内存的动态区存储中分配 n 个长度为 size 的连续空间。函数返回一个指向分配空间起始地址的指针,如果分配不成功,返回 NULL。
  用 calloc 函数可以为一维数组开辟动态存储空间, n 为数组元素个数,每个元素长度为 size。  
  (3)free 函数
  void free(void *p);
  其作用是释放由 p 指向的内存区,使这部分内存区能被其它变量使用, p 是最后一次调用 calloc 或 malloc 函数时返回的值。free 函数无返回值。
  请注意:以前的C版本提供的 malloc 和 calloc 函数得到的是指向字符型数据的指针。ANSI C 提供的 malloc 和 calloc 函数规定为 void * 类型。

 1 void printlist(struct student *head) {
 2     struct student *p;
 3     p = head;
 4 
 5     if (head != NULL) {
 6         do {
 7             printf("num=%d score=%5.2fn", p->num, p->score);
 8             p = p->next;
 9         } while (p != NULL);
10     }
11     /* while(p -> next != NULL)
12      {
13      printf("num=%d score=%fn", p->num, p->score);
14      p = p->next;
15      }*/
16 }

  c. 成员也可以是一个结构体变量。

先声明一个 struct date 类型,它代表 日期 包括3个成员 month, day, year。然后在声明 struct student 类型时,将成员 birthday 指定为 struct date 类型。
  d. 成员名可以与程序中的变量名相同,二者不代表同一对象。

struct student
{
  int num;
  char name[20];
  char sex;
       int age;
  float score;
  char addr[30];
};

  类型名 成员名;

  struct
     {

       成员表列2
  }变量名表列;

  在定义了结构体变量后,系统会为之分配内存单元。例如 student1 和 student2在内存中各占59个字节。

4 结构体变量的初始化
  和其它类型变量一样,对结构体变量可以在定义时指定初始值。
如:
#include <stdio.h>
struct student
{
  long int num;
  char name[20];
  char sex;
  char addr[30];
}a = {89031, "Li Lin", 'M', "123 Beijing Road"};
void main()
{
 printf("NO. : %dnname: %snsex: %cnaddress: %sn", a.num, a.name, a.sex, a.addr);
}

  struct student  //结构体类型名

5.2 结构体数组的初始化
  与其它类型数组一样,对结构体数组可以初始化如:
  struct student
  {
    int mum;
    char name[20];
    char sex;
    int age;
    float score;
    char addr[30];
  }stu[3] = {{10101,"Li Lin", 'M', 18, 87.5, "103 Beijing Road"},
        {10101,"Li Lin", 'M', 18, 87.5, "103 Beijing Road"},
        {10101,"Li Lin", 'M', 18, 87.5, "103 Beijing Road"}};
  定义数组 stu 时,元素个数可以不指定,即写成以下形式:
  stu[] = {{...},{...},{...}};
编译时,系统会根据给出初值的结构体常量的个数来确定数组元素的个数。

  student1, student2//结构体变量名

例如:
struct student
{
  int num;
  char name[20];
    char sex;
  int age;
  float score;
  char addr[30];
}student1, student2;

 

 

  (4)可以引用结构体变量成员的地址,也可以引用结构体变量的地址。如:
  scanf("%d", &student1.num);// 输入 student1.num 的值
  printf("%o", &student1);// 输出 student1 的首地址
但不能用以下语句整体读入结构体变量如:
  scanf("%d,%s,%c,%d,%f,%s", &student1);
结构体变量的地址主要用于作函数参数,传递结构体的地址。

  struct 结构体名
  {
    成员表列
  }变量名表列;