结构体与共用体

概述

定义一个结构的一般形式为:

struct 结构名{成员表列};

成员表列由若干个成员组成,每个成员都是该结构的一个组成部分。对每个成员也必须作类型说明,其形式为:

类型说明符 成员名;

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

定义结构体类型变量的方法

可以采取以下3种方法定义结构体类型变量:

(1)先声明结构体类型再定义变量名

例如:

struct student student1, student2;

|             |           |                |

类型名    结构体   变量名     变量名

定义了student1和student2为struct student类型的变量,即它们具有struct student类型的结构.

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

在定义了结构体变量后,系统会为之分配内存单元

例如: student1和student2在内存中各占 ? 个字节。 ( 4 + 20 + 1 + 4 + 4 + 30 = 67 )

(2)在声明类型的同时定义变量 这种形式的定义的一般形式为:

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

(3) 直接定义结构体类型变量 其一般形式为:即不出现结构体名。

struct{成员表列}变量名表列;

嵌套结构

首先定义一个结构date,由month(月)、day(日)、year(年) 三个成员组成。

在定义并说明变量 boy1 和 boy2 时,其中的成员birthday被说明为data结构类型。成员名可与程序中其它变量同名,互不干扰。

struct date
{int month;int day;int year;
};

struct
{int num;char name[20];char sex;struct date birthday;float score;
}boy1, boy2;

结构体变量的引用

在定义了结构体变量以后,当然可以引用这个变量。但应遵守以下规则:

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

例如: 打印student1的各个变量的值。

可以这样吗? printf(″%d,%s,%c,%d,%f,%\n″,student1);  不可以

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

结构体变量名.成员名

student1.num:   表示student1变量中的num成员,即student1的num(学号)项。

可以对变量的成员赋值,例如:student1.num=100;

“.”是成员(分量)运算符,它在所有的运算符中优先级最高,因此可以把student1.num作为一个整体来看待。上面赋值语句的作用是将整数100赋给student1变量中的成员num。

(2) 如果成员本身又属一个结构体类型,则要用若干个成员运算符,一级一级地找到最低的一级的成员。只能对最低级的成员进行赋值或存取以及运算。

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

student1.numstudent1.birthday.month

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

student2.score = student1.score;
sum = student1.score + student2.score;
student1.age++;  ++student2.age;

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

#include <stdio.h>void main()
{struct student{int num;char *name;char sex;float score;} boy1;boy1.num = 007;boy1.name = "Jane";printf("The address of struct is %o :\n", &boy1 );printf("The address of num is %o :\n", &boy1.num );}

名称和第一个变量的地址是相等的

输出结果

但不能用以下语句整体读入结构体变量:

scanf(″%d,%s,%c,%d,%f,%s″,&student1);

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

结构体变量的初始化

#include <stdio.h>void main()
{struct student    /*定义结构*/{            int num;            char *name;            char sex;            float score;            }boy1, boy2 = { 102, "Jane", 'M', 98.5 };boy1 = boy2;printf("Number = %d\nName = %s\nScore = %d\n", boy1.num, boy1.name, boy1.score);printf("\n\n");printf("Number = %d\nName = %s\nScore = %d\n", boy2.num, boy2.name, boy2.score);}

结构体数组

一个结构体变量中可以存放一组数据(如一个学生的学号、姓名、成绩等数据)。

如果有10个学生的数据需要参加运算,显然应该用数组,这就是结构体数组。

结构体数组与以前介绍过的数值型数组不同之处在于每个数组元素都是一个结构体类型的数据,它们都分别包括各个成员(分量)项。

定义结构体数组

和定义结构体变量的方法相仿,只需说明其为数组即可。例如:

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

也可以这样:

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

结构体数组的初始化

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

struct student
{int num;char name[20]; char sex;     int age; float score; char addr[30];
}stu[2]= {{101,″LiLin″,′M′,18,87.5,″Beijing″},               {102,″Zhang″,′F′,19,99,″Shanghai″}};  

当然,数组的初始化也可以用以下形式:

struct student
{int num;…
};struct student str[]{{…},{…},{…}};

即先声明结构体类型,然后定义数组为该结构体类型,在定义数组时初始化。

例题:对候选人得票的统计程序。设有3个候选人,每次输入一个得票的候选人的名字,要求最后输出各人得票结果。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>#define NUM 4
struct person
{char name[20];int count;
}candidate[NUM] = {{"小甲鱼", 0},{"苍井空", 0}, {"松岛枫", 0},{"莫丁丁", 0}};char *winner();void main()
{int i, j;char candidate_name[20];printf("欢迎进入良好公民评选投票系统:() \n\n");printf("候选人有: 小甲鱼, 苍井空, 松岛枫, 莫丁丁(路人甲)\n\n");for( i=1; i <= 10; i++ ){printf("第 %2d 位投票, 请写下支持的候选人名字: ", i);scanf("%s", candidate_name);for( j=0; j < NUM; j++ ){if( 0 == strcmp(candidate_name, candidate[j].name) ){candidate[j].count++;}}}printf("\n");for( i=0; i < 4; i++ ){printf("%s 同学得票数为: %d\n", candidate[i].name, candidate[i].count );}printf("\n");printf("本次投票活动的胜利者的: %s", winner() );printf("\n");system("pause");
}char *winner()
{int i =0 , winner = i;for( i=1; i < NUM; i++ ){if( candidate[winner].count < candidate[i].count ){winner = i;}}return candidate[winner].name;
}

指向结构体类型数据的指针

一个结构体变量的指针就是该结构体变量所占据的内存段的起始地址。

可以设一个指针变量,用来指向一个结构体变量,此时该指针变量的值是结构体变量的起始地址。

指针变量也可以用来指向结构体数组中的元素。

结构指针变量说明的一般形式为:

struct 结构名 *结构指针变量名

例如,在前面的例题中定义了stu这个结构,如要说明一个指向stu的指针变量pstu,可写为:

struct stu *pstu;

当然也可在定义stu结构时同时说明pstu。与前面讨论的各类指针变量相同,结构指针变量也必须要先赋值后才能使用。

赋值是把结构变量的首地址赋予该指针变量,不能把结构名赋予该指针变量。

如果boy是被说明为stu类型的结构变量,则:

pstu=&boy

是正确的。

pstu=&stu

是错误的。

因为,结构名和结构变量是两个不同的概念,不能混淆。结构名只能表示一个结构形式,编译系统并不对它分配内存空间。

只有当某变量被说明为这种类型的结构时,才对该变量分配存储空间。

因此上面&stu这种写法是错误的,不可能去取一个结构名的首地址。有了结构指针变量,就能更方便地访问结构变量的各个成员。

其访问的一般形式为:

(*结构指针变量).成员名

或为:

结构指针变量->成员名

例如:

(*pstu).num

或者:

pstu->num

示例:

#include <stdio.h>struct stu
{      int num;     char *name;      char sex;      float score;
} boy1 = {102, "Fishc", 'M', 78.5};void main()
{struct stu *pstu;pstu = &boy1;printf("Number = %d\nName = %s\n", boy1.num, boy1.name);      printf("Sex = %c\nScore = %f\n\n", boy1.sex, boy1.score);      printf("Number = %d\nName = %s\n", (*pstu).num, (*pstu).name);      printf("Sex = %c\nScore = %f\n\n", (*pstu).sex, (*pstu).score);      printf("Number = %d\nName = %s\n", pstu->num, pstu->name);      printf("Sex = %c\nScore = %f\n\n", pstu->sex, pstu->score);
}

输出结果

结构指针变量作函数参数

将一个结构体变量的值传递给另一个函数,有3个方法:

(1)用结构体变量的成员作参数

(2)用结构体变量作实参

(3)用指向结构体变量(或数组)的指针作实参,将结构体变量(或数组)的地址传给形参

例题:有一个结构体变量stu,内含学生学号、姓名和3门课程的成绩。通过调用函数print中将它们输出。

要求: 先用结构体变量作函数参数

#include <stdio.h>
#include <string.h>struct student
{int num;char *name;float score[3];
};void print(struct student);void main()
{struct student stu;stu.num = 8;stu.name = "Fishc.com!";stu.score[0] = 98.5;stu.score[1] = 99.0;stu.score[2] = 99.5;print( stu );
}void print( struct student stu )
{printf("\tnum     : %d\n", stu.num);printf("\tname    : %s\n", stu.name);printf("\tscore_1 : %5.2f\n", stu.score[0]);printf("\tscore_2 : %5.2f\n", stu.score[1]);printf("\tscore_3 : %5.2f\n", stu.score[2]);printf("\n");
}

改用指向结构体变量的指针作实参: example02.c

#include <stdio.h>
#include <string.h>struct student
{int num;char name[20];float score[3];
};void print(struct student *);void main()
{struct student stu;stu.num = 8;strcpy(stu.name, "Fishc.com!"); stu.score[0] = 98.5;stu.score[1] = 99.0;stu.score[2] = 99.5;print( &stu );
}void print( struct student *p )
{printf("\tnum     : %d\n", p -> num);printf("\tname    : %s\n", p -> name);printf("\tscore_1 : %5.2f\n", p -> score[0]);printf("\tscore_2 : %5.2f\n", p -> score[1]);printf("\tscore_3 : %5.2f\n", p -> score[2]);printf("\n");
}

动态存储分配

例如:int a[n]; 用变量表示长度,想对数组的大小作动态说明,这是错误的。

但是在实际的编程中,往往会发生这种情况,即所需的内存空间取决于实际输入的数据,而无法预先确定。

以对于这种问题,用数组的办法很难解决!

为了解决上述问题,C语言提供了一些内存管理函数,这些内存管理函数可以按需要动态地分配内存空间,也可把不再使用的空间回收待用,为有效地利用内存资源提供了手段。

常用的内存管理函数有以下三个:

1. 分配内存空间函数 malloc、calloc

2. 释放内存空间函数 free

malloc函数

函数原型为

void *malloc(unsigned int size);

其作用是在内存的动态存储区中分配一个长度为size的连续空间(size是一个无符号数)。

此函数的返回值是一个指向分配域起始地址的指针(类型为void)。

如果此函数未能成功地执行(例如内存空间不足),则返回空指针(NULL)。

calloc函数

函数原型为

void *calloc(unsigned n, unsigned size);

其作用是在内存的动态存储区中分配n个长度为size的连续空间。 函数返回一个指向分配域起始地址的指针;

如果分配不成功,返回NULL。

用calloc函数可以为一维数组开辟动态存储空间,n为数组元素个数,每个元素长度为size。

free函数

函数原型为

void free(void *p);

其作用是释放由p指向的内存区,使这部分内存区能被其他变量使用。 p是最近一次调用calloc或malloc函数时返回的值。 free函数无返回值。

链 表

什么是链表?

链表是一种常见的重要的数据结构,是动态地进行存储分配的一种结构。

链表的组成:

头指针:存放一个地址,该地址指向第一个元素

结点:用户需要的实际数据和链接节点的指针

#include <stdio.h>struct student
{long num;  float score;struct student *next;
};void main()
{struct student a, b, c, *head;a.num = 10101; a.score = 89.5;b.num = 10103;b.score = 90;c.num = 10107;c.score = 85;head = &a;a.next = &b;b.next = &c;c.next = NULL;do{printf("%ld %5.1f\n", head->num, head->score);head = head->next;}while( head != NULL );
}

建立动态链表

所谓建立动态链表是指在程序执行过程中从无到有地建立起一个链表,即一个一个地开辟结点和输入各结点数据,并建立起前后相链的关系。

根据下面的分析写一函数建立一个含有学生(学号,成绩)数据的单向动态链表。 (约定:我们约定学号不会为零,如果输入的学号为0,则表示建立链表的过程完成,该结点不应连接到链表中。)

#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>#define LEN sizeof(struct student)  // student结构的大小struct student *creat();            //创建链表
void print(struct student *head);   //打印链表struct student
{int num;float score;struct student *next;
};int n; //全局变量,用来记录存放了多少数据。void main()
{struct student *stu;stu = creat();print( stu );printf("\n\n");system("pause");
}struct student *creat()
{struct student *head;struct student *p1, *p2;p1 = p2 = (struct student *)malloc(LEN);  // LEN是student结构的大小
printf("Please enter the num :");scanf("%d", &p1->num);printf("Please enter the score :");scanf("%f", &p1->score);head = NULL;     n = 0;    while( 0 != p1->num ){n++;if( 1 == n ){head = p1;                 }else{p2->next = p1;}p2 = p1;p1 = (struct student *)malloc(LEN);printf("\nPlease enter the num :");scanf("%d", &p1->num);printf("Please enter the score :");scanf("%f", &p1->score);}p2->next = NULL;return head;
}void print(struct student *head)
{struct student *p;printf("\nThere are %d records!\n\n", n);p = head;if( NULL != head ){do{printf("学号为 %d 的成绩是: %f\n", p->num, p->score);p = p->next;}while( NULL != p );}
}

转载于:https://www.cnblogs.com/yangyuqing/p/10299548.html

C语言基础学习day10相关推荐

  1. java跳转控制语句有哪些_Java语言基础学习之流程控制语句和跳转控制语句实例分析...

    本文主要向大家介绍了Java语言基础学习之流程控制语句和跳转控制语句实例分析,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助. 一.流程控制语句 在一个程序执行的过程中,各条语句的执行顺 ...

  2. 01_Go语言基础学习_Golang语言特性、环境搭建、第一个Go程序、包

    1. Golang语言特性: 垃圾回收: 1.内存自动回收,再也不需要开发人员管理内存: 2.开发人员专注业务实现,降低了心智负担 : 3.只需要new分配内存,不需要释放 天然并发: 1.从语言层面 ...

  3. 《C语言基础学习笔记》—— 黑马程序员 <Part1-4>

    <C语言基础学习笔记 Part1-4> Part 1 基础介绍 1. 第一个C语言程序:HelloWorld 1.1 编写C语言代码:hello.c 1.2代码分析 1)include 头 ...

  4. 猿来小课web前端介绍html语言基础学习

    HTML是一种超文本标记语言,属于互联网开发技术里面的前端开发技术.同时也是计算机和你所访问的Web站点的沟通语言.当你访问Web服务器所寄存的站点并与其交互时,Web服务器会接收到来自你的浏览器的请 ...

  5. C语言基础学习Day12(完结篇)

    C语言基础学习(Day12) 笔者有话说:         从Day1到Day12跟着笔者每天打卡到的朋友们,恭喜你们超前完成了C语言学习任务!

  6. R语言基础学习记录4:重要函数

    时间: 2018-07-18(学习) 2018-07-22(学习记录) 教程:慕课网 <R语言基础> 讲师:Angelayuan 补充内容: R语言常用函数总结大全.gl()函数 学习内容 ...

  7. Python语言基础学习不错的网站

    最近在学习Python语言,记录一些查到的相关资料,会持续更新..... Python基础学习:廖雪峰的网站 3.x版本:http://www.liaoxuefeng.com/wiki/0014316 ...

  8. JAVA 语言基础学习

    JAVA 语言基础 文章目录 JAVA 语言基础 Java概述 Java语言的诞生 JAVA语言的发展 JAVA语言的特点 什么是跨平台性? 跨平台的实现原理 JDK与JRE JDK的下载和安装 Pa ...

  9. 【吐血整理】Java语言基础学习资料(电子书+视频+视频)

    0.前言 最近决定开始系统学习Java.之前学过Java基础,但太久没用,也有些生疏了,所以重新复习一下JavaSE基础.顺便整理了一些资料分享一下. 出于分享原则,本文内容可直接获取.不过顺便推广, ...

最新文章

  1. asp.net MVC2 初探四
  2. ORACLE 12C安装
  3. php mysql 超时时间_php mysql超时设置方法
  4. 面向对象第七章,内存的管理,面向对象三大特征封装、继承、多态
  5. 【已解决】对JSP页面传值中文乱码的简单解决方法
  6. Android Kotlin Exception处理
  7. html5 打印api,HTML5的API(一)
  8. pandas-数据分析
  9. 2021年茂名市高考成绩查询,2021年茂名高考最高分多少分,历年茂名高考状元
  10. Oracle之例外(异常)
  11. ubuntu 缺少php安装包,ubuntu 16.04 lts安装php环境和composer依赖包管理
  12. java printf与println_浅析Java中print、printf、println的区别
  13. Android学习--广播机制
  14. Google 要进军游戏行业了?!
  15. 华为云计算工程师_华为云计算工程师认证考试
  16. 五、肺癌检测-数据集训练 training.py model.py
  17. 一文详解 TCP/IP 协议
  18. 计算机中丢失 msvcr110.dll 怎么办
  19. Android版本自带游戏,植物大战僵尸自带花园版
  20. logistic逻辑回归公式推导及R语言实现

热门文章

  1. 神经植入物是如何工作的?
  2. VR教育进入新加坡课堂
  3. 正经聊一聊脑机接口的发展现状|脑科学开放日
  4. AI给老照片上色,真的准吗?技术圈和历史圈吵了1000帖
  5. 2020中国年度AI评选结果揭晓!领航企业、商业人物、创新产品……AI落地最佳参考就在这里...
  6. 让一个人从照片中消失,从未如此简单 | AI应用体验
  7. 004_列表list操作
  8. RocketMQ(六):namesrv再探
  9. 一次网站性能排查实录
  10. 内存泄露从入门到精通三部曲之常见原因与用户实践